diff --git a/tlatools/.classpath b/tlatools/.classpath index 5df2c0e31ace0682e9abd3d638adafa4534c24fa..af7fe93b8e8c46dccdd67e1df4ebf45458fdefc9 100644 --- a/tlatools/.classpath +++ b/tlatools/.classpath @@ -1,6 +1,13 @@ <?xml version="1.0" encoding="UTF-8"?> <classpath> - <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"/> + <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-11"> + <attributes> + <attribute name="module" value="true"/> + </attributes> + <accessrules> + <accessrule kind="accessible" pattern="jdk/internal/**"/> + </accessrules> + </classpathentry> <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/> <classpathentry kind="src" path="src"/> <classpathentry excluding="tlc2/tool/WorkerMonitorAspect.aj|tlc2/tool/distributed/TLCServerMonitorAspect.aj|tlc2/tool/distributed/RMIMethodMonitorAspect.aj|tlc2/tool/distributed/RMIMethodMonitorAspect.aj|tlc2/tool/distributed/TLCServerMonitorAspect.aj" kind="src" path="src-aj"/> @@ -8,7 +15,15 @@ <classpathentry kind="src" path="test-long"/> <classpathentry kind="src" path="test-concurrent"/> <classpathentry kind="src" path="test-verify"/> - <classpathentry kind="lib" path="lib/javax.mail.jar"/> + <classpathentry kind="src" path="test-benchmark"/> + <classpathentry kind="lib" path="lib/javax.mail/mailapi-1.6.3.jar"/> <classpathentry kind="lib" path="lib/jpf.jar"/> + <classpathentry kind="lib" path="lib/jmh/jmh-core-1.21.jar"/> + <classpathentry kind="lib" path="lib/jmh/commons-math3-3.2.jar"/> + <classpathentry kind="src" path=".apt_generated"> + <attributes> + <attribute name="optional" value="true"/> + </attributes> + </classpathentry> <classpathentry kind="output" path="class"/> </classpath> diff --git a/tlatools/.factorypath b/tlatools/.factorypath new file mode 100644 index 0000000000000000000000000000000000000000..82707435cb204e21ae2b472b2d0493bf1c86acd4 --- /dev/null +++ b/tlatools/.factorypath @@ -0,0 +1,4 @@ +<factorypath> + <factorypathentry kind="WKSPJAR" id="/tlatools/lib/jmh/jmh-core-1.21.jar" enabled="true" runInBatchMode="false"/> + <factorypathentry kind="WKSPJAR" id="/tlatools/lib/jmh/jmh-generator-annprocess-1.21.jar" enabled="true" runInBatchMode="false"/> +</factorypath> diff --git a/tlatools/.project b/tlatools/.project index 7d018f8bd294b7baccfeb0e513d92bce4e08cc5a..71aaca256bda77b7ff4b51738798b65ed3343e0c 100644 --- a/tlatools/.project +++ b/tlatools/.project @@ -20,8 +20,14 @@ <arguments> </arguments> </buildCommand> + <buildCommand> + <name>org.eclipse.m2e.core.maven2Builder</name> + <arguments> + </arguments> + </buildCommand> </buildSpec> <natures> + <nature>org.eclipse.m2e.core.maven2Nature</nature> <nature>org.eclipse.jdt.core.javanature</nature> <nature>org.eclipse.pde.PluginNature</nature> </natures> diff --git a/tlatools/.settings/org.eclipse.jdt.apt.core.prefs b/tlatools/.settings/org.eclipse.jdt.apt.core.prefs new file mode 100644 index 0000000000000000000000000000000000000000..fa6bcfb3fdb3a5ff5ccf658c79e656a02d8dbf61 --- /dev/null +++ b/tlatools/.settings/org.eclipse.jdt.apt.core.prefs @@ -0,0 +1,5 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.apt.aptEnabled=true +org.eclipse.jdt.apt.genSrcDir=.apt_generated +org.eclipse.jdt.apt.genTestSrcDir=.apt_generated_tests +org.eclipse.jdt.apt.reconcileEnabled=true diff --git a/tlatools/.settings/org.eclipse.jdt.core.prefs b/tlatools/.settings/org.eclipse.jdt.core.prefs index 812fbdb54710711e928854c8b1a4473c53289b0c..b17a04a86a31280939bd38107722182912f58e5d 100644 --- a/tlatools/.settings/org.eclipse.jdt.core.prefs +++ b/tlatools/.settings/org.eclipse.jdt.core.prefs @@ -6,9 +6,10 @@ org.eclipse.jdt.core.compiler.annotation.nonnullbydefault=org.eclipse.jdt.annota org.eclipse.jdt.core.compiler.annotation.nullable=org.eclipse.jdt.annotation.Nullable org.eclipse.jdt.core.compiler.annotation.nullanalysis=disabled org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled -org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6 +org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve -org.eclipse.jdt.core.compiler.compliance=1.6 +org.eclipse.jdt.core.compiler.compliance=11 org.eclipse.jdt.core.compiler.debug.lineNumber=generate org.eclipse.jdt.core.compiler.debug.localVariable=generate org.eclipse.jdt.core.compiler.debug.sourceFile=generate @@ -22,6 +23,7 @@ org.eclipse.jdt.core.compiler.problem.deprecationInDeprecatedCode=disabled org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=disabled org.eclipse.jdt.core.compiler.problem.discouragedReference=warning org.eclipse.jdt.core.compiler.problem.emptyStatement=ignore +org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled org.eclipse.jdt.core.compiler.problem.enumIdentifier=error org.eclipse.jdt.core.compiler.problem.explicitlyClosedAutoCloseable=ignore org.eclipse.jdt.core.compiler.problem.fallthroughCase=ignore @@ -65,6 +67,7 @@ org.eclipse.jdt.core.compiler.problem.redundantSpecificationOfTypeArguments=igno org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=ignore org.eclipse.jdt.core.compiler.problem.reportMethodCanBePotentiallyStatic=ignore org.eclipse.jdt.core.compiler.problem.reportMethodCanBeStatic=ignore +org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=warning org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=warning org.eclipse.jdt.core.compiler.problem.suppressOptionalErrors=disabled @@ -96,4 +99,6 @@ org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=warning org.eclipse.jdt.core.compiler.problem.unusedTypeParameter=ignore org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning -org.eclipse.jdt.core.compiler.source=1.6 +org.eclipse.jdt.core.compiler.processAnnotations=enabled +org.eclipse.jdt.core.compiler.release=enabled +org.eclipse.jdt.core.compiler.source=11 diff --git a/tlatools/META-INF/MANIFEST.MF b/tlatools/META-INF/MANIFEST.MF index 92192777b4ce1d8187176261d0266bb71ba4472b..74de04aeca0d4d8ec85c1c5faec1483f3941b356 100644 --- a/tlatools/META-INF/MANIFEST.MF +++ b/tlatools/META-INF/MANIFEST.MF @@ -17,16 +17,19 @@ Export-Package: pcal, tla2sany.utilities, tla2tex, tlc2, + tlc2.model, tlc2.module, tlc2.output, tlc2.pprint, tlc2.tool, + tlc2.tool.coverage, tlc2.tool.distributed, tlc2.tool.distributed.fp, tlc2.tool.distributed.management, tlc2.tool.distributed.selector, tlc2.tool.fp, tlc2.tool.fp.management, + tlc2.tool.impl, tlc2.tool.liveness, tlc2.tool.management, tlc2.tool.other, @@ -35,11 +38,12 @@ Export-Package: pcal, tlc2.value, util Bundle-Vendor: Leslie Lamport, Markus Alexander Kuppe -Bundle-RequiredExecutionEnvironment: JavaSE-1.6 +Bundle-RequiredExecutionEnvironment: JavaSE-11, + JavaSE-1.8 Eclipse-BundleShape: dir Require-Bundle: org.aspectj.runtime;bundle-version="1.6.0";resolution:=optional;x-installation:=greedy, - org.junit;bundle-version="3.8.0";resolution:=optional;x-installation:=greedy, + org.junit;bundle-version="4.12.0";resolution:=optional;x-installation:=greedy, javax.mail;bundle-version="1.4.0";resolution:=optional, - javax.activation;bundle-version="1.1.0";resolution:=optional, org.easymock;bundle-version="2.4.0";resolution:=optional Bundle-ClassPath: . +Automatic-Module-Name: org.lamport.tlatools diff --git a/tlatools/build.properties b/tlatools/build.properties index 40edd6e44e2d13cee0b67d4d03ec4eadef80c7d4..06d8afaaaa5f8d1144de1907d762b14f8892112c 100644 --- a/tlatools/build.properties +++ b/tlatools/build.properties @@ -1,9 +1,5 @@ source.. = src/,\ - src-aj/,\ - test/ + src-aj/ bin.includes = META-INF/,\ . -output.. = bin/ -jre.compilation.profile = J2SE-1.5 -javacSource = 1.5 -javacTarget = 1.5 +output.. = class/ diff --git a/tlatools/customBuild.xml b/tlatools/customBuild.xml index f062665e9d0c452b5a1d712264ce952e6f3f93f1..88a386dd0d204a2c4827416405d524f933acd138 100644 --- a/tlatools/customBuild.xml +++ b/tlatools/customBuild.xml @@ -24,26 +24,55 @@ <taskdef resource="org/aspectj/tools/ant/taskdefs/aspectjTaskdefs.properties"> <classpath> - <pathelement location="lib/aspectjtools-1.8.5.jar" /> + <pathelement location="lib/aspectjtools-1.9.2.jar" /> </classpath> </taskdef> - + <condition property="test.skip"> <istrue value="${maven.test.skip}"/> </condition> - <target name="info" description="Prints information"> - <echo message="ANT_HOME : ${env.ANT_HOME}" /> - <echo message="Ant Version : ${ant.version}" /> - <echo message="Java Version : ${ant.java.version}" /> + <condition property="test.halt"> + <istrue value="${maven.test.halt}"/> + </condition> + + <!-- https://github.com/alx3apps/jgit-buildnumber --> + <target name="git-revision"> + <taskdef name="jgit-buildnumber" classname="ru.concerteza.util.buildnumber.JGitBuildNumberAntTask"> + <classpath> + <pathelement location="lib/org.eclipse.jgit-2.3.1.201302201838-r.jar"/> + <pathelement location="lib/jgit-buildnumber-ant-task-1.2.10.jar" /> + </classpath> + </taskdef> + <jgit-buildnumber/> + </target> + + <target name="info" description="Prints information" depends="git-revision"> + <echo message="ANT_HOME : ${env.ANT_HOME}" /> + <echo message="Ant Version : ${ant.version}" /> + <echo message="Java Version : ${ant.java.version}" /> <echo message="Skipping Tests : ${test.skip}" /> - <echo message="With AspectJ: ${withaj}" /> + <echo message="With AspectJ : ${withaj}" /> + <echo message="Git BuildNumber: ${git.branch}_${git.tag}_${git.shortRevision}" /> </target> <target name="default" depends="info" description="Default"> <antcall target="compile" inheritall="true" inheritrefs="true" /> <antcall target="compile-aj" inheritall="true" inheritrefs="true" /> + <antcall target="compile-test" inheritall="true" inheritrefs="true" /> <antcall target="test" inheritall="true" inheritrefs="true" /> + <antcall target="git-revision" inheritall="true" inheritrefs="true" /> + <antcall target="dist" inheritall="true" inheritrefs="true" /> + <antcall target="test-dist" inheritall="true" inheritrefs="true" /> + </target> + + <!-- Similar to default except it skips test and only runs test-dist. This is called from pom.xml --> + <target name="default-maven" depends="info" description="Default"> + <antcall target="compile" inheritall="true" inheritrefs="true" /> + <antcall target="compile-aj" inheritall="true" inheritrefs="true" /> + <antcall target="compile-test" inheritall="true" inheritrefs="true" /> + <!-- <antcall target="test" inheritall="true" inheritrefs="true" /> --> + <antcall target="git-revision" inheritall="true" inheritrefs="true" /> <antcall target="dist" inheritall="true" inheritrefs="true" /> <antcall target="test-dist" inheritall="true" inheritrefs="true" /> </target> @@ -53,16 +82,46 @@ <antcall target="dist-mixed-zip" inheritall="true" inheritrefs="true" /> </target> + <!-- Compiles and runs the short-running tests. --> + <target name="shorttests" depends="info" description="Run tests"> + <antcall target="default" inheritall="true" inheritrefs="true" /> + <antcall target="test-verify" inheritall="true" inheritrefs="true" /> + </target> - - <target name="clean" description="Cleans the compilation output directory" unless="noclean"> + <!-- Compiles and runs *only* the long-running tests. Not the ones. --> + <target name="longtests" depends="info" description="Run long-running tests"> + <antcall target="compile" inheritall="true" inheritrefs="true" /> + <antcall target="compile-aj" inheritall="true" inheritrefs="true"> + <!-- long tests require AspjectJ --> + <param name="withaj" value="true"/> + </antcall> + <antcall target="compile-test" inheritall="true" inheritrefs="true" /> + <antcall target="dist" inheritall="true" inheritrefs="true" /> + <antcall target="test-dist-long" inheritall="true" inheritrefs="true" /> + </target> + + <!-- ===========================================================--> + <!-- Don't call any of the targets below, use the umbrella --> + <!-- targets above which correctly call a target's dependencies --> + <!-- ===========================================================--> + + <!-- Cleans the compilation output directory --> + <target name="clean" unless="noclean"> <delete includeemptydirs="true" failonerror="false"> <fileset dir="${class.dir}" includes="*/**" /> <fileset dir="${test.class.dir}" includes="*/**" /> </delete> + <!-- Delete leftovers from previous junit invocation --> + <delete includeemptydirs="true" failonerror="false"> + <!-- Delete ALL states folders in the tlatools/ directory --> + <!-- A stale states/ folder in src/model/ once caused the --> + <!-- Maven build to fail with an OOM in the pack200 task. --> + <fileset dir="${basedir}" includes="**/states/**" /> + </delete> </target> - <target name="compile" depends="clean" description="Compile"> + <!-- Compiles the TLA+ tools code --> + <target name="compile" depends="clean"> <echo> ================================================================ = The following warnings about sun.misc.Unsafe can be ignored. = @@ -73,11 +132,11 @@ </echo> <!-- compile --> <mkdir dir="${class.dir}" /> - <javac includeantruntime="false" srcdir="${src.dir}" destdir="${class.dir}" debug="true" verbose="false" source="1.5" target="1.5"> + <javac includeantruntime="false" srcdir="${src.dir}" destdir="${class.dir}" debug="true" verbose="false" source="1.8" target="1.8"> <!-- compilerarg value="-Xlint:deprecation"/--> <classpath refid="project.classpath" /> <classpath> - <pathelement location="lib/javax.mail.jar" /> + <pathelement location="lib/javax.mail/mailapi-1.6.3.jar" /> </classpath> </javac> @@ -88,10 +147,15 @@ <exclude name="**/*.java" /> <exclude name="**/*.~*" /> <exclude name="**/*##*" /> + <exclude name="**/*.09-09-07" /> + <exclude name="**/*.09-07-02" /> + <exclude name="**/*.11-02-10" /> + <exclude name="**/*.jpg" /> </fileset> </copy> </target> + <!-- Compiles AspectJ auxiliary code --> <target name="compile-aj" if="withaj"> <echo> ==================================================================== @@ -101,11 +165,15 @@ ==================================================================== </echo> <!-- compile aspectj related class files --> - <iajc destdir="${class.dir}" sourceRoots="${src-aj.dir}" debug="true"> + <iajc destdir="${class.dir}" debug="true" verbose="true" source="1.8" target="1.8"> + <sourceroots> + <!-- Here we implicitly decide to use AspectJ Load-Time weaving by excludign ${src.dir} as a pathelement. Adding ${src.dir} results in aspects being woven at compile-time as part of the ant build. --> + <pathelement location="${src-aj.dir}"/> + </sourceroots> <classpath refid="project.classpath" /> <classpath> - <pathelement location="lib/aspectjrt-1.8.5.jar" /> - <pathelement location="lib/aspectjtools-1.8.5.jar" /> + <pathelement location="lib/aspectjrt-1.9.2.jar" /> + <pathelement location="lib/aspectjtools-1.9.2.jar" /> <pathelement path="${class.dir}" /> </classpath> </iajc> @@ -122,32 +190,91 @@ <!-- These files are requried for load time weaving and during runtime --> <copy todir="${class.dir}/lib"> <fileset dir="lib/"> - <include name="aspectjrt-1.8.5.jar" /> - <include name="aspectjweaver-1.8.5.jar" /> + <include name="aspectjrt-1.9.2.jar" /> + <include name="aspectjweaver-1.9.2.jar" /> </fileset> </copy> </target> - <target name="dist" description="Build a distribution"> + <!-- Creates a distribution out of compiled code --> + <target name="dist"> <!-- Extract javax.mail jar into class directory to be packaged into tla2tools.jar --> <!-- javax.mail is used by the MailSender functionality, that mails the output --> <!-- of TLC to a given email address. --> <!-- The javax.mail dependency as well as javax.activation is also depended upon --> <!-- by the tla2tools OSGi bundle and is expected to be the same version. --> - <unzip src="lib/javax.mail.jar" dest="${class.dir}"/> + <unzip src="lib/javax.mail/mailapi-1.6.3.jar" dest="${class.dir}"> + <patternset> + <include name="**/*.class"/> + <include name="META-INF/LICENSE.txt"/> + <include name="META-INF/mailcap"/> + <include name="META-INF/javamail.charset.map"/> + <exclude name="javax/mail/search/**"/> + </patternset> + </unzip> + <unzip src="lib/javax.mail/smtp-1.6.3.jar" dest="${class.dir}"> + <patternset> + <include name="**/*.class"/> + </patternset> + </unzip> + <unzip src="lib/javax.mail/javax.activation_1.1.0.v201211130549.jar" dest="${class.dir}"> + <patternset> + <include name="**/*.class"/> + <exclude name="org/**"/> + </patternset> + </unzip> + <touch file="${class.dir}/META-INF/javamail.default.address.map"/> <!-- Extract javax.mail to maven classes folder. This script is triggered by --> <!-- maven. However, maven compiles the code again to target/classes from --> <!-- where it creates the plugin --> - <unzip src="lib/javax.mail.jar" dest="target/classes"/> - - <!-- TODO: add javax.activation to lib/ and extract it here too. Otherwise we have a --> - <!-- runtime dependency on Java 6. --> + <unzip src="lib/javax.mail/mailapi-1.6.3.jar" dest="target/classes"> + <patternset> + <include name="**/*.class"/> + <include name="META-INF/LICENSE.txt"/> + <include name="META-INF/mailcap"/> + <include name="META-INF/javamail.charset.map"/> + <exclude name="javax/mail/search/**"/> + </patternset> + </unzip> + <unzip src="lib/javax.mail/smtp-1.6.3.jar" dest="target/classes"> + <patternset> + <include name="**/*.class"/> + </patternset> + </unzip> + <unzip src="lib/javax.mail/javax.activation_1.1.0.v201211130549.jar" dest="target/classes"> + <patternset> + <include name="**/*.class"/> + <exclude name="org/**"/> + </patternset> + </unzip> + <touch file="target/classes/META-INF/javamail.default.address.map"/> + <!-- create a JAR file for the users --> <mkdir dir="${dist.dir}" /> - <jar destfile="${dist.file}"> - <fileset dir="${class.dir}" includes="**/*" /> + <jar destfile="${dist.file}" level="9"> + <fileset dir="${class.dir}" includes="**/*" excludes=" + **/*.jpg, + README.txt, + heapstats.jfc, + jpf.properties, + tlc2/tool/fp/*.tla, + tlc2/value/*.tla, + pcal/*.tla, + META-INF/maven/, + src/tla2sany/parser/ParseException.09-09-07, + src/tla2sany/parser/TLAplusParser.09-07-02, + src/tla2sany/parser/TLAplusParser.11-02-10, + src/tla2sany/parser/TLAplusParserConstants.09-07-02, + src/tla2sany/parser/TLAplusParserConstants.09-09-07, + src/tla2sany/parser/TLAplusParserConstants.11-02-10, + src/tla2sany/parser/TLAplusParserTokenManager.09-07-02, + src/tla2sany/parser/TLAplusParserTokenManager.09-09-07, + src/tla2sany/parser/TLAplusParserTokenManager.11-02-10, + src/tla2sany/parser/Token.09-09-07, + src/tla2sany/parser/TokenMgrError.09-09-07"/> + <fileset dir="${doc.dir}" includes="License.txt"/> <manifest> <attribute name="Built-By" value="${user.name}" /> <attribute name="Build-Tag" value="${env.BUILD_TAG}" /> @@ -155,26 +282,45 @@ <attribute name="Implementation-Title" value="TLA+ Tools" /> <attribute name="Implementation-Version" value="${version} ${TODAY}" /> <attribute name="Implementation-Vendor" value="Microsoft Corp." /> + <!-- The jar files contains many main classes (SANY, TEX, pcal, ...) --> + <!-- but lets consider TLC the one users primarily use. --> + <attribute name="Main-class" value="tlc2.TLC" /> + <attribute name="Class-Path" value="CommunityModules.jar" /> + <!-- Git revision --> + <attribute name="X-Git-Branch" value="${git.branch}" /> + <attribute name="X-Git-Tag" value="${git.tag}" /> + <attribute name="X-Git-Revision" value="${git.revision}" /> + <attribute name="X-Git-ShortRevision" value="${git.shortRevision}" /> + <attribute name="X-Git-BuildNumber" value="${git.branch}_${git.tag}_${git.shortRevision}" /> + <attribute name="X-Git-Commits-Count" value="${git.commitsCount}" /> <!-- App-Name and Permissions is required by Java Webstart used by distributed TLC --> <!-- Depending on security level, the user will see a warning otherwise. --> <!-- http://docs.oracle.com/javase/7/docs/technotes/guides/jweb/security/manifest.html --> <attribute name="Application-Name" value="TLC" /> - <attribute name="permissions" value="all-permissions" /> <!-- won't work with 'sandbox' as it writes files --> + <attribute name="permissions" value="all-permissions" /> + <!-- won't work with 'sandbox' as it writes files --> </manifest> </jar> </target> - <target name="test" description="Executes accompining unit tests" unless="test.skip"> + <!-- Compiles the TLA+ tools *test* code --> + <target name="compile-test" unless="test.skip"> <!-- compile unit tests --> <mkdir dir="${test.class.dir}" /> - <javac includeantruntime="false" srcdir="${test.dir}" destdir="${test.class.dir}" debug="true" verbose="false"> + <javac includeantruntime="false" srcdir="${test.dir}" destdir="${test.class.dir}" debug="true" verbose="false" source="1.8" target="1.8"> <classpath refid="project.classpath" /> <classpath> - <pathelement location="lib/junit-4.8.2.jar" /> + <pathelement location="lib/junit-4.12.jar" /> + <pathelement location="lib/hamcrest-core-1.3.jar" /> <pathelement location="lib/easymock-3.3.1.jar" /> <pathelement path="${class.dir}" /> </classpath> </javac> + <!-- Include auxilliary files when compiling classes. --> + <copy todir="${test.class.dir}"> + <fileset dir="${test.dir}" includes="**/*.dot"/> + <fileset dir="${test.dir}" includes="**/*.dump"/> + </copy> <!-- copy class.dir to path with whitespace --> <!-- this is required by some tests to make sense --> <!-- even throw a non digit in --> @@ -182,107 +328,303 @@ <copy todir="${ws.class.dir}"> <fileset dir="${class.dir}" /> </copy> - <!-- run junit tests --> - <mkdir dir="${test.reports}" /> - <jacoco:coverage destfile="target/code-coverage.exec"> - <junit printsummary="yes" haltonfailure="no" haltonerror="no" forkmode="perTest" fork="yes"> - <!-- Uncomment to open a debug port in each forked VM to remote attach your Eclipse at port 1044. - <jvmarg value="-Xdebug" /> - <jvmarg value="-Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=1044" /> - --> - <classpath refid="project.classpath" /> - <classpath> - <pathelement location="lib/junit-4.8.2.jar" /> - <pathelement location="lib/cglib-nodep-3.1.jar" /> - <pathelement location="lib/objenesis-2.1.jar" /> - <pathelement location="lib/easymock-3.3.1.jar" /> - <pathelement path="${ws.class.dir}" /> - <pathelement path="${test.class.dir}" /> - </classpath> - <formatter type="xml" /> - - <!-- Pass the base path of the tlatools project to unit tests in case they need it to locate TLA+ specs or configs --> - <sysproperty key="basedir" value="${basedir}/"/> - <batchtest fork="yes" todir="${test.reports}"> - <fileset dir="${test.dir}"> - <include name="**/*Test*.java" /> - <exclude name="**/ModelCheckerTestCase.java" /> - <exclude name="**/SuccessfulSimulationTestCase.java" /> - <exclude name="**/AbstractExampleTestCase.java" /> - <exclude name="**/TestMPRecorder.java" /> - <exclude name="**/Abstract*Test.java" /> - <exclude name="**/TestDriver.java" /> - <exclude name="**/TestDriver2.java" /> - <exclude name="**/AllTests.java" /> - </fileset> - </batchtest> - </junit> - </jacoco:coverage> - - <!-- remove copied class.dir --> - <delete dir="${ws.class.dir}" deleteonexit="true"/> </target> - <target name="test-dist" description="Executes accompining unit tests on jar file" unless="test.skip"> - <!-- compile unit tests --> - <mkdir dir="${test.class.dir}" /> - <javac includeantruntime="false" srcdir="${test.dir}" destdir="${test.class.dir}" debug="true" verbose="false"> - <classpath refid="project.classpath" /> - <classpath> - <pathelement location="lib/junit-4.8.2.jar" /> - <pathelement location="lib/easymock-3.3.1.jar" /> - <pathelement path="${class.dir}" /> - </classpath> - </javac> - <!-- copy class.dir to path with whitespace --> - <!-- this is required by some tests to make sense --> - <!-- even throw a "+" and whitespace into the mix --> - <property name="ws.class.dir" value="TLA+ Tools" /> - <copy todir="${ws.class.dir}"> - <fileset dir="${class.dir}" /> - </copy> - <!-- run junit tests on tlatools.jar --> - <mkdir dir="${test.reports}/onJar" /> - <junit printsummary="yes" haltonfailure="no" haltonerror="no" forkmode="perTest" fork="yes"> + <!-- Executes accompining unit tests --> + <target name="test" unless="test.skip"> + <!-- run junit tests --> + <mkdir dir="${test.reports}" /> + <!-- forkmode used to be "perBatch" on Java 1.8 using the util.IsolatedTestCaseRunner. + This concept broken with Java11 for unknown reasons which is why forkmode has been + changed to perTest to run each test in a separate VM. This is slower compared to + running all tests in a single VM. --> + <junit printsummary="yes" haltonfailure="${test.halt}" showoutput="no" haltonerror="${test.halt}" forkmode="perTest" fork="yes"> + <!-- enable all assertions --> + <jvmarg value="-ea"/> + <jvmarg value="-XX:MaxDirectMemorySize=512k"/> + <jvmarg value="-XX:+UseParallelGC"/> + <!-- Uncomment to open a debug port in each forked VM to remote attach your Eclipse at port 1044. + <jvmarg value="-Xdebug" /> + <jvmarg value="-Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=1044" /> + --> <classpath refid="project.classpath" /> <classpath> - <pathelement location="lib/junit-4.8.2.jar" /> + <pathelement location="lib/junit-4.12.jar" /> + <pathelement location="lib/hamcrest-core-1.3.jar" /> <pathelement location="lib/cglib-nodep-3.1.jar" /> <pathelement location="lib/objenesis-2.1.jar" /> <pathelement location="lib/easymock-3.3.1.jar" /> - <pathelement location="${dist.file}" /> + <pathelement location="lib/javax.mail/mailapi-1.6.3.jar" /> + <pathelement location="test-model/UserModuleOverrideFromJar.jar" /> + <pathelement path="${class.dir}" /> <pathelement path="${test.class.dir}" /> + <pathelement path="test-model/" /> </classpath> - <formatter type="xml" /> - <sysproperty key="basedir" value="${basedir}/"/> - <batchtest fork="yes" todir="${test.reports}/onJar"> + <!-- Pass the base path of the tlatools project to unit tests in case they need it to locate TLA+ specs or configs --> + <sysproperty key="basedir" value="${basedir}/"/> + <sysproperty key="tlc2.tool.fp.FPSet.impl" value="tlc2.tool.fp.OffHeapDiskFPSet"/> + <sysproperty key="util.FileUtil.milliseconds" value="true"/> + <sysproperty key="tlc2.tool.distributed.TLCWorker.threadCount" value="4"/> + <!-- The tests below can be tricked into running in a single VM by fiddling with the classloader + to reload and thus initialize all classes for each tests. --> + <batchtest fork="yes" todir="${test.reports}"> <fileset dir="${test.dir}"> <include name="**/*Test*.java" /> - <exclude name="**/ModelCheckerTestCase.java" /> + + <!-- Produce bogus crash with Quarantine runner. --> + <exclude name="tlc2/tool/distributed/TLCSetTest.java" /> + <exclude name="tlc2/tool/distributed/DistributedDoInitFunctorEvalExceptionTest.java" /> + <!-- Just fail because of Quarantine runner. --> + <exclude name="tlc2/TLCTest.java"/> + <exclude name="tlc2/tool/fp/OffHeapDiskFPSetTest.java"/> + <exclude name="tlc2/tool/EvaluatingValueTest.java" /> + <exclude name="tlc2/tool/UserModuleOverrideTest.java" /> + <exclude name="tlc2/tool/UserModuleOverrideFromJarTest.java" /> + <exclude name="tlc2/tool/UserModuleOverrideAnnotationTest.java" /> + + <exclude name="**/PCalTest.java" /> + <exclude name="**/CommonTestCase.java" /> + <exclude name="**/ModelCheckerTestCase.java" /> + <exclude name="**/PCalModelCheckerTestCase.java" /> + <exclude name="**/SuiteTestCase.java" /> + <exclude name="**/SuiteETestCase.java" /> + <exclude name="**/DistributedTLCTestCase.java" /> + <exclude name="**/TLCServerTestCase.java" /> <exclude name="**/SuccessfulSimulationTestCase.java" /> <exclude name="**/AbstractExampleTestCase.java" /> - <exclude name="**/TestMPRecorder.java" /> + <exclude name="**/AbstractCoverageTest.java" /> + <exclude name="**/InitEvalOrderTest.java" /> + <exclude name="**/TestMPRecorder.java" /> + <exclude name="**/TestPrintStream.java" /> + <exclude name="**/BidirectionalTransitions1BTest.java" /> + <exclude name="**/BidirectionalTransitions2CTest.java" /> <exclude name="**/Abstract*Test.java" /> <exclude name="**/TestDriver.java" /> <exclude name="**/TestDriver2.java" /> <exclude name="**/AllTests.java" /> + <exclude name="util/IsolatedTestCaseRunner.java"/> + <!-- Known test failures --> + <exclude name="**/pcal/StackTestTest.java" /> + <exclude name="**/pcal/TestPCandStackTest.java" /> + <!-- These take too long --> + <exclude name="**/MacroQuicksortTest.java" /> + <exclude name="**/MacroRealQuicksortTest.java" /> + <exclude name="**/PcalPaxosTest.java" /> + <exclude name="**/DetlefsTest.java" /> + <exclude name="**/StarkMutexTest.java" /> + <exclude name="**/SimpleMultiProcTest.java" /> + </fileset> + </batchtest> + <batchtest fork="yes" todir="${test.reports}"> + <fileset dir="${test.dir}"> + <include name="tlc2/tool/EvaluatingValueTest.java" /> + </fileset> + </batchtest> + <batchtest fork="yes" todir="${test.reports}"> + <fileset dir="${test.dir}"> + <include name="tlc2/tool/UserModuleOverrideAnnotationTest.java" /> + </fileset> + </batchtest> + <batchtest fork="yes" todir="${test.reports}"> + <fileset dir="${test.dir}"> + <include name="tlc2/tool/UserModuleOverrideFromJarTest.java" /> + </fileset> + </batchtest> + <batchtest fork="yes" todir="${test.reports}"> + <fileset dir="${test.dir}"> + <include name="tlc2/tool/UserModuleOverrideTest.java" /> + </fileset> + </batchtest> + <batchtest fork="yes" todir="${test.reports}"> + <fileset dir="${test.dir}"> + <include name="tlc2/tool/distributed/TLCSetTest.java" /> </fileset> </batchtest> + <batchtest fork="yes" todir="${test.reports}"> + <fileset dir="${test.dir}"> + <include name="tlc2/tool/distributed/DistributedDoInitFunctorEvalExceptionTest.java" /> + </fileset> + </batchtest> + <batchtest fork="yes" todir="${test.reports}"> + <fileset dir="${test.dir}"> + <include name="tlc2/TLCTest.java"/> + <include name="tlc2/tool/fp/OffHeapDiskFPSetTest.java"/> + </fileset> + </batchtest> + </junit> + + <!-- remove copied class.dir --> + <delete dir="${ws.class.dir}" deleteonexit="true"/> + </target> + + <!-- Executes a single unit test. --> + <target name="test-single" unless="test.skip"> + <!-- run junit test --> + <junit printsummary="yes" haltonfailure="true" showoutput="no" haltonerror="true" forkmode="perTest" fork="yes"> + <!-- enable all assertions --> + <jvmarg value="-ea"/> + <jvmarg value="-XX:MaxDirectMemorySize=512k"/> + <jvmarg value="-XX:+UseParallelGC"/> + <!-- Uncomment to open a debug port in each forked VM to remote attach your Eclipse at port 1044. + <jvmarg value="-Xdebug" /> + <jvmarg value="-Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=1044" /> + --> + <classpath refid="project.classpath" /> + <classpath> + <pathelement location="lib/junit-4.12.jar" /> + <pathelement location="lib/hamcrest-core-1.3.jar" /> + <pathelement location="lib/cglib-nodep-3.1.jar" /> + <pathelement location="lib/objenesis-2.1.jar" /> + <pathelement location="lib/easymock-3.3.1.jar" /> + <pathelement location="lib/javax.mail/mailapi-1.6.3.jar" /> + <pathelement location="test-model/UserModuleOverrideFromJar.jar" /> + <pathelement path="${class.dir}" /> + <pathelement path="${test.class.dir}" /> + <pathelement path="${test.class.dir}" /> + <pathelement path="test-model/" /> + </classpath> + + <!-- Pass the base path of the tlatools project to unit tests in case they need it to locate TLA+ specs or configs --> + <sysproperty key="basedir" value="${basedir}/"/> + <sysproperty key="tlc2.tool.fp.FPSet.impl" value="tlc2.tool.fp.OffHeapDiskFPSet"/> + <sysproperty key="util.FileUtil.milliseconds" value="true"/> + <sysproperty key="tlc2.tool.distributed.TLCWorker.threadCount" value="4"/> + + <!-- Print all output of the test to the screen. --> + <formatter type="plain" usefile="false" /> + <!-- Run a single test case specified by its class name, by setting the property 'test.testcase'. --> + <test name="${test.testcase}" skipNonTests="true"/> </junit> <!-- remove copied class.dir --> <delete dir="${ws.class.dir}" deleteonexit="true"/> </target> - <target name="test-dist-long" description="Executes accompining long-running unit tests on jar file" unless="test.skip"> + <!-- Executes accompining unit tests on jar file --> + <target name="test-dist" unless="test.skip"> + <!-- run junit tests on tlatools.jar --> + <mkdir dir="${test.reports}/onJar" /> + <jacoco:coverage destfile="target/code-coverage.exec" includes="pcal.*:tla2sany.*:tla2tex.*:tlc2.*:util.*:org.lamport.*" excludes="com.sun.*:javax.*:**/Tests.*:**/*Test*"> + <junit printsummary="yes" haltonfailure="${test.halt}" haltonerror="${test.halt}" forkmode="perTest" fork="yes"> + <!-- enable all assertions --> + <jvmarg value="-ea"/> + <jvmarg value="-XX:MaxDirectMemorySize=512k"/> + <jvmarg value="-XX:+UseParallelGC"/> + <classpath refid="project.classpath" /> + <classpath> + <pathelement location="lib/junit-4.12.jar" /> + <pathelement location="lib/hamcrest-core-1.3.jar" /> + <pathelement location="lib/cglib-nodep-3.1.jar" /> + <pathelement location="lib/objenesis-2.1.jar" /> + <pathelement location="lib/easymock-3.3.1.jar" /> + <pathelement location="${dist.file}" /> + <pathelement path="${test.class.dir}" /> + <pathelement path="test-model/" /> + <pathelement location="test-model/UserModuleOverrideFromJar.jar" /> + </classpath> + + <formatter type="xml" /> + <sysproperty key="basedir" value="${basedir}/"/> + <sysproperty key="util.FileUtil.milliseconds" value="true"/> + <sysproperty key="tlc2.tool.fp.FPSet.impl" value="tlc2.tool.fp.OffHeapDiskFPSet"/> + <sysproperty key="tlc2.tool.distributed.TLCWorker.threadCount" value="4"/> + + <batchtest fork="yes" todir="${test.reports}/onJar"> + <fileset dir="${test.dir}"> + <include name="**/*Test*.java" /> + + <!-- Produce bogus crash with Quarantine runner. --> + <exclude name="tlc2/tool/distributed/TLCSetTest.java" /> + <exclude name="tlc2/tool/distributed/DistributedDoInitFunctorEvalExceptionTest.java" /> + <!-- Just fail because of Quarantine runner. --> + <exclude name="tlc2/TLCTest.java"/> + <exclude name="tlc2/tool/fp/OffHeapDiskFPSetTest.java"/> + <exclude name="tlc2/tool/UserModuleOverrideTest.java" /> + <exclude name="tlc2/tool/UserModuleOverrideFromJarTest.java" /> + <exclude name="tlc2/tool/UserModuleOverrideAnnotationTest.java" /> + + <exclude name="**/PCalTest.java" /> + <exclude name="**/CommonTestCase.java" /> + <exclude name="**/ModelCheckerTestCase.java" /> + <exclude name="**/PCalModelCheckerTestCase.java" /> + <exclude name="**/SuiteTestCase.java" /> + <exclude name="**/SuiteETestCase.java" /> + <exclude name="**/DistributedTLCTestCase.java" /> + <exclude name="**/TLCServerTestCase.java" /> + <exclude name="**/SuccessfulSimulationTestCase.java" /> + <exclude name="**/AbstractExampleTestCase.java" /> + <exclude name="**/AbstractCoverageTest.java" /> + <exclude name="**/InitEvalOrderTest.java" /> + <exclude name="**/BidirectionalTransitions1BTest.java" /> + <exclude name="**/BidirectionalTransitions2CTest.java" /> + <exclude name="**/TestMPRecorder.java" /> + <exclude name="**/TestPrintStream.java" /> + <exclude name="**/Abstract*Test.java" /> + <exclude name="**/TestDriver.java" /> + <exclude name="**/TestDriver2.java" /> + <exclude name="**/AllTests.java" /> + <exclude name="util/IsolatedTestCaseRunner.java"/> + <!-- Known test failures --> + <exclude name="**/pcal/StackTestTest.java" /> + <exclude name="**/pcal/TestPCandStackTest.java" /> + <!-- These take too long --> + <exclude name="**/MacroQuicksortTest.java" /> + <exclude name="**/MacroRealQuicksortTest.java" /> + <exclude name="**/PcalPaxosTest.java" /> + <exclude name="**/DetlefsTest.java" /> + <exclude name="**/StarkMutexTest.java" /> + <exclude name="**/SimpleMultiProcTest.java" /> + </fileset> + </batchtest> + <batchtest fork="yes" todir="${test.reports}"> + <fileset dir="${test.dir}"> + <include name="tlc2/tool/UserModuleOverrideAnnotationTest.java" /> + </fileset> + </batchtest> + <batchtest fork="yes" todir="${test.reports}"> + <fileset dir="${test.dir}"> + <include name="tlc2/tool/UserModuleOverrideFromJarTest.java" /> + </fileset> + </batchtest> + <batchtest fork="yes" todir="${test.reports}/onJar"> + <fileset dir="${test.dir}"> + <include name="tlc2/tool/UserModuleOverrideTest.java" /> + </fileset> + </batchtest> + <batchtest fork="yes" todir="${test.reports}/onJar"> + <fileset dir="${test.dir}"> + <include name="tlc2/tool/distributed/TLCSetTest.java" /> + </fileset> + </batchtest> + <batchtest fork="yes" todir="${test.reports}/onJar"> + <fileset dir="${test.dir}"> + <include name="tlc2/tool/distributed/DistributedDoInitFunctorEvalExceptionTest.java" /> + </fileset> + </batchtest> + <batchtest fork="yes" todir="${test.reports}/onJar"> + <fileset dir="${test.dir}"> + <include name="tlc2/TLCTest.java"/> + <include name="tlc2/tool/fp/OffHeapDiskFPSetTest.java"/> + </fileset> + </batchtest> + </junit> + </jacoco:coverage> + + <!-- remove copied class.dir --> + <delete dir="${ws.class.dir}" deleteonexit="true"/> + </target> + + <!-- Executes accompining long-running unit tests on jar file --> + <target name="test-dist-long" unless="test.skip"> <!-- compile unit tests --> <mkdir dir="${test.class.dir}" /> <javac includeantruntime="false" srcdir="${test.dir}-long" destdir="${test.class.dir}" debug="true" verbose="false"> <classpath refid="project.classpath" /> <classpath> - <pathelement location="lib/junit-4.8.2.jar" /> + <pathelement location="lib/junit-4.12.jar" /> + <pathelement location="lib/hamcrest-core-1.3.jar" /> <pathelement path="${class.dir}" /> </classpath> </javac> @@ -296,14 +638,19 @@ <!-- run junit tests on tlatools.jar --> <mkdir dir="${test.reports}/onJarLong" /> <junit printsummary="yes" haltonfailure="no" haltonerror="no" maxmemory="4096m" forkmode="perTest" fork="yes"> - <jvmarg value="-javaagent:lib/aspectjweaver-1.8.5.jar" /> + <!-- enable all assertions --> + <jvmarg value="-ea"/> + <jvmarg value="-javaagent:lib/aspectjweaver-1.9.2.jar" /> <sysproperty key="org.aspectj.weaver.showWeaveInfo" value="false"/> <sysproperty key="aj.weaving.verbose" value="false"/> <classpath refid="project.classpath" /> <classpath> - <pathelement location="lib/junit-4.8.2.jar" /> + <pathelement location="lib/junit-4.12.jar" /> + <pathelement location="lib/hamcrest-core-1.3.jar" /> <pathelement location="${dist.file}" /> <pathelement path="${test.class.dir}" /> + <!-- Need class.dir on path to find AspectJ related classes which don't get packaged into dist --> + <pathelement path="${class.dir}" /> </classpath> <formatter type="xml" /> @@ -329,8 +676,9 @@ <delete dir="${ws.class.dir}" deleteonexit="true"/> </target> - <!-- --> - <target name="test-verify" description="Verifies TLC parts with NASA's pathfinder model checker"> + <!-- Verifies TLC parts with NASA's pathfinder model checker --> + <target name="test-verify"> + <mkdir dir="${test.class.dir}"/> <!-- Compile our boilerplate code needed to verify TLC. E.g. the StateQueueVerify creates --> <!-- examplary produces & consumers and a dummy implementation of StateQueue. --> <javac includeantruntime="false" srcdir="${test.dir}-verify" destdir="${test.class.dir}" debug="true" verbose="false"> @@ -346,7 +694,8 @@ dir="${basedir}/test-verify" > <classpath refid="project.classpath" /> <classpath> - <pathelement location="lib/junit-4.8.2.jar" /> + <pathelement location="lib/junit-4.12.jar" /> + <pathelement location="lib/hamcrest-core-1.3.jar" /> <pathelement location="lib/jpf.jar" /> <pathelement location="lib/jpf-classes.jar" /> <pathelement path="${class.dir}" /> @@ -363,7 +712,38 @@ </junit> </target> - <target name="dist-mixed-jar" description="Build a distribution" depends="default"> + <!-- http://hg.openjdk.java.net/code-tools/jmh/file/3769055ad883/jmh-ant-sample/build.xml --> + <target name="benchmark" description="Generate the self-contained JMH benchmark JAR (run with java -jar target/benchmarks.jar)"> + <!-- Compile benchmark class files in test-benchmark against jmh. --> + <delete dir="target/benchmark/" /> + <mkdir dir="target/benchmark/" /> + <javac includeantruntime="false" srcdir="test-benchmark/" destdir="target/benchmark/"> + <classpath refid="project.classpath" /> + <classpath> + <pathelement path="${class.dir}" /> + <pathelement path="${test.class.dir}" /> + <pathelement location="lib/jmh/jmh-core-1.21.jar" /> + <pathelement location="lib/jmh/jmh-generator-annprocess-1.21.jar" /> + </classpath> + </javac> + + <!-- Build benchmark jar which includes third-party deps, TLC proper (including tests bc of tlc2.tool.TLCStates.createDummyState) and the benchmark files compiled in the previous step. --> + <delete file="target/benchmarks.jar" /> + <jar jarfile="target/benchmarks.jar" basedir="target/benchmark/"> + <manifest> + <attribute name="Main-Class" value="org.openjdk.jmh.Main" /> + </manifest> +<!-- <zipfileset dir="${src.dir}" includes="**/*.java" />--> + <fileset dir="${class.dir}" includes="**/*" /> + <fileset dir="${test.class.dir}" includes="**/*" /> + <zipfileset src="lib/jmh/jmh-core-1.21.jar" excludes="**/META-INF/services/**" /> + <zipfileset src="lib/jmh/jopt-simple-4.6.jar" /> + <zipfileset src="lib/jmh/commons-math3-3.2.jar" /> + </jar> + </target> + + <!-- Build a distribution --> + <target name="dist-mixed-jar" depends="default"> <!-- create a JAR file for the users --> <jar destfile="${dist-mixed.file.jar}"> <fileset dir="${class.dir}" includes="**/*" /> @@ -389,7 +769,8 @@ </target> - <target name="dist-mixed-zip" description="Build a distribution" depends="default"> + <!-- Build a distribution --> + <target name="dist-mixed-zip" depends="default"> <!-- create a zip file for the users and developers--> <zip destfile="${dist-mixed.file}"> <zipfileset dir="${src.dir}" includes="**/*.java" prefix="tla" /> diff --git a/tlatools/doc/License.txt b/tlatools/doc/License.txt index b7894bc47219e78b13a9923649f067bbb09d2cde..c8f71de5d088dc159f4b482904901e938538598c 100644 --- a/tlatools/doc/License.txt +++ b/tlatools/doc/License.txt @@ -1,109 +1,21 @@ - -1) Where the following copyright notice appears: - -Copyright (c) 2003 Compaq Corporation. All rights reserved. - -Portions Copyright (c) 2003 Microsoft Corporation. All rights reserved. or - -Copyright (c) 2003 Microsoft Corporation. All rights reserved. - -that code is licensed as follows: - -This Microsoft Research end user license agreement ("MSR-EULA") is a -legal agreement between you and Microsoft Corporation -("Microsoft" or "we") for the software identified above, -which includes source code, and any associated materials and "online" -or electronic documentation (together, the "Software"). - -By installing, copying, or otherwise using the Software, found at -http://research.microsoft.com/downloads, you agree to be bound by the -terms of this MSR-EULA. If you do not agree, do not install, copy or -use the Software. The Software is protected by copyright and other -intellectual property laws and is licensed, not sold. - -Upon your agreement to the terms below, you may do anything you want -with the Software source code for research, teaching purposes, or -internal use, free of charge, provided that you agree to the -following: - -(a) To leave in place all copyright notices and licensing information -that you might find in the Software. - -(b) That you will not use the Software in a live operating environment -where it may be relied upon to perform in the same manner as a -commercially released product, or with data that has not been -sufficiently backed up. - -(c) That any feedback about the Software provided by you to us is -voluntarily given, and Microsoft shall be free to use the feedback as -it sees fit without obligation or restriction of any kind, even if the -feedback is designated by you as confidential. - -(d) To make freely available to others the source code or data of any -modifications or additions you make to the Software, and any related -documentation, solely and exclusively under the same terms as this -License. - -(e) That Microsoft is granted back, without limitations, the rights to -reproduce, install, use, modify, distribute and transfer your -modifications to the Software source code or data. - -(f) NO WARRANTIES WHATSOEVER: That the Software comes "AS IS", -with all faults and with no warranties, conditions or -representations. The implied warranties of merchantability and fitness -for a particular purpose, and any warranty against interference with -your enjoyment of the Software or against infringement, do not apply -to the Software. The entire risk as to satisfactory quality, -performance, accuracy, and effort concerning the Software is assumed -by you. There is no warranty that this Software will fulfill any of -your particular purposes or needs. - -(g) That we have no duty of reasonable care or lack of negligence, and -we are not obligated to (and will not) provide technical support for -the Software. - -(h) That we will not be liable for any damages, including those known -as direct, indirect, special, consequential, or incidental damages -related to the Software, this MSR-EULA, or under any legal theory -(such as negligence), to the maximum extent that overriding applicable -law permits. - -(i) That if you sue or threaten to sue anyone over patents that you -think may apply to the Software, or if you breach this MSR-EULA in any -way, your license to the Software ends automatically. - -(j) That this MSR-EULA shall be construed and controlled by the laws -of the State of Washington, USA, without regard to conflicts of law. - - -2) Where the following copyright notice appears: Copyright (c) 2003 -Compaq Corporation. All rights reserved. - -that code is licensed as follows: -Compaq Computer Corporation, a Delaware corporation with offices at -20555 SH 249, Houston, TX, (COMPAQ) and you the CUSTOMER agree as -follows: - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL COMPAQ BE LIABLE FOR ANY CLAIM, -DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR -THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -Except as contained in this notice, the name of COMPAQ shall not be -used in advertising or otherwise to promote the sale, use or other -dealings in this Software without prior written authorization from -COMPAQ. +Copyright (c) 2019 Microsoft Research. All rights reserved. + +The MIT License (MIT) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/tlatools/doc/TraceExplorer_README.txt b/tlatools/doc/TraceExplorer_README.txt new file mode 100644 index 0000000000000000000000000000000000000000..41afc32e81f6bd94f7e2b0523f259a449c3d6a46 --- /dev/null +++ b/tlatools/doc/TraceExplorer_README.txt @@ -0,0 +1,78 @@ +As of December 2019, the tlatools ship with the TraceExplorer command line application. + +The current usage of this is as follows; this usage text can also be seen by invoking the application (tla2.TraceExplorer) with no arguments: + + To generate a SpecTE file pair: + java tlc2.TraceExplorer -generateSpecTE \ + [-source=_directory_containing_prior_run_output_] \ + [-overwrite] \ + SpecName + o source defaults to CWD if not specified. + o if a SpecTE.tla already exists and overwrite is not specified, execution will halt. + o if no SpecName is specified, output will be expected to arrive via stdin; -source will be ignored in this case. + + To pretty print the error states of a previous run: + java tlc2.TraceExplorer -prettyPrint \ + [-source=_directory_containing_prior_run_output_] \ + SpecName + o source defaults to CWD if not specified. + o if no SpecName is specified, output will be expected to arrive via stdin; -source will be ignored in this case. + + +------------------------------------------------------------------------------------ + +An example of the pretty print output is: + +algebraic:Model_1_SnapShot_1576778796288 loki$ java ... tlc2.TraceExplorer -prettyPrint -source=/Users/loki/arbeit/microsoft/dev/tlaplus/runtime-org.lamport.tla.toolbox.product.product.product/Queens/Queens.toolbox/FourQueens/ + <Initial predicate> + /\ todo = {<<>>} + /\ sols = {} + + <PlaceQueen line 50, col 15 to line 62, col 26 of module Queens> + /\ todo = {<<1>>, <<2>>, <<3>>} + /\ sols = {} + + <PlaceQueen line 50, col 15 to line 62, col 26 of module Queens> + /\ todo = {<<2>>, <<3>>, <<1, 3>>} + /\ sols = {} + + <PlaceQueen line 50, col 15 to line 62, col 26 of module Queens> + /\ todo = {<<3>>, <<1, 3>>} + /\ sols = {} + + <PlaceQueen line 50, col 15 to line 62, col 26 of module Queens> + /\ todo = {<<1, 3>>, <<3, 1>>} + /\ sols = {} + + <PlaceQueen line 50, col 15 to line 62, col 26 of module Queens> + /\ todo = {<<3, 1>>} + /\ sols = {} + + <PlaceQueen line 50, col 15 to line 62, col 26 of module Queens> + /\ todo = {} + /\ sols = {} + +algebraic:Model_1_SnapShot_1576778796288 loki$ + +------------------------------------------------------------------------------------ + +An example of a piped run from TLC: + +algebraic:FourQueens loki$ /Library/Java/JavaVirtualMachines/adoptopenjdk-13.jdk/Contents/Home/bin/java -XX:MaxDirectMemorySize=5460m -Xmx2732m -Dtlc2.tool.fp.FPSet.impl=tlc2.tool.fp.OffHeapDiskFPSet -XX:+UseParallelGC -Dfile.encoding=UTF-8 -classpath /Users/loki/arbeit/microsoft/dev/tlaplus/git/tlaplus/tlatools:/Users/loki/arbeit/microsoft/dev/tlaplus/git/tlaplus/tlatools/lib/*:/Users/loki/arbeit/microsoft/dev/tlaplus/git/tlaplus/tlatools/lib/javax.mail/*:/Users/loki/arbeit/microsoft/dev/tlaplus/git/tlaplus/tlatools/class tlc2.TLC -fpbits 1 -fp 4 -config MC.cfg -coverage 3 -workers 1 -tool -metadir /Users/loki/arbeit/microsoft/dev/tlaplus/runtime-org.lamport.tla.toolbox.product.product.product/Queens/Queens.toolbox/FourQueens MC | /Library/Java/JavaVirtualMachines/adoptopenjdk-13.jdk/Contents/Home/bin/java -XX:+UseParallelGC -Dfile.encoding=UTF-8 -classpath /Users/loki/arbeit/microsoft/dev/tlaplus/git/tlaplus/tlatools:/Users/loki/arbeit/microsoft/dev/tlaplus/git/tlaplus/tlatools/lib/*:/Users/loki/arbeit/microsoft/dev/tlaplus/git/tlaplus/tlatools/lib/javax.mail/*:/Users/loki/arbeit/microsoft/dev/tlaplus/git/tlaplus/tlatools/class tlc2.TraceExplorer -generateSpecTE +Parsing file /Users/loki/arbeit/microsoft/dev/tlaplus/runtime-org.lamport.tla.toolbox.product.product.product/Queens/Queens.toolbox/FourQueens/MC.tla +Parsing file /Users/loki/arbeit/microsoft/dev/tlaplus/runtime-org.lamport.tla.toolbox.product.product.product/Queens/Queens.toolbox/FourQueens/Queens.tla +Parsing file /Users/loki/arbeit/microsoft/dev/quaeler_repo/tlaplus/tlatools/class/tla2sany/StandardModules/TLC.tla +Parsing file /Users/loki/arbeit/microsoft/dev/quaeler_repo/tlaplus/tlatools/class/tla2sany/StandardModules/Naturals.tla +Parsing file /Users/loki/arbeit/microsoft/dev/quaeler_repo/tlaplus/tlatools/class/tla2sany/StandardModules/Sequences.tla +Parsing file /Users/loki/arbeit/microsoft/dev/quaeler_repo/tlaplus/tlatools/class/tla2sany/StandardModules/FiniteSets.tla +Semantic processing of module Naturals +Semantic processing of module Sequences +Semantic processing of module Queens +Semantic processing of module FiniteSets +Semantic processing of module TLC +Semantic processing of module MC +Starting... (2019-12-22 12:56:00) +The file /Users/loki/arbeit/microsoft/dev/tlaplus/runtime-org.lamport.tla.toolbox.product.product.product/Queens/Queens.toolbox/FourQueens/SpecTE.tla has been created. +The file /Users/loki/arbeit/microsoft/dev/tlaplus/runtime-org.lamport.tla.toolbox.product.product.product/Queens/Queens.toolbox/FourQueens/SpecTE.cfg has been created. +algebraic:FourQueens loki$ + diff --git a/tlatools/doc/environment.txt b/tlatools/doc/environment.txt index c486f4f5075548a5599e2e0c9964183b362b3d6e..8dcd3e63f73bebbd632cc60b75e86a32c3975e62 100644 --- a/tlatools/doc/environment.txt +++ b/tlatools/doc/environment.txt @@ -1,6 +1,13 @@ Introduction =============================================================================== - This document describes the environment setup for development of TLA+ Tools. +THIS DOCUMENT IS OBSOLETE - DO NOT USE AS CONSULTATION. +THIS DOCUMENT IS OBSOLETE - DO NOT USE AS CONSULTATION. +THIS DOCUMENT IS OBSOLETE - DO NOT USE AS CONSULTATION. +THIS DOCUMENT IS OBSOLETE - DO NOT USE AS CONSULTATION. +THIS DOCUMENT IS OBSOLETE - DO NOT USE AS CONSULTATION. +THIS DOCUMENT IS OBSOLETE - DO NOT USE AS CONSULTATION. +=============================================================================== +This document describes the environment setup for development of TLA+ Tools. It consists of the following sections: - Used versions - Environment setup diff --git a/tlatools/github.xml b/tlatools/github.xml new file mode 100644 index 0000000000000000000000000000000000000000..141044f3086b7f14a12392cb2746a6cab4c90349 --- /dev/null +++ b/tlatools/github.xml @@ -0,0 +1,87 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" + xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> + <modelVersion>4.0.0</modelVersion> + + <!-- The settings below have been duplicated from ../pom.xml because the + Toolbox - among other things - uses a different groupId for historical reasons. --> + + <groupId>org.lamport</groupId> + <artifactId>tla2tools</artifactId> + <name>TLA+ Tools</name> + <version>1.7.3-SNAPSHOT</version> + <description>The TLC model checker, the syntax and semantic checker SANY, the PlusCal translator, and the LaTeX pretty printer.</description> + <packaging>jar</packaging> + + <organization> + <name>Microsoft Research Inria Joint Centre</name> + <url>http://msr-inria.inria.fr/</url> + </organization> + + <licenses> + <license> + <name>MIT License</name> + <url>http://www.opensource.org/licenses/mit-license.php</url> + </license> + </licenses> + + <issueManagement> + <system>GitHub</system> + <url>https://github.com/tlaplus/tlaplus/issues</url> + </issueManagement> + + <scm> + <connection>scm:git:https://github.com/tlaplus/tlaplus</connection> + <developerConnection>scm:git:https://github.com/tlaplus/tlaplus</developerConnection> + <tag>HEAD</tag> + </scm> + + <distributionManagement> + <repository> + <id>github</id> + <name>GitHub TLAPlus Apache Maven Packages</name> + <url>https://maven.pkg.github.com/tlaplus/tlaplus</url> + </repository> + </distributionManagement> + + <dependencies> + <dependency> + <groupId>com.sun.mail</groupId> + <artifactId>mailapi</artifactId> + <version>1.6.3</version> + </dependency> + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <version>4.12</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.easymock</groupId> + <artifactId>easymock</artifactId> + <version>2.4</version> + <scope>test</scope> + </dependency> + </dependencies> + + <build> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-gpg-plugin</artifactId> + <version>1.6</version> + <executions> + <execution> + <id>sign-artifacts</id> + <phase>verify</phase> + <goals> + <goal>sign</goal> + </goals> + </execution> + </executions> + </plugin> + </plugins> + </build> +</project> + diff --git a/tlatools/ide/RunAllTLCTests.launch b/tlatools/ide/RunAllTLCTests.launch new file mode 100644 index 0000000000000000000000000000000000000000..03463076c018d99aaab0f1a51edeb8aa21a44304 --- /dev/null +++ b/tlatools/ide/RunAllTLCTests.launch @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<launchConfiguration type="org.eclipse.ant.AntLaunchConfigurationType"> +<booleanAttribute key="org.eclipse.ant.ui.DEFAULT_VM_INSTALL" value="false"/> +<stringAttribute key="org.eclipse.debug.core.ATTR_REFRESH_SCOPE" value="${project}"/> +<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS"> +<listEntry value="/tlatools/customBuild.xml"/> +</listAttribute> +<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_TYPES"> +<listEntry value="1"/> +</listAttribute> +<stringAttribute key="org.eclipse.jdt.launching.CLASSPATH_PROVIDER" value="org.eclipse.ant.ui.AntClasspathProvider"/> +<stringAttribute key="org.eclipse.jdt.launching.JRE_CONTAINER" value="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.7"/> +<stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value="org.eclipse.ant.internal.launching.remote.InternalAntRunner"/> +<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="tlatools"/> +<stringAttribute key="org.eclipse.jdt.launching.SOURCE_PATH_PROVIDER" value="org.eclipse.ant.ui.AntClasspathProvider"/> +<stringAttribute key="org.eclipse.ui.externaltools.ATTR_ANT_TARGETS" value="test,"/> +<stringAttribute key="org.eclipse.ui.externaltools.ATTR_LAUNCH_CONFIGURATION_BUILD_SCOPE" value="${none}"/> +<stringAttribute key="org.eclipse.ui.externaltools.ATTR_LOCATION" value="${workspace_loc:/tlatools/customBuild.xml}"/> +<stringAttribute key="org.eclipse.ui.externaltools.ATTR_WORKING_DIRECTORY" value="${workspace_loc:/tlatools}"/> +<stringAttribute key="process_factory_id" value="org.eclipse.ant.ui.remoteAntProcessFactory"/> +</launchConfiguration> diff --git a/tlatools/javacc/README/README.txt b/tlatools/javacc/README/README.txt new file mode 100644 index 0000000000000000000000000000000000000000..92b6b2cecb5506f25e0bee7d71be50260e926d01 --- /dev/null +++ b/tlatools/javacc/README/README.txt @@ -0,0 +1,13 @@ +The lexer code for SANY is produced by running JavaCC version 4.0 on the file tla+.jj +and then making some hand modifications of the output. + +The file javacc-tutorial.pdf contains documentation for JavaCC, which I believe was +written for version 4.0. + +The files method.txt and methods-sany1.txt contain instructions for making the +necessary modifications, along with miscellaneous documentation of the SANY code. + +The last modifications of those files seem to have been made on: + + methods.txt - 5 October 2009 + methods-sany1.txt - 15 March 2007 diff --git a/tlatools/javacc/README/javacc-4.0.zip b/tlatools/javacc/README/javacc-4.0.zip new file mode 100644 index 0000000000000000000000000000000000000000..f0afe4f59bbbeeccc1200b23a41397aa02176fe1 Binary files /dev/null and b/tlatools/javacc/README/javacc-4.0.zip differ diff --git a/tlatools/javacc/README/javacc-tutorial.pdf b/tlatools/javacc/README/javacc-tutorial.pdf new file mode 100644 index 0000000000000000000000000000000000000000..2bd0fe0e6c9c97108f09b986d0b3b64e5b5fa9ff Binary files /dev/null and b/tlatools/javacc/README/javacc-tutorial.pdf differ diff --git a/tlatools/javacc/README/methods-sany1.txt b/tlatools/javacc/README/methods-sany1.txt new file mode 100644 index 0000000000000000000000000000000000000000..3aef1cf5933af22b450f2dd0a605c37aa9d1cf55 --- /dev/null +++ b/tlatools/javacc/README/methods-sany1.txt @@ -0,0 +1,428 @@ +Note: There are almost identical copies of the files Token.java and +TokenMgrError.java in both the parser/ and configuration/ directories. +Both need to be there to compile correctly. Clearly, something is +wrong here. + +tlasany/drivers directory +----------------- +FrontEndException.java + Simple extension to Exception to create an exception specific to the FrontEnd + +InitException.java + Simple extension to Exception to create an exception specific to the FrontEnd + +SemanticException.java + Simple extension to Exception to create an exception specific to the FrontEnd + +SANY.java + static SANYmain + The main driver, called by tlasany/SANY.java. It handles + the command line and calles frontEndMain. + static frontEndMain + The real main driver, called by SANYmain. + static frontEndInitialize + [called from] frontEndMain + initializes Configuration, Context, level data. + throws InitException in case of problems. + static frontEndParse + [called from] frontEndMain + loads and parse the spec file. + uses SpecObj.loadSpec + static frontEndSemanticAnalysis + [called from] frontEndMain + Coming out of parsing, we have all the modules required to analyse + the file from the SpecObj. They are organized in a vector, ordered + from modules with no dependencies to modules with most dependencies. + Semantic analysis is done incrementally, with the context of each + module stored in a gobal table for easy retrieval. + +================================================================== +tlasany/parser directory +----------------------- + +This directory contains classes implementing the parser. There are +different group of classes: + 1. implementing support for identifying matching alignments + 2. implementing the expression parser + 3. implementing the parser + 4. support + +1. Implementing support for identifying matching alignments +----------------------------------------------------------- +BracketStack.java + class to support the enforcement of alignment as delimiter in TLA+, e.g. + /\ + /\ + /\ + /\ + /\ + we keep track of the kind of "bracket", e.g. AND, OR, but + identified from the type of STN. It is organized as a stack as + operators are embedded in expressions. Stacks are created, + pushed or popped. With the top reference, we can compare whether + an operator will be aligned, to the right or the left. + +StackElement.java + An element in BracketStack + +2. Implementing the expression parser +------------------------------------- +Expression parser: + The "high level" parser simply pushed expression elements on a stack, + on which well formed expressions are reduced, according to precedence + + associativity rules. + +OSelement.java + Defines the objects that go on the operator stack: the operator and + the syntax node. An OSelement object can be viewed as an + information cache, really. + +Operator.java + The Operator object associated with an operator defines the symbol + and its properties--e.g. "+" is infix. An operator will also have + low and high precedence levels, to direct the order in which + expressions are evaluated. e.g. a*b+c is (a*b)+c + + Note that there is a lack of modularity between Operator and + Operators. An interface should be defined to define in one place + constant values (nofix, infix, ...) and string representations + thereof. + +OperatorStack.java + This class name is a misnommer. Or rather a short for Operator + Expression Reduction Stack. It is also really a stack of stacks. + See comments in file. + + Operators are pushed onto the stack, and reduction operations called + periodically. As well as a final operation once we have captured + all elements of the expression. + +Operators.java + Creates and manages all symbols defined in the grammar. Initialized + by Configuration. + +3. implementing the parser +-------------------------- +SyntaxTreeNode.java + +The following are automatically generated from tla+.jj, the +implementation of the parser. + ParseException.java + SimpleCharStream.java + TLAplusParser.java + TLAplusParserConstants.java + TLAplusParserTokenManager.java + TokenMgrError.java + Token.java + +The ParseException.java file produced by javacc must be modified for +use by the other parser files. The modification consists of adding the +method getShortMessage(), whose current code is given at the end of +this file. That method is called by code in TLAplusParser.java that is +obtained from the tla+.jj grammar file. + +4. Support +---------- +ParseError.java + An exception. +ParseErrors.java + An array of ParseError objects. + + +Note: +The current directory also contains ASCII_CharStream.java, which is +obsolete. It was constructed by an earlier version of javacc, which +now constructs SimpleCharStream.java instead. + +================================================================== +tlasany/st directory +----------------------- + +st (short for Syntax Tree) creates an abstract view of the parse tree. +It was created for decoupling from other operations, but its use is +certainly not strictly necessary. + +Only Location and SyntaxTreeNode are required. + +Location.java + A location specifies the position of a syntactic unit in the source + - beginLine() start line of a syntactic unit + - beginColumn() start line of a syntactic unit + - endLine() start column of a syntactic unit + - endColumn() end column of a syntactic unit + - source() returns source file + - toString() returns a pprintable version of content + +SyntaxTreeConstants.java + Interface defining all constants for SyntaxTreeNodes. This extends + the numbering of tokens, which is done by javacc and put in + TLAplusParserConstants.java, to SyntaxTreeNode objects' `kind' + field. Both tokens and nodes are in the syntax tree. This class + also contains an array of strings which give a printable version of + the tree node kinds. + +The ParseError and ParseErrors interfaces in this directory (st) are +implemented by the classes of the same name in the parser/ directory. +These interfaces are also implemented by classes in modanalyzer/. +------ +ParseError.java + interface for a parse error + prescribes + - String reportedError() + - String defaultError() + +ParseErrors.java + interface to an Array of Parse Errors. + + +ParseTree.java + Interface to parse tree. Used in modanalyzer/ classes. + +TreeNode.java + interface for TreeNode. Used in semantic/ classes + +================================================================== +tlasany/utilities directory +--------------------------- +Assert.java + Assertion, obsolete in java 1.5 +IntWrapper.java + Abstraction of int: value can be set, read, or incremented. +Stack.java + Stack abstraction, pre-java Generics +Strings.java + Add an indentation method to basic (java) strings. +Vector.java + A generic (Object-based) java abstraction for dynamic vectors. Can be + resized. supported methods: + - int size () + - String toString() + - void addElement( Obj) + - void setElementAt( Obj, int) + - bool contains (Obj) + - void append( Vector) + - void appendNoRepeats ( Vector) + - Enumeration elements () + - void removeAllElements () + - void removeElementAt (int) + - void insertElementAt (Obj, int) + +VectorEnumeration.java + Support class for Vector. Creates an enumeration. + - implements nextElement, hasMoreElements for an Enumeration on vectors + - create a local copy of references to obj content. + +================================================================== +tlasany/explorer directory +----------------------- + This is an interface to the semantic Tree, for exploration, that is + mainly to display the semantic tree. It interacts with an inputStream + for commands. The operations are really straightforward: identifying + a symbol and printing relevant information. + +ExploreNode.java + interface to retrieve information fron node +Explorer.java + getLine + printNode + lookUpAndPrintSyntaxTree( String) + lookUpAndPrintDef( String) + levelDataPrint( String ) + executeCommand() + parseAndExecuteCommand() + printSyntaxTree() + main() + +ExplorerQuitException.java + Exception in case of Explorer problem. + +================================================================== +tlasany/configuration directory +------------------------------- + +ConfigConstants.java + This file was originally generated by javacc for parsing + defaultConfig, and then J-Ch added the definition of defaultConfig + to it. + +The following files were originally generated by javacc from a +grammar file that has vanished. They parse the string +ConfigConstants.defaultConfig to build Operators.BuiltinTable. +-------------------- +ASCII_CharStream.java +Configuration.java +ConfigurationTokenManager.java +ParseException.java +Token.java +TokenMgrError.java + +================================================================== +tlasany/error directory +----------------------- + +As noted below, this entire directory can be eliminated. + +ErrorRegistry.java + Doesn't appear to be used. + +Log.java + Used by BracketStack for no good purpose. It can be removed and + references to it eliminated. + +LogCategories.java + Used only by Log.java + +Timing.java + Appears to be unused. + +ToolkitError.java + Appears to be unused. +================================================================== +tlasany/modanalyzer directory +----------------------------- +The files in this directory are used as a interface between the driver +and the single-module parser. It contains the mechanics to extract +which modules have been referenced, to map module names to files, and +to keep track of which modules have already been parsed. + +ModuleContext.java +ModulePointer.java +ModuleRelationships.java +ModuleRelatives.java +NameToFileIStream.java +NamedInputStream.java +ParseUnit.java +ParseUnitRelatives.java +ParseUnitsTable.java +SpecObj.java +StringToNamedInputStream.java +SyntaxTreePrinter.java + +================================================================== +tlasany/semantic directory +----------------------- +ASTConstants.java + Defines the values of the semantic nodes' `kind' field and their + printed names. + +AbortException.java + Random exception. +SemanticsException.java + Random exception. + +ArgLevelParam.java + Seems to be the object that contains the level information for an + operator. + +BuiltInLevel.java + Seems to define the levels of the built-in operators. + +Context.java + A hashtable of definition and declaration nodes. + +Errors.java + Probably used to keep track of the errors found during semantic + analysis. + +ExternalModuleTable.java + Seems to keep track of contexts and level (constant or non-constant) + of modules that have been semantically analyzed. + +FrontEnd.java + This class contains the methods by which a tool calls the Front End + to parse input and to create a semantic tree, and for various other + interactions. + +Generator.java + Generates a semantic graph from a parse tree. It also uses the list + of modules to access contexts to instantiate or extend. + +LevelConstants.java + Trivial constants. + +LevelException.java + A random exception. + +Subst.java + This class represents a single substitution of the form + op <- expr such as appears in module instantiations + +SetOfArgLevelConstraints.java + Implements a map mapping arg-level parameters (ParamAndPosition) to + levels (Integer). An entry <pap,x> means that the argument + pap.position of the operator pap.param must have a level >= x. + +SetOfLevelConstraints.java + Implements a map mapping parameters to levels. An entry <p,x> in + the set means that p must have a level <= x. + +SymbolTable.java + The Symbol Table builds the stack of context tables. + +SemanticNode.java + The abstract class that is the superclass of all individual node + classes. These node classes are: + AssumeNode.java + AssumeProveNode.java + ParamAndPosition.java + ProofNode.java + TheoremNode.java + LevelNode.java + This is an abstract class that extends SemanticNode + and is extended by: + InstanceNode.java + ExprOrOpArgNode.java + This is abstract and is extended by + ExprNode.java + This is an abstract node that is extended by: + LetInNode.java + AtNode.java + DecimalNode.java + NumeralNode.java + StringNode.java + SubstInNode.java + OpApplNode.java + OpArgNode.java + SymbolNode.java + This is an abstract class that is extended by + FormalParamNode.java ModuleNode.java + OpDefOrDeclNode.java + Is extended by: + OpDeclNode.java + OpDefNode.java + + +================================================================== + +The method getShortMessage() to be inserted into ParseException.java. + + /** + * * shorter variation on the following + * + */ + public String getShortMessage() { + if (!specialConstructor) { + return super.getMessage(); + } + int maxSize = 0; + for (int i = 0; i < expectedTokenSequences.length; i++) { + if (maxSize < expectedTokenSequences[i].length) { + maxSize = expectedTokenSequences[i].length; + } + } + String retval = "Encountered \""; + Token tok = currentToken.next; + + for (int i = 0; i < maxSize; i++) { + if (i != 0) retval += " "; + if (tok.kind == 0) { + retval += tokenImage[0]; + break; + } + retval += tok.image; + // retval += add_escapes(tok.image); + tok = tok.next; + } + retval += "\" at line " + currentToken.next.beginLine + ", column " + currentToken.next.beginColumn; + return retval; + } diff --git a/tlatools/javacc/README/methods.txt b/tlatools/javacc/README/methods.txt new file mode 100644 index 0000000000000000000000000000000000000000..aaeabd2812cab8ffc722870dfef6957207df9b9f --- /dev/null +++ b/tlatools/javacc/README/methods.txt @@ -0,0 +1,491 @@ +Note: The following files produced by javacc have been edited. + + parser/ParseException.java + The method at the end of this file was added--I believe by J-Ch + The method in question seems to be getShortMessage(). + + configuration/Configuration.java + configuration/ASCII_CharStream.java + configuration/ConfigurationTokenManager + These have small bug fixes introduced by David Jefferson. Search for + "DRJ" to find them. These files were originally produced by running + javacc on config.jj, a grammar file that specifies the + parsing of the string ConfigConstants.defaultConfig. + I suspect that current the version of javacc would not produce + the ASCI_CharStream.java file, but I'm not sure. I just know that + running javacc on tla+.jj doesn't produce such a file. + +tlasany/drivers directory +----------------- +FrontEndException.java + Simple extension to Exception to create an exception specific to the FrontEnd + +InitException.java + Simple extension to Exception to create an exception specific to the FrontEnd + +SemanticException.java + Simple extension to Exception to create an exception specific to the FrontEnd + +SANY.java + static SANYmain + The main driver, called by tlasany/SANY.java. It handles + the command line and calles frontEndMain. + static frontEndMain + The real main driver, called by SANYmain. + static frontEndInitialize + [called from] frontEndMain + initializes Configuration, Context, level data. + throws InitException in case of problems. + static frontEndParse + [called from] frontEndMain + loads and parse the spec file. + uses SpecObj.loadSpec + static frontEndSemanticAnalysis + [called from] frontEndMain + Coming out of parsing, we have all the modules required to analyse + the file from the SpecObj. They are organized in a vector, ordered + from modules with no dependencies to modules with most dependencies. + Semantic analysis is done incrementally, with the context of each + module stored in a gobal table for easy retrieval. + +================================================================== +tlasany/parser directory +----------------------- + +This directory contains classes implementing the parser. There are +different group of classes: + 1. implementing support for identifying matching alignments + 2. implementing the expression parser + 3. implementing the parser + 4. support + +1. Implementing support for identifying matching alignments +----------------------------------------------------------- +BracketStack.java + class to support the enforcement of alignment as delimiter in TLA+, e.g. + /\ + /\ + /\ + /\ + /\ + we keep track of the kind of "bracket", e.g. AND, OR, but + identified from the type of STN. It is organized as a stack as + operators are embedded in expressions. Stacks are created, + pushed or popped. With the top reference, we can compare whether + an operator will be aligned, to the right or the left. + +StackElement.java + An element in BracketStack + +2. Implementing the expression parser +------------------------------------- +Expression parser: + The "high level" parser simply pushed expression elements on a stack, + on which well formed expressions are reduced, according to precedence + + associativity rules. + +OSelement.java + Defines the objects that go on the operator stack: the operator and + the syntax node. An OSelement object can be viewed as an + information cache, really. + +Operator.java + The Operator object associated with an operator defines the symbol + and its properties--e.g. "+" is infix. An operator will also have + low and high precedence levels, to direct the order in which + expressions are evaluated. e.g. a*b+c is (a*b)+c + + Note that there is a lack of modularity between Operator and + Operators. An interface should be defined to define in one place + constant values (nofix, infix, ...) and string representations + thereof. + +OperatorStack.java + This class name is a misnommer. Or rather a short for Operator + Expression Reduction Stack. It is also really a stack of stacks. + See comments in file. + + Operators are pushed onto the stack, and reduction operations called + periodically. As well as a final operation once we have captured + all elements of the expression. + +Operators.java + Creates and manages all symbols defined in the grammar. Initialized + by Configuration. + +3. implementing the parser +-------------------------- +SyntaxTreeNode.java + +The following are automatically generated from tla+.jj, the +implementation of the parser. + ParseException.java + SimpleCharStream.java + TLAplusParser.java + TLAplusParserConstants.java + TLAplusParserTokenManager.java + TokenMgrError.java + Token.java + +The ParseException.java file produced by javacc must be modified for +use by the other parser files. The modification consists of adding the +method getShortMessage(), whose current code is given at the end of +this file. That method is called by code in TLAplusParser.java that is +obtained from the tla+.jj grammar file. + +4. Support +---------- +ParseError.java + An exception. +ParseErrors.java + An array of ParseError objects. + + +Note: +The current directory also contains ASCII_CharStream.java, which is +obsolete. It was constructed by an earlier version of javacc, which +now constructs SimpleCharStream.java instead. + +================================================================== +tlasany/st directory +----------------------- + +st (short for Syntax Tree) creates an abstract view of the parse tree. +It was created for decoupling from other operations, but its use is +certainly not strictly necessary. + +Only Location and SyntaxTreeNode are required. + +Location.java + A location specifies the position of a syntactic unit in the source + - beginLine() start line of a syntactic unit + - beginColumn() start line of a syntactic unit + - endLine() start column of a syntactic unit + - endColumn() end column of a syntactic unit + - source() returns source file + - toString() returns a pprintable version of content + +SyntaxTreeConstants.java + Interface defining all constants for SyntaxTreeNodes. This extends + the numbering of tokens, which is done by javacc and put in + TLAplusParserConstants.java, to SyntaxTreeNode objects' `kind' + field. Both tokens and nodes are in the syntax tree. This class + also contains an array of strings which gives a printable version of + the tree node kinds. + +The ParseError and ParseErrors interfaces in this directory (st) are +implemented by the classes of the same name in the parser/ directory. +These interfaces are also implemented by classes in modanalyzer/. +------ +ParseError.java + interface for a parse error + prescribes + - String reportedError() + - String defaultError() + +ParseErrors.java + interface to an Array of Parse Errors. + + +ParseTree.java + Interface to parse tree. Used in modanalyzer/ classes. + +TreeNode.java + Interface for TreeNode. Imported by a lot of classes + in semantic/ and modanalyzer/, and by + parser/{SyntaxTreeNode,TLAplusParser, TLAplusParserTokenManager} + It seems to be implemented only by SyntaxTreeNode. + +================================================================== +tlasany/utilities directory +--------------------------- +Assert.java + Assertion, obsolete in java 1.5 +IntWrapper.java + Abstraction of int: value can be set, read, or incremented. +Stack.java + Stack abstraction, pre-java Generics +Strings.java + Add an indentation method to basic (java) strings. +Vector.java + A generic (Object-based) java abstraction for dynamic vectors. Can be + resized. supported methods: + - int size () + - String toString() + - void addElement( Obj) + - void setElementAt( Obj, int) + - bool contains (Obj) + - void append( Vector) + - void appendNoRepeats ( Vector) + - Enumeration elements () + - void removeAllElements () + - void removeElementAt (int) + - void insertElementAt (Obj, int) + +VectorEnumeration.java + Support class for Vector. Creates an enumeration. + - implements nextElement, hasMoreElements for an Enumeration on vectors + - create a local copy of references to obj content. + +================================================================== +tlasany/explorer directory +----------------------- + This is an interface to the semantic Tree, for exploration, that is + mainly to display the semantic tree. It interacts with an inputStream + for commands. The operations are really straightforward: identifying + a symbol and printing relevant information. + +ExploreNode.java + interface to retrieve information fron node + methods: String toString(int depth); + String levelDataToString(); + void walkGraph(Hashtable semNodesTable); + + +Explorer.java + getLine + printNode + lookUpAndPrintSyntaxTree( String) + lookUpAndPrintDef( String) + levelDataPrint( String ) + executeCommand() + parseAndExecuteCommand() + printSyntaxTree() + main() + +ExplorerQuitException.java + Exception in case of Explorer problem. + +================================================================== +tlasany/configuration directory +------------------------------- + +ConfigConstants.java + This file was originally generated by javacc for parsing + defaultConfig, and then J-Ch added the definition of defaultConfig + to it. + +The following files were originally generated by javacc from a +grammar file that has vanished. They parse the string +ConfigConstants.defaultConfig to build Operators.BuiltinTable. +-------------------- +ASCII_CharStream.java +Configuration.java +ConfigurationTokenManager.java +ParseException.java +Token.java +TokenMgrError.java + +================================================================== +tlasany/error directory +----------------------- + +As noted below, this entire directory can be eliminated. + +ErrorRegistry.java + Doesn't appear to be used. + +Log.java + Used by BracketStack for no good purpose. It can be removed and + references to it eliminated. + +LogCategories.java + Used only by Log.java + +Timing.java + Appears to be unused. + +ToolkitError.java + Appears to be unused. +================================================================== +tlasany/modanalyzer directory +----------------------------- +The files in this directory are used as a interface between the driver +and the single-module parser. It contains the mechanics to extract +which modules have been referenced, to map module names to files, and +to keep track of which modules have already been parsed. + +ModuleContext.java +ModulePointer.java +ModuleRelationships.java +ModuleRelatives.java +NameToFileIStream.java +NamedInputStream.java +ParseUnit.java + This class contains the code that walks through a module's parse + tree to find the names of EXTENDed or INSTANCEd modules. +ParseUnitRelatives.java +ParseUnitsTable.java +SpecObj.java +StringToNamedInputStream.java +SyntaxTreePrinter.java + +================================================================== +tlasany/semantic directory +----------------------- +ASTConstants.java + Defines the values of the semantic nodes' `kind' field and their + printed names. + +AbortException.java + Random exception. +SemanticsException.java + Random exception. + +ArgLevelParam.java + Seems to be the object that contains the level information for an + operator. + +BuiltInLevel.java + Seems to define the levels of the built-in operators. + +Context.java + A hashtable of definition and declaration nodes. + +Errors.java + Probably used to keep track of the errors found during semantic + analysis. + +ExternalModuleTable.java + Seems to keep track of contexts and level (constant or non-constant) + of modules that have been semantically analyzed. + +FrontEnd.java + This class contains the methods by which a tool calls the Front End + to parse input and to create a semantic tree, and for various other + interactions. + +Generator.java + Generates a semantic graph from a parse tree. It also uses the list + of modules to access contexts to instantiate or extend. + +LevelConstants.java + Trivial constants. + +LevelException.java + A random exception. + +OpDefOrLabelNode.java + An interface implemented by OpDefNode, ThmOrAssumpDefNode, and + LabelNode. It contains methods for accessing the set of labels + "immediately within" this node. [Added by LL on 23 Apr 2007] + +Subst.java + This class represents a single substitution of the form + op <- expr such as appears in module instantiations + +SetOfArgLevelConstraints.java + Implements a map mapping arg-level parameters (ParamAndPosition) to + levels (Integer). An entry <pap,x> means that the argument + pap.position of the operator pap.param must have a level >= x. + +SetOfLevelConstraints.java + Implements a map mapping parameters to levels. An entry <p,x> in + the set means that p must have a level <= x. + +SymbolTable.java + The Symbol Table builds the stack of context tables. + +SemanticNode.java + The abstract class that is the superclass of all individual node + classes. These node classes are: + ParamAndPosition.java + LevelNode.java + This is an abstract class that extends SemanticNode + and is extended by: + AssumeNode.java + TheoremNode.java + [AssumeNode and TheoremNode moved here by LL 22 Jul 2007. + I don't know why they weren't already here.] + ProofNode.java + This is abstract and is extended by + LeafProofNode.java + NonLeafProofNode.java + InstanceNode.java + obsolete: QEDStepNode.java + ExprOrOpArgNode.java + This is abstract and is extended by + ExprNode.java + This is an abstract node that is extended by: + LabelNode.java [Added by LL 23 Apr 2007] + LetInNode.java + AtNode.java + DecimalNode.java + NumeralNode.java + StringNode.java + OpApplNode.java + SubstInNode.java + OpArgNode.java + AssumeProveNode.java [Moved here by LL 15 Mar 2007] + NewSymbNode [Added by LL 15 Mar 2007 ] + UseOrHideNode.java [Added by LL 29 Jul 2007] + DefStepNode.java [Added by LL 16 Aug 2007] + APSubstInNode.java [Added by LL 6 Aug 2007] + SymbolNode.java + This is an abstract class that extends LevelNode by + adding the following concrete methods: + getName(), occur(), isParam() + and the following abstract methods: + getArity(), isLocal(), and match() [for testing arity]. + It is extended by + FormalParamNode.java + ModuleNode.java + OpDefOrDeclNode.java + Abstract class that adds the fields + ModuleNode originallyDefinedInModule + SymbolTable st + int arity + Is extended by: + OpDeclNode.java + OpDefNode.java + ThmOrAssumpDefNode.java [Added by LL 17 Mar 2007] + +================================================================== + +To add a new BuiltIn operator, search for $Nop and OP_nop in the files + + semantic/ASTConstants.java + semantic/BuiltInLevel.java + configuration/ConfigConstants.java + +and copy what's done there. If the model checker needs to be able to +evaluate the operator, search for Op_nop and OPCODE_nop in + + tool/BuiltInOPs.java + tool/Tool.java + tool/ToolGlobals.java + +================================================================== + +The method getShortMessage() to be inserted into ParseException.java. + + /** + * * shorter variation on the following + * + */ + public String getShortMessage() { + if (!specialConstructor) { + return super.getMessage(); + } + int maxSize = 0; + for (int i = 0; i < expectedTokenSequences.length; i++) { + if (maxSize < expectedTokenSequences[i].length) { + maxSize = expectedTokenSequences[i].length; + } + } + String retval = "Encountered \""; + Token tok = currentToken.next; + + for (int i = 0; i < maxSize; i++) { + if (i != 0) retval += " "; + if (tok.kind == 0) { + retval += tokenImage[0]; + break; + } + retval += tok.image; + // retval += add_escapes(tok.image); + tok = tok.next; + } + retval += "\" at line " + currentToken.next.beginLine + ", column " + currentToken.next.beginColumn; + return retval; + } diff --git a/tlatools/javacc/TLAplusParser.java b/tlatools/javacc/TLAplusParser.java index eec70becf90a22bf8545358dd1bc857d1d34d025..dde171b77bd0bcc5c7119fd33d24f256342c93ed 100644 --- a/tlatools/javacc/TLAplusParser.java +++ b/tlatools/javacc/TLAplusParser.java @@ -8,6 +8,7 @@ import tlc2.output.EC; import tla2sany.utilities.Vector; import tla2sany.utilities.Stack; import util.Assert; +import util.TLAConstants; import util.UniqueString; import util.ToolIO; @@ -1645,7 +1646,7 @@ Token t; SyntaxTreeNode tn, sn[]; Token t; bpa("Parameter declaration"); - expecting = "CONSTANT"; + expecting = TLAConstants.KeyWords.CONSTANT; tn = ParamSubDecl(); addHeir(tn); expecting = "Identifier, operator or _"; diff --git a/tlatools/jfr/Overview.jpg b/tlatools/jfr/Overview.jpg new file mode 100644 index 0000000000000000000000000000000000000000..8ecb56cfa28f403492d40d8d72c17aa5b0551f72 Binary files /dev/null and b/tlatools/jfr/Overview.jpg differ diff --git a/tlatools/jfr/README.md b/tlatools/jfr/README.md new file mode 100644 index 0000000000000000000000000000000000000000..c1789a8627567d245ea3c9aea9654cacaa482633 --- /dev/null +++ b/tlatools/jfr/README.md @@ -0,0 +1,42 @@ +This folder contains all libraries and config files to profile TLC with Java Flight Recorder (JFR). + +A default JFR is created with: + +-XX:StartFlightRecording=settings=default +-XX:FlightRecorderOptions=defaultrecording=true,disk=true,repository=/tmp,dumponexit=true,dumponexitpath=dump.jfr,maxage=1h,settings=${project_loc:tlatools}/jfr/tlc.jfc + +To additionally record JMX data in the flight recording, add: + +-javaagent:${project_loc:tlatools}/jfr/jmx2jfr.jar=${project_loc:tlatools}/jfr/jmxprobes.xml + +and replace the "default" settings with ${project_loc:tlatools}/jfr/tlc.jfc + +Afterwards, the resulting flight recording can be opened with Java Mission Control (JMC). + + +Further processing (charting...) is best done by converting the .jfr to xml: + +java oracle.jrockit.jfr.parser.Parser -xml dump.jfr > dump.xml + +To select all TLC related elements, do: + +xmlstarlet sel + -N jfr=http://www.oracle.com/hotspot/jvm/ + -N jvm=http://www.oracle.com/hotspot/jvm/ + -N p4108=http://www.hirt.se/jfr/jmx2jfr/ + -t -c "//p4108:MBeans_tlc2_tool_ModelChecker" dump.xml + +Do print all the attributes of "StatesGeneratedPerMinute do: + +xmlstarlet sel + -N jfr=http://www.oracle.com/hotspot/jvm/ + -N jvm=http://www.oracle.com/hotspot/jvm/ + -N p4108=http://www.hirt.se/jfr/jmx2jfr/ + -t -v "//p4108:MBeans_tlc2_tool_ModelChecker/p4108:StatesGeneratedPerMinute" dump.xml + +--- +http://www.slideshare.net/marcushirt/java-mission-control-java-flight-recorder-deep-dive +https://docs.oracle.com/javase/8/docs/technotes/guides/troubleshoot/tooldescr004.html#BABHCDEA +https://docs.oracle.com/javacomponents/jmc-5-4/jfr-runtime-guide/run.htm#JFRUH179 +http://hirt.se/blog/?p=689 +http://hirt.se/blog/?p=446 \ No newline at end of file diff --git a/tlatools/jfr/jmxprobes.xml b/tlatools/jfr/jmxprobes.xml new file mode 100644 index 0000000000000000000000000000000000000000..0a17520e52dfab302b1060d737a1801b76915b31 --- /dev/null +++ b/tlatools/jfr/jmxprobes.xml @@ -0,0 +1,241 @@ +<!-- + This is an example configuration file. It should be edited to capture the MBean + attributes you are interested in. + + Use it with the agent in the following manner: + java -javaagent:<path to jmx2jfr.jar>=<path to this file> <your normal vmargs> +--> +<jmx2jfr> + <!-- Time to wait before retrying getting an MBean attribute in ms --> + <retrytime>60000</retrytime> + + <!-- Time to wait until starting in ms --> + <delay>10000</delay> + + <!-- How often to get the attributes and emit events for them, in ms --> + <period>30000</period> + + <!-- clean: Use name or type keys for event name. Leads to cleaner event + type names, but can result in name collisions if you have MBeans with + the same name in the same domain. + canonical: ugly event type names, but guaranteed not to collide --> + <namingmode>clean</namingmode> + + <!-- objectname: MBean name - look it up in JMC + attributename: Attribute name - look it up in JMC + contenttype: [None | Bytes | Timestamp | Millis | Nanos | Ticks | Address] --> + <attributes> + <attribute> + <objectname>java.lang:type=OperatingSystem</objectname> + <attributename>FreeSwapSpaceSize</attributename> + <contenttype>Bytes</contenttype> + </attribute> + <attribute> + <objectname>java.lang:type=OperatingSystem</objectname> + <attributename>TotalSwapSpaceSize</attributename> + <contenttype>Bytes</contenttype> + </attribute> + <attribute> + <objectname>java.lang:type=OperatingSystem</objectname> + <attributename>AvailableProcessors</attributename> + <contenttype>None</contenttype> + </attribute> + <attribute> + <objectname>java.lang:type=OperatingSystem</objectname> + <attributename>AvailableProcessors</attributename> + <contenttype>None</contenttype> + </attribute> + <attribute> + <objectname>java.lang:type=OperatingSystem</objectname> + <attributename>Name</attributename> + <contenttype>None</contenttype> + </attribute> + <attribute> + <objectname>java.lang:type=Threading</objectname> + <attributename>PeakThreadCount</attributename> + <contenttype>None</contenttype> + </attribute> + <attribute> + <objectname>java.lang:type=Threading</objectname> + <attributename>CurrentThreadCpuTime</attributename> + <contenttype>Nanos</contenttype> + </attribute> + <attribute> + <objectname>java.lang:type=Runtime</objectname> + <attributename>StartTime</attributename> + <contenttype>Timestamp</contenttype> + </attribute> + <attribute> + <objectname>java.lang:type=Runtime</objectname> + <attributename>StartTime</attributename> + <contenttype>Timestamp</contenttype> + </attribute> + <attribute> + <objectname>java.lang:type=Runtime</objectname> + <attributename>Uptime</attributename> + <contenttype>Millis</contenttype> + </attribute> + <!-- tlc2.tool:type=ModelChecker --> + <attribute> + <objectname>tlc2.tool:type=ModelChecker</objectname> + <attributename>StatesGenerated</attributename> + <contenttype>None</contenttype> + </attribute> + <attribute> + <objectname>tlc2.tool:type=ModelChecker</objectname> + <attributename>StatesGeneratedPerMinute</attributename> + <contenttype>None</contenttype> + </attribute> + <attribute> + <objectname>tlc2.tool:type=ModelChecker</objectname> + <attributename>DistinctStatesGenerated</attributename> + <contenttype>None</contenttype> + </attribute> + <attribute> + <objectname>tlc2.tool:type=ModelChecker</objectname> + <attributename>DistinctStatesGeneratedPerMinute</attributename> + <contenttype>None</contenttype> + </attribute> + <attribute> + <objectname>tlc2.tool:type=ModelChecker</objectname> + <attributename>StateQueueSize</attributename> + <contenttype>None</contenttype> + </attribute> + <attribute> + <objectname>tlc2.tool:type=ModelChecker</objectname> + <attributename>Progress</attributename> + <contenttype>None</contenttype> + </attribute> + <attribute> + <objectname>tlc2.tool:type=ModelChecker</objectname> + <attributename>WorkerCount</attributename> + <contenttype>None</contenttype> + </attribute> + <attribute> + <objectname>tlc2.tool:type=ModelChecker</objectname> + <attributename>AverageBlockCnt</attributename> + <contenttype>None</contenttype> + </attribute> + <attribute> + <objectname>tlc2.tool:type=ModelChecker</objectname> + <attributename>RuntimeRatio</attributename> + <contenttype>None</contenttype> + </attribute> + <attribute> + <objectname>tlc2.tool:type=ModelChecker</objectname> + <attributename>SpecName</attributename> + <contenttype>None</contenttype> + </attribute> + <attribute> + <objectname>tlc2.tool:type=ModelChecker</objectname> + <attributename>ModelName</attributename> + <contenttype>None</contenttype> + </attribute> + <attribute> + <objectname>tlc2.tool:type=ModelChecker</objectname> + <attributename>CommandLine</attributename> + <contenttype>None</contenttype> + </attribute> + <!-- tlc2.tool.fp:type=DiskFPSet0 --> + <attribute> + <objectname>tlc2.tool.fp:type=DiskFPSet0</objectname> + <attributename>BucketCapacity</attributename> + <contenttype>None</contenttype> + </attribute> + <attribute> + <objectname>tlc2.tool.fp:type=DiskFPSet0</objectname> + <attributename>TblCapacity</attributename> + <contenttype>None</contenttype> + </attribute> + <attribute> + <objectname>tlc2.tool.fp:type=DiskFPSet0</objectname> + <attributename>IndexCapacity</attributename> + <contenttype>None</contenttype> + </attribute> + <attribute> + <objectname>tlc2.tool.fp:type=DiskFPSet0</objectname> + <attributename>OverallCapacity</attributename> + <contenttype>None</contenttype> + </attribute> + <attribute> + <objectname>tlc2.tool.fp:type=DiskFPSet0</objectname> + <attributename>TblLoad</attributename> + <contenttype>None</contenttype> + </attribute> + <attribute> + <objectname>tlc2.tool.fp:type=DiskFPSet0</objectname> + <attributename>TblCnt</attributename> + <contenttype>None</contenttype> + </attribute> + <attribute> + <objectname>tlc2.tool.fp:type=DiskFPSet0</objectname> + <attributename>MaxTblCnt</attributename> + <contenttype>None</contenttype> + </attribute> + <attribute> + <objectname>tlc2.tool.fp:type=DiskFPSet0</objectname> + <attributename>FileCnt</attributename> + <contenttype>None</contenttype> + </attribute> + <attribute> + <objectname>tlc2.tool.fp:type=DiskFPSet0</objectname> + <attributename>DiskLookupCnt</attributename> + <contenttype>None</contenttype> + </attribute> + <attribute> + <objectname>tlc2.tool.fp:type=DiskFPSet0</objectname> + <attributename>MemHitCnt</attributename> + <contenttype>None</contenttype> + </attribute> + <attribute> + <objectname>tlc2.tool.fp:type=DiskFPSet0</objectname> + <attributename>DiskHitCnt</attributename> + <contenttype>None</contenttype> + </attribute> + <attribute> + <objectname>tlc2.tool.fp:type=DiskFPSet0</objectname> + <attributename>DiskWriteCnt</attributename> + <contenttype>None</contenttype> + </attribute> + <attribute> + <objectname>tlc2.tool.fp:type=DiskFPSet0</objectname> + <attributename>DiskSeekCnt</attributename> + <contenttype>None</contenttype> + </attribute> + <attribute> + <objectname>tlc2.tool.fp:type=DiskFPSet0</objectname> + <attributename>DiskSeekCache</attributename> + <contenttype>None</contenttype> + </attribute> + <attribute> + <objectname>tlc2.tool.fp:type=DiskFPSet0</objectname> + <attributename>GrowDiskMark</attributename> + <contenttype>None</contenttype> + </attribute> + <attribute> + <objectname>tlc2.tool.fp:type=DiskFPSet0</objectname> + <attributename>CheckPointMark</attributename> + <contenttype>None</contenttype> + </attribute> + <attribute> + <objectname>tlc2.tool.fp:type=DiskFPSet0</objectname> + <attributename>FlushTime</attributename> + <contenttype>None</contenttype> + </attribute> + <attribute> + <objectname>tlc2.tool.fp:type=DiskFPSet0</objectname> + <attributename>ReaderWriterCnt</attributename> + <contenttype>None</contenttype> + </attribute> + <attribute> + <objectname>tlc2.tool.fp:type=DiskFPSet0</objectname> + <attributename>LoadFactor</attributename> + <contenttype>None</contenttype> + </attribute> + <attribute> + <objectname>tlc2.tool.fp:type=DiskFPSet0</objectname> + <attributename>LockCnt</attributename> + <contenttype>None</contenttype> + </attribute> + </attributes> +</jmx2jfr> \ No newline at end of file diff --git a/tlatools/jfr/tlc.jfc b/tlatools/jfr/tlc.jfc new file mode 100644 index 0000000000000000000000000000000000000000..89b444acaca862cd7bc7aed983ad94278344b6eb --- /dev/null +++ b/tlatools/jfr/tlc.jfc @@ -0,0 +1,456 @@ +<?xml version="1.0" encoding="UTF-8"?> +<configuration version="1.0" name="Settings for 'My Recording'" description="These settings were used to start the recording 'flight_recording_18091tlc2TLC18748.jfr'." provider="Oracle"> + + <producer uri="http://www.oracle.com/hotspot/jvm/" label="Oracle JDK"> + + <event path="java/statistics/thread_allocation"> + <setting name="enabled">true</setting> + <setting name="period">everyChunk</setting> + </event> + + <event path="java/statistics/class_loading"> + <setting name="enabled">true</setting> + <setting name="period">1000 ms</setting> + </event> + + <event path="java/statistics/threads"> + <setting name="enabled">true</setting> + <setting name="period">1000 ms</setting> + </event> + + <event path="java/thread_start"> + <setting name="enabled">true</setting> + </event> + + <event path="java/thread_end"> + <setting name="enabled">true</setting> + </event> + + <event path="java/thread_sleep"> + <setting name="enabled">true</setting> + <setting name="stackTrace">true</setting> + <setting name="threshold">10 ms</setting> + </event> + + <event path="java/thread_park"> + <setting name="enabled">true</setting> + <setting name="stackTrace">true</setting> + <setting name="threshold">10 ms</setting> + </event> + + <event path="java/monitor_enter"> + <setting name="enabled">true</setting> + <setting name="stackTrace">true</setting> + <setting name="threshold">10 ms</setting> + </event> + + <event path="java/monitor_wait"> + <setting name="enabled">true</setting> + <setting name="stackTrace">true</setting> + <setting name="threshold">10 ms</setting> + </event> + + <event path="vm/class/load"> + <setting name="enabled">false</setting> + <setting name="stackTrace">true</setting> + <setting name="threshold">0 ms</setting> + </event> + + <event path="vm/class/unload"> + <setting name="enabled">false</setting> + </event> + + <event path="vm/info"> + <setting name="enabled">true</setting> + <setting name="period">everyChunk</setting> + </event> + + <event path="vm/initial_system_property"> + <setting name="enabled">true</setting> + <setting name="period">everyChunk</setting> + </event> + + <event path="vm/prof/execution_sample"> + <setting name="enabled">true</setting> + <setting name="period">10 ms</setting> + </event> + + <event path="vm/prof/execution_sampling_info"> + <setting name="enabled">false</setting> + <setting name="threshold">1 ms</setting> + </event> + + <event path="vm/runtime/execute_vm_operation"> + <setting name="enabled">true</setting> + <setting name="threshold">10 ms</setting> + </event> + + <event path="vm/runtime/thread_dump"> + <setting name="enabled">true</setting> + <setting name="period">60 s</setting> + </event> + + <event path="vm/flag/long"> + <setting name="enabled">true</setting> + <setting name="period">everyChunk</setting> + </event> + + <event path="vm/flag/ulong"> + <setting name="enabled">true</setting> + <setting name="period">everyChunk</setting> + </event> + + <event path="vm/flag/double"> + <setting name="enabled">true</setting> + <setting name="period">everyChunk</setting> + </event> + + <event path="vm/flag/boolean"> + <setting name="enabled">true</setting> + <setting name="period">everyChunk</setting> + </event> + + <event path="vm/flag/string"> + <setting name="enabled">true</setting> + <setting name="period">everyChunk</setting> + </event> + + <event path="vm/flag/long_changed"> + <setting name="enabled">true</setting> + </event> + + <event path="vm/flag/ulong_changed"> + <setting name="enabled">true</setting> + </event> + + <event path="vm/flag/double_changed"> + <setting name="enabled">true</setting> + </event> + + <event path="vm/flag/boolean_changed"> + <setting name="enabled">true</setting> + </event> + + <event path="vm/flag/string_changed"> + <setting name="enabled">true</setting> + </event> + + <event path="vm/gc/detailed/object_count"> + <setting name="enabled">false</setting> + <setting name="period">everyChunk</setting> + </event> + + <event path="vm/gc/configuration/gc"> + <setting name="enabled">true</setting> + <setting name="period">everyChunk</setting> + </event> + + <event path="vm/gc/configuration/heap"> + <setting name="enabled">true</setting> + <setting name="period">everyChunk</setting> + </event> + + <event path="vm/gc/configuration/young_generation"> + <setting name="enabled">true</setting> + <setting name="period">everyChunk</setting> + </event> + + <event path="vm/gc/configuration/tlab"> + <setting name="enabled">true</setting> + <setting name="period">everyChunk</setting> + </event> + + <event path="vm/gc/configuration/survivor"> + <setting name="enabled">true</setting> + <setting name="period">everyChunk</setting> + </event> + + <event path="vm/gc/detailed/object_count_after_gc"> + <setting name="enabled">false</setting> + </event> + + <event path="vm/gc/heap/summary"> + <setting name="enabled">true</setting> + </event> + + <event path="vm/gc/heap/ps_summary"> + <setting name="enabled">true</setting> + </event> + + <event path="vm/gc/heap/metaspace_summary"> + <setting name="enabled">true</setting> + </event> + + <event path="vm/gc/metaspace/gc_threshold"> + <setting name="enabled">true</setting> + </event> + + <event path="vm/gc/metaspace/allocation_failure"> + <setting name="enabled">true</setting> + <setting name="stackTrace">true</setting> + </event> + + <event path="vm/gc/metaspace/out_of_memory"> + <setting name="enabled">true</setting> + <setting name="stackTrace">true</setting> + </event> + + <event path="vm/gc/metaspace/chunk_free_list_summary"> + <setting name="enabled">true</setting> + </event> + + <event path="vm/gc/collector/garbage_collection"> + <setting name="enabled">true</setting> + <setting name="threshold">0 ms</setting> + </event> + + <event path="vm/gc/collector/parold_garbage_collection"> + <setting name="enabled">true</setting> + <setting name="threshold">0 ms</setting> + </event> + + <event path="vm/gc/collector/young_garbage_collection"> + <setting name="enabled">true</setting> + <setting name="threshold">0 ms</setting> + </event> + + <event path="vm/gc/collector/old_garbage_collection"> + <setting name="enabled">true</setting> + <setting name="threshold">0 ms</setting> + </event> + + <event path="vm/gc/collector/g1_garbage_collection"> + <setting name="enabled">true</setting> + <setting name="threshold">0 ms</setting> + </event> + + <event path="vm/gc/phases/pause"> + <setting name="enabled">true</setting> + <setting name="threshold">0 ms</setting> + </event> + + <event path="vm/gc/phases/pause_level_1"> + <setting name="enabled">true</setting> + <setting name="threshold">0 ms</setting> + </event> + + <event path="vm/gc/phases/pause_level_2"> + <setting name="enabled">true</setting> + <setting name="threshold">0 ms</setting> + </event> + + <event path="vm/gc/phases/pause_level_3"> + <setting name="enabled">false</setting> + <setting name="threshold">0 ms</setting> + </event> + + <event path="vm/gc/reference/statistics"> + <setting name="enabled">true</setting> + </event> + + <event path="vm/gc/detailed/promotion_failed"> + <setting name="enabled">true</setting> + </event> + + <event path="vm/gc/detailed/evacuation_failed"> + <setting name="enabled">true</setting> + </event> + + <event path="vm/gc/detailed/evacuation_info"> + <setting name="enabled">true</setting> + </event> + + <event path="vm/gc/detailed/concurrent_mode_failure"> + <setting name="enabled">true</setting> + </event> + + <event path="vm/gc/detailed/allocation_requiring_gc"> + <setting name="enabled">false</setting> + <setting name="stackTrace">true</setting> + </event> + + <event path="vm/compiler/config"> + <setting name="enabled">true</setting> + <setting name="period">everyChunk</setting> + </event> + + <event path="vm/compiler/stats"> + <setting name="enabled">true</setting> + <setting name="period">1000 ms</setting> + </event> + + <event path="vm/compiler/compilation"> + <setting name="enabled">true</setting> + <setting name="threshold">100 ms</setting> + </event> + + <event path="vm/compiler/phase"> + <setting name="enabled">true</setting> + <setting name="threshold">10 s</setting> + </event> + + <event path="vm/compiler/failure"> + <setting name="enabled">true</setting> + </event> + + <event path="vm/code_sweeper/config"> + <setting name="enabled">true</setting> + <setting name="period">everyChunk</setting> + </event> + + <event path="vm/code_sweeper/stats"> + <setting name="enabled">true</setting> + <setting name="period">everyChunk</setting> + </event> + + <event path="vm/code_sweeper/sweep"> + <setting name="enabled">true</setting> + <setting name="threshold">100 ms</setting> + </event> + + <event path="vm/code_cache/config"> + <setting name="enabled">true</setting> + <setting name="period">everyChunk</setting> + </event> + + <event path="vm/code_cache/stats"> + <setting name="enabled">true</setting> + <setting name="period">everyChunk</setting> + </event> + + <event path="vm/code_cache/full"> + <setting name="enabled">true</setting> + </event> + + <event path="os/information"> + <setting name="enabled">true</setting> + <setting name="period">everyChunk</setting> + </event> + + <event path="os/processor/cpu_information"> + <setting name="enabled">true</setting> + <setting name="period">everyChunk</setting> + </event> + + <event path="os/processor/context_switch_rate"> + <setting name="enabled">true</setting> + <setting name="period">10 s</setting> + </event> + + <event path="os/processor/cpu_load"> + <setting name="enabled">true</setting> + <setting name="period">1000 ms</setting> + </event> + + <event path="os/processor/cpu_tsc"> + <setting name="enabled">true</setting> + <setting name="period">everyChunk</setting> + </event> + + <event path="os/system_process"> + <setting name="enabled">true</setting> + <setting name="period">everyChunk</setting> + </event> + + <event path="os/initial_environment_variable"> + <setting name="enabled">true</setting> + <setting name="period">everyChunk</setting> + </event> + + <event path="os/memory/physical_memory"> + <setting name="enabled">true</setting> + <setting name="period">everyChunk</setting> + </event> + + <event path="java/object_alloc_in_new_TLAB"> + <setting name="enabled">true</setting> + <setting name="stackTrace">true</setting> + </event> + + <event path="java/object_alloc_outside_TLAB"> + <setting name="enabled">true</setting> + <setting name="stackTrace">true</setting> + </event> + + </producer> + + <producer uri="http://www.oracle.com/hotspot/jdk/" label="Oracle JDK"> + + <event path="java/file_read"> + <setting name="enabled">true</setting> + <setting name="stackTrace">true</setting> + <setting name="threshold">10 ms</setting> + </event> + + <event path="java/file_write"> + <setting name="enabled">true</setting> + <setting name="stackTrace">true</setting> + <setting name="threshold">10 ms</setting> + </event> + + <event path="java/socket_read"> + <setting name="enabled">true</setting> + <setting name="stackTrace">true</setting> + <setting name="threshold">10 ms</setting> + </event> + + <event path="java/socket_write"> + <setting name="enabled">true</setting> + <setting name="stackTrace">true</setting> + <setting name="threshold">10 ms</setting> + </event> + + <event path="java/exception_throw"> + <setting name="enabled">false</setting> + <setting name="stackTrace">true</setting> + </event> + + <event path="java/error_throw"> + <setting name="enabled">true</setting> + <setting name="stackTrace">true</setting> + </event> + + <event path="java/statistics/throwables"> + <setting name="enabled">true</setting> + <setting name="period">1000 ms</setting> + </event> + + </producer> + + <producer uri="http://www.oracle.com/hotspot/jfr-info/" label="Oracle JDK"> + + <event path="recordings/recording"> + <setting name="enabled">true</setting> + </event> + + <event path="recordings/recording_setting"> + <setting name="enabled">true</setting> + </event> + + </producer> + + <producer uri="http://www.hirt.se/jfr/jmx2jfr/"> + + <event path="MBeans/java_lang/Runtime"> + <setting name="enabled">true</setting> + </event> + + <event path="MBeans/java_lang/OperatingSystem"> + <setting name="enabled">true</setting> + </event> + + <event path="MBeans/java_lang/Threading"> + <setting name="enabled">true</setting> + </event> + + <event path="MBeans/tlc2_tool/ModelChecker"> + <setting name="enabled">true</setting> + </event> + + <event path="MBeans/tlc2_tool_fp/DiskFPSet0"> + <setting name="enabled">true</setting> + </event> + + </producer> + +</configuration> diff --git a/tlatools/lib/aspectjrt-1.8.5.jar b/tlatools/lib/aspectjrt-1.8.5.jar deleted file mode 100644 index b8241c820410996cc4a13dc942c27c6b0d9d6050..0000000000000000000000000000000000000000 Binary files a/tlatools/lib/aspectjrt-1.8.5.jar and /dev/null differ diff --git a/tlatools/lib/aspectjtools-1.8.5.jar b/tlatools/lib/aspectjtools-1.8.5.jar deleted file mode 100644 index 31f045d54150a074202fafac517f9be9243d0519..0000000000000000000000000000000000000000 Binary files a/tlatools/lib/aspectjtools-1.8.5.jar and /dev/null differ diff --git a/tlatools/lib/aspectjweaver-1.8.5.jar b/tlatools/lib/aspectjweaver-1.8.5.jar deleted file mode 100644 index 952a8e4c7cae616b1d20da5dde99a820911df527..0000000000000000000000000000000000000000 Binary files a/tlatools/lib/aspectjweaver-1.8.5.jar and /dev/null differ diff --git a/tlatools/lib/jacocoant.jar b/tlatools/lib/jacocoant.jar index 43340ba1cc3fecd7345a296fcaa72ef5b86fc55e..09ac4a806f595e54eeb4623d20e134ebf2706b89 100644 Binary files a/tlatools/lib/jacocoant.jar and b/tlatools/lib/jacocoant.jar differ diff --git a/tlatools/lib/javax.mail.jar b/tlatools/lib/javax.mail.jar deleted file mode 100644 index 0c5ac3967e2fb98b74f86b41de47666f9bd2758d..0000000000000000000000000000000000000000 Binary files a/tlatools/lib/javax.mail.jar and /dev/null differ diff --git a/tlatools/lib/junit-4.8.2.jar b/tlatools/lib/junit-4.8.2.jar deleted file mode 100644 index 5b4bb849af9583fec1ea0a0ccc0d571ef49aa8ba..0000000000000000000000000000000000000000 Binary files a/tlatools/lib/junit-4.8.2.jar and /dev/null differ diff --git a/tlatools/logging.properties b/tlatools/logging.properties index 68d1db468dcd42188bf41335ef66080e2f324e21..f4fb74c8d53ff25375ece6a261db66300aa69a1f 100644 --- a/tlatools/logging.properties +++ b/tlatools/logging.properties @@ -18,10 +18,8 @@ # "handlers" specifies a comma separated list of log Handler # classes. These handlers will be installed during VM startup. # Note that these classes must be on the system classpath. -# By default we only configure a FileHandler, which will only -# show messages at the INFO and above levels. -handlers= java.util.logging.FileHandler - +# By default we only configure a ConsoleHandler. +handlers= java.util.logging.ConsoleHandler # To also add the ConsoleHandler, use the following line instead. #handlers= java.util.logging.FileHandler, java.util.logging.ConsoleHandler @@ -38,6 +36,9 @@ handlers= java.util.logging.FileHandler # Describes specific configuration info for Handlers. ############################################################ +# Omit the date line (requires Java >=1.7) +java.util.logging.SimpleFormatter.format=%5$s%6$s%n + # default file output is in user's home directory. # %u stands for a simple counter java.util.logging.FileHandler.pattern = %h/java%u.log @@ -45,11 +46,13 @@ java.util.logging.FileHandler.limit = 50000 java.util.logging.FileHandler.count = 1 java.util.logging.FileHandler.formatter = java.util.logging.SimpleFormatter +java.util.logging.ConsoleHandler.level = ALL +java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter ############################################################ # Facility specific properties. # Provides extra control for each logger. ############################################################ -# For example, set the com.xyz.foo logger to only log SEVERE -# messages: -com.xyz.foo.level = SEVERE +#tlc2.tool.level = ALL +#tlc2.tool.fp.level = ALL +#tlc2.tool.liveness.level = ALL diff --git a/tlatools/ossrh.xml b/tlatools/ossrh.xml new file mode 100644 index 0000000000000000000000000000000000000000..b4cc5f211b66247c9af45228d363b41ee17b0624 --- /dev/null +++ b/tlatools/ossrh.xml @@ -0,0 +1,97 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" + xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> + <modelVersion>4.0.0</modelVersion> + + <parent> + <groupId>org.sonatype.oss</groupId> + <artifactId>oss-parent</artifactId> + <version>9</version> + <relativePath>../do-not-reference-our-parent-pom</relativePath> + </parent> + + <!-- The settings below have been duplicated from ../pom.xml because the + Toolbox - among other things - uses a different groupId for historical reasons. --> + + <groupId>org.lamport</groupId> + <artifactId>tla2tools</artifactId> + <name>TLA+ Tools</name> + <version>1.7.3-SNAPSHOT</version> + <description>The TLC model checker, the syntax and semantic checker SANY, the PlusCal translator, and the LaTeX pretty printer.</description> + <packaging>jar</packaging> + + <organization> + <name>Microsoft Research Inria Joint Centre</name> + <url>http://msr-inria.inria.fr/</url> + </organization> + + <licenses> + <license> + <name>MIT License</name> + <url>http://www.opensource.org/licenses/mit-license.php</url> + </license> + </licenses> + + <issueManagement> + <system>GitHub</system> + <url>https://github.com/tlaplus/tlaplus/issues</url> + </issueManagement> + + <scm> + <connection>scm:git:https://github.com/tlaplus/tlaplus</connection> + <developerConnection>scm:git:https://github.com/tlaplus/tlaplus</developerConnection> + <tag>HEAD</tag> + </scm> + + <distributionManagement> + <snapshotRepository> + <id>ossrh</id> + <url>https://oss.sonatype.org/content/repositories/snapshots</url> + </snapshotRepository> + <repository> + <id>ossrh</id> + <url>https://oss.sonatype.org/service/local/staging/deploy/maven2</url> + </repository> + </distributionManagement> + + <dependencies> + <dependency> + <groupId>com.sun.mail</groupId> + <artifactId>mailapi</artifactId> + <version>1.6.3</version> + </dependency> + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <version>4.12</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.easymock</groupId> + <artifactId>easymock</artifactId> + <version>2.4</version> + <scope>test</scope> + </dependency> + </dependencies> + + <build> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-gpg-plugin</artifactId> + <version>1.6</version> + <executions> + <execution> + <id>sign-artifacts</id> + <phase>verify</phase> + <goals> + <goal>sign</goal> + </goals> + </execution> + </executions> + </plugin> + </plugins> + </build> +</project> + diff --git a/tlatools/ossrh.xml.README b/tlatools/ossrh.xml.README new file mode 100644 index 0000000000000000000000000000000000000000..9e7f08f253b96c6a38d5ace59571857f77a1fae0 --- /dev/null +++ b/tlatools/ossrh.xml.README @@ -0,0 +1,47 @@ +Maven Central is the canonical repository for Java libraries. To allow +users to build software on top of tla2tools.jar, we publish tla2tools.jar +to Maven Central. Users have asked us to do this [1]. Moreover, our +own Apalache [2] will find this useful [3]. + +We initially had to claim [4] the org.lamport namespace to be allowed to +publish tla2tools.jar into it. The artifacts are available online [5] +and so far we have only pushed snapshots. + +[1] https://groups.google.com/forum/#!msg/tlaplus/xzmsu6qRMyo/YGgRNFE1CAAJ +[2] https://github.com/konnov/apalache +[3] https://github.com/konnov/apalache/issues/48 +[4] https://issues.sonatype.org/browse/OSSRH-50021 +[5] https://oss.sonatype.org/content/repositories/snapshots/org/lamport/ + +Manually deploy tla2tools.jar +----------------------------- + +1) Strip javax/ directory from tla2tools.jar built with regular customBuild.xml. + The version of tla2tools.jar - that we will upload to MC - will declare its + dependencies to javax packages instead of bundling them. + +zip -d dist/tla2tools.jar \ + javax/\* \ + com/\* \ + META-INF/mailcap \ + META-INF/javamail* + +2) Upload to Maven Central (credentials have to be set in ~/.m2/settings.xml). + +mvn gpg:sign-and-deploy-file \ + -Durl=https://oss.sonatype.org/content/repositories/snapshots \ + -DrepositoryId=ossrh \ + -DpomFile=ossrh.xml \ + -Dfile=dist/tla2tools.jar + + +Background material: +* <https://central.sonatype.org/pages/manual-staging-bundle-creation-and-deployment.html> + * Credentials are the login details for Maven Central (OSSRH) +* What is wrong when deploy returns a 400 bad request response: <https://stackoverflow.com/a/18702353/6291195> + +Delete existing/old artifacts +----------------------------- + +1) Login to the OSSRH web front-end <https://oss.sonatype.org/index.html> +2) Follow steps outlined in: <https://issues.sonatype.org/browse/OSSRH-776?focusedCommentId=117817&page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel#comment-117817> diff --git a/tlatools/pom.xml b/tlatools/pom.xml index 147cd9c783d0dc531cd1cb83915fc5965992da1a..924f664036cebd2b05aa49e27faf9aa9ab1af4b8 100644 --- a/tlatools/pom.xml +++ b/tlatools/pom.xml @@ -8,18 +8,60 @@ <artifactId>parent</artifactId> <groupId>tlatoolbox</groupId> <version>0.0.1-SNAPSHOT</version> - <relativePath>../pom.xml</relativePath> + <relativePath>../../pom.xml</relativePath> </parent> <groupId>tlatoolbox</groupId> <artifactId>org.lamport.tlatools</artifactId> <version>1.0.0-SNAPSHOT</version> <packaging>eclipse-plugin</packaging> + <properties> + <!-- tlatools is a non-standard maven project. Its heart is customBuild.xml written for ant --> + <!-- which gets invoked from maven. Unfortunately, it means that the tasks which are executed --> + <!-- by ant are not visible to maven. In this case it results in sonar not finding necessary --> + <!-- data to include coverage information. The properties below make sure that sonar finds the data. --> + <sonar.sources>src,src-aj</sonar.sources> + <sonar.tests>test,test-verify,test-long,test-concurrent</sonar.tests> + + <sonar.java.binaries>class</sonar.java.binaries> + <sonar.java.libraries>lib/*.jar</sonar.java.libraries> + + <sonar.java.test.binaries>class,test-class</sonar.java.test.binaries> + <sonar.java.test.libraries>lib/*.jar</sonar.java.test.libraries> + + <sonar.java.coveragePlugin>jacoco</sonar.java.coveragePlugin> + <sonar.surefire.reportsPath>target/surefire-reports</sonar.surefire.reportsPath> + <sonar.junit.reportsPath>target/surefire-reports</sonar.junit.reportsPath> + <sonar.jacoco.reportPaths>target/code-coverage.exec</sonar.jacoco.reportPaths> + </properties> + + <build> <plugins> - <plugin> + <!-- Delete the ../states/ folder created by the Ant JUnit tests as part + of the maven-antrun-plugin of customBuild.xml. On the CI-machine, the ../states/ + folder gradually grows to eventually fill-up the entire disk. --> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-clean-plugin</artifactId> + <version>3.0.0</version> + <configuration> + <filesets> + <fileset> + <directory>../states/</directory> + <includes> + <include>**/*</include> + </includes> + <followSymlinks>false</followSymlinks> + </fileset> + </filesets> + </configuration> + </plugin> + + <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-antrun-plugin</artifactId> + <version>1.8</version> <executions> <execution> <id>compile</id> @@ -33,9 +75,32 @@ <property name="build.compiler" value="extJavac"/> <property name="maven.test.skip" value="${maven.test.skip}"/> + <property name="maven.test.halt" value="${maven.test.halt}"/> <ant antfile="customBuild.xml" inheritRefs="true"> - <target name="default" /> - <target name="dist-mixed-zip" /> + <target name="default-maven" /> + </ant> + </tasks> + </configuration> + <goals> + <goal>run</goal> + </goals> + </execution> + <!-- Run customBuild.xml's clean target --> + <execution> + <id>clean</id> + <phase>clean</phase> + <configuration> + <tasks> + + <taskdef name="junit" + classname="org.apache.tools.ant.taskdefs.optional.junit.JUnitTask" + classpathref="maven.test.classpath" /> + + <property name="build.compiler" value="extJavac"/> + <property name="maven.test.skip" value="${maven.test.skip}"/> + <property name="maven.test.halt" value="${maven.test.halt}"/> + <ant antfile="customBuild.xml" inheritRefs="true"> + <target name="clean" /> </ant> </tasks> </configuration> @@ -60,11 +125,52 @@ </configuration> </plugin> + <!-- Update the Manifest.mf packaged into the OSGi variant of tla2tools.jar + which is embedded in the Toolbox. The standalone tla2tools.jar (see customBuild.xml) + defines various properties in its manifest among which are the git revision + and the implementation title. These two properties are read by TLCGlobals + to determine TLCs revision. This is turn is reported as part of execution statistics. + To correctly report the TLC revision when it is launched from inside the Toolbox, the + embedded tla2tools.jar created here has to also include the two properties. --> + <plugin> + <groupId>org.codehaus.mojo</groupId> + <artifactId>buildnumber-maven-plugin</artifactId> + <version>1.4</version> + <executions> + <execution> + <phase>validate</phase> + <goals> + <goal>create</goal> + </goals> + </execution> + </executions> + <configuration> + <getRevisionOnlyOnce>true</getRevisionOnlyOnce> + <shortRevisionLength>7</shortRevisionLength> + <doCheck>false</doCheck> + <doUpdate>false</doUpdate> + </configuration> + </plugin> + <plugin> + <groupId>org.eclipse.tycho</groupId> + <artifactId>tycho-packaging-plugin</artifactId> + <version>${tycho-version}</version> + <configuration> + <archive> + <manifestEntries> + <Implementation-Title>TLA+ Tools</Implementation-Title> + <X-Git-ShortRevision>${buildNumber}</X-Git-ShortRevision> + </manifestEntries> + </archive> + </configuration> + </plugin> + <!-- Compile aspects --> <plugin> - <groupId>org.codehaus.mojo</groupId> + <!-- https://github.com/nickwongdev/aspectj-maven-plugin --> + <groupId>com.nickwongdev</groupId> <artifactId>aspectj-maven-plugin</artifactId> - <version>1.7</version> + <version>1.12.1</version> <configuration> <ajdtBuildDefFile>build.ajproperties</ajdtBuildDefFile> </configuration> @@ -73,6 +179,11 @@ <goals> <goal>compile</goal> </goals> + <configuration> + <complianceLevel>1.8</complianceLevel> + <source>1.8</source> + <target>1.8</target> + </configuration> </execution> </executions> </plugin> @@ -81,15 +192,15 @@ <dependencies> <dependency> - <groupId>ant</groupId> + <groupId>org.apache.ant</groupId> <artifactId>ant-junit</artifactId> - <version>1.6.2</version> + <version>1.7.3</version> <scope>compile</scope> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjrt</artifactId> - <version>1.8.2</version> + <version>1.9.1</version> </dependency> </dependencies> diff --git a/tlatools/project.properties b/tlatools/project.properties index b98226fd30c7696b0ab55dfc66744510f9069373..170b370d0f59900dbfb579a684b26451d5db4556 100644 --- a/tlatools/project.properties +++ b/tlatools/project.properties @@ -12,8 +12,6 @@ dist.file =${dist.dir}/tla2tools.jar dist-mixed.file =${dist.dir}/tla.zip dist-mixed.file.jar =${dist.dir}/tla2tools-mixed.jar -srcgen.dir =src-gen - test.reports =target/surefire-reports/ examples.dir =../examples diff --git a/tlatools/src-aj/META-INF/aop-ajc.xml b/tlatools/src-aj/META-INF/aop-ajc.xml index c425d69c2e85a51b25349b374bea97f310a8b088..71712cc740e474162e42029adff926e78fb8f5c1 100644 --- a/tlatools/src-aj/META-INF/aop-ajc.xml +++ b/tlatools/src-aj/META-INF/aop-ajc.xml @@ -5,6 +5,18 @@ <aspect name="tlc2.tool.WorkerMonitorAspect"/> <aspect name="tlc2.tool.distributed.RMIMethodMonitorAspect"/> <aspect name="tlc2.tool.distributed.TLCServerMonitorAspect"/> + <!-- + <aspect name="tlc2.tool.CostModelAspect"/> + --> </aspects> - + + <!-- + Dump load-time weaving classes to disk. + https://www.eclipse.org/aspectj/doc/released/pdguide/ltwdump.html#ltwdump-examples + --> + <weaver options="-verbose -debug"> + <!-- dump before and after they are woven. --> + <dump within="tlc2..*" beforeandafter="true"/> + <include within="tlc2..*"/> + </weaver> </aspectj> diff --git a/tlatools/src-aj/META-INF/aop.xml b/tlatools/src-aj/META-INF/aop.xml index c425d69c2e85a51b25349b374bea97f310a8b088..102e0e21286937a8ac5e7838e17aa15eb1b6a47d 100644 --- a/tlatools/src-aj/META-INF/aop.xml +++ b/tlatools/src-aj/META-INF/aop.xml @@ -5,6 +5,18 @@ <aspect name="tlc2.tool.WorkerMonitorAspect"/> <aspect name="tlc2.tool.distributed.RMIMethodMonitorAspect"/> <aspect name="tlc2.tool.distributed.TLCServerMonitorAspect"/> + <!-- + <aspect name="tlc2.tool.CostModelAspect"/> + --> </aspects> + <!-- + Dump load-time weaving classes to disk. + https://www.eclipse.org/aspectj/doc/released/pdguide/ltwdump.html#ltwdump-examples + --> + <weaver options="-verbose -debug"> + <!-- dump before and after they are woven. --> + <dump within="tlc2..*" beforeandafter="true"/> + <include within="tlc2..*"/> + </weaver> </aspectj> diff --git a/tlatools/src-aj/tlc2/tool/CostModelAspect.aj b/tlatools/src-aj/tlc2/tool/CostModelAspect.aj new file mode 100644 index 0000000000000000000000000000000000000000..a050d7549097664583a61ae31ca309e6ded2a7e3 --- /dev/null +++ b/tlatools/src-aj/tlc2/tool/CostModelAspect.aj @@ -0,0 +1,244 @@ +///******************************************************************************* +// * Copyright (c) 2018 Microsoft Research. All rights reserved. +// * +// * The MIT License (MIT) +// * +// * Permission is hereby granted, free of charge, to any person obtaining a copy +// * of this software and associated documentation files (the "Software"), to deal +// * in the Software without restriction, including without limitation the rights +// * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// * of the Software, and to permit persons to whom the Software is furnished to do +// * so, subject to the following conditions: +// * +// * The above copyright notice and this permission notice shall be included in all +// * copies or substantial portions of the Software. +// * +// * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +// * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +// * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +// * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// * +// * Contributors: +// * Markus Alexander Kuppe - initial API and implementation +// ******************************************************************************/ +//package tlc2.tool; +// +//import tlc2.tool.Action; +//import tlc2.tool.ActionItemList; +//import tlc2.tool.IStateFunctor; +//import tlc2.tool.StateVec; +//import tlc2.tool.TLCState; +//import tlc2.tool.coverage.CostModel; +//import tlc2.util.Context; +//import tlc2.TLCGlobals; +//import tla2sany.semantic.OpApplNode; +//import tla2sany.semantic.SemanticNode; +// +//public final aspect CostModelAspect { +// +// // -------------------------------- // +// +///* +// private final void getInitStates(ActionItemList acts, TLCState ps, IStateFunctor states, CostModel cm) { +// */ +// +// final pointcut getInitStates(ActionItemList acts, TLCState ps, IStateFunctor states, CostModel cm): +// execution(private void tlc2.tool.Tool.getInitStates(ActionItemList, TLCState, IStateFunctor, CostModel)) +// && args(acts, ps, states, cm) && !within(CostModelAspect) && if(TLCGlobals.isCoverageEnabled()) ; +// +// void around(ActionItemList acts, TLCState ps, IStateFunctor states, CostModel cm): (getInitStates(acts, ps, states, cm)) { +// if (acts.isEmpty() || ps.allAssigned()) { +// cm.incInvocations(); +// cm.getRoot().incInvocations(); +// } +// proceed(acts, ps, states, cm); +// } +// +// // -------------------------------- // +// +///* +// public StateVec getNextStates(Action action, TLCState state) { +// action.cm.incInvocations(nss.size()); +// } +//*/ +// +// final pointcut getNextStatesAction(Action action, TLCState state): +// execution(public tlc2.tool.StateVec tlc2.tool.Tool.getNextStates(Action, TLCState)) +// && args(action, state) && !within(CostModelAspect) && if(TLCGlobals.isCoverageEnabled()); +// +// //TODO Rewrite to after returning? +// StateVec around(final Action action, final TLCState state): (getNextStatesAction(action, state)) { +// final StateVec nss = (StateVec) proceed(action, state); +// action.cm.incInvocations(nss.size()); +// +// return nss; +// } +// +// // -------------------------------- // +// +///* +// private final TLCState getNextStates(ActionItemList acts, final TLCState s0, final TLCState s1, final StateVec nss, CostModel cm) { +// final TLCState copy = getNextStates0(acts, s0, s1, nss, cm); +// if (copy != s1) { +// cm.incInvocations(); +// } +// return copy; +// } +//*/ +// +// final pointcut getNextStates(ActionItemList acts, TLCState s0, TLCState s1, StateVec nss, CostModel cm): +// execution(private tlc2.tool.TLCState tlc2.tool.Tool.getNextStates(ActionItemList, TLCState, TLCState, StateVec, CostModel)) +// && args(acts, s0, s1, nss, cm) && !within(CostModelAspect) && if(TLCGlobals.isCoverageEnabled()); +// +// TLCState around(ActionItemList acts, TLCState s0, TLCState s1, StateVec nss, CostModel cm): (getNextStates(acts, s0, s1, nss, cm)) { +// final TLCState copy = (TLCState) proceed(acts, s0, s1, nss, cm); +// if (copy != s1) { +// cm.incInvocations(); +// } +// return copy; +// } +// +// // -------------------------------- // +// +///* +// private final Value setSource(final SemanticNode expr, final Value value, CostModel cm) { +// value.setCostModel(cm.get(expr)); +//*/ +// +// final pointcut setSource(SemanticNode expr, final Value value, CostModel cm): +// execution(private tlc2.value.Value tlc2.tool.Tool.setSource(SemanticNode, Value, CostModel)) +// && args(expr, value, cm) && !within(CostModelAspect) && if(TLCGlobals.isCoverageEnabled()); +// +// Value around(SemanticNode expr, final Value value, CostModel cm): (setSource(expr, value, cm)) { +// value.setCostModel(cm.get(expr)); +// return proceed(expr, value, cm); +// } +// +// // -------------------------------- // +// +///* +// private final Value evalAppl(final OpApplNode expr, Context c, TLCState s0, TLCState s1, final int control, CostModel cm) { +// cm = cm.get(expr); +// cm.incInvocations(); +//*/ +// +// final pointcut evalsAppl(OpApplNode expr, Context c, TLCState s0, TLCState s1, int control, CostModel cm): +// execution(private tlc2.value.Value tlc2.tool.Tool.evalAppl(tla2sany.semantic.OpApplNode, Context, TLCState, TLCState, int, CostModel)) +// && args(expr, c, s0, s1, control, cm) && !within(CostModelAspect) && if(TLCGlobals.isCoverageEnabled()); +// +// Value around(OpApplNode expr, Context c, TLCState s0, TLCState s1, int control, CostModel cm): (evalsAppl(expr, c, s0, s1, control, cm)) { +// return proceed(expr, c, s0, s1, control, cm.get(expr).incInvocations()); +// } +// +// // -------------------------------- // +// +///* +// +// private final TLCState getNextStates(SemanticNode pred, ActionItemList acts, Context c, TLCState s0, TLCState s1, StateVec nss, CostModel cm) { +// cm = cm.get(pred); +// +// private final TLCState processUnchanged(SemanticNode expr, ActionItemList acts, Context c, TLCState s0, TLCState s1, StateVec nss, CostModel cm) { +// cm = cm.get(expr); +//*/ +// +// final pointcut evals6(SemanticNode expr, ActionItemList acts, Context c, TLCState s0, TLCState s1, StateVec nss, +// CostModel cm): +// (execution(private tlc2.tool.TLCState tlc2.tool.Tool.getNextStates(SemanticNode, ActionItemList, Context, TLCState, TLCState, StateVec, CostModel)) || +// execution(private tlc2.tool.TLCState tlc2.tool.Tool.processUnchanged(SemanticNode, ActionItemList, Context, TLCState, TLCState, StateVec, CostModel))) +// && args(expr, acts, c, s0, s1, nss, cm) && !within(CostModelAspect) && if(TLCGlobals.isCoverageEnabled()); +// +// TLCState around(SemanticNode expr, ActionItemList acts, Context c, TLCState s0, TLCState s1, StateVec nss, CostModel cm): (evals6(expr, acts, c, s0, s1, nss, cm)) { +// return proceed(expr, acts, c, s0, s1, nss, cm.get(expr)); +// } +// +// // -------------------------------- // +// +///* +// private final void getInitStatesAppl(OpApplNode init, ActionItemList acts, Context c, TLCState ps, IStateFunctor states, CostModel cm) { +// cm = cm.get(init); +//*/ +// +// final pointcut getInitStatesAppl(OpApplNode expr, ActionItemList acts, Context c, TLCState ps, IStateFunctor states, CostModel cm): +// execution(private void tlc2.tool.Tool.getInitStatesAppl(OpApplNode, ActionItemList, Context, TLCState, IStateFunctor, CostModel)) +// && args(expr, acts, c, ps, states, cm) && !within(CostModelAspect) && if(TLCGlobals.isCoverageEnabled()); +// +// void around(OpApplNode expr, ActionItemList acts, Context c, TLCState ps, IStateFunctor states, CostModel cm): (getInitStatesAppl(expr, acts, c, ps, states, cm)) { +// proceed(expr, acts, c, ps, states, cm.get(expr)); +// } +// +// // -------------------------------- // +// +///* +// private final TLCState enabledAppl(OpApplNode pred, ActionItemList acts, Context c, TLCState s0, TLCState s1, CostModel cm) { +// cm = cm.get(pred); +//*/ +// +// final pointcut enableAppl(OpApplNode expr, ActionItemList acts, Context c, TLCState s0, TLCState s1, CostModel cm): +// execution(private tlc2.tool.TLCState tlc2.tool.Tool.enabledAppl(OpApplNode, ActionItemList, Context, TLCState, TLCState, CostModel)) +// && args(expr, acts, c, s0, s1, cm) && !within(CostModelAspect) && if(TLCGlobals.isCoverageEnabled()); +// +// TLCState around(OpApplNode expr, ActionItemList acts, Context c, TLCState s0, TLCState s1, CostModel cm): (enableAppl(expr, acts, c, s0, s1, cm)) { +// return proceed(expr, acts, c, s0, s1, cm.get(expr)); +// } +// +// // -------------------------------- // +// +///* +// public final Value eval(SemanticNode expr, Context c, TLCState s0, TLCState s1, final int control, CostModel cm) { +// cm = cm.get(expr) +//*/ +// +// final pointcut eval(SemanticNode expr, Context c, TLCState s0, TLCState s1, int control, CostModel cm): +// execution(public tlc2.value.Value tlc2.tool.Tool.eval(SemanticNode, Context, TLCState, TLCState, int, CostModel)) +// && args(expr, c, s0, s1, control, cm) && !within(CostModelAspect) && if(TLCGlobals.isCoverageEnabled()); +// +// Value around(SemanticNode expr, Context c, TLCState s0, TLCState s1, int control, CostModel cm): (eval(expr, c, s0, s1, control, cm)) { +// return proceed(expr, c, s0, s1, control, cm.get(expr)); +// } +// +// // ---------------- Pass on CostModel instances from existing Value to newly instantiated ones. ---------------- // +// +// final pointcut newValueCtor() : call(tlc2.value.Value+.new(..)) && !within(CostModelAspect) && if(TLCGlobals.isCoverageEnabled()); +// +// after() returning(final Value newValue) : newValueCtor() { +// if (thisJoinPoint.getThis() instanceof Value) { +// // Get CostModel instance from existing Value and attach to new one. +// final Value existingValue = (Value) thisJoinPoint.getThis(); +// newValue.setCostModel(existingValue.getCostModel()); +// } +// } +// +// // ---------------- (Finally) count invocations to ValueEnumerator#elements to measure costs. ---------------- // +// +// final pointcut elementsExec(Enumerable en): +// execution(public tlc2.value.ValueEnumeration tlc2.value.Enumerable+.elements(..)) +// && target(en) && !within(CostModelAspect) && if(TLCGlobals.isCoverageEnabled()); +// +// ValueEnumeration around(Enumerable en): (elementsExec(en)) { +// return new WrappingValueEnumeration(((Enumerable) en).getCostModel(), (ValueEnumeration) proceed(en)); +// } +// +// private static class WrappingValueEnumeration implements ValueEnumeration { +// +// private final CostModel cm; +// private final ValueEnumeration ve; +// +// public WrappingValueEnumeration(CostModel costModel, ValueEnumeration ve) { +// this.cm = costModel.incInvocations(1); +// this.ve = ve; +// } +// +// @Override +// public void reset() { +// ve.reset(); +// } +// +// @Override +// public Value nextElement() { +// cm.incSecondary(); +// return ve.nextElement(); +// } +// } +//} diff --git a/tlatools/src/model/InJarFilenameToStream.java b/tlatools/src/model/InJarFilenameToStream.java index 39f07a222b12497ee02896bf683a98b692b25bc1..e3ff7afc4e65530e2ed1cea9bc1eab948177ac2f 100644 --- a/tlatools/src/model/InJarFilenameToStream.java +++ b/tlatools/src/model/InJarFilenameToStream.java @@ -23,7 +23,7 @@ public class InJarFilenameToStream extends SimpleFilenameToStream implements InputStream is = InJarFilenameToStream.class.getResourceAsStream(prefix + name); if(is != null) { try { - File sourceFile = new File(TMPDIR + File.separator + name); + File sourceFile = new TLAFile(TMPDIR + File.separator + name, this); sourceFile.deleteOnExit(); FileOutputStream fos = new FileOutputStream(sourceFile); diff --git a/tlatools/src/model/ModelInJar.java b/tlatools/src/model/ModelInJar.java index d823fda2e6e6aac46ce612d1ab8161be0aae3aa1..1d51d4ecbc5708bb68e2dea136140045f2c9056a 100644 --- a/tlatools/src/model/ModelInJar.java +++ b/tlatools/src/model/ModelInJar.java @@ -1,20 +1,40 @@ package model; +import java.io.File; import java.io.IOException; import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardCopyOption; import java.util.Enumeration; import java.util.Properties; +import util.TLAConstants; + public abstract class ModelInJar { public static final String PATH = "/model/"; public static boolean hasModel() { - return ModelInJar.class.getResource("/model/MC.tla") != null; + return ModelInJar.class.getResource(PATH + TLAConstants.Files.MODEL_CHECK_TLA_FILE) != null; + } + + public static boolean hasCfg() { + return ModelInJar.class.getResource(PATH + TLAConstants.Files.MODEL_CHECK_CONFIG_FILE) != null; + } + + public static File getCfg() { + try { + final InputStream source = ModelInJar.class.getResourceAsStream(PATH + TLAConstants.Files.MODEL_CHECK_CONFIG_FILE); + Path target = Files.createTempFile(TLAConstants.Files.MODEL_CHECK_FILE_BASENAME, TLAConstants.Files.CONFIG_EXTENSION); + Files.copy(source, target, StandardCopyOption.REPLACE_EXISTING); + return target.toFile(); + } catch (IOException notExpectedToHappen) { + return new File(""); + } } public static boolean loadProperties() { - InputStream in = ModelInJar.class - .getResourceAsStream("/model/generated.properties"); + InputStream in = ModelInJar.class.getResourceAsStream(PATH + "generated.properties"); if (in != null) { Properties prop = new Properties(); try { diff --git a/tlatools/src/pcal/AST.java b/tlatools/src/pcal/AST.java index c9d203d445fc1f2ab05d16a1ccbd03a6536a25d6..5107add92b89272ed1baf35682471a01f441a08e 100644 --- a/tlatools/src/pcal/AST.java +++ b/tlatools/src/pcal/AST.java @@ -17,7 +17,7 @@ * However, there are the following classes that do not represent explicit * * non-terminals of the +CAL grammar. * * * -* Uniprocess MultiProcess SingleAssign CallReturn * +* Uniprocess MultiProcess SingleAssign CallReturn CallGoto * * * * Every AST has col and line fields that contain the position of the * * first character of the corresponding portion of the algorithm text (as * @@ -100,6 +100,7 @@ public class AST public static AST.Call CallObj ; public static AST.Return ReturnObj ; public static AST.CallReturn CallReturnObj ; + public static AST.CallGoto CallGotoObj ; public static AST.Goto GotoObj ; public static AST.Macro MacroObj ; public static AST.MacroCall MacroCallObj ; @@ -214,6 +215,7 @@ public class AST CallObj = new AST.Call() ; ReturnObj = new AST.Return() ; CallReturnObj = new AST.CallReturn() ; + CallGotoObj = new AST.CallGoto() ; GotoObj = new AST.Goto() ; MacroObj = new AST.Macro() ; MacroCallObj = new AST.MacroCall() ; @@ -845,6 +847,24 @@ public class AST } } + public static class CallGoto extends AST + { public String after = "" ; + public String to = "" ; + public Vector args = null ; // of TLAExpr + public CallGoto() { }; + public String toString() + { return + Indent(lineCol()) + + "[type |-> \"callGoto\"," + NewLine() + + " after |-> \"" + after + "\"," + NewLine() + + " to |-> \"" + to + "\"," + NewLine() + + Indent("args |-> ") + VectorToSeqString(args) + + "]" + NewLine() + + EndIndent() + + EndIndent() ; + } + } + public static class Goto extends AST { public String to = "" ; public Goto() {}; diff --git a/tlatools/src/pcal/PCalTLAGenerator.java b/tlatools/src/pcal/PCalTLAGenerator.java index 6887de50c617c3649772b2dc713da5d5878b1810..75054b0aaacbd36e6f00ce6075277bc32a41ae40 100644 --- a/tlatools/src/pcal/PCalTLAGenerator.java +++ b/tlatools/src/pcal/PCalTLAGenerator.java @@ -64,13 +64,13 @@ public class PCalTLAGenerator * Note that this requires RemoveNameConflicts to be called first * * because of the grotty use of the class variable st. * ********************************************************************/ - public Vector translate() throws RemoveNameConflictsException + public Vector<String> translate() throws RemoveNameConflictsException { - Vector result = new Vector(); + Vector<String> result = new Vector<String>(); AST xast = null; // Set to the exploded AST for (int i = 0; i < st.disambiguateReport.size(); i++) - result.addElement(st.disambiguateReport.elementAt(i)); + result.addElement((String) st.disambiguateReport.elementAt(i)); // System.out.println("Before: " + ast.toString()); // System.out.println("After renaming: " + ast.toString()); try diff --git a/tlatools/src/pcal/ParseAlgorithm.java b/tlatools/src/pcal/ParseAlgorithm.java index 753f5d5be77df98a8f05b8b430e93443e93a5efe..913b348c61815a1d355ef0154f634ed2fbb94a6d 100644 --- a/tlatools/src/pcal/ParseAlgorithm.java +++ b/tlatools/src/pcal/ParseAlgorithm.java @@ -49,9 +49,10 @@ * holds the label (the absence of a label represented by * * a lbl field equal to the empty string ""). The only difference * * from the simple grammar is that a <Call> followed by an unlabeled * -* <Return> is converted into a single AST.CallReturn object. This * -* pass does not produce any AST.If or AST.Either objects, and any * -* AST.While object it produces has an empty labDo field. * +* <Return> or <Goto> is converted into a single AST.CallReturn or * +* AST.CallGoto object, respectively. This pass does not produce any * +* AST.If or AST.Either objects, and any AST.While object it produces * +* has an empty labDo field. * * * * - It calls the procedure AddLabelsToStmtSeq to check for missing * * labels and either add them or report an error if it finds one, * @@ -107,11 +108,11 @@ package pcal; import java.util.Hashtable; import java.util.Vector; + import pcal.exception.ParseAlgorithmException; import pcal.exception.TLAExprException; import pcal.exception.TokenizerException; import pcal.exception.UnrecoverableException; -import tla2sany.parser.ParseError; import tla2tex.Debug; @@ -292,6 +293,12 @@ public class ParseAlgorithm * Performs the initialization method of the AST to create default * * objects for each subclass. * *******************************************************************/ + + /* + * Following added by LL on 17 Aug 2015 to fix bug apparently caused by + * incomplete initialization + */ + LATsize = 0 ; } @@ -307,7 +314,8 @@ public class ParseAlgorithm * should be set to the position of the PcalParams.BeginAlg string by * * whatever method finds that string and calls GetAlgorithm. * **********************************************************************/ - { Init(charR) ; + { try { + Init(charR) ; if (fairAlgorithm) { String nextToken = GetAlgToken() ; if (!nextToken.equals(PcalParams.BeginFairAlg2)) { @@ -538,6 +546,14 @@ public class ParseAlgorithm GetLastLocationEnd())) ; return uniproc ; } + } catch (final RuntimeException e) { + // Catch generic/unhandled errors (ArrayIndexOutOfbounds/NullPointers/...) that + // the nested code might throw at us. Not converting them into a + // ParseAlgorithmException means the Toolbox silently fails to translate an + // algorithm (see Github issue at https://github.com/tlaplus/tlaplus/issues/313) + ParsingError("Unknown error at or before"); + return null; + } } /** @@ -947,6 +963,8 @@ public class ParseAlgorithm } else { result.unlabDo = GetCStmt() ; } ; + if (result.unlabDo.size() == 0) + { ParsingError("Missing body of while statement at"); } result.labDo = new Vector() ; /****************************************************************** * For historical reasons, some methods expect a labDo field to * @@ -1071,7 +1089,7 @@ public class ParseAlgorithm if (nextTok.equals("print")) { return GetPrintS() ; } ; if (nextTok.equals("assert")) { return GetAssert() ; } ; if (nextTok.equals("skip")) { return GetSkip() ; } ; - if (nextTok.equals("call")) { return GetCallOrCallReturn() ; } ; + if (nextTok.equals("call")) { return GetCallOrCallReturnOrCallGoto() ; } ; if (nextTok.equals("return")) { return GetReturn() ; } ; if (nextTok.equals("goto")) { return GetGoto() ; } ; if (nextTok.equals("while")) { return GetWhile() ; } ; @@ -1378,9 +1396,13 @@ public class ParseAlgorithm { result.Do = new Vector() ; result.Do.addElement(InnerGetWith(depth+1, begLoc)) ; }; - result.setOrigin(new Region(begLoc, - ((AST) result.Do.elementAt(result.Do.size()-1)).getOrigin().getEnd())) ; - return result ; + try { + result.setOrigin(new Region(begLoc, + ((AST) result.Do.elementAt(result.Do.size()-1)).getOrigin().getEnd())) ; + } catch (ArrayIndexOutOfBoundsException e) { + throw new ParseAlgorithmException("Missing body of with statement", result); + } + return result ; } public static AST.Assign GetAssign() throws ParseAlgorithmException @@ -1575,7 +1597,7 @@ public class ParseAlgorithm return result ; } - public static AST GetCallOrCallReturn() throws ParseAlgorithmException + public static AST GetCallOrCallReturnOrCallGoto() throws ParseAlgorithmException /********************************************************************** * Note: should not complain if it finds a return that is not inside * * a procedure because it could be in a macro that is called only * @@ -1595,6 +1617,23 @@ public class ParseAlgorithm result.setOrigin(new Region(theCall.getOrigin().getBegin(), end)) ; return result ; } + else if (PeekAtAlgToken(1).equals("goto")) + { MustGobbleThis("goto") ; + AST.CallGoto result = new AST.CallGoto(); + result.col = theCall.col ; + result.line = theCall.line ; + result.to = theCall.to ; + result.after = GetAlgToken() ; + result.args = theCall.args ; + PCalLocation end = new PCalLocation(lastTokLine-1, lastTokCol-1+result.to.length()); + result.setOrigin(new Region(theCall.getOrigin().getBegin(), end)) ; + gotoUsed = true ; + if (result.to.equals("Done") || result.to.equals("\"Done\"")) { + gotoDoneUsed = true; + } + GobbleThis(";") ; + return result ; + } else { return theCall; } } @@ -2010,15 +2049,16 @@ public class ParseAlgorithm * set assignedVbls to the set of variables being assigned * * by this statement. Should set nextStepNeedsLabel to * * true and set assignedVbls to a non-empty set iff this is * - * a call, return, or callReturn. * + * a call, return, callReturn, or callGoto. * ************************************************************/ nextStepNeedsLabel = false ; Vector assignedVbls = new Vector() ; /************************************************************ - * Set isCallOrReturn true iff this is a call, return, or * - * callReturn. Will set setsPrcdVbls true iff this is a * - * return or callReturn or a call of currentProcedure. * + * Set isCallOrReturn true iff this is a call, return, * + * callReturn, or callGoto. Will set setsPrcdVbls true iff * + * this is a return or callReturn or a call of * + * currentProcedure. * ************************************************************/ boolean isCallOrReturn = false ; boolean setsPrcdVbls = false ; @@ -2031,6 +2071,12 @@ public class ParseAlgorithm if (obj.to.equals(currentProcedure)) { setsPrcdVbls = true ; } ; } + else if (stmt.getClass().equals(AST.CallGotoObj.getClass())) + { AST.CallGoto obj = (AST.CallGoto) stmt ; + isCallOrReturn = true ; + if (obj.to.equals(currentProcedure)) + { setsPrcdVbls = true ; } ; + } else if ( stmt.getClass().equals(AST.ReturnObj.getClass()) || stmt.getClass().equals(AST.CallReturnObj.getClass()) ) @@ -2483,8 +2529,10 @@ public class ParseAlgorithm } ; result = 1 ; } - else if (node.getClass().equals( - AST.GotoObj.getClass()) + else if ( node.getClass().equals( + AST.GotoObj.getClass()) + || node.getClass().equals( + AST.CallGotoObj.getClass()) ) { result = 1 ; } @@ -3151,6 +3199,25 @@ public class ParseAlgorithm return result; } ; + if (stmt.getClass().equals( AST.CallGotoObj.getClass())) + { AST.CallGoto tstmt = (AST.CallGoto) stmt ; + AST.CallGoto result = new AST.CallGoto() ; + result.col = tstmt.col ; + result.line = tstmt.line ; + result.macroCol = tstmt.macroCol ; + result.macroLine = tstmt.macroLine ; + result.setOrigin(tstmt.getOrigin()) ; + if (macroLine > 0) + { result.macroLine = macroLine ; + result.macroCol = macroCol ; + } ; + result.to = tstmt.to ; + result.after = tstmt.after ; + result.args = + TLAExpr.SeqSubstituteForAll(tstmt.args, args, params) ; + return result; + } ; + if (stmt.getClass().equals( AST.GotoObj.getClass())) { AST.Goto tstmt = (AST.Goto) stmt ; AST.Goto result = new AST.Goto() ; @@ -3424,20 +3491,17 @@ public class ParseAlgorithm + tok + "\"") ; } ; } - public static void MustGobbleThis(String str) throws ParseAlgorithmException - { - String tok; - try - { - tok = GetAlgToken(); - } catch (ParseAlgorithmException e) - { - throw new ParseAlgorithmException(e.getMessage()); - } - if (! tok.equals(str) ) - { PcalDebug.ReportBug("Expected \"" + str + "\" but found \"" - + tok + "\"") ; } ; - } + public static void MustGobbleThis(final String str) throws ParseAlgorithmException { + final String tok; + try { + tok = GetAlgToken(); + } catch (ParseAlgorithmException e) { + throw new ParseAlgorithmException(e.getMessage()); + } + if (!tok.equals(str)) { + ParsingError("Expected \"" + str + "\" but found \"" + tok + "\""); + } + } public static boolean GobbleEqualOrIf() throws ParseAlgorithmException /********************************************************************** diff --git a/tlatools/src/pcal/PcalBuiltInSymbols.java b/tlatools/src/pcal/PcalBuiltInSymbols.java index 0f27eb7d85dc9cf6bd50e9bd7a7c3bdc6d8e8e5f..b3de7693e788b11d5ead668d9178c91c4ccda2cd 100644 --- a/tlatools/src/pcal/PcalBuiltInSymbols.java +++ b/tlatools/src/pcal/PcalBuiltInSymbols.java @@ -37,25 +37,26 @@ import java.util.Hashtable; import tla2tex.Misc; import tla2tex.Symbol; +import util.TLAConstants; public final class PcalBuiltInSymbols { /*********************************************************************** * The following three hash tables are built by the Initialize method. * ***********************************************************************/ - private static Hashtable builtInHashTable = new Hashtable(200); + private static Hashtable<String, Symbol> builtInHashTable = new Hashtable<>(200); /********************************************************************* * Maps built-in symbols (which are strings) to their Symbol * * objects. * *********************************************************************/ - private static Hashtable prefixHashTable = new Hashtable(700); + private static Hashtable<String, String> prefixHashTable = new Hashtable<>(700); /********************************************************************* * A table containing the prefixes of all built-in symbols. (It * * holds only their keys.) * *********************************************************************/ - private static Hashtable stringCharTable = new Hashtable(100); + private static Hashtable<String, String> stringCharTable = new Hashtable<>(100); /********************************************************************* * A table of all the characters that may appear in a TLA+ string * * token. * @@ -79,7 +80,7 @@ public final class PcalBuiltInSymbols } ; public static Symbol GetBuiltInSymbol(String str) - { return (Symbol) builtInHashTable.get(str); + { return builtInHashTable.get(str); } ; public static boolean IsBuiltInPrefix(String str) @@ -134,20 +135,20 @@ public final class PcalBuiltInSymbols add("AXIOM", "{\\AXIOM}", Symbol.KEYWORD, 0); add("BOOLEAN", "{\\BOOLEAN}", Symbol.KEYWORD, 0); add("CASE", "{\\CASE}", Symbol.KEYWORD, 0); - add("CONSTANT", "{\\CONSTANT}", Symbol.KEYWORD, 0); - add("CONSTANTS", "{\\CONSTANTS}", Symbol.KEYWORD, 0); + add(TLAConstants.KeyWords.CONSTANT, "{\\CONSTANT}", Symbol.KEYWORD, 0); + add(TLAConstants.KeyWords.CONSTANTS, "{\\CONSTANTS}", Symbol.KEYWORD, 0); add("EXCEPT", "{\\EXCEPT}", Symbol.KEYWORD, 0); - add("EXTENDS", "{\\EXTENDS}", Symbol.KEYWORD, 0); + add(TLAConstants.KeyWords.EXTENDS, "{\\EXTENDS}", Symbol.KEYWORD, 0); add("FALSE", "{\\FALSE}", Symbol.KEYWORD, 0); add("IF", "{\\IF}", Symbol.KEYWORD, 0); add("INSTANCE", "{\\INSTANCE}", Symbol.KEYWORD, 0); add("LOCAL", "{\\LOCAL}", Symbol.KEYWORD, 0); - add("MODULE", "{\\MODULE}", Symbol.KEYWORD, 0); + add(TLAConstants.KeyWords.MODULE, "{\\MODULE}", Symbol.KEYWORD, 0); add("OTHER", "{\\OTHER}", Symbol.KEYWORD, 0); add("STRING", "{\\STRING}", Symbol.KEYWORD, 0); add("THEOREM", "{\\THEOREM}", Symbol.KEYWORD, 0); add("TRUE", "{\\TRUE}", Symbol.KEYWORD, 0); - add("VARIABLE", "{\\VARIABLE}", Symbol.KEYWORD, 0); + add(TLAConstants.KeyWords.VARIABLE, "{\\VARIABLE}", Symbol.KEYWORD, 0); add("VARIABLES", "{\\VARIABLES}", Symbol.KEYWORD, 0); add("WITH", "{\\WITH}", Symbol.KEYWORD, 0); @@ -340,9 +341,9 @@ public final class PcalBuiltInSymbols * Initializes prefixHashTable, assuming that builtInHashTable is * * already initialized. * *********************************************************************/ - { Enumeration builtInEnum = builtInHashTable.keys(); + { Enumeration<String> builtInEnum = builtInHashTable.keys(); while (builtInEnum.hasMoreElements()) - { String symbol = (String) builtInEnum.nextElement(); + { String symbol = builtInEnum.nextElement(); if ( Misc.IsLetter(symbol.charAt(0)) || (symbol.length() > 1) && (symbol.charAt(0) == '\\') diff --git a/tlatools/src/pcal/PcalDebug.java b/tlatools/src/pcal/PcalDebug.java index 87e4933479a4b26ebd7641d140bd1a84a753d906..896e00bb6eda7148da865cd7a2da256d53467f57 100644 --- a/tlatools/src/pcal/PcalDebug.java +++ b/tlatools/src/pcal/PcalDebug.java @@ -105,14 +105,15 @@ public class PcalDebug * This method is called to report a bug in the program and abort. * *********************************************************************/ { + final StringBuilder sb = new StringBuilder("You have discovered a bug in pcal.trans.\n"); + sb.append("Send the following information and the\ninput file to the current maintainer(s).\n\n -- "); + sb.append(msg).append("."); + ToolIO.out.println(""); - ToolIO.out.println("You have discovered a bug in pcal.trans."); - ToolIO.out.println("Send the following information and the"); - ToolIO.out.println("input file to the current maintainer(s)."); + ToolIO.out.println(sb.toString()); ToolIO.out.println(""); - ToolIO.out.println(" -- " + msg + "."); - ToolIO.out.println(""); - throw new Error(); + + throw new Error(sb.toString()); }; public static void printObjectArray(Object[] array, String name) diff --git a/tlatools/src/pcal/PcalFixIDs.java b/tlatools/src/pcal/PcalFixIDs.java index 282c77cdc81921944749280c5ec865155137d016..38a43533e1ec81819e09e56d2d76c2e5e395e6ed 100644 --- a/tlatools/src/pcal/PcalFixIDs.java +++ b/tlatools/src/pcal/PcalFixIDs.java @@ -73,6 +73,8 @@ public class PcalFixIDs { FixReturn((AST.Return) ast, context); else if (ast.getClass().equals(AST.CallReturnObj.getClass())) FixCallReturn((AST.CallReturn) ast, context); + else if (ast.getClass().equals(AST.CallGotoObj.getClass())) + FixCallGoto((AST.CallGoto) ast, context); else if (ast.getClass().equals(AST.GotoObj.getClass())) FixGoto((AST.Goto) ast, context); @@ -181,10 +183,25 @@ public class PcalFixIDs { // For some reason, this originally called PcalDebug.ReportBug. // Since it can occur just because there is no procedure by that // name in the code, it occurs on an error. Fixed 31 May 2013 by LL. + // This fix caused the for loop that follows to throw an ArrayIndexOutOfBounds + // exception. When run from the Toolbox, this caused the error message + // to be lost and the Translate command to fail with no message. + // It appears that it's not necessary to report the error here, because + // it will be caught later. Moreover, when caught later, the location + // of the bad procedure name is indicated. That location doesn't seem to + // available here. However, I'm hesitant to remove the error report in case + // the error isn't caught later in all cases. So I have added the return + // to avoid the exception. Hopefully, no further exceptions can be caused + // by the error. However, I don't know how to make sure that's the + // case without figuring out how the code works. + // I also removed an unhelpful part of the message. LL 30 Aug 2015 PcalDebug.reportError( - "Could not find procedure name `" + pName + - "' in method FixMultiprocess"); - } + "Could not find procedure name `" + pName +"'" + // + "' in method FixMultiprocess" + ) ; + return ; // Added 30 Aug 2015 + + } // For each k such that path[pNum][k] is true // (meaning that procedure number pNum calls // procedure number k), add @@ -380,6 +397,13 @@ public class PcalFixIDs { FixExpr((TLAExpr) ast.args.elementAt(i), context); } + private static void FixCallGoto(AST.CallGoto ast, String context) throws PcalFixIDException { + ast.after = st.UseThis(PcalSymTab.PROCEDURE, ast.after, context); + ast.to = st.UseThis(PcalSymTab.PROCEDURE, ast.to, context); + for (int i = 0; i < ast.args.size(); i++) + FixExpr((TLAExpr) ast.args.elementAt(i), context); + } + private static void FixGoto(AST.Goto ast, String context) throws PcalFixIDException { /* * Report an error if the goto destination is not a label. This check diff --git a/tlatools/src/pcal/PcalParams.java b/tlatools/src/pcal/PcalParams.java index c7315d531780237fcaa76c6237110856c3213658..51be055c814bf01e5d2a60033e494c3e902295e3 100644 --- a/tlatools/src/pcal/PcalParams.java +++ b/tlatools/src/pcal/PcalParams.java @@ -1,318 +1,328 @@ -/*************************************************************************** -* CLASS PcalParams * -* * -* The fields of this class consist of all the parameters used by * -* pcal.trans. Some of them are set by program options. * -***************************************************************************/ -package pcal ; -import java.util.Vector; - -/** - * Holds parameters for the PCal Translator - * @author Keith Marzullo - * @author Simon Zambrovski - * @version $Id$ - */ -public final class PcalParams -{ - /** - * Parameters to be updated on each new release. - */ - public static final String modDate = "2 Apr 2013"; - public static final String version = "1.8"; - /** - * SZ Mar 9, 2009: - * Added re-initialization method. Since PcalParams class - * is used instead of PcalParams instance, it is required to - * take care of parameter initialization and de-initialization - * by explicit method. This is required in order to make PCal - * instance reentrant. - * - * Maybe at some point in time this should be converted to an ordinary - * configuration object, from the collection of public static variables. - */ - public static void resetParams() - { - Debug = false; - SpecOption = false; - MyspecOption = false; - Spec2Option = false; - Myspec2Option = false; - SpecFile = ""; - WriteASTFlag = false; - LabelFlag = false; - ReportLabelsFlag = false; - LabelRoot = "Lbl_"; - FairnessOption = ""; - FairAlgorithm = false; - CheckTermination = false; - Nocfg = false; - NoDoneDisjunct = false; - optionsInFile = false; - versionOption = null; - inputVersionNumber = VersionToNumber(PcalParams.version); - PcalTLAGen.wrapColumn = 78; - PcalTLAGen.ssWrapColumn = 45; - tlaPcalMapping = null ; - - } - - - /************************************************************************* - * Parameters related to non-file Options * - *************************************************************************/ - public static boolean Debug = false ; - /*********************************************************************** - * True if the -debug option is chosen. * - ***********************************************************************/ - - public static boolean SpecOption = false ; - /*********************************************************************** - * True if the -spec option is chosen. * - ***********************************************************************/ - - public static boolean MyspecOption = false ; - /*********************************************************************** - * True if the -myspec option is chosen. * - ***********************************************************************/ - - public static boolean Spec2Option = false ; - /*********************************************************************** - * True if the -spec2 option is chosen. * - ***********************************************************************/ - - public static boolean Myspec2Option = false ; - /*********************************************************************** - * True if the -myspec2 option is chosen. * - ***********************************************************************/ - - public static String SpecFile = "" ; - /*********************************************************************** - * The file name if the -spec option is chosen. * - ***********************************************************************/ - - public static boolean WriteASTFlag = false ; - /*********************************************************************** - * True if the -writeAST option is chosen. * - ***********************************************************************/ - - public static boolean LabelFlag = false ; - /********************************************************************* - * True iff the -label option is chosen. * - *********************************************************************/ - - public static boolean ReportLabelsFlag = false ; - /********************************************************************* - * True iff the -reportLabels option is chosen. * - *********************************************************************/ - - public static String LabelRoot = "Lbl_" ; - /********************************************************************* - * The root of translator-generated labels, set to its non-default * - * value by the -labelRoot option. * - *********************************************************************/ - - /************************************************************************* - * Parameters for Spec and .cfg file options. * - *************************************************************************/ - public static String FairnessOption = "" ; - /********************************************************************* - * Should be "", "wf", "sf", "wfNext", or "sfNext". * - *********************************************************************/ - - public static boolean CheckTermination = false ; - /********************************************************************* - * True iff there is a -termination option. * - *********************************************************************/ - - public static boolean Nocfg = false ; - /********************************************************************* - * True iff there is a -nocfg option. * - *********************************************************************/ - - public static boolean NoDoneDisjunct = false ; - /********************************************************************* - * True iff there is a -noDoneDisjunct option. * - *********************************************************************/ - - /********************************************************************** - * The following parameter is set true if --fair algorithm is used. * - *********************************************************************/ - public static boolean FairAlgorithm = false ; - - /************************************************************************* - * Parameters related to language definition. * - *************************************************************************/ - public static TLAExpr DefaultVarInit() - /********************************************************************* - * Returns the default initial value of a variable declared with no * - * value specified, which is herein defined to be "{}". * - * * - * Default initial value changed to "defaultInitValue" * - * by LL on 22 Aug 2007 * - *********************************************************************/ - { Vector line = new Vector() ; -// line.addElement(new TLAToken("{", 0, 0)) ; -// line.addElement(new TLAToken("}", 0, 0)) ; - line.addElement(new TLAToken("defaultInitValue", 0, 0)); - Vector vec = new Vector() ; - vec.addElement(line) ; - TLAExpr exp = new TLAExpr(vec) ; - exp.normalize() ; - return exp ; - } - - /*********************************************************************** - * The strings identifying the beginning of the algorithm in the .tla * - * file. * - * The strings "--fair" and "algorithm" added by LL on 6 July 2011. * - ***********************************************************************/ - public static final String BeginAlg = "--algorithm" ; - public static final String BeginFairAlg = "--fair" ; - public static final String BeginFairAlg2 = "algorithm" ; - - - - /*********************************************************************** - * The strings marking the beginning and end of the translated * - * algorithm in a .tla input file. The translation is put immediately * - * after any line containing * - * * - * BeginXLation1 [one or more spaces] BeginXlation2 * - * * - * It is followed by a line containing * - * * - * EndXLation1 [one or more spaces] EndXlation2. * - ***********************************************************************/ - public static final String BeginXlation1 = "BEGIN" ; - public static final String BeginXlation2 = "TRANSLATION" ; - - public static final String EndXlation1 = "END" ; - public static final String EndXlation2 = "TRANSLATION" ; - - /************************************************************************* - * The string identifying the end of the automatically generated part of * - * the .cfg file and the beginning of the user-added part. * - *************************************************************************/ - public static String CfgFileDelimiter = - "\\* Add statements after this line." ; - -/************ Stuff for .pcal file ***************************************/ - -// /*********************************************************************** -// * The string identifying the beginning of the algorithm in the .pcal * -// * file. * -// ***********************************************************************/ -// public static final String PcalBeginAlg = "algorithm"; -// -// /** -// * The <row, col> (in Java coordinates) of the beginning of the -// * "algorithm" token in the .pcal file. -// */ -// public static IntPair endOfPreamble = null; -// -// /** -// * The <row, col> (in Java coordinates) of the beginning of the TLA+ -// * "code" that follows the algorithm in the input (.pcal) and output -// * (.tla) files. For outputSuffixLoc, the column always equals 0. -// */ -// public static IntPair inputSuffixLoc = null; -// public static IntPair outputSuffixLoc = null; -// - /************************************************************************* - * File parameters. * - *************************************************************************/ - public static String TLAInputFile = "" ; - /*********************************************************************** - * The name of the input file, with no extension. It is set to equal * - * the argument with which the program is called, minus the extension. * - * With the introduction of pcal files, the name no longer makes sense. * - ***********************************************************************/ - - /** - * Pcal-File Parameters - * The following parameters were introduced when .pcal files were - * (briefly) added. However, most of them still seem to be used. - */ - - public static boolean optionsInFile = false ; - // Set true when an options statement has been found in the - // module. It is a kludgy way to pass an argument to - // trans.parseAndProcessStringArguments; things are done this - // way because of the way the code evolved, and no intelligent - // design has stepped in to fix it. - public static String versionOption = null; - public static int inputVersionNumber = VersionToNumber(PcalParams.version); - // The input file's version number * 1000 -// public static boolean readOnly = false; - // True iff this is a .pcal input file and the .tla file should - // not be writable. This is obsolete and is not used. - - /** - * The following parameter is used to hold the TLAtoPCalMapping object - * that is computed by the translation. Some of that object's fields are - * used in creating the actual mapping. It's easier to have the methods - * that need to use those fields access them via this parameter than to do - * the more politically correct thing and pass the fields or the object - * as a parameter in the method calls. - */ - public static TLAtoPCalMapping tlaPcalMapping ; - - /** - * If str is a version number like 3.17, then this returns 1000 times - * its numeric value--e.g., 3170. Otherwise, it returns -1. - * - */ - public static int VersionToNumber(String str) { - boolean error = false ; - int i = 0; - int result = 0; - int digitsAfterDecimal = 0; - boolean afterDecimal = false; - while ((!error) && i < str.length()) { - char c = str.charAt(i); - if (('0' <= c) && (c <= '9')) { - result = 10 * result + (c - '0'); - if (afterDecimal) { - digitsAfterDecimal++; - if (digitsAfterDecimal > 3) { - error = true; - } - } - } else if (c == '.') { - afterDecimal = true; - } else { - error = true; - } - i++; - } - if (error) { - return -1; - } - for (int j = 0; j < 3 - digitsAfterDecimal; j++) { - result = 10 * result; - } - return result; - } - /** - * Processes the version argument ver. It sets versionNumber - * and returns true if it is a legal version number; otherwise, - * it reports the error with PcalDebug.reportError and returns false. - */ - static boolean ProcessVersion(String ver) { - int vnum = VersionToNumber(ver); - if (vnum < 0) { - PcalDebug.reportError("Illegal version " + ver + " specified."); - return false; - } - if (vnum > VersionToNumber(PcalParams.version)) { - PcalDebug.reportError("Specified version " + ver + - " later than current version " + PcalParams.version); - return false; - } - inputVersionNumber = vnum; - return true ; - } - } - -/* last modified on Thu 23 Aug 2007 at 10:40:25 PST by lamport */ +/*************************************************************************** +* CLASS PcalParams * +* * +* The fields of this class consist of all the parameters used by * +* pcal.trans. Some of them are set by program options. * +***************************************************************************/ +package pcal ; +import java.util.Vector; + +/** + * Holds parameters for the PCal Translator + * @author Keith Marzullo + * @author Simon Zambrovski + * @version $Id$ + */ +public final class PcalParams +{ + /** + * Parameters to be updated on each new release. + */ + public static final String modDate = "31 December 2020"; + // Can't increment 1.9 to 1.10 because VersionToNumber(str) calculates a lower + // numerical value for 1.10 than it does for 1.9. This breaks the FairSeq?Test + // tests. Until we switch from 1.xx to 2.0, increment versionWeight along with + // version. + public static final int versionWeight = 1902; + public static final String version = "1.11"; + /** + * SZ Mar 9, 2009: + * Added re-initialization method. Since PcalParams class + * is used instead of PcalParams instance, it is required to + * take care of parameter initialization and de-initialization + * by explicit method. This is required in order to make PCal + * instance reentrant. + * + * Maybe at some point in time this should be converted to an ordinary + * configuration object, from the collection of public static variables. + */ + public static void resetParams() + { + Debug = false; + SpecOption = false; + MyspecOption = false; + Spec2Option = false; + Myspec2Option = false; + SpecFile = ""; + WriteASTFlag = false; + LabelFlag = false; + ReportLabelsFlag = false; + LabelRoot = "Lbl_"; + FairnessOption = ""; + FairAlgorithm = false; + CheckTermination = false; + Nocfg = false; + NoDoneDisjunct = false; + optionsInFile = false; + versionOption = null; + inputVersionNumber = PcalParams.versionWeight; + PcalTLAGen.wrapColumn = 78; + PcalTLAGen.ssWrapColumn = 45; + tlaPcalMapping = null ; + + } + + + /************************************************************************* + * Parameters related to non-file Options * + *************************************************************************/ + public static boolean Debug = false ; + /*********************************************************************** + * True if the -debug option is chosen. * + ***********************************************************************/ + + public static boolean SpecOption = false ; + /*********************************************************************** + * True if the -spec option is chosen. * + ***********************************************************************/ + + public static boolean MyspecOption = false ; + /*********************************************************************** + * True if the -myspec option is chosen. * + ***********************************************************************/ + + public static boolean Spec2Option = false ; + /*********************************************************************** + * True if the -spec2 option is chosen. * + ***********************************************************************/ + + public static boolean Myspec2Option = false ; + /*********************************************************************** + * True if the -myspec2 option is chosen. * + ***********************************************************************/ + + public static String SpecFile = "" ; + /*********************************************************************** + * The file name if the -spec option is chosen. * + ***********************************************************************/ + + public static boolean tlcTranslation() { + return PcalParams.SpecOption || PcalParams.MyspecOption || PcalParams.Spec2Option + || PcalParams.Myspec2Option; + } + + public static boolean WriteASTFlag = false ; + /*********************************************************************** + * True if the -writeAST option is chosen. * + ***********************************************************************/ + + public static boolean LabelFlag = false ; + /********************************************************************* + * True iff the -label option is chosen. * + *********************************************************************/ + + public static boolean ReportLabelsFlag = false ; + /********************************************************************* + * True iff the -reportLabels option is chosen. * + *********************************************************************/ + + public static String LabelRoot = "Lbl_" ; + /********************************************************************* + * The root of translator-generated labels, set to its non-default * + * value by the -labelRoot option. * + *********************************************************************/ + + /************************************************************************* + * Parameters for Spec and .cfg file options. * + *************************************************************************/ + public static String FairnessOption = "" ; + /********************************************************************* + * Should be "", "wf", "sf", "wfNext", or "sfNext". * + *********************************************************************/ + + public static boolean CheckTermination = false ; + /********************************************************************* + * True iff there is a -termination option. * + *********************************************************************/ + + public static boolean Nocfg = false ; + /********************************************************************* + * True iff there is a -nocfg option. * + *********************************************************************/ + + public static boolean NoDoneDisjunct = false ; + /********************************************************************* + * True iff there is a -noDoneDisjunct option. * + *********************************************************************/ + + /********************************************************************** + * The following parameter is set true if --fair algorithm is used. * + *********************************************************************/ + public static boolean FairAlgorithm = false ; + + /************************************************************************* + * Parameters related to language definition. * + *************************************************************************/ + public static TLAExpr DefaultVarInit() + /********************************************************************* + * Returns the default initial value of a variable declared with no * + * value specified, which is herein defined to be "{}". * + * * + * Default initial value changed to "defaultInitValue" * + * by LL on 22 Aug 2007 * + *********************************************************************/ + { Vector<TLAToken> line = new Vector<TLAToken>() ; +// line.addElement(new TLAToken("{", 0, 0)) ; +// line.addElement(new TLAToken("}", 0, 0)) ; + line.addElement(new TLAToken("defaultInitValue", 0, 0)); + Vector<Vector<TLAToken>> vec = new Vector<Vector<TLAToken>>() ; + vec.addElement(line) ; + TLAExpr exp = new TLAExpr(vec) ; + exp.normalize() ; + return exp ; + } + + /*********************************************************************** + * The strings identifying the beginning of the algorithm in the .tla * + * file. * + * The strings "--fair" and "algorithm" added by LL on 6 July 2011. * + ***********************************************************************/ + public static final String BeginAlg = "--algorithm" ; + public static final String BeginFairAlg = "--fair" ; + public static final String BeginFairAlg2 = "algorithm" ; + + + + /*********************************************************************** + * The strings marking the beginning and end of the translated * + * algorithm in a .tla input file. The translation is put immediately * + * after any line containing * + * * + * BeginXLation1 [one space] BeginXlation2 [one space] BeginXlation3 * + * * + * It is followed by a line containing * + * * + * EndXLation1 [one space] EndXlation2 [one space] EndXlation3 * + ***********************************************************************/ + public static final String BeginXlation1 = "BEGIN" ; + public static final String BeginXlation2 = "TRANSLATION" ; + + public static final String EndXlation1 = "END" ; + public static final String EndXlation2 = "TRANSLATION" ; + + /************************************************************************* + * The string identifying the end of the automatically generated part of * + * the .cfg file and the beginning of the user-added part. * + *************************************************************************/ + public static String CfgFileDelimiter = + "\\* Add statements after this line." ; + +/************ Stuff for .pcal file ***************************************/ + +// /*********************************************************************** +// * The string identifying the beginning of the algorithm in the .pcal * +// * file. * +// ***********************************************************************/ +// public static final String PcalBeginAlg = "algorithm"; +// +// /** +// * The <row, col> (in Java coordinates) of the beginning of the +// * "algorithm" token in the .pcal file. +// */ +// public static IntPair endOfPreamble = null; +// +// /** +// * The <row, col> (in Java coordinates) of the beginning of the TLA+ +// * "code" that follows the algorithm in the input (.pcal) and output +// * (.tla) files. For outputSuffixLoc, the column always equals 0. +// */ +// public static IntPair inputSuffixLoc = null; +// public static IntPair outputSuffixLoc = null; +// + /************************************************************************* + * File parameters. * + *************************************************************************/ + public static String TLAInputFile = "" ; + /*********************************************************************** + * The name of the input file, with no extension. It is set to equal * + * the argument with which the program is called, minus the extension. * + * With the introduction of pcal files, the name no longer makes sense. * + ***********************************************************************/ + + /** + * Pcal-File Parameters + * The following parameters were introduced when .pcal files were + * (briefly) added. However, most of them still seem to be used. + */ + + public static boolean optionsInFile = false ; + // Set true when an options statement has been found in the + // module. It is a kludgy way to pass an argument to + // trans.parseAndProcessStringArguments; things are done this + // way because of the way the code evolved, and no intelligent + // design has stepped in to fix it. + public static String versionOption = null; + public static int inputVersionNumber = PcalParams.versionWeight; + // The input file's version number * 1000 +// public static boolean readOnly = false; + // True iff this is a .pcal input file and the .tla file should + // not be writable. This is obsolete and is not used. + + /** + * The following parameter is used to hold the TLAtoPCalMapping object + * that is computed by the translation. Some of that object's fields are + * used in creating the actual mapping. It's easier to have the methods + * that need to use those fields access them via this parameter than to do + * the more politically correct thing and pass the fields or the object + * as a parameter in the method calls. + */ + public static TLAtoPCalMapping tlaPcalMapping ; + + /** + * If str is a version number like 3.17, then this returns 1000 times + * its numeric value--e.g., 3170. Otherwise, it returns -1. + * + */ + public static int VersionToNumber(String str) { + boolean error = false ; + int i = 0; + int result = 0; + int digitsAfterDecimal = 0; + boolean afterDecimal = false; + while ((!error) && i < str.length()) { + char c = str.charAt(i); + if (('0' <= c) && (c <= '9')) { + result = 10 * result + (c - '0'); + if (afterDecimal) { + digitsAfterDecimal++; + if (digitsAfterDecimal > 3) { + error = true; + } + } + } else if (c == '.') { + afterDecimal = true; + } else { + error = true; + } + i++; + } + if (error) { + return -1; + } + for (int j = 0; j < 3 - digitsAfterDecimal; j++) { + result = 10 * result; + } + return result; + } + /** + * Processes the version argument ver. It sets versionNumber + * and returns true if it is a legal version number; otherwise, + * it reports the error with PcalDebug.reportError and returns false. + */ + static boolean ProcessVersion(String ver) { + int vnum = VersionToNumber(ver); + if (vnum < 0) { + PcalDebug.reportError("Illegal version " + ver + " specified."); + return false; + } + if (vnum > PcalParams.versionWeight) { + PcalDebug.reportError("Specified version " + ver + + " later than current version " + PcalParams.version); + return false; + } + inputVersionNumber = vnum; + return true ; + } + } + +/* last modified on Thu 23 Aug 2007 at 10:40:25 PST by lamport */ diff --git a/tlatools/src/pcal/PcalResourceFileReader.java b/tlatools/src/pcal/PcalResourceFileReader.java index db66736b0597694a3d92a9fc38bb2ee1ff6dd82e..ebfb9f307dfb34bbbd9a9b12421be1c537824fe1 100644 --- a/tlatools/src/pcal/PcalResourceFileReader.java +++ b/tlatools/src/pcal/PcalResourceFileReader.java @@ -47,12 +47,12 @@ public class PcalResourceFileReader inputReader = new BufferedReader(new InputStreamReader(input)) ; }; - public static Vector ResourceFileToStringVector(String fileName) throws PcalResourceFileReaderException + public static Vector<String> ResourceFileToStringVector(String fileName) throws PcalResourceFileReaderException /*********************************************************************** * Reads file fileName into a StringVector, a vector in which each * * element is a line of the file. * ***********************************************************************/ - { Vector inputVec = new Vector(100) ; + { Vector<String> inputVec = new Vector<String>(100) ; PcalResourceFileReader wordFileReader = new PcalResourceFileReader(fileName); diff --git a/tlatools/src/pcal/PcalSymTab.java b/tlatools/src/pcal/PcalSymTab.java index f3ce69d66be24b78589a9e17d842060c6c9dc65e..e31fffa8d5b55978eb776c14c66e3a2b49ce99d9 100644 --- a/tlatools/src/pcal/PcalSymTab.java +++ b/tlatools/src/pcal/PcalSymTab.java @@ -484,6 +484,8 @@ public class PcalSymTab { ExtractReturn((AST.Return) ast, context, cType); else if (ast.getClass().equals(AST.CallReturnObj.getClass())) ExtractCallReturn((AST.CallReturn) ast, context, cType); + else if (ast.getClass().equals(AST.CallGotoObj.getClass())) + ExtractCallGoto((AST.CallGoto) ast, context, cType); else if (ast.getClass().equals(AST.GotoObj.getClass())) ExtractGoto((AST.Goto) ast, context, cType); @@ -669,6 +671,11 @@ public class PcalSymTab { String cType) { } + private void ExtractCallGoto(AST.CallGoto ast, + String context, + String cType) { + } + private void ExtractGoto(AST.Goto ast, String context, String cType) { } diff --git a/tlatools/src/pcal/PcalTLAGen.java b/tlatools/src/pcal/PcalTLAGen.java index 5d040ea3caf6c40945d040497ef12ff4f1dea73d..45c84dd6b9000818e2fc1d1677776b0b20a042e2 100644 --- a/tlatools/src/pcal/PcalTLAGen.java +++ b/tlatools/src/pcal/PcalTLAGen.java @@ -1,4327 +1,4372 @@ -package pcal; - -import java.util.Vector; - -import pcal.AST.VarDecl; -import pcal.exception.PcalTLAGenException; -import pcal.exception.TLAExprException; -import tla2tex.Debug; - -/**************************************************************************** - * Given an exploded and disambiguated AST, generate the equivalent TLA+. - * <br> - * {@link PcalTLAGen#generate(AST, PcalSymTab)} returns a vector of Strings, one entry per line of generated TLA+. - * - * @version $Id$ - * @author Leslie Lamport (modified on Thu 6 March 2008 at 10:16:22 PST) - * (minor change on 9 December 2009) - * @author keith (modified on Mon 3 Oct 2005 at 21:43:09 UT) - * - ****************************************************************************/ -public class PcalTLAGen -{ - // Constants that control formatting - public final static boolean boxUnderCASE = true; /* else [] at end of line */ - - // The following two variables made non-final on 9 Dec 2009 so they can - // be set by options. They are initialized in PcalParams.resetParams(). - public static int wrapColumn ; - /* If the line width will be greater than this, then try to wrap */ - public static int ssWrapColumn ; - // I think that this is used as follows: - // when translating an assignment statement (or multiassignment?) - // to var' = [var EXCEPT ...], it begins the ... on a new line - // iff the ... would begin in a column > ssWrapColumn. - // For the time being, it is set to wrapColumn - 33. We may want - // to do something cleverer or else make it a user option. - - // Private class variables - /** The tlacode field accumulates the translation as it is constructed. It - * should be a vector of separate lines. Keiths original implementation put - * multiple lines in a single element of tlacode in: - * - * GenVarsAndDefs - * GenVarDecl - */ - private Vector tlacode = new Vector(); /* of lines */ - - /** - * The tlacodeNextLine field accumulates characters for the next - * line of tlacode. It is always a string. It is assumed that - * when it equals "", then a new line can be started without - * adding the current string in tlacodeNextLine as a new line. - */ - private String tlacodeNextLine = "" ; - - /** - * mappingVector is a local pointer to {@link TLAtoPCalMapping#mappingVector}, - * which is used to accumulate the TLA+ to PlusCal mapping. It approximately - * reflects the TLA+ that has been inserted in the {@link PcalTLAGen#tlacode} - * vector. It is set in the {@link TLAtoPCalMapping#generate} method. - */ - private Vector mappingVector; - - /** - * mappingVectorNextLine contains the sequence of MappingObject objects - * that correspond to the strings added to tlacodeNextLine. - */ - private Vector mappingVectorNextLine = new Vector() ; - - /** - * The self field is set to "self" by GenProcess when called for a single process - * (rather than a process set) and by GenProcedure for a multiprocess algorithm. - * It is set to the process id by GenProcess when called for a single process. - * selfIsSelf is set to true when self is set to "self", and to false when self is - * set to a process id. The self field never seems to be reset to null. - */ - private TLAExpr self = null; // changed by LL on 22 jan 2011 from: private String self = null; /* for current process */ - private boolean selfIsSelf = false; - - private Vector vars = new Vector(); /* list of all disambiguated vars */ - private Vector pcV = new Vector(); /* sublist of vars of variables representing - procedure parameters and procedure variables */ - private Vector psV = new Vector(); /* sublist of vars local to a process set */ - private PcalSymTab st = null; /* symbol table */ - private boolean mp = false; /* true if multiprocess, else unip */ - private Vector nextStep = new Vector(); /* unparam actions */ // For multiprocess alg, these are the individual (=) processes - private Vector nextStepSelf = new Vector(); /* param actions */ // These are process sets (\in processes) and procedures - // Following added to keep track of the length of the "lbl... == /\ " - // that precedes all the statements in the definition of a label's action - // because Keith screwed up and handled the assignment to the pc different - // from that of all other variables, forgetting that the subscript exp - // in pc[exp] := ... can be multi-line. - private int kludgeToFixPCHandlingBug ; - /** - * The public method: generate TLA+ as a vector of strings. - * @param ast the AST of the PCal - * @param symtab the symbol table - * @return the vector of strings with TLA+ code - * @throws PcalTLAGenException on any unrecoverable methods - */ - - /* - * The string currentProcName is the name of the current process (for a multiprocess algorithm) - * or "Next" for a uniprocess algorithm. When ParseAlgorithm.omitPC is true, this is used - * instead of the label when generating the process's or the entire algorithm's next-state - * action. Thus, with a single label lbl, this generates - * Next == Xlation - * instead of - * lbl == Xlation - * Next == lbl - */ - private String currentProcName ; - - /** - * Generates the translation. - * - * @param ast The AST produced by parsing and exploding. - * @param symtab The symbol table. - * @param report A vector of strings, containing the reports of renaming. - * @return A vector of strings. - * @throws PcalTLAGenException - */ - public Vector generate(AST ast, PcalSymTab symtab, Vector report) throws PcalTLAGenException - { - TLAtoPCalMapping map = PcalParams.tlaPcalMapping; - mappingVector = new Vector(50); - /* - * Add the reports of renaming to the output. - */ - for (int i = 0; i < report.size(); i++) { - addOneLineOfTLA((String) report.elementAt(i)); - } - - st = symtab; - GenSym(ast, ""); - - /* - * We put at the beginning and end of mappingVector a LeftParen - * and RightParen with location (0, 0), so that location will - * be found by the TLA+ to PCal translation algorithm if the - * user selects the entire algorithm, in which case it will - * return the null region to GotoPCalSourceHandler.execute. - */ - PCalLocation ZeroLocation = new PCalLocation(0, 0); - ((Vector) mappingVector.elementAt(0)). - add(0, new MappingObject.LeftParen(ZeroLocation)); - Vector lastLine = (Vector) mappingVector.elementAt(mappingVector.size()-1); - lastLine.add(lastLine.size(), new MappingObject.RightParen(ZeroLocation)); - - /* - * For testing, throw a null pointer exception if the parentheses are not - * properly matching in mappingVector. - */ - //int[] depths = new int[10000]; - - int parenDepth = 0; - for (int i = 0; i < mappingVector.size(); i++) { - Vector line = (Vector) mappingVector.elementAt(i); - for (int j = 0; j < line.size(); j++) { - MappingObject obj = (MappingObject) line.elementAt(j); - if (obj.getType() == MappingObject.LEFT_PAREN) { - parenDepth++; - } - else if (obj.getType() == MappingObject.RIGHT_PAREN) { - parenDepth--; - if (parenDepth < 0) { - throw new NullPointerException("paren depth < 0"); - } - } - } - // depths[i] = parenDepth; - } - if (parenDepth != 0) { - throw new NullPointerException("Unmatched Left Paren"); - } - /* ------------------ end testing --------------------------*/ - Vector nonredundantMappingVector = - TLAtoPCalMapping.RemoveRedundantParens(mappingVector); - map.makeMapping(nonredundantMappingVector); - -//System.out.println("Original mappingvector:"); -//MappingObject.printMappingVector(mappingVector); -//System.out.println("RemoveRedundantParens(mappingVector)"); -//MappingObject.printMappingVector(TLAtoPCalMapping.RemoveRedundantParens(mappingVector)); -//System.out.println("Should be original mappingvector:"); -//MappingObject.printMappingVector(mappingVector); -// Debugging -//for (int i = 0; i < tlacode.size(); i++) { -//System.out.println("\nline " + i); -//System.out.println((String) tlacode.elementAt(i)) ; -//} -//MappingObject.printMappingVector(mappingVector); - - return tlacode; - } - - /****************************************************************/ - /* Returns whether the string is present in a vector of string. */ - /****************************************************************/ - private static boolean InVector(String var, Vector v) - { - for (int i = 0; i < v.size(); i++) - if (var.equals((String) v.elementAt(i))) - return true; - return false; - } - - /******************************************************/ - /* True if var is in the list of procedure variables. */ - /******************************************************/ - private boolean IsProcedureVar(String var) - { - return InVector(var, pcV); - } - - /****************************************************/ - /* True if var is in the list of process variables. */ - /****************************************************/ - private boolean IsProcessSetVar(String var) - { - return InVector(var, psV); - } - - /**********************************************/ - /* Returns a string of length n of all spaces */ - /**********************************************/ - private static String NSpaces(int n) - { - StringBuffer sb = new StringBuffer(); - AddSpaces(sb, n); - return sb.toString(); - } - - /*********************************************/ - /* Appends n spaces to the string buffer sb. */ - /*********************************************/ - private static void AddSpaces(StringBuffer sb, int num) - { - for (int i = 0; i < num; i++) - sb.append(" "); - } - - /****************************************/ - /* True if expr is an empty expression. */ - /****************************************/ - private static boolean EmptyExpr(TLAExpr expr) - { - if (expr == null) - return true; - if (expr.tokens == null || expr.tokens.size() == 0) - return true; - return false; - } - - /*****************************************************************/ - /* Top level routines. Context is "", "procedure", or "process". */ - /** - ****************************************************************/ - private void GenSym(AST ast, String context) throws PcalTLAGenException - { - if (ast.getClass().equals(AST.UniprocessObj.getClass())) - GenUniprocess((AST.Uniprocess) ast, context); - else if (ast.getClass().equals(AST.MultiprocessObj.getClass())) - GenMultiprocess((AST.Multiprocess) ast, context); - else if (ast.getClass().equals(AST.ProcedureObj.getClass())) - GenProcedure((AST.Procedure) ast, context); - else if (ast.getClass().equals(AST.ProcessObj.getClass())) - GenProcess((AST.Process) ast, context); - else if (ast.getClass().equals(AST.LabeledStmtObj.getClass())) - GenLabeledStmt((AST.LabeledStmt) ast, context); - } - - private void GenUniprocess(AST.Uniprocess ast, String context) throws PcalTLAGenException - { - mp = false; - currentProcName = "Next"; - GenVarsAndDefs(ast.decls, ast.prcds, null, ast.defs); - GenInit(ast.decls, ast.prcds, null); - for (int i = 0; i < ast.prcds.size(); i++) - GenProcedure((AST.Procedure) ast.prcds.elementAt(i), ""); - for (int i = 0; i < ast.body.size(); i++) - { - AST.LabeledStmt ls = (AST.LabeledStmt) ast.body.elementAt(i); - /* Add this step to the disjunct of steps */ - nextStep.addElement(ls.label); - GenLabeledStmt(ls, ""); - } - GenNext(); - GenSpec(); - GenTermination(); - } - - private void GenMultiprocess(AST.Multiprocess ast, String context) throws PcalTLAGenException - { - mp = true; - GenVarsAndDefs(ast.decls, ast.prcds, ast.procs, ast.defs); - GenProcSet(); - GenInit(ast.decls, ast.prcds, ast.procs); - for (int i = 0; i < ast.prcds.size(); i++) - GenProcedure((AST.Procedure) ast.prcds.elementAt(i), ""); - for (int i = 0; i < ast.procs.size(); i++) - GenProcess((AST.Process) ast.procs.elementAt(i), ""); - GenNext(); - GenSpec(); - GenTermination(); - } - - private void GenProcedure(AST.Procedure ast, String context) throws PcalTLAGenException { - /* - * First, generate the body's actions. Must set self and selfIsSelf (?) for - * use by GenLabeledStmt. - */ - if (mp) { - - self = selfAsExpr(); // subscript for variables is "self" - selfIsSelf = true; -// /* Add this step to the disjunct of steps with (self) */ - nextStepSelf.addElement(ast.name + "(self)"); - } else - { - /* Add this step to the disjunct of steps */ - nextStep.addElement(ast.name); - } - for (int i = 0; i < ast.body.size(); i++) { - AST.LabeledStmt stmt = (AST.LabeledStmt) ast.body.elementAt(i); - GenLabeledStmt(stmt, "procedure"); - } - - /* - * Next add the definition of the procedure--e.g., - * - * procedureName(self) == label_1(self) \/ ... \/ label_k(self) - * - * We put Left/RightParens for the entire procedure around the entire - * definition, and Left/RightParens around each disjunction for - * the labeled statement. - */ - addLeftParen(ast.getOrigin()); - String argument = (mp) ? "(self)" : ""; - StringBuffer buf = new StringBuffer(ast.name + argument + " == "); - addOneTokenToTLA(buf.toString()); - String indentSpaces = NSpaces(buf.length() + 2); - for (int i = 0; i < ast.body.size(); i++) { - AST.LabeledStmt stmt = (AST.LabeledStmt) ast.body.elementAt(i); - String disjunct = stmt.label + argument; - if ( i != 0 - && tlacodeNextLine.length() + 7 /* the 7 was obtained empirically */ - + disjunct.length() > wrapColumn) { - endCurrentLineOfTLA(); - } - if (i != 0) { - addOneTokenToTLA(((tlacodeNextLine.length() == 0)? indentSpaces : "") + " \\/ "); - } - addLeftParen(stmt.getOrigin()); - addOneTokenToTLA(disjunct); - addRightParen(stmt.getOrigin()); - } - addRightParen(ast.getOrigin()); - addOneLineOfTLA(""); - -// The previous version was very convoluted just to avoid having to go through the -// list of labeled statements twice. It seemed easier to just reimplement from -// scratch. -// /* ns and nsV accumulate the disjunct of the steps of the procedure, where -// * ns contains the contents of the current line and nsV is the vector of -// * already accumulated lines. -// * -// */ -// StringBuffer ns = new StringBuffer(); -// Vector nsV = new Vector(); -// -// int nsC = ast.name.length() + ((mp) ? "(self)".length() : 0) + " == ".length(); -// if (mp) -// { -// self = selfAsExpr(); // subscript for variables is "self" -// selfIsSelf = true; -// /* Add this step to the disjunct of steps with (self) */ -// nextStepSelf.addElement(ast.name + "(self)"); -// } else -// { -// /* Add this step to the disjunct of steps */ -// nextStep.addElement(ast.name); -// } -// for (int i = 0; i < ast.body.size(); i++) -// { -// AST.LabeledStmt stmt = (AST.LabeledStmt) ast.body.elementAt(i); -// if ((ns.length() + stmt.label.length() + " \\/ ".length() + ((mp) ? "(self)".length() : 0)) > wrapColumn -// - nsC - " \\/ ".length()) -// { -// nsV.addElement(ns.toString()); -// ns = new StringBuffer(); -// } -// if (ns.length() > 0) -// ns.append(" \\/ "); -// ns.append(stmt.label); -// if (mp) -// ns.append("(self)"); -// GenLabeledStmt(stmt, "procedure"); -// } -// nsV.addElement(ns.toString()); -// // Generate definition of procedure steps -// ns = new StringBuffer(); -// ns.append(ast.name); -// if (mp) -// ns.append("(self)"); -// ns.append(" == "); -// ns.append((String) nsV.elementAt(0)); -// tlacode.addElement(ns.toString()); -// for (int i = 1; i < nsV.size(); i++) -// { -// ns = new StringBuffer(NSpaces(nsC + 2)); -// ns.append(" \\/ "); -// ns.append((String) nsV.elementAt(i)); -// tlacode.addElement(ns.toString()); -// } -// tlacode.addElement(""); - } - - private void GenProcess(AST.Process ast, String context) throws PcalTLAGenException - { - currentProcName = ast.name; - - /* - * Generate the body's actions. Must set self and selfIsSelf (?) for - * use by GenLabeledStmt. - */ - boolean isSet = true; - /************************************************************/ - /* Decide if it is a process set or not. If so, set self to */ - /* the string "self"; otherwise set self to the process id. */ - /************************************************************/ - if (ast.isEq) - { - self = ast.id ; - selfIsSelf = false; - isSet = false; - } else { - self = selfAsExpr(); - selfIsSelf = true; - } - - if (isSet) - { - nextStepSelf.addElement(ast.name + "(self)"); - } else - nextStep.addElement(ast.name); - - for (int i = 0; i < ast.body.size(); i++) { - AST.LabeledStmt stmt = (AST.LabeledStmt) ast.body.elementAt(i); - GenLabeledStmt(stmt, "process"); - } - - /* - * Next add the definition of the process--e.g., - * - * processName(self) == label_1(self) \/ ... \/ label_k(self) - * - * We put Left/RightParens for the entire procedure around the entire - * definition, and Left/RightParens around each disjunction for - * the labeled statement. - * - * However, we don't add this definition if we are omitting the pc, - * because we have already defined the process name to equal the - * only label action. - */ - - if (! ParseAlgorithm.omitPC) { - addLeftParen(ast.getOrigin()); - String argument = (isSet) ? "(self)" : ""; - StringBuffer buf = new StringBuffer(ast.name + argument + " == "); - addOneTokenToTLA(buf.toString()); - String indentSpaces = NSpaces(buf.length() + 2); - for (int i = 0; i < ast.body.size(); i++) { - AST.LabeledStmt stmt = (AST.LabeledStmt) ast.body.elementAt(i); - String disjunct = stmt.label + argument; - if ( i != 0 - && tlacodeNextLine.length() + 7 /* the 7 was obtained empirically */ - + disjunct.length() > wrapColumn) { - endCurrentLineOfTLA(); - } - if (i != 0) { - addOneTokenToTLA(((tlacodeNextLine.length() == 0)? indentSpaces : "") + " \\/ "); - } - addLeftParen(stmt.getOrigin()); - addOneTokenToTLA(disjunct); - addRightParen(stmt.getOrigin()); - } - addRightParen(ast.getOrigin()); - addOneLineOfTLA(""); - } - - -// As with GenProcedure, the original implementation was quite convoluted, so -// it was rewritten. The code above was copied with modifications from -// the rewritten GenProcedure code. -// /* ns accumulates the disjunt of the steps of the process */ -// StringBuffer ns = new StringBuffer(); -// Vector nsV = new Vector(); -// boolean isSet = true; -// /************************************************************/ -// /* Decide if it is a process set or not. If so, set self to */ -// /* the string "self"; otherwise set self to the process id. */ -// /************************************************************/ -// if (ast.isEq) -// { -// self = ast.id ; -// selfIsSelf = false; -// isSet = false; -// } else { -// self = selfAsExpr(); -// selfIsSelf = true; -// } -// -// int nsC = ast.name.length() + ((isSet) ? "(self)".length() : 0) + " == ".length(); -// if (isSet) -// { -// nextStepSelf.addElement(ast.name + "(self)"); -// } else -// nextStep.addElement(ast.name); -// for (int i = 0; i < ast.body.size(); i++) -// { -// AST.LabeledStmt stmt = (AST.LabeledStmt) ast.body.elementAt(i); -// if ((ns.length() + stmt.label.length() + " \\/ ".length() + ((isSet) ? "(self)".length() : 0)) > wrapColumn -// - nsC - " \\/ ".length()) -// { -// nsV.addElement(ns.toString()); -// ns = new StringBuffer(); -// } -// if (ns.length() > 0) -// ns.append(" \\/ "); -// ns.append(stmt.label); -// if (isSet) -// ns.append("(self)"); -// GenLabeledStmt(stmt, "process"); -// } -// nsV.addElement(ns.toString()); -// // Generate definition of process steps -// // This apparently defines the process name -// // to equal the disjunction of all the individual -// // label-named actions. If we are omitting -// // the pc, we have already defined the process name -// // to equal the only label action, so we skip -// // this. -// if (! ParseAlgorithm.omitPC) { -// ns = new StringBuffer(); -// ns.append(ast.name); -// if (isSet) -// ns.append("(self)"); -// ns.append(" == "); -// ns.append((String) nsV.elementAt(0)); -// tlacode.addElement(ns.toString()); -// for (int i = 1; i < nsV.size(); i++) -// { -// ns = new StringBuffer(NSpaces(nsC + 2)); -// ns.append(" \\/ "); -// ns.append((String) nsV.elementAt(i)); -// tlacode.addElement(ns.toString()); -// } -// } -// tlacode.addElement(""); - } - - /*****************************************************/ - /* Generates an action with name equal to the label. */ - /** - ****************************************************/ - private void GenLabeledStmt(AST.LabeledStmt ast, String context) throws PcalTLAGenException - { - // Set actionName to the name of the action being defined. - // This is the label, except when we are omitting the PC, - // in which case it is "Next" for a uniprocess algorithm - // and the process name for a multiprocess algorithm. - String actionName = ast.label; - if (ParseAlgorithm.omitPC) { - actionName = currentProcName; - } - StringBuffer sb = new StringBuffer(actionName); - /* c is used to determine which vars are in UNCHANGED. */ - Changed c = new Changed(vars); - if (mp && (context.equals("procedure") || selfIsSelf)) { // self.equals("self"))) - sb.append("(self)"); - } - sb.append(" == "); - int col = sb.length(); - kludgeToFixPCHandlingBug = col; - // There's a problem here. If ast.stmts.size() = 1, then we don't preface - // the statement's translation with "/\". However, that means that if we - // then add an UNCHANGED conjunct, we wind up with - // A - // /\ UNCHANGED ... - // where A is the statement's translation. This looks bad and could wind up - // putting the UNCHANGED inside prefix operator--e.g., - // x' = CHOOSE t : ... - // /\ UNCHANGED ... - // This is seems to be a problem only when omitPC = true, since otherwise the - // testing and setting of pc ensures that there is more than one element in ast.stmts. - // What we do is always add the "/\ ", but remove it afterwards if there's only - // a single statement in ast.stmts and there is no UNCHANGED clause. - // The code for doing this is based on the observation that the contents of - // StringBuffer sb begin the next line added to tlacode. - // - /* if (ast.stmts.size() > 1) { */ - sb.append("/\\ "); - kludgeToFixPCHandlingBug = kludgeToFixPCHandlingBug + 3; - /* } */ - - // We set defStartLine to the index of the next line added to tlacode and - // colAfterAnd to the column position in that line immediately following - // the added "/\ ". This gives us the information needed to remove the - // "/\ " later from tlacode. - int defStartLine = tlacode.size(); - int colAfterAnd = sb.length(); - - /* - * Note: it would make sense for this method to insert sb into the tlacode - * output, but it seems safer to maintain the current structure in which - * each GenX for each statement type X does that. - */ - - /* - * We set macroBeginLeft to the macroOriginBegin field of the first statement - * in ast.stmts with a non-null origin, and macroEndRight to the macroOriginEnd - * field of the last statement in ast.stmts with a non-null origin. - */ - PCalLocation macroBeginLeft = null; - PCalLocation macroEndRight = null; - boolean nonNullNotFound = true; - for (int i = 0 ; i < ast.stmts.size(); i++) { - AST stmt = (AST) ast.stmts.elementAt(i); - if (stmt.getOrigin() != null) { - if (nonNullNotFound) { - nonNullNotFound = false; - macroBeginLeft = stmt.macroOriginBegin; - } - macroEndRight = stmt.macroOriginEnd; - } - } - - /* - * addLeftParenV used instead of addLeftParen because if the first statement - * that came from PlusCal code arose from a macro call, then we want the - * location of the macro call rather than that of the macro's code. - */ - addLeftParenV(ast, macroBeginLeft); - for (int i = 0; i < ast.stmts.size(); i++) - { - GenStmt((AST) ast.stmts.elementAt(i), c, context, sb.toString(), sb.length()); - sb = new StringBuffer(NSpaces(col)); - sb.append("/\\ "); - } - - /* - * Since the UNCHANGED conjunct just consists of TLATokens, with no - * SourceTokens, we can just use the old code, simply replacing each - * tlacode.addElement call with a call of addOneLineOfTLA--except that - * the last one is replaced with a call of addOneTokenToTLA so we can - * put the RightParen object in mappingVector. - */ - Vector unc = c.Unchanged(wrapColumn - col - "/\\ UNCHANGED << ".length()); - if (c.NumUnchanged() > 1) - { - sb = new StringBuffer(NSpaces(col)); - sb.append("/\\ UNCHANGED << "); - int here = sb.length(); - sb.append((String) unc.elementAt(0)); - for (int i = 1; i < unc.size(); i++) - { -// tlacode.addElement(sb.toString()); - addOneLineOfTLA(sb.toString()); - sb = new StringBuffer(NSpaces(here)); - sb.append((String) unc.elementAt(i)); - } - sb.append(" >>"); -// tlacode.addElement(sb.toString()); - addOneTokenToTLA(sb.toString()); - } else if (c.NumUnchanged() == 1) { - // Change made by LL on 16 Mar 2011 so that, if there is a single - // unchanged variable v, it produces v' = v if v is a short variable, - // otherwise it produces UNCHANGED v - if (c.Unchanged().length() > 5) { -// tlacode.addElement(NSpaces(col) + "/\\ UNCHANGED " + c.Unchanged()); - addOneTokenToTLA(NSpaces(col) + "/\\ UNCHANGED " + c.Unchanged()); - } else { -// tlacode.addElement(NSpaces(col) + "/\\ " + c.Unchanged() + "' = " -// + c.Unchanged()); - addOneTokenToTLA(NSpaces(col) + "/\\ " + c.Unchanged() + "' = " - + c.Unchanged()); - } - } else { - // No unchanged. If there was only one conjunction, remove it. - // To do that, we must remove the "/\ " and then remove three spaces - // from all other lines that were added. We must also modify the - // TLAToken objects appropriately in the corresponding lines of - // mappingVector - if (ast.stmts.size() == 1) { - for (int i = defStartLine; i < tlacode.size(); i++) { - String line = (String) tlacode.elementAt(i); - if (i == defStartLine) { - // remove the "/\ " added - tlacode.setElementAt(line.substring(0, colAfterAnd-3) + - line.substring(colAfterAnd, line.length()) , i); - shiftMappingVectorTokensLeft(i, colAfterAnd, 3); - - } else { - // Remove three blanks from any following lines. We test the length - // of the line just in case one or more short (hopefully blank) lines - // have been added. - if (line.length() > 3) { - tlacode.setElementAt(line.substring(3, line.length()) , i); - shiftMappingVectorTokensLeft(i, colAfterAnd, 3); - } - } - } - } - } - /* - * We call addRightParenV rather than addRightParen because it the last statement - * that came from the PCal code arose from the expansion of a macro call, then we - * want the RightParen's location to be the end of the call, not the end of the - * macro's code. - */ - addRightParenV(ast, macroEndRight); - addOneLineOfTLA(""); -// tlacode.addElement(""); - } - - /** - * Adjusts the objects in line lineNum of mappingVector so all column - * numbers starting with startCol are decreased by `shift'. If any Begin/EndTLAToken - * pairs are changed to have a non-positive width, a bug is reported. - * - * Note: It is assumed that there is no aliasing of MappingTokens in mappingVector. - * That is, other than in transient local variables, the only pointer to a - * MappingToken in mappingVector is the single one in its line of mappingVector. - * I can't see how any aliasing of MappingTokens could arise. - * - * This method is called only by GenLabeledStmts. - * - * @param lineNum - * @param startCol - * @param shift - */ - private void shiftMappingVectorTokensLeft(int lineNum, int startCol, int shift) { - boolean lastWasBeginTLAToken = false; - int lastBeginTLATokCol = -777; // to keep the compiler happy. - Vector line = (Vector) mappingVector.elementAt(lineNum); - for (int i = 0; i < line.size(); i++) { - MappingObject obj = (MappingObject) line.elementAt(i); - if (obj.getType() == MappingObject.BEGIN_TLATOKEN) { - MappingObject.BeginTLAToken tobj = (MappingObject.BeginTLAToken) obj; - int col = tobj.getColumn(); - if (col >= startCol) { - tobj.setColumn(col - shift); - } - lastWasBeginTLAToken = true; - lastBeginTLATokCol = tobj.getColumn(); - } - else { - if (obj.getType() == MappingObject.END_TLATOKEN) { - MappingObject.EndTLAToken tobj = (MappingObject.EndTLAToken) obj; - int col = tobj.getColumn(); - if (col >= startCol) { - tobj.setColumn(col - shift); - } - if (lastWasBeginTLAToken && tobj.getColumn() <= lastBeginTLATokCol) { - PcalDebug.ReportBug( - "PcalTLAGen.shiftMappingVectorTokensLeft created a null TLA Token"); - } - } - else if (obj.getType() == MappingObject.SOURCE_TOKEN) { - MappingObject.SourceToken tobj = (MappingObject.SourceToken) obj; - int col = tobj.getBeginColumn(); - if (col >= startCol) { - tobj.setBeginColumn(col - shift); - } - col = tobj.getEndColumn(); - if (col >= startCol) { - tobj.setEndColumn(col - shift); - } - - lastWasBeginTLAToken = false; - } - } - } - - - } - - /*************************************************************************** - * LL Comment added 27 Jan 2006: * - * * - * There is a basic flaw in the way GenStmt works. It now generates the * - * output on the fly. This means that * - * * - * - There is no way to avoid the prefix /\ on a one-element conjunct * - * because GenStmt has no way of knowing if there's another conjunct * - * coming. * - * * - * - The handling of the UNCHANGEDs of the THEN and ELSE clauses of * - * an IF is a kludge, because the UNCHANGED of the THEN clause is * - * output before it can be known. * - * * - * The correct way of doing things is to define GenStmt so it returns a * - * sequence (vector) of string vectors, each string vector being a * - * conjunction expression (without a leading /\ or any leading spaces) and * - * the new Changed object (which it can do as it now does by modifying its * - * Changed object argument). It would also be useful to define a * - * GenStmtSeq that simply calls GenStmt iteratively on a sequence of * - * simple statements. The method that calls GenStmtSeq would then add the * - * Unchanged conjunct and call a method that returns a sequence of * - * conjuncts and a prefix into a string vector containing the prefix and * - * the necessary /\s. * - ***************************************************************************/ - - /*****************************************************************/ - /* General entry for generating the TLA+ for a simple statement. */ - /* Prefix is the prefix of the first line. Col is where to start */ - /* subsequent lines (I think we could replace it with the length */ - /* of prefix). */ - /* */ - /* And what on earth are `c' and `context'? LL */ - /** - ****************************************************************/ - private void GenStmt(AST ast, Changed c, String context, String prefix, int col) throws PcalTLAGenException - { - if (ast.getClass().equals(AST.AssignObj.getClass())) - GenAssign((AST.Assign) ast, c, context, prefix, col); - else if (ast.getClass().equals(AST.IfObj.getClass())) - GenIf((AST.If) ast, c, context, prefix, col); - // Either case added by LL on 27 Jan 2006 - else if (ast.getClass().equals(AST.EitherObj.getClass())) - GenEither((AST.Either) ast, c, context, prefix, col); - else if (ast.getClass().equals(AST.WithObj.getClass())) - GenWith((AST.With) ast, c, context, prefix, col); - else if (ast.getClass().equals(AST.WhenObj.getClass())) - GenWhen((AST.When) ast, c, context, prefix, col); - else if (ast.getClass().equals(AST.PrintSObj.getClass())) - GenPrintS((AST.PrintS) ast, c, context, prefix, col); - else if (ast.getClass().equals(AST.AssertObj.getClass())) - GenAssert((AST.Assert) ast, c, context, prefix, col); - else if (ast.getClass().equals(AST.SkipObj.getClass())) - GenSkip((AST.Skip) ast, c, context, prefix, col); - else - PcalDebug.ReportBug("Unexpected AST type " + ast.toString()); - } - - /*****************************************************************/ - /* Generates a sequence of single assigns. Since all of them are */ - /* executed "at the same time", we accumulate the changes in a */ - /* separate Changed cThis, and use c to determine which vars in */ - /* the right hand side are primed. */ - /** - * ***************************************************************/ - - /** - * @param ast - * @param c - * @param context - * @param prefix - * @param col - * @throws PcalTLAGenException - */ - private void GenAssign(AST.Assign ast, Changed c, String context, String prefix, int col) - throws PcalTLAGenException - { - Changed cThis = new Changed(c); - StringBuffer sb = new StringBuffer(); -// Vector vlines = new Vector(); - /* - * Sort the vector ast.ass so that assignments to the same variable - * follow one another. - */ - ast.ass = SortSass(ast.ass); - - addOneTokenToTLA(prefix); - addLeftParen(ast.getOrigin()); - - int i = 0; - int numAssigns = 0; - /* - * hasMultipleVars set true iff the assignment assigns values to - * more than one variable, and hence the statement's translation - * has multiple conjuncts. - */ - boolean hasMultipleVars = false; - while (i < ast.ass.size()) - { - int iFirst = i; - AST.SingleAssign sF = (AST.SingleAssign) ast.ass.elementAt(i); - int iLast = i; - boolean hasAssignmentWithNoSubscript = false; - boolean lastAssignmentHasNoSubscript = EmptyExpr(sF.lhs.sub); - AST.SingleAssign sL = (AST.SingleAssign) ast.ass.elementAt(i); - while (iLast < ast.ass.size() && sF.lhs.var.equals(sL.lhs.var)) - { - if (lastAssignmentHasNoSubscript) { - hasAssignmentWithNoSubscript = true; - } - iLast = iLast + 1; - if (iLast < ast.ass.size()) { - sL = (AST.SingleAssign) ast.ass.elementAt(iLast); - if (EmptyExpr(sL.lhs.sub)) { - lastAssignmentHasNoSubscript = true; - } - } - } - - /* - * If there are assignments to multiple variables, then this sets - * hasMultiplevars true on the first execution of the outer while loop. - */ - if (iLast != ast.ass.size()) { - hasMultipleVars = true; - } - - iLast = iLast - 1; - // All statements from iFirst to iLast are to the same variable - - /* - * Throws an error if there are multiple assignments to the variable - * in different statements, or if there are multiple assignments to - * the variable in this statement and at least one of them has no - * subscript. - */ - if (cThis.Set(sF.lhs.var) > 1 || - (iLast - iFirst > 0 && hasAssignmentWithNoSubscript)) { - /*********************************************************** - * The following was changed by LL on 3 Mar 06 to use * - * AST.location to properly report the location of an * - * error in a line created by expanding a macro. * - * However, it doesn't work very well otherwise. This * - * should be fixed. * - ***********************************************************/ - throw new PcalTLAGenException("Multiple assignment to " + sF.lhs.var, ast /* sF */); - } - numAssigns = numAssigns + 1; - Vector lines = new Vector(); // For collecting generated lines - - if (hasMultipleVars) { - sb.append("/\\ "); - } - if (iFirst == iLast) - { - /* - * This is a single assignment to the variable. - */ - AST.SingleAssign sass = sF; - - addLeftParen(sass.getOrigin()); - TLAExpr sub = AddSubscriptsToExpr(sass.lhs.sub, SubExpr(Self(context)), c); - TLAExpr rhs = AddSubscriptsToExpr(sass.rhs, SubExpr(Self(context)), c); - if (mp - && (sass.lhs.var.equals("pc") || IsProcedureVar(sass.lhs.var) || IsProcessSetVar(sass.lhs.var) || sass.lhs.var - .equals("stack"))) - { - /* Generate single assignment to variable with self subscript */ - sb.append(sass.lhs.var); - sb.append("' = ["); - int wrapCol = sb.length() + 2; - sb.append(sass.lhs.var); - sb.append(" EXCEPT "); - - Vector selfAsSV = self.toStringVector(); - - // The test for selfAsSV size added by LL on 22 Jan 2011 - // because wrapping screws up the kludgeToFixPCHandlingBug - // hack. - if ( (sb.length() + prefix.length() > ssWrapColumn) - && (selfAsSV.size() == 0)) - { -// lines.addElement(sb.toString()); - addOneLineOfTLA(sb.toString()); - sb = new StringBuffer(NSpaces(wrapCol)); - } - sb.append("!["); - addOneTokenToTLA(sb.toString()); - addLeftParen(self.getOrigin()); - addExprToTLA(self); - addRightParen(self.getOrigin()); - -// -// // following code was modified by LL on 22 Jan 2011 as part of -// // fixing bug 11_01_13, which required modifications to handle -// // the case where self is a multi-line formula, which can happen -// // for a "process (P = exp)" when exp is multi-line. -// int here = sb.length(); -// for (int idx = 0; idx < selfAsSV.size(); idx++) { -// if (idx > 0) { -// sb.append("\n"); -// sb.append(NSpaces(here + kludgeToFixPCHandlingBug)); -// } -// sb.append((String) selfAsSV.elementAt(idx)) ; -// } -//// sb.append(self); -// sb.append("]"); -// here = here + ((String) selfAsSV.elementAt(selfAsSV.size()-1)).length() + 1; - addOneTokenToTLA("]"); - Vector sv = sub.toStringVector(); - /***************************************************** - * Was * - * * - * Vector sv = sass.lhs.sub.toStringVector(); * - * * - * Changed by Chi Ho on 3 Aug 2006 to add * - * subscript. See bug_06_08_03. * - *****************************************************/ - if (sv.size() > 0) - { - addLeftParen(sub.getOrigin()); - addExprToTLA(sub); - addRightParen(sub.getOrigin()); - -// sb.append((String) sv.elementAt(0)); -// for (int v = 1; v < sv.size(); v++) -// { -// lines.addElement(sb.toString()); -// sb = new StringBuffer(NSpaces(here)); -// sb.append((String) sv.elementAt(v)); -// } - } - addOneTokenToTLA(" = ");; - addLeftParen(rhs.getOrigin()); - addExprToTLA(rhs); - addRightParen(rhs.getOrigin()); - addOneTokenToTLA("]"); -// sb.append(" = "); -// here = sb.length(); -// sv = rhs.toStringVector(); -// sb.append((String) sv.elementAt(0)); -// for (int v = 1; v < sv.size(); v++) -// { -// lines.addElement(sb.toString()); -// sb = new StringBuffer(NSpaces(here)); -// sb.append((String) sv.elementAt(v)); -// } -// sb.append("]"); -// lines.addElement(sb.toString()); - sb = new StringBuffer(); - } else if (!EmptyExpr(sass.lhs.sub)) - { - /* - * Generate single assignment to variable with no [self] subscript - * but with an explicit subscript. - */ - sb.append(sass.lhs.var); - sb.append("' = ["); - sb.append(sass.lhs.var); - sb.append(" EXCEPT !"); - addOneTokenToTLA(sb.toString()); - addLeftParen(sub.getOrigin()); - addExprToTLA(sub); - addRightParen(sub.getOrigin()); - addOneTokenToTLA(" = "); - addLeftParen(rhs.getOrigin()); - addExprToTLA(rhs); - addRightParen(rhs.getOrigin()); - addOneTokenToTLA("]"); -// -// int here = sb.length(); -// Vector sv = sub.toStringVector(); -// sb.append((String) sv.elementAt(0)); -// for (int v = 1; v < sv.size(); v++) -// { -// lines.addElement(sb.toString()); -// sb = new StringBuffer(NSpaces(here)); -// sb.append((String) sv.elementAt(v)); -// } -// sb.append(" = "); -// here = sb.length(); -// sv = rhs.toStringVector(); -// sb.append((String) sv.elementAt(0)); -// for (int v = 1; v < sv.size(); v++) -// { -// lines.addElement(sb.toString()); -// sb = new StringBuffer(NSpaces(here)); -// sb.append((String) sv.elementAt(v)); -// } -// sb.append("]"); -// lines.addElement(sb.toString()); - sb = new StringBuffer(); - } else - { - /* - * Generate assignment to a variable with no subscript at all. - */ - sb.append(sass.lhs.var); - sb.append("' = "); -// int here = sb.length(); - boolean needsParens = NeedsParentheses(rhs.toStringVector()); - if (needsParens) { - sb.append("("); - } - addOneTokenToTLA(sb.toString()); - addLeftParen(rhs.getOrigin()); - addExprToTLA(rhs); - addRightParen(rhs.getOrigin()); - if (needsParens) { - addOneTokenToTLA(")"); - } - -// Vector sv = Parenthesize(rhs.toStringVector()); -// /******************************************************* -// * Call of Parenthesize added by LL on 27 Feb 2008. * -// * See bug_08-02-18. * -// *******************************************************/ -// for (int v = 0; v < sv.size(); v++) -// { -// sb.append((String) sv.elementAt(v)); -// lines.addElement(sb.toString()); -// sb = new StringBuffer(NSpaces(here)); -// } -// Debugging -//Vector exprVec = rhs.toMappingVector(); -//MappingObject.shiftMappingVector(exprVec, here); -//MappingObject.printMappingVector(exprVec); -//System.out.println("origin: " + rhs.getOrigin().toString() ); - - sb = new StringBuffer(); - } - addRightParen(sass.getOrigin()); - } else - { - /* - * Multiple assignments to the same variable, which must therefore - * each have a user-specified subscript. - */ - AST.SingleAssign sass = sF; - sb.append(sass.lhs.var); - sb.append("' = ["); - sb.append(sass.lhs.var); - sb.append(" EXCEPT "); - int cc = sb.length(); - /* - * If this the first variable, so i = 0, then sb does not contain - * any spaces to compensate for the missing prefix; otherwise it - * does. - */ - if (i == 0) { - cc = cc + prefix.length(); - } - boolean subscript = (mp && (IsProcedureVar(sass.lhs.var) || IsProcessSetVar(sass.lhs.var))); - while (iFirst <= iLast) - { - sass = (AST.SingleAssign) ast.ass.elementAt(iFirst); - TLAExpr sub = AddSubscriptsToExpr(sass.lhs.sub, SubExpr(Self(context)), c); - TLAExpr rhs = AddSubscriptsToExpr(sass.rhs, SubExpr(Self(context)), c); - addLeftParen(sass.getOrigin()); - sb.append("!"); - - // On 21 Jan 2011, LL moved the following statement to below the if - // to correct part 3 of bug_11_01_13. - // -// int here = sb.length(); - if (subscript) { - /* - * This variable has a "self" subscript in addition to its user-specified - * subscript. - */ - sb.append("["); - addOneTokenToTLA(sb.toString()); - TLAExpr self = Self(context); - addLeftParen(self.getOrigin()); - addExprToTLA(self); - addOneTokenToTLA("]"); - -// Vector selfAsSV = Self(context).toStringVector(); -// for (int idx = 0; idx < selfAsSV.size(); idx++) { -// String start = " "; -// if (idx == 0) { -// sb.append("["); -// } else { -// sb.append("\n"); -// sb.append(NSpaces(here + 1)); -// } -// sb.append((String) selfAsSV.elementAt(idx)); -// } -// sb.append("]"); -// here = here + ((String) selfAsSV.elementAt(selfAsSV.size()-1)).length() + 2; - } - else { - addOneTokenToTLA(sb.toString()); - } - - addLeftParen(sub.getOrigin()); - addExprToTLA(sub); - addRightParen(sub.getOrigin()); - addOneTokenToTLA(" = "); - addLeftParen(rhs.getOrigin()); - addExprToTLA(rhs); - addRightParen(rhs.getOrigin()); - addRightParen(sass.getOrigin()); - addOneTokenToTLA((iFirst == iLast) ? "]" : ","); -// Vector sv = sub.toStringVector(); -// if (sv.size() > 0) -// { -// sb.append((String) sv.elementAt(0)); -// for (int v = 1; v < sv.size(); v++) -// { -// lines.addElement(sb.toString()); -// sb = new StringBuffer(NSpaces(here)); -// sb.append((String) sv.elementAt(v)); -// } -// } -// sb.append(" = "); -// here = sb.length(); -// sv = rhs.toStringVector(); -// sb.append((String) sv.elementAt(0)); -// for (int v = 1; v < sv.size(); v++) -// { -// lines.addElement(sb.toString()); -// sb = new StringBuffer(NSpaces(here)); -// sb.append((String) sv.elementAt(v)); -// } -// sb.append(((iFirst == iLast) ? "]" : ",")); -// lines.addElement(sb.toString()); - sb = new StringBuffer(); - if (iFirst < iLast) { - endCurrentLineOfTLA(); - AddSpaces(sb, cc); - } - iFirst = iFirst + 1; - } - } - -// vlines.addElement(lines); - i = iLast + 1; - if (i < ast.ass.size()) { - endCurrentLineOfTLA(); - AddSpaces(sb, prefix.length()); - } - } - addRightParen(ast.getOrigin()); - endCurrentLineOfTLA(); - - c.Merge(cThis); - // Append generated code to tlacode -// sb = new StringBuffer(prefix); -// col = sb.length(); -// if (numAssigns > 1) -// sb.append("/\\ "); -// if (vlines.size() > 0) -// { -// for (int v1 = 0; v1 < vlines.size(); v1++) -// { -// Vector vl = (Vector) vlines.elementAt(v1); -// for (int v2 = 0; v2 < vl.size(); v2++) -// { -// sb.append((String) vl.elementAt(v2)); -// tlacode.addElement(sb.toString()); -// sb = new StringBuffer(NSpaces(col)); -// if ((v1 > 0 || numAssigns > 1) && (v2 != vl.size() - 1)) -// sb.append(" "); -// } -// sb.append("/\\ "); -// } -// } - } - - /** - * Generate TLA+ for if statement. Each branch has its own - * UNCHANGED that lists variables that were changed in the - * other branch. This is a little difficult since we don't - * know the UNCHANGED for the Then branch until the code - * for the Else branch is generated. So, we fix the - * line in the Then branch after the Else branch is done. - * The corresponding mappingVector line also has to be changed, - * but that's not a problem because the UNCHANGED is a TLA+ - * token with no corresponding source. - */ - private void GenIf(AST.If ast, Changed c, String context, String prefix, int col) throws PcalTLAGenException - { - Changed cThen = new Changed(c); - Changed cElse = new Changed(c); - int lineUncThen; - StringBuffer sb = new StringBuffer(prefix); - TLAExpr test = null; - test = AddSubscriptsToExpr(ast.test, SubExpr(Self(context)), c); -// Vector sv = test.toStringVector(); - sb.append("IF "); - int here = sb.length(); - /************************************************************* - * LL removed a bogus "- 1" here on 31 Jan 2006. * - *************************************************************/ - addLeftParen(ast.getOrigin()); - addOneTokenToTLA(sb.toString()); - addExprToTLA(test); - endCurrentLineOfTLA(); - -// sb.append((String) sv.elementAt(0)); -// for (int v = 1; v < sv.size(); v++) -// { -// tlacode.addElement(sb.toString()); -// sb = new StringBuffer(NSpaces(here)); -// sb.append((String) sv.elementAt(v)); -// } -// tlacode.addElement(sb.toString()); - - sb = new StringBuffer(NSpaces(here)); - sb.append("THEN "); - here = sb.length(); - - sb.append("/\\ "); - for (int i = 0; i < ast.Then.size(); i++) - { - GenStmt((AST) ast.Then.elementAt(i), cThen, context, sb.toString(), - /******************************************************************* - * LL added the +3 on 18 Feb 2006 to take account of the * - * indentation of the "IF ". * - *******************************************************************/ - here + 3); - sb = new StringBuffer(NSpaces(here) + "/\\ "); - } - lineUncThen = tlacode.size(); -// tlacode.addElement(sb.toString()); - addOneLineOfTLA(sb.toString()); - sb = new StringBuffer(NSpaces(here - "THEN ".length()) + "ELSE "); - here = sb.length(); - if (ast.Else.size() == 0) - { - sb.append("/\\ TRUE"); -// tlacode.addElement(sb.toString()); - addOneLineOfTLA(sb.toString()); - - sb = new StringBuffer(NSpaces(here) + "/\\ "); - } else - { - sb.append("/\\ "); - for (int i = 0; i < ast.Else.size(); i++) - { - GenStmt((AST) ast.Else.elementAt(i), cElse, context, sb.toString(), - /******************************************************************* - * LL added the +3 on 18 Feb 2006 to take account of the * - * indentation of the "IF ". * - *******************************************************************/ - here + 3); - sb = new StringBuffer(NSpaces(here) + "/\\ "); - } - } - // Generate UNCHANGED for the ELSE branch - if (cElse.NumUnchanged(cThen) > 1) - { - Vector uncElse = cElse.Unchanged(cThen, wrapColumn - sb.length() - "UNCHANGED << ".length()); - sb.append("UNCHANGED << "); - int cc = sb.length(); - sb.append((String) uncElse.elementAt(0)); - for (int i = 1; i < uncElse.size(); i++) - { -// tlacode.addElement(sb.toString()); - addOneLineOfTLA(sb.toString()); - sb = new StringBuffer(NSpaces(cc)); - sb.append((String) uncElse.elementAt(i)); - } - sb.append(" >>"); -// tlacode.addElement(sb.toString()); - addOneTokenToTLA(sb.toString()); - addRightParen(ast.getOrigin()); - endCurrentLineOfTLA(); - } else if (cElse.NumUnchanged(cThen) == 1) - { // Change made by LL on 16 Mar 2011 so that, if there is a single - // unchanged variable v, it produces v' = v if v is a short variable, - // otherwise it produces UNCHANGED v - // - // sb.append("UNCHANGED " + cElse.Unchanged(cThen)); - String uc = cElse.Unchanged(cThen); - if (uc.length() > 5) { - sb.append("UNCHANGED " + uc); - } else { - sb.append(uc + "' = " + uc); - } -// tlacode.addElement(sb.toString()); - addOneTokenToTLA(sb.toString()); - addRightParen(ast.getOrigin()); - endCurrentLineOfTLA(); - } else - { - /* - * There is no UNCHANGED after the ELSE, so we have to put - * the RightParen for the whole if statement at the end of - * the last line already generated - */ - ((Vector) mappingVector.elementAt(mappingVector.size()-1)) - .add(new MappingObject.RightParen(ast.getOrigin().getEnd())); - } - - // Patch up the UNCHANGED for the THEN branch - sb = new StringBuffer((String) tlacode.elementAt(lineUncThen)); - tlacode.removeElementAt(lineUncThen); - mappingVector.removeElementAt(lineUncThen); - if (cThen.NumUnchanged(cElse) > 1) - { - Vector uncThen = cThen.Unchanged(cElse, wrapColumn - sb.length() - "UNCHANGED << ".length()); - sb.append("UNCHANGED << "); - int cc = sb.length(); - sb.append((String) uncThen.elementAt(0)); - for (int i = 1; i < uncThen.size(); i++) - { - tlacode.insertElementAt(sb.toString(), lineUncThen); - - //set the mappingVector entry - mappingVector.insertElementAt(stringToTLATokens(sb.toString()), lineUncThen); - - lineUncThen = lineUncThen + 1; - sb = new StringBuffer(NSpaces(cc)); - sb.append((String) uncThen.elementAt(i)); - } - sb.append(" >>"); - tlacode.insertElementAt(sb.toString(), lineUncThen); - Vector vec = stringToTLATokens(sb.toString()); - - // The following is bogus because the RightParen for the - // entire procedure is inserted after (or instead of) the - // ELSE's UNCHANGED - // vec.add(new MappingObject.RightParen(ast.getOrigin().getEnd())); - - mappingVector.insertElementAt(vec, lineUncThen); - - } else if (cThen.NumUnchanged(cElse) == 1) - { // Change made by LL on 16 Mar 2011 so that, if there is a single - // unchanged variable v, it produces v' = v if v is a short variable, - // otherwise it produces UNCHANGED v - // - // sb.append("UNCHANGED "); - // sb.append(cThen.Unchanged(cElse)); - String uc = cThen.Unchanged(cElse); - if (uc.length() > 5) { - sb.append("UNCHANGED " + uc); - } else { - sb.append(uc + "' = " + uc); - } - tlacode.insertElementAt(sb.toString(), lineUncThen); - Vector vec = stringToTLATokens(sb.toString()); - // The following is bogus because the RightParen for the - // entire procedure is inserted after (or instead of) the - // ELSE's UNCHANGED - // vec.add(new MappingObject.RightParen(ast.getOrigin().getEnd())); - mappingVector.insertElementAt(vec, lineUncThen); - } - - // Merge the change lists together - c.Merge(cThen); - c.Merge(cElse); - } - - /** - * Returns the vector of MappingObjects containing the BeginTLAToken and - * EndTLAToken that are put in the mappingVector by a call of addOneLineOfTLA. - * The code was essentially copied from addOneTokenToTLA. - * - * @param token - * @return - */ - private Vector stringToTLATokens(String token) { - Vector result = new Vector(3); - - String trimmedToken = token.trim() ; - - int numberOfLeftTrimmedTokens = - (trimmedToken.length() == 0) ? -1 : - token.indexOf(trimmedToken.charAt(0)); - - /** - * Handle a token of only space characters. - */ - if (numberOfLeftTrimmedTokens == -1) { - numberOfLeftTrimmedTokens = 0 ; - trimmedToken = token ; - } - - int objBegin = numberOfLeftTrimmedTokens; - result.addElement(new MappingObject.BeginTLAToken(objBegin)); - result.addElement(new MappingObject.EndTLAToken(objBegin + trimmedToken.length())); - return result; - } - - /*********************************************************************** - * Added by LL on 30 Jan 2006. * - * * - * Generate TLA+ for the `either' statement. This performs the same * - * sort of hackery as for the `if' statement, necessitated by the * - * design flaw commented on above. - ** - ***********************************************************************/ - private void GenEither(AST.Either ast, Changed c, String context, String prefix, int col) - throws PcalTLAGenException - { - Changed allC = new Changed(c); - /******************************************************************* - * Accumulates the variable changes of all the clauses. * - *******************************************************************/ - Changed[] cOrs = new Changed[ast.ors.size()]; - /******************************************************************* - * cOrs[i] is the Changed vector for the i-th `or' clause. * - *******************************************************************/ - int[] ucLocs = new int[ast.ors.size()]; // location of unchangeds. - /****************************************************************** - * tlaout.elementAt(ucLocs[i]) is the UNCHANGED clause for the * - * i-th `or' clause. * - ******************************************************************/ - StringBuffer sb = new StringBuffer(prefix); - int prefixIndent = sb.length(); - sb.append("\\/ "); - int here = sb.length(); - /******************************************************************* - * The number of columns to the left of the code generated for * - * each `or' clause. * - *******************************************************************/ - - /* - * Add the left paren for the statement. - */ - addLeftParen(ast.getOrigin()); - /********************************************************************* - * Produce the output for the clauses, but with a dummy line in * - * place of the UNCHANGED clause, and compute allC, cOrs, and * - * ucLocs. * - *********************************************************************/ - for (int i = 0; i < ast.ors.size(); i++) - { - if (i != 0) - { - sb = new StringBuffer(NSpaces(prefixIndent) + "\\/ "); - } - ; - sb.append("/\\ "); - Vector orClause = (Vector) ast.ors.elementAt(i); - Changed cC = new Changed(c); - for (int j = 0; j < orClause.size(); j++) - { - /*********************************************************** - * On 6 Jun 2010, LL added the "+3" in the following call * - * of GenStmt. This seems to fix a bug which caused * - * * - * either when \/ A * - * \/ B * - * or ... * - * * - * to produce * - * \/ /\ \/ A * - * \/ B * - * \/ ... * - ***********************************************************/ - GenStmt((AST) orClause.elementAt(j), cC, context, sb.toString(), here + 3); - sb = new StringBuffer(NSpaces(here) + "/\\ "); - } - ; - cOrs[i] = cC; - allC.Merge(cC); - ucLocs[i] = tlacode.size(); -// tlacode.addElement("Replace by UNCHANGED"); // - addOneLineOfTLA("Replace by UNCHANGED"); - } - ; // End of for i - - /********************************************************************** - * Insert real UNCHANGED clauses. Note that we have to go through * - * loop backwards since we will remove a line of output for each `or' * - * clause that doesn't get an UNCHANGED. * - **********************************************************************/ - int i = ast.ors.size(); - while (i > 0) - { - i = i - 1; - tlacode.removeElementAt(ucLocs[i]); - mappingVector.removeElementAt(ucLocs[i]); - int numUnchanged = cOrs[i].NumUnchanged(allC); - String NotChanged = cOrs[i].Unchanged(allC); - if (numUnchanged > 1) - { - /* - * The line should be wrapped if it's too long. - */ - String line = NSpaces(here) + "/\\ UNCHANGED <<" + NotChanged + ">>"; - tlacode.insertElementAt(line, ucLocs[i]); - mappingVector.insertElementAt(stringToTLATokens(line), ucLocs[i]); - } else if (numUnchanged == 1) - { // Change made by LL on 16 Mar 2011 so that, if there is a single - // unchanged variable v, it produces v' = v if v is a short variable, - // otherwise it produces UNCHANGED v - // - // tlacode.insertElementAt(NSpaces(here) + "/\\ UNCHANGED " + NotChanged, ucLocs[i]); - if (NotChanged.length() > 5) { - String line = NSpaces(here) + "/\\ UNCHANGED " + NotChanged; - tlacode.insertElementAt(line, ucLocs[i]); - mappingVector.insertElementAt(stringToTLATokens(line), ucLocs[i]); -// tlacode.insertElementAt(NSpaces(here) + "/\\ UNCHANGED " + NotChanged, ucLocs[i]); - } else { - String line = NSpaces(here) + "/\\ " + NotChanged + "' = " + NotChanged; - tlacode.insertElementAt(line, ucLocs[i]); - mappingVector.insertElementAt(stringToTLATokens(line), ucLocs[i]); -// tlacode.insertElementAt(NSpaces(here) + "/\\ " + NotChanged + "' = " -// + NotChanged, ucLocs[i]); - } - } - } - ; - /* - * Add the right paren for the entire statement. - */ - ((Vector) mappingVector.elementAt(mappingVector.size()-1)) - .add(new MappingObject.RightParen(ast.getOrigin().getEnd())); - /********************************************************************** - * Add the statement's unchangeds to c. * - **********************************************************************/ - c.Merge(allC); - } - - private void GenWith(AST.With ast, Changed c, String context, String prefix, int col) throws PcalTLAGenException - { - addLeftParen(ast.getOrigin()); - StringBuffer sb = new StringBuffer(prefix); - TLAExpr exp = AddSubscriptsToExpr(ast.exp, SubExpr(Self(context)), c); -// Vector sv = exp.toStringVector(); - if (ast.isEq) - { - /* generate LET statement */ - sb.append("LET "); - sb.append(ast.var); - sb.append(" == "); - addOneTokenToTLA(sb.toString()); - addLeftParen(exp.getOrigin()); - addExprToTLA(exp); - addRightParen(exp.getOrigin()); -// int here = sb.length(); -// sb.append((String) sv.elementAt(0)); -// for (int v = 1; v < sv.size(); v++) -// { -// tlacode.addElement(sb.toString()); -// sb = new StringBuffer(NSpaces(here)); -// sb.append((String) sv.elementAt(v)); -// } - addOneTokenToTLA(" IN"); - endCurrentLineOfTLA(); -// sb.append(" IN"); -// tlacode.addElement(sb.toString()); - sb = new StringBuffer(NSpaces(col + 2)); - /************************************************************* - * LL changed "col + 4" to "col + 2" here to correct an * - * alignment problem on 31 Jan 2006. * - *************************************************************/ - if (ast.Do.size() > 1) - sb.append("/\\ "); - } else - { - /* generate \E statement */ - sb.append("\\E "); - sb.append(ast.var); - sb.append(" \\in "); - addOneTokenToTLA(sb.toString()); - addLeftParen(exp.getOrigin()); - addExprToTLA(exp); - addRightParen(exp.getOrigin()); -// int here = sb.le - addOneTokenToTLA(":"); - endCurrentLineOfTLA(); -// sb.append(":"); -// tlacode.addElement(sb.toString()); - sb = new StringBuffer(NSpaces(col + 2)); - if (ast.Do.size() > 1) - sb.append("/\\ "); - } - for (int i = 0; i < ast.Do.size(); i++) - { - GenStmt((AST) ast.Do.elementAt(i), c, context, sb.toString(), sb.length()); - sb = new StringBuffer(NSpaces(col + 2) + "/\\ "); - } - // tlacode.addElement(NSpaces(col) + ")"); - - /* - * Add the right paren for the entire statement. - */ - ((Vector) mappingVector.elementAt(mappingVector.size()-1)) - .add(new MappingObject.RightParen(ast.getOrigin().getEnd())); - } - - private void GenWhen(AST.When ast, Changed c, String context, String prefix, int col) throws PcalTLAGenException - { - addOneTokenToTLA(prefix); - -// StringBuffer sb = new StringBuffer(prefix); - TLAExpr exp = AddSubscriptsToExpr(ast.exp, SubExpr(Self(context)), c); - addLeftParen(exp.getOrigin()); - addExprToTLA(exp); - addRightParen(exp.getOrigin()); - endCurrentLineOfTLA(); -// Vector sv = exp.toStringVector(); -// -//// Debugging -////Vector vec = exp.toMappingVector(); -////System.out.println("Original vec:"); -////MappingObject.printMappingVector(vec); -////System.out.println("RemoveRedundantParens(vec)"); -////MappingObject.printMappingVector(TLAtoPCalMapping.RemoveRedundantParens(vec)); -////System.out.println("Should be original mappingvector:"); -////MappingObject.printMappingVector(vec); -// -// sb.append((String) sv.elementAt(0)); -// for (int v = 1; v < sv.size(); v++) -// { -// tlacode.addElement(sb.toString()); -// sb = new StringBuffer(NSpaces(col)); -// sb.append((String) sv.elementAt(v)); -// } -// tlacode.addElement(sb.toString()); - } - - private void GenPrintS(AST.PrintS ast, Changed c, String context, String prefix, int col) - throws PcalTLAGenException - { - StringBuffer sb = new StringBuffer(prefix); - TLAExpr exp = AddSubscriptsToExpr(ast.exp, SubExpr(Self(context)), c); - addLeftParen(ast.getOrigin()); - addOneTokenToTLA(prefix + "PrintT("); - addExprToTLA(exp); - addOneTokenToTLA(")"); - addRightParen(ast.getOrigin()); - endCurrentLineOfTLA(); - -// Vector sv = exp.toStringVector(); -// // The following modified 19 Nov 05 by LL to use PrintT instead of Print -// sb.append("PrintT("); -// sb.append((String) sv.elementAt(0)); -// for (int v = 1; v < sv.size(); v++) -// { -// tlacode.addElement(sb.toString()); -// sb = new StringBuffer(NSpaces(col + "PrintT(".length())); -// sb.append((String) sv.elementAt(v)); -// } -// sb.append(")"); -// tlacode.addElement(sb.toString()); - } - - /********************************************************/ - /* Assert(ast.expr, "Failure of assertion at... ") */ - /** - *******************************************************/ - private void GenAssert(AST.Assert ast, Changed c, String context, String prefix, int col) - throws PcalTLAGenException - { - addLeftParen(ast.getOrigin()); - StringBuffer sb = new StringBuffer(prefix); - StringBuffer sc = new StringBuffer(); - TLAExpr exp = AddSubscriptsToExpr(ast.exp, SubExpr(Self(context)), c); -// Vector sv = exp.toStringVector(); - sb.append("Assert("); - addOneTokenToTLA(sb.toString()); - addLeftParen(exp.getOrigin()); - addExprToTLA(exp); - addRightParen(exp.getOrigin()); - int here = sb.length(); -// sb.append((String) sv.elementAt(0)); -// for (int v = 1; v < sv.size(); v++) -// { -// tlacode.addElement(sb.toString()); -// sb = new StringBuffer(NSpaces(col + "Assert(".length())); -// sb.append((String) sv.elementAt(v)); -// } -// sb.append(", "); - sb = new StringBuffer(", "); - sc.append("\"Failure of assertion at "); - sc.append(ast.location()); - // modified on 23 Mar 2006 by LL to use location() instead of - // ast.line and ast.col - sc.append(".\")"); - if (tlacodeNextLine.length() + sb.length() + sc.length() < wrapColumn) { - addOneTokenToTLA(sb.toString() + sc.toString()); - } -// if (sb.length() + sc.length() < wrapColumn) -// tlacode.addElement(sb.toString() + sc.toString()); - else - { - addOneTokenToTLA(sb.toString()); - endCurrentLineOfTLA(); - addOneTokenToTLA(NSpaces(here) + sc.toString()); -// tlacode.addElement(sb.toString()); -// tlacode.addElement(NSpaces(here) + sc.toString()); - } - addRightParen(ast.getOrigin()); - endCurrentLineOfTLA(); - } - - /********************************************************/ - /* I generate a TRUE conjunct, which is useless, but so */ - /* is a skip statement. */ - /********************************************************/ - private void GenSkip(AST.Skip ast, Changed c, String context, String prefix, int col) - { -// tlacode.addElement(prefix + "TRUE"); - addOneTokenToTLA(prefix); - addLeftParen(ast.getOrigin()); - addOneTokenToTLA("TRUE"); - addRightParen(ast.getOrigin()); - endCurrentLineOfTLA(); - } - - /*********************************************************************** - * Generate the VARIABLES declaration(s), output the TLA+ "code" from * - * a `define' statement, if any, and generate the definition of * - * `vars'. * - * * - * Method renamed from GenVars and given the defs argument by LL on * - * 25 Jan 2006 to handle the `define' statement. * - ***********************************************************************/ - private void GenVarsAndDefs(Vector globals, Vector procs, Vector processes, TLAExpr defs) - throws PcalTLAGenException - { - /******************************************************************* - * lVars and gVars are vectors of strings, each element being a * - * variable name. They hold the local and global variables, * - * respectively. * - *******************************************************************/ - Vector lVars = new Vector(); - Vector gVars = new Vector(); - - /** - * lVarsSource and gVarsSource are vectors of AST.VarDecl objects that - * generated the elements of lVars and gVars, where PVarDecl objects - * are converted to VarDecl objects. - */ - Vector lVarsSource = new Vector(); - Vector gVarsSource = new Vector(); - - /******************************************************************* - * Set gVars to the global variables, including pc and `stack' if * - * there are procedures, and add these variables to vars. * - *******************************************************************/ - if (globals != null) - for (int i = 0; i < globals.size(); i++) - { - AST.VarDecl decl = (AST.VarDecl) globals.elementAt(i); - gVars.addElement(decl.var); - gVarsSource.addElement(decl); - vars.addElement(decl.var); - } - if (! ParseAlgorithm.omitPC) { - gVars.addElement("pc"); - /** - * For added variables, create a VarDecl with null origin. - */ - AST.VarDecl pcVarDecl = new AST.VarDecl(); - pcVarDecl.var = "pc"; - gVarsSource.addElement(pcVarDecl); - vars.addElement("pc"); - } - if (procs != null && procs.size() > 0) - { - gVars.addElement("stack"); - /** - * For added variables, create a VarDecl with null origin. - */ - AST.VarDecl pcVarDecl = new AST.VarDecl(); - pcVarDecl.var = "stack"; - gVarsSource.addElement(pcVarDecl); - vars.addElement("stack"); - } - /******************************************************************* - * Add local procedure variables to lVars, vars, and pcV. * - *******************************************************************/ - if (procs != null) - for (int i = 0; i < procs.size(); i++) - { - AST.Procedure proc = (AST.Procedure) procs.elementAt(i); - if (proc.params != null) - for (int p = 0; p < proc.params.size(); p++) - { - AST.PVarDecl decl = (AST.PVarDecl) proc.params.elementAt(p); - lVars.addElement(decl.var); - lVarsSource.addElement(decl.toVarDecl()) ; - vars.addElement(decl.var); - pcV.addElement(decl.var); - } - if (proc.decls != null) - for (int p = 0; p < proc.decls.size(); p++) - { - AST.PVarDecl decl = (AST.PVarDecl) proc.decls.elementAt(p); - lVars.addElement(decl.var); - lVarsSource.addElement(decl.toVarDecl()) ; - vars.addElement(decl.var); - pcV.addElement(decl.var); - } - } - - /******************************************************************* - * Add local process variables to lVars, vars, and psV for * - * variables local to process sets. * - *******************************************************************/ - if (processes != null) - for (int i = 0; i < processes.size(); i++) - { - AST.Process proc = (AST.Process) processes.elementAt(i); - if (proc.decls != null) - for (int p = 0; p < proc.decls.size(); p++) - { - AST.VarDecl decl = (AST.VarDecl) proc.decls.elementAt(p); - lVars.addElement(decl.var); - lVarsSource.addElement(decl); - vars.addElement(decl.var); - if (!proc.isEq) - psV.addElement(decl.var); - } - } - - /******************************************************************** - * Add a declaration of the constant defaultInitValue if it is * - * used. (Added by LL on 22 Aug 2007.) * - ********************************************************************/ - if (ParseAlgorithm.hasDefaultInitialization) - { - addOneLineOfTLA("CONSTANT defaultInitValue"); - } - ; - - if (EmptyExpr(defs)) - { - /****************************************************************** - * There is no `define' statement. In this case, generate a * - * single VARIABLES statement and set gVars to vector of all * - * variables. * - ******************************************************************/ - gVars.addAll(lVars); - gVarsSource.addAll(lVarsSource) ; - GenVarDecl(gVars, gVarsSource); - } else - { - /****************************************************************** - * There is a `define' statement. In this case, must declare * - * global and local variables separately. Also, set gVars to * - * vector of all variables. * - ******************************************************************/ - GenVarDecl(gVars, gVarsSource); - addOneLineOfTLA(""); - addOneLineOfTLA("(* define statement *)"); - addExprToTLA(defs); -// Vector sv = defs.toStringVector(); -// for (int i = 0; i < sv.size(); i++) -// { -// tlacode.addElement((String) sv.elementAt(i)); -// } - ; - addOneLineOfTLA(""); - GenVarDecl(lVars, lVarsSource); // to be fixed - gVars.addAll(lVars); - gVarsSource.addAll(lVarsSource); - } - ; - addOneLineOfTLA(""); - - /* - * We check for the unlikely case in which there are no variables. - * Without this check, the Init is not generated but appears in - * the definition of Spec. - */ - if (gVars.size() == 0) { - throw new PcalTLAGenException("The algorithm has no variables."); - } - /******************************************************************* - * Generate definition of var. * - *******************************************************************/ -// StringBuffer var = new StringBuffer("vars == << "); -// StringBuffer curLine = new StringBuffer("vars == << "); - addOneTokenToTLA("vars == << ") ; - int indent = tlacodeNextLine.length(); - for (int i = 0; i < gVars.size(); i++) - { - if (i > 0) - { -// var.append(", "); -// curLine.append(", "); -// tlacodeNextLine = tlacodeNextLine + ", "; - addOneTokenToTLA(", "); - } - ; - String vbl = (String) gVars.elementAt(i); - AST.VarDecl vblDecl = (AST.VarDecl) gVarsSource.elementAt(i); - Region vblOrigin = vblDecl.getOrigin(); -// if (curLine.length() + vbl.length() + 1 > wrapColumn) - if (tlacodeNextLine.length() + vbl.length() + 1 > wrapColumn) - { -// curLine = new StringBuffer("vars == << "); -// var.append("\n" + NSpaces("vars == << ".length())); - endCurrentLineOfTLA(); - tlacodeNextLine = NSpaces(indent); - } -// var.append(vbl); -// curLine.append(vbl); - addOneSourceTokenToTLA(vbl, vblOrigin); - } -// if (curLine.length() + " >>".length() + 1 > wrapColumn) - if (tlacodeNextLine.length() + " >>".length() + 1 > wrapColumn) - { -// var.append("\n" + NSpaces("vars ==".length())); - endCurrentLineOfTLA() ; - tlacodeNextLine = NSpaces("vars ==".length()); - } - ; -// var.append(" >>"); -// tlacodeNextLine = tlacodeNextLine + " >>"; - addOneTokenToTLA(" >>"); -// tlacode.addElement(var.toString()); - addOneLineOfTLA(""); - } - - /** - * Generate a VARIABLE(S) declarations. The varVec argument is a vector of - * strings that are the variables to be declared. It does nothing if - * the vector has length 0. The varVecSource argument is a vector - * of the same size as varVec that contains the AST.VarDecl objects. - * <p> - * Method added by LL on 25 Jan 2006. - * - * Modified 16 Dec 2011 to add varVecSource argument and generate TLA to - * PCal mapping. - * - * @param varVec A vector of strings. - * - * @param varVecSource A vector of AST.VarDecl objects. - */ - public void GenVarDecl(Vector varVec, Vector varVecSource) - { -// StringBuffer res = new StringBuffer(); -// StringBuffer curLine = new StringBuffer("VARIABLES "); - // for measuring length - if (varVec.size() == 0) - { - return; - } - ; - if (varVec.size() > 1) - { -// res.append("VARIABLES "); - addOneTokenToTLA("VARIABLES "); - } else - { -// res.append("VARIABLE "); - addOneTokenToTLA("VARIABLE "); - } - ; - for (int i = 0; i < varVec.size(); i++) - { - if (i > 0) - { -// res.append(", "); -// curLine.append(", "); - addOneTokenToTLA(", "); - } - ; - String vbl = (String) varVec.elementAt(i); - AST vblsource = (AST) varVecSource.elementAt(i); -// if (curLine.length() + vbl.length() + 1 > wrapColumn) - if (tlacodeNextLine.length() + vbl.length() + 1 > wrapColumn) - { -// curLine = new String - endCurrentLineOfTLA(); - if (varVec.size() > 1) - { -// res.append(NSpaces("VARIABLES ".length())); - tlacodeNextLine = tlacodeNextLine + NSpaces("VARIABLES ".length()); - } else - { -// res.append(NSpaces("VARIABLE ".length())); - tlacodeNextLine = tlacodeNextLine + NSpaces("VARIABLE ".length()); - } - ; - } - ; -// res.append(vbl); -// curLine.append(vbl); - addOneSourceTokenToTLA(vbl, vblsource.getOrigin()); - } - ; -// tlacode.addElement(res.toString()); - endCurrentLineOfTLA(); - } - - /** - * Generates the "ProcSet == ..." output. It is just a union of all the - * process sets, all on one line (except if a process set is a multi-line - * expression). It wouldn't be too hard to break long lines, but that - * should be done later, if desired, after the TLA to PCal translation - * is finished. - */ - public void GenProcSet() - { - StringBuffer ps = new StringBuffer(); - if (st.processes == null || st.processes.size() == 0) - return; -// ps.append("ProcSet == "); - addOneTokenToTLA("ProcSet == "); - for (int i = 0; i < st.processes.size(); i++) - { - PcalSymTab.ProcessEntry proc = (PcalSymTab.ProcessEntry) st.processes.elementAt(i); -// Vector sv = proc.id.toStringVector(); - if (i > 0) { -// ps.append(" \\cup "); - addOneTokenToTLA(" \\cup "); - } - addLeftParen(proc.id.getOrigin()); - if (proc.isEq) { -// ps.append("{"); - addOneTokenToTLA("{"); - } - else { -// ps.append("("); - addOneTokenToTLA("("); - } - int col = ps.length(); -// ps.append((String) sv.elementAt(0)); -// for (int v = 1; v < sv.size(); v++) -// { -// tlacode.addElement(ps.toString()); -// ps = new StringBuffer(NSpaces(col)); -// ps.append((String) sv.elementAt(v)); -// } - addExprToTLA(proc.id); - if (proc.isEq) { -// ps.append("}"); - addOneTokenToTLA("}"); - } - else { -// ps.append(")"); - addOneTokenToTLA(")"); - } - addRightParen(proc.id.getOrigin()); - } -// tlacode.addElement(ps.toString()); -// tlacode.addElement(""); - endCurrentLineOfTLA(); - addOneLineOfTLA(""); - } - - /***********************************/ - /* Generate the Init == statement. */ - /** - **********************************/ - private void GenInit(Vector globals, Vector procs, Vector processes) throws PcalTLAGenException - { - int col = "Init == ".length(); - StringBuffer is = new StringBuffer(); - is.append("Init == "); - - /* Global variables */ - if (globals != null && globals.size() > 0) - { - is.append("(* Global variables *)"); -// tlacode.addElement(is.toString()); - addOneLineOfTLA(is.toString()) ; - is = new StringBuffer(NSpaces(col)); - for (int i = 0; i < globals.size(); i++) - { - AST.VarDecl decl = (AST.VarDecl) globals.elementAt(i); - addVarDeclToTLA(decl, is); - is = new StringBuffer(NSpaces(col)); - } - } - if (procs != null && procs.size() > 0) - { - /* Procedure variables and parameters */ - for (int i = 0; i < procs.size(); i++) - { - AST.Procedure proc = (AST.Procedure) procs.elementAt(i); - if (proc.params.size() == 0 && proc.decls.size() == 0) - // No parameters or procedure variables in this procedure - continue; - is.append("(* Procedure "); - is.append(proc.name); - is.append(" *)"); -// tlacode.addElement(is.toString()); - addOneLineOfTLA(is.toString()); - is = new StringBuffer(NSpaces(col)); - for (int p = 0; p < proc.params.size(); p++) - { - AST.PVarDecl decl = (AST.PVarDecl) proc.params.elementAt(p); - if (!mp) { - addVarDeclToTLA(decl.toVarDecl(), is); - } - else { - is.append("/\\ "); - addOneTokenToTLA(is.toString()); - addLeftParen(decl.getOrigin()); -// is.append(decl.var); - is = new StringBuffer(decl.var); - /******************************************************* - * Modified on 31 Jan 2006 by LL to add subscripts to * - * initialization expression if needed. Also replaced * - * test for "\\in" with assertion that it can't occur, * - * since it's forbidden by the grammar. * - *******************************************************/ - PcalDebug.Assert(decl.isEq); - is.append(" = "); - -// Vector sv; -// if (mp) -// { -// sv = AddSubscriptsToExpr(decl.val, -// SubExpr(Self("procedure")), new Changed(new Vector())) -// .toStringVector(); -// } else -// { -// sv = Parenthesize(decl.val.toStringVector()); -// /************************************************* -// * Call to Parenthesize added by LL on 27 Feb 2008. * -// * See bug_08-02-18. * -// *************************************************/ -// } -// ; -// if (mp) -// { - is.append("[ self \\in ProcSet |-> "); -// } - addOneTokenToTLA(is.toString()); - addLeftParen(decl.val.getOrigin()); - addExprToTLA( - AddSubscriptsToExpr(decl.val, - SubExpr(Self("procedure")), - new Changed(new Vector()))); - addRightParen(decl.val.getOrigin()); - addOneTokenToTLA("]"); - addRightParen(decl.getOrigin()); - endCurrentLineOfTLA(); - -// int col2 = is.length(); -// is.append((String) sv.elementAt(0)); -// for (int v = 1; v < sv.size(); v++) -// { -// tlacode.addElement(is.toString()); -// is = new StringBuffer(NSpaces(col2)); -// is.append((String) sv.elementAt(v)); -// } -// if (mp) -// is.append("]"); -// tlacode.addElement(is.toString()); - } - is = new StringBuffer(NSpaces(col)); - } - for (int p = 0; p < proc.decls.size(); p++) - { - /* - * Note: the following code is identical to the for loop - * code above for procedure variables. (Well done, Keith!) - * I realized this too late to feel like procedurizing it. - */ - AST.PVarDecl decl = (AST.PVarDecl) proc.decls.elementAt(p); - if (!mp) { - addVarDeclToTLA(decl.toVarDecl(), is); - } - else { - is.append("/\\ "); - addOneTokenToTLA(is.toString()); - addLeftParen(decl.getOrigin()); -// is.append(decl.var); - is = new StringBuffer(decl.var); -// is.append("/\\ "); -// is.append(decl.var); - - /******************************************************* - * Modified on 31 Jan 2006 by LL to add subscripts to * - * initialization expression if needed. Also replaced * - * test for "\\in" with assertion that it can't occur, * - * since it's forbidden by the grammar. * - *******************************************************/ - PcalDebug.Assert(decl.isEq); - is.append(" = "); -// Vector sv; -// if (mp) -// { -// sv = AddSubscriptsToExpr(decl.val, SubExpr(Self("procedure")), new Changed(new Vector())) -// .toStringVector(); -// } else -// { -// sv = Parenthesize(decl.val.toStringVector()); -// /************************************************* -// * Call to Parenthesize added by LL on * -// * 27 Feb 2008. See bug_08-02-18. * -// *************************************************/ -// } -// ; -// if (mp) -// { - is.append("[ self \\in ProcSet |-> "); -// } - addOneTokenToTLA(is.toString()); - addLeftParen(decl.val.getOrigin()); - addExprToTLA(AddSubscriptsToExpr( - decl.val, - SubExpr(Self("procedure")), - new Changed(new Vector()))); - addRightParen(decl.val.getOrigin()); - addOneTokenToTLA("]"); - addRightParen(decl.getOrigin()); - endCurrentLineOfTLA(); - -// int col2 = is.length(); -// is.append((String) sv.elementAt(0)); -// for (int v = 1; v < sv.size(); v++) -// { -// tlacode.addElement(is.toString()); -// is = new StringBuffer(NSpaces(col2)); -// is.append((String) sv.elementAt(v)); -// } -// if (mp) -// is.append("]"); -// tlacode.addElement(is.toString()); - } - is = new StringBuffer(NSpaces(col)); - } - } - } - if (processes != null && processes.size() > 0) - { - /* Process variables */ - for (int i = 0; i < processes.size(); i++) - { - AST.Process proc = (AST.Process) processes.elementAt(i); - if (proc.decls.size() == 0) // No variables in this procedure - continue; - is.append("(* Process "); - is.append(proc.name); - is.append(" *)"); -// tlacode.addElement(is.toString()); - addOneLineOfTLA(is.toString()); - is = new StringBuffer(NSpaces(col)); - for (int p = 0; p < proc.decls.size(); p++) - { - /* - * In the comments below, (( and )) represent - * MappingObject.LeftParen and MappingObject.RightParen - * objects. - */ - AST.VarDecl decl = (AST.VarDecl) proc.decls.elementAt(p); - is.append("/\\ "); - /* - * The following adds /\ (( to the TLA+ output. - */ - addOneTokenToTLA(is.toString()); - addLeftParen(decl.getOrigin()); - - - if (proc.isEq) { - /* - * The source is - * - * process (P = S) variables ... v @@ Val - * - * where @@ is either "=" or "\in". The TLA+ output is - * - * /\ (( v @@ (( Val )) )) - */ - is = new StringBuffer(decl.var); - if (decl.isEq) { - is.append(" = "); - } - else { - is.append(" \\in "); - } - addOneTokenToTLA(is.toString()); - addLeftParen(decl.val.getOrigin()); - addExprToTLA(decl.val); - addRightParen(decl.val.getOrigin()); - } - else { - if (decl.isEq) { - /* - * The source is - * - * process (P \in S) variables ... v = Val - * - * The TLA+ output is - * - * /\ (( v = [self \in (( S )) |-> (( ValBar )) ] )) - * - * where ValBar obtained from Val by replacing each - * variable w of the process with w[self]. - */ - is = new StringBuffer(decl.var); - is.append(" = [self \\in "); - addOneTokenToTLA(is.toString()); - addLeftParen(proc.id.getOrigin()); - addExprToTLA(proc.id); - addRightParen(proc.id.getOrigin()); - addOneTokenToTLA(" |-> " ); - addLeftParen(decl.val.getOrigin()); - addExprToTLA(AddSubscriptsToExpr( - decl.val, - SubExpr(Self("procedure")), - new Changed(new Vector()))); - addRightParen(decl.val.getOrigin()); - addOneTokenToTLA("]"); - - } - else { - /* - * The source is - * - * process (P \in S) variables ... v \in Val - * - * The TLA+ output is - * - * /\ (( v \in [ (( S )) -> (( ValBar )) ] )) - * - * where ValBar is obtained from Val by replacing each - * variable w of the process with - * - * w[CHOOSE self \in S : TRUE] - * - * We first set expr to the TLAExpr "CHOOSE self \in S : TRUE". - * (This is Keith's original code.) - */ - TLAExpr subexpr = proc.id.cloneAndNormalize(); - TLAExpr expr = new TLAExpr(); - expr.addLine(); - expr.addToken(new TLAToken("[", 0, TLAToken.BUILTIN)); - expr.addToken(new TLAToken("CHOOSE", 1, TLAToken.BUILTIN)); - expr.addToken(new TLAToken("self", 8, TLAToken.IDENT)); - expr.addToken(new TLAToken("\\in ", 13, TLAToken.BUILTIN)); - expr.normalize(); - expr.setOrigin(subexpr.getOrigin()); // see what this does. - try - { - subexpr.prepend(expr, 1); - expr = new TLAExpr(); - expr.addLine(); - expr.addToken(new TLAToken(":", 0, TLAToken.BUILTIN)); - expr.addToken(new TLAToken("TRUE", 2, TLAToken.BUILTIN)); - expr.addToken(new TLAToken("]", 6, TLAToken.BUILTIN)); - expr.prepend(subexpr, 1); - } catch (TLAExprException e) - { - throw new PcalTLAGenException(e.getMessage()); - } - - /* - * Now we output the TLA+ code. - */ - is = new StringBuffer(decl.var); - is.append(" \\in ["); - addOneTokenToTLA(is.toString()); - addLeftParen(proc.id.getOrigin()); - addExprToTLA(proc.id); - addRightParen(proc.id.getOrigin()); - addOneTokenToTLA(" -> " ); - addLeftParen(decl.val.getOrigin()); - addExprToTLA(AddSubscriptsToExpr( - decl.val, expr, new Changed(new Vector())) ); - addRightParen(decl.val.getOrigin()); - addOneTokenToTLA("]"); - } - } - /* - * This adds the final )) . - */ - addRightParen(decl.getOrigin()); - endCurrentLineOfTLA(); - is = new StringBuffer(NSpaces(col)); - -// everything from here down to the end of the for p loop should be commented out -// is.append(decl.var); -// is = new StringBuffer(decl.var); -// if (decl.isEq) -// is.append(" = "); -// else -// is.append(" \\in "); -// /******************************************************* -// * Modified on 31 Jan 2006 by LL to add subscripts to * -// * initialization expression for process set. Note * -// * tricky subscript that is added in expr for * -// * declaration of form "v \in expr". * -// * * -// * Also modified the whole method of producing the * -// * variable declaration because the original destroyed * -// * the formatting of the expression proc.id, leading * -// * to bad or incorrect formatting if the process id * -// * set expression was not trivial. * -// *******************************************************/ -// Vector sv; -// TLAExpr sve; -// if (proc.isEq) -// { -// /*************************************************** -// * No substitution unless it's a process set. * -// ***************************************************/ -// sve = decl.val; // .toStringVector(); -// } else -// { -// if (decl.isEq) -// { -// /*********************************************** -// * For declaration "v = ...", add subscript * -// * "[self]". * -// ***********************************************/ -// sve = AddSubscriptsToExpr(decl.val, SubExpr(Self("procedure")), new Changed(new Vector())); -// } else -// { -// /************************************************ -// * For declaration "v \in ...", add subscript * -// * "[CHOOSE self \in Process Id Set : TRUE]". * -// * * -// * This weird subscript is needed in the * -// * following weird case: * -// * * -// * process (P \in S) * -// * variable v \in T, w \in R(v) * -// * * -// * This produces the following conjunct in * -// * the initial predicate for w: * -// * * -// * w \in [S -> R(v[CHOOSE self \in S : TRUE])] * -// ************************************************/ -// TLAExpr subexpr = proc.id.cloneAndNormalize(); -// -// TLAExpr expr = new TLAExpr(); -// expr.addLine(); -// expr.addToken(new TLAToken("[", 0, TLAToken.BUILTIN)); -// expr.addToken(new TLAToken("CHOOSE", 1, TLAToken.BUILTIN)); -// expr.addToken(new TLAToken("self", 8, TLAToken.IDENT)); -// expr.addToken(new TLAToken("\\in ", 13, TLAToken.BUILTIN)); -// expr.normalize(); -// -// try -// { -// subexpr.prepend(expr, 1); -// expr = new TLAExpr(); -// expr.addLine(); -// expr.addToken(new TLAToken(":", 0, TLAToken.BUILTIN)); -// expr.addToken(new TLAToken("TRUE", 2, TLAToken.BUILTIN)); -// expr.addToken(new TLAToken("]", 6, TLAToken.BUILTIN)); -// expr.prepend(subexpr, 1); -// } catch (TLAExprException e) -// { -// throw new PcalTLAGenException(e.getMessage()); -// } -// -// sve = AddSubscriptsToExpr(decl.val, expr, new Changed(new Vector())); -// } -// ; -// } -// ; -// TLAExpr expr = new TLAExpr(); -// expr.addLine(); -// if (!proc.isEq) -// { -// expr.addToken(new TLAToken("[", 0, TLAToken.BUILTIN)); -// if (decl.isEq) -// { -// expr.addToken(new TLAToken("self", 1, TLAToken.IDENT)); -// expr.addToken(new TLAToken("\\in ", 6, TLAToken.BUILTIN)); -// } -// ; -// expr.normalize(); -// TLAExpr expr2 = proc.id.cloneAndNormalize(); -// try -// { -// expr2.prepend(expr, 0); -// expr = new TLAExpr(); -// expr.addLine(); -// if (decl.isEq) -// { -// expr.addToken(new TLAToken("|->", 0, TLAToken.BUILTIN)); -// } else -// { -// expr.addToken(new TLAToken("->", 0, TLAToken.BUILTIN)); -// } -// ; -// expr.prepend(expr2, 1); -// sve.prepend(expr, 1); -// } catch (TLAExprException e) -// { -// throw new PcalTLAGenException(e.getMessage()); -// } -// } -// ; -// sv = sve.toStringVector(); -// if (proc.isEq) -// { -// sv = Parenthesize(sv); -// } -// ; -// /***************************************************** -// * Call to Parenthesize added by LL on 27 Feb 2008. * -// * See bug_08-02-18. * -// *****************************************************/ -// int col2 = is.length(); -// is.append((String) sv.elementAt(0)); -// for (int v = 1; v < sv.size(); v++) -// { -// tlacode.addElement(is.toString()); -// is = new StringBuffer(NSpaces(col2)); -// is.append((String) sv.elementAt(v)); -// } -// if (!proc.isEq) -// is.append("]"); -// tlacode.addElement(is.toString()); -// is = new StringBuffer(NSpaces(col)); -// end of section to be commented out - } // end of for p loop. - } - } - - /* stack initial value */ - if (procs != null && procs.size() > 0) - { - if (mp) - is.append("/\\ stack = [self \\in ProcSet |-> << >>]"); - else - is.append("/\\ stack = << >>"); -// tlacode.addElement(is.toString()); - addOneLineOfTLA(is.toString()); - is = new StringBuffer(NSpaces(col)); - } - /* pc initial value */ - if (! ParseAlgorithm.omitPC) { - if (mp) - { - // On 4 May 2012, LL added useCase flag to inhibit adding of CASE for - // a single process or process set. - boolean useCase = st.processes.size() != 1; - if (useCase) { - is.append("/\\ pc = [self \\in ProcSet |-> CASE "); - } else { - is.append("/\\ pc = [self \\in ProcSet |-> "); - } - int colPC = is.length(); - if (boxUnderCASE) - colPC = colPC - 3; - for (int p = 0; p < st.processes.size(); p++) - { - PcalSymTab.ProcessEntry pe = (PcalSymTab.ProcessEntry) st.processes.elementAt(p); - if (useCase) { - is.append("self "); - if (pe.isEq) { - is.append("= "); - // int colExpr = is.length(); - addOneTokenToTLA(is.toString()); - addLeftParen(pe.id.getOrigin()); - addExprToTLA(pe.id); - addRightParen(pe.id.getOrigin()); - - // Vector sv = pe.id.toStringVector(); - // is.append((String) sv.elementAt(0)); - // for (int v = 1; v < sv.size(); v++) - // { - // addOneLineOfTLA(is.toString()); - // // tlacode.addElement(is.toString()); - // is = new StringBuffer(NSpaces(colExpr)); - // is.append((String) sv.elementAt(v)); - // } - } else { - is.append("\\in "); - // int colExpr = is.length(); - // Vector sv = pe.id.toStringVector(); - // is.append((String) sv.elementAt(0)); - // for (int v = 1; v < sv.size(); v++) - // { - // tlacode.addElement(is.toString()); - // is = new StringBuffer(NSpaces(colExpr)); - // is.append((String) sv.elementAt(v)); - // } - addOneTokenToTLA(is.toString()); - addLeftParen(pe.id.getOrigin()); - addExprToTLA(pe.id); - addRightParen(pe.id.getOrigin()); - - } - // is.append(" -> \""); - is = new StringBuffer(" -> \""); - is.append(pe.iPC); - if (p == st.processes.size() - 1) - is.append("\"]"); - else if (!boxUnderCASE) - is.append("\" []"); - else - is.append("\""); - } // end if (useCase) - else { - is.append("\"" + pe.iPC + "\"]"); - } -// tlacode.addElement(is.toString()); - addOneTokenToTLA(is.toString()); - endCurrentLineOfTLA(); - is = new StringBuffer(NSpaces(colPC)); - if (boxUnderCASE && p < st.processes.size() - 1) - is.append("[] "); - } - } else - { - is.append("/\\ pc = \"" + st.iPC + "\""); -// tlacode.addElement(is.toString()); - addOneLineOfTLA(is.toString()); - } - } -// tlacode.addElement(""); - addOneLineOfTLA(""); - } - - /************************************/ - /* Generate the Next == definition. */ - /************************************/ - private void GenNext() - { - // It we are omitting pc and this is a uniprocess - // algorithm, then the definition of Next has - // already been added. - if (ParseAlgorithm.omitPC && !mp) { - return; - } - Vector nextS = new Vector(); - StringBuffer sb = new StringBuffer(); - int max, col; - - // Steps with no parameter - max = wrapColumn - ("Next == \\/ ".length()); - for (int i = 0; i < nextStep.size(); i++) - { - String a = (String) nextStep.elementAt(i); - if (a.length() + " \\/ ".length() + sb.length() > max) - { - nextS.addElement(sb.toString()); - sb = new StringBuffer(); - } - if (sb.length() > 0) - sb.append(" \\/ "); - sb.append(a); - } - if (sb.length() > 0) - nextS.addElement(sb.toString()); - - // Steps with (self) from ProcSet - // These are procedures in a multiprocess algorithm - Vector nextSS = new Vector(); - String nextSSstart = "(\\E self \\in ProcSet: "; - sb = new StringBuffer(); - max = wrapColumn - ("Next == \\/ (\\E self \\in ProcSet: \\/ ".length()); - if (mp && st.procs.size() > 0) - { - for (int i = 0; i < st.procs.size(); i++) - { - PcalSymTab.ProcedureEntry p = (PcalSymTab.ProcedureEntry) st.procs.elementAt(i); - if ((p.name.length() + "(self) \\/ ".length() + sb.length()) > max) - { - nextSS.addElement(sb.toString()); - sb = new StringBuffer(); - } - if (sb.length() > 0) - sb.append(" \\/ "); - sb.append(p.name); - sb.append("(self)"); - } - if (sb.length() > 0) - nextSS.addElement(sb.toString() + ")"); - } - - // Steps with (self) from a set - // These are process sets - Vector nextSSP = new Vector(); // of Vector - if (mp && st.processes.size() > 0) - for (int i = 0; i < st.processes.size(); i++) - { - PcalSymTab.ProcessEntry p = (PcalSymTab.ProcessEntry) st.processes.elementAt(i); - if (p.isEq) - continue; - Vector vec = new Vector(); - sb = new StringBuffer(); - sb.append("(\\E self \\in "); - Vector sv = p.id.toStringVector(); - col = sb.length(); - sb.append((String) sv.elementAt(0)); - for (int j = 1; j < sv.size(); j++) - { - vec.addElement(sb.toString()); - sb = new StringBuffer(NSpaces(col)); - sb.append((String) sv.elementAt(j)); - } - sb.append(": "); - sb.append(p.name); - sb.append("(self))"); - vec.addElement(sb.toString()); - nextSSP.addElement(vec); - } - - // assemble the line from the pieces - sb = new StringBuffer("Next == "); - col = sb.length() + 2; - for (int i = 0; i < nextS.size(); i++) - { - sb.append((String) nextS.elementAt(i)); - addOneLineOfTLA(sb.toString()); -// tlacode.addElement(sb.toString()); - sb = new StringBuffer(NSpaces(col) + " \\/ "); - } - if (nextSS.size() > 0) - { - sb.append(nextSSstart); - int col2 = sb.length(); - if (nextSS.size() > 1) - sb.append(" \\/ "); - for (int i = 0; i < nextSS.size(); i++) - { - sb.append((String) nextSS.elementAt(i)); - addOneLineOfTLA(sb.toString()); -// tlacode.addElement(sb.toString()); - sb = new StringBuffer(NSpaces(col2) + " \\/ "); - } - sb = new StringBuffer(NSpaces(col) + " \\/ "); - } - if (nextSSP.size() > 0) - for (int i = 0; i < nextSSP.size(); i++) - { - Vector v = (Vector) nextSSP.elementAt(i); - for (int j = 0; j < v.size(); j++) - { - String line = (String) v.elementAt(j); - sb.append(line); - addOneLineOfTLA(sb.toString()); -// tlacode.addElement(sb.toString()); - - // The following if case was added by LL on 22 Jan 2011 - // to correct part 1 of bug bug_11_01_13. This bug occurs - // when an "\in" process's set is multi-line and that - // process's next-state action comes immediately after - // the Next == ..., with no " \/ " preceding it. To fix the - // problem, we must add 6 fewer spaces to all lines after - // the first in that process's set than in other such sets. - if ((nextS.size() == 0) && (nextSS.size() == 0) && (i == 0)) { - sb = new StringBuffer(NSpaces(col - 2)); - } else { - sb = new StringBuffer(NSpaces(col + 4)); - } - } - sb = new StringBuffer(NSpaces(col) + " \\/ "); - } - if (! (PcalParams.NoDoneDisjunct || ParseAlgorithm.omitStutteringWhenDone)) - { sb.append("(* Disjunct to prevent deadlock on termination *)"); - addOneLineOfTLA(sb.toString()); -// tlacode.addElement(sb.toString()); - sb = new StringBuffer(NSpaces(col + 4)); - if (mp) - /************************************************************ - * Bug fix by LL on 6 Sep 2007. Added parentheses to * - * change * - * * - * (*) \A self \in ProcSet: ... /\ UNCHANGED vars * - * * - * to * - * * - * (**) (\A self \in ProcSet: ...) /\ UNCHANGED vars * - * * - * thus moving the UNCHANGED vars outside the quantifier. * - * Since self does not appear in UNCHANGED vars, the two * - * expressions are equivalent except when ProcSet is the * - * empty set, in which case (*) equals TRUE and (**) equals * - * UNCHANGED vars. * - ************************************************************/ - sb.append("((\\A self \\in ProcSet: pc[self] = \"Done\") /\\ " + "UNCHANGED vars)"); - else - sb.append("(pc = \"Done\" /\\ UNCHANGED vars)"); - addOneLineOfTLA(sb.toString()); -// tlacode.addElement(sb.toString()); - } ; - addOneLineOfTLA(""); -// tlacode.addElement(""); - } - - /****************************************/ - /* Generate the Spec == ... definition. */ - /****************************************/ - /*********************************************************************** - * The spec can contain the following conjuncts * - * * - * 1. Init /\ [][Next]_vars * - * Always present * - * * - * 2. WF_var(Next) * - * present if (a) The wfNext option is specified, or * - * (b) It is a uniprocess algorithm and one of the * - * options -wf, -sf, -termination is specified. * - * * - * 3. A sequence of process fairness formulas, containing * - * one for each process for which there is a fairness condition. * - * * - * A process has * - * (a) a WF fairness condition iff * - * (i) it is preceded by the keyword "fair" and the -nof * - * option is not specified, or * - * (ii) the -wf option is specified. * - * (b) an SF fairness condition iff it is not preceded * - * by the keyword "fair" and the -sf option is specified. * - * * - * Let P be a process specified by either * - * * - * [fair] process (P = exp) ... * - * [fair] process (P \in exp) ... * - * * - * In the first case we say that P is a single process, in the second * - * that it is a process set. Let * - * * - * - p_1, ... , p_Np be the labels of P modified by "+" * - * * - * - m_1, ... , m_Nm be the set of labels of P modified by "-" * - * * - * - pSelf = IF P is a single process THEN "pname" * - * ELSE "self" * - * * - * - qSelf = IF /\ P is a single process * - * /\ pSelf a multi-line formula * - * THEN "self" * - * ELSE pSelf * - * * - * - pName = IF P is a single process THEN "P" * - * ELSE "P(self)" * - * * - * A process fairness formula is described by the following: * - * * - * XF: * - * either WF or SF depending on P's fairness condition * - * * - * prefix: * - * IF P is a single process * - * THEN IF pSelf a multi-line formula * - * THEN "LET self = pSelf IN" * - * ELSE "" * - * ELSE "\A self \in exp" * - * * - * wfFormula: * - * "XF( (pc[qSelf] \notin {"m_1", ... , "m_Np"}) /\ pName )" * - * * - * sfFormula: * - * if XF = SF * - * then null * - * else if P a single process * - * then "SF_vars(p_1) /\ ... /\ SF_vars(p_Np)" * - * else "SF_vars(p_1(self)) /\ ... * - * /\ SF_vars(p_Np(self))" * - * * - * prcdFormulas: * - * A sequence consisting of the following two formulas for each * - * procedure D called within P. Let * - * * - * - pd_1, ... , pd_Npd be the labels of D modified by "+" * - * - md_1, ... , md_Nmd be the labels of D modified by "-" * - * * - * in the formulas: * - * * - * wfFormula: * - * "XF( (pc[qSelf] \notin {"md_1", ... , "md_Nmd"}) * - * /\ D(qSelf) )" * - * * - * sfFormula: * - * if XF = SF * - * then null * - * else "SF_vars(pd_1(qSelf)) /\ ... * - * /\ SF_vars(pd_Npd(qSelf))" * - * * - * ------- * - * * - * If there is at least one fairness formula, the definition of Spec * - * will be formatted in one of two ways. If there is either a * - * WF_vars(Next) condition or a process fairness formula, then it may * - * be formatted as: * - * * - * Spec == Init /\ [][Next]_vars * - * * - * otherwise, it will be formatted as * - * * - * Spec == /\ Init /\ [][Next]_vars * - * [/\ WF_vars(Next)] * - * /\ F_1 * - * ... * - * /\ F_n * - * * - * where each F_i is a process fairness formulas. * - ***********************************************************************/ - private void GenSpec() - { String safetyFormula = "Init /\\ [][Next]_vars" ; - - if ( PcalParams.FairnessOption.equals("nof") - || (!mp && PcalParams.FairnessOption.equals(""))) { - addOneLineOfTLA("Spec == " + safetyFormula); - addOneLineOfTLA(""); -// tlacode.addElement("Spec == " + safetyFormula ); -// tlacode.addElement(""); - return; - } -//System.out.println("foo |-> " + st.UseThis(PcalSymTab.PROCEDURE, "foo", "")); -//int to = st.FindProc("foo"); -//PcalSymTab.ProcedureEntry pe = -// (PcalSymTab.ProcedureEntry) st.procs.elementAt(to); -//AST.Procedure procAst = pe.ast; - - StringBuffer sb = new StringBuffer("Spec == "); - // Generate the requested fairness conjuncts - - // wfNextConj is either null or " /\ WF_(Next)" - String wfNextConj = null; - if ( PcalParams.FairnessOption.equals("wfNext") - || PcalParams.FairAlgorithm - || (!mp && ( PcalParams.FairnessOption.equals("wf") - || PcalParams.FairnessOption.equals("sf")))) - { - // If uniprocess then wf and sf are the same as wfNext - wfNextConj = " /\\ WF_vars(Next)"; - } - - // Now compute procFairnessFormulas to equal the processes' fairness - // formulas, which is never null but may have zero length. - Vector procFairnessFormulas = new Vector() ; - if (mp) { - for (int i = 0; i < st.processes.size(); i++) { - PcalSymTab.ProcessEntry p = (PcalSymTab.ProcessEntry) st.processes.elementAt(i); - AST.Process pAst = p.ast ; - int fairness = pAst.fairness; - if (fairness != AST.UNFAIR_PROC) { - String xf = (fairness == AST.WF_PROC) ? "WF" : "SF"; - - Vector pSelf = p.id.toStringVector(); - - // makeLetIn is true iff prefix will be LET self == ... IN - boolean makeLetIn = false ; - - String qSelf = "self"; - if (p.isEq) { - if (pSelf.size() > 1) { - makeLetIn = true ; - } else { - qSelf = (String) pSelf.elementAt(0); - } - } - - Vector prefix = new Vector(); - if (makeLetIn || !p.isEq) { - int prefixSize = pSelf.size(); - String prefixBegin; - String prefixEnd; - if (p.isEq) { - prefixBegin = "LET self == "; - prefixEnd = ""; - } else { - prefixBegin = "\\A self \\in "; - prefixEnd = " : "; - } - String padding = NSpaces(prefixBegin.length()); - for (int j = 0; j < prefixSize; j++) { - String line = (String) pSelf.elementAt(j); - if (j == 0) { - line = prefixBegin + line; - } else { - line = padding + line; - } - if (j == prefixSize - 1) { - line = line + prefixEnd; - } - prefix.addElement(line); - } - if (makeLetIn) { - prefix.addElement("IN "); - } - } // end if (makeLetIn || !p.isEq) - - StringBuffer wfSB = new StringBuffer(xf + "_vars("); - if (pAst.minusLabels != null && pAst.minusLabels.size() > 0) { - wfSB.append("(pc["); - wfSB.append(qSelf); - if (pAst.minusLabels.size() == 1) { - wfSB.append("] # \""); - wfSB.append(pAst.minusLabels.elementAt(0)); - wfSB.append("\""); - } else { - wfSB.append("] \\notin {\""); - for (int j = 0; j < pAst.minusLabels.size(); j++) { - wfSB.append(pAst.minusLabels.elementAt(j)); - if (j == pAst.minusLabels.size() - 1) { - wfSB.append("\"}"); - } else { - wfSB.append("\", \""); - } - } - } - wfSB.append(") /\\ "); - } - - String pName = p.name; - if (!p.isEq) { - pName = p.name + "(self)"; - } - wfSB.append(pName); - wfSB.append(")"); - - StringBuffer sfSB = null ; - if ( xf.equals("WF") - && (pAst.plusLabels != null) - && (pAst.plusLabels.size() != 0)) { - sfSB = new StringBuffer() ; - for (int j = 0; j < pAst.plusLabels.size(); j++) { - if (j != 0) { - sfSB.append(" /\\ "); - } - sfSB.append("SF_vars("); - sfSB.append(pAst.plusLabels.elementAt(j)); - if (!p.isEq) { - sfSB.append("(self)"); - } - sfSB.append(")"); - } - } - - Vector prcdFormulas = new Vector(); - Vector procedures = pAst.proceduresCalled; - for (int k = 0; k < procedures.size(); k++) { - String originalName = (String) procedures.elementAt(k); - String name = st.UseThis(PcalSymTab.PROCEDURE, originalName, ""); - int procedureIndex = st.FindProc(name); - PcalSymTab.ProcedureEntry pe = - (PcalSymTab.ProcedureEntry) st.procs.elementAt(procedureIndex); - AST.Procedure prcAst = pe.ast; - - StringBuffer wfPrcSB = new StringBuffer(xf + "_vars("); - if (prcAst.minusLabels != null && prcAst.minusLabels.size() > 0) { - wfPrcSB.append("(pc["); - wfPrcSB.append(qSelf); - if (prcAst.minusLabels.size() == 1) { - wfPrcSB.append("] # \""); - wfPrcSB.append(prcAst.minusLabels.elementAt(0)); - wfPrcSB.append("\""); - } else { - wfPrcSB.append("] \\notin {\""); - for (int j = 0; j < prcAst.minusLabels.size(); j++) { - wfPrcSB.append(prcAst.minusLabels.elementAt(j)); - if (j == prcAst.minusLabels.size() - 1) { - wfPrcSB.append("\"}"); - } else { - wfPrcSB.append("\", \""); - } - } - } - wfPrcSB.append(") /\\ "); - } - - String prcName = pe.name + "(" + qSelf + ")"; - wfPrcSB.append(prcName); - wfPrcSB.append(")"); - - StringBuffer sfPrcSB = null; - if ( xf.equals("WF") - && (prcAst.plusLabels != null) - && (prcAst.plusLabels.size() != 0)) { - sfPrcSB = new StringBuffer() ; - for (int j = 0; j < prcAst.plusLabels.size(); j++) { - if (j != 0) { - sfPrcSB.append(" /\\ "); - } - sfPrcSB.append("SF_vars("); - sfPrcSB.append(prcAst.plusLabels.elementAt(j)); - sfPrcSB.append("(" + qSelf + ")") ; - sfPrcSB.append(")"); - } - } - prcdFormulas.addElement( - new FormulaPair( - wfPrcSB.toString(), - (sfPrcSB == null) ? null : sfPrcSB.toString()) - ) ; - } // end construction of prcdFormulas - - procFairnessFormulas.addElement( - new ProcessFairness( - xf, - prefix, - wfSB.toString(), - (sfSB == null) ? null : sfSB.toString(), - prcdFormulas) - ) ; - } // end if (fairness != AST.UNFAIR_PROC) - } - } // ends construction of procFairnessFormulas - - if (wfNextConj == null && procFairnessFormulas.size() == 0) { - addOneLineOfTLA("Spec == " + safetyFormula); - addOneLineOfTLA(""); -// tlacode.addElement("Spec == " + safetyFormula); -// tlacode.addElement(""); - return; - } - addOneLineOfTLA("Spec == /\\ " + safetyFormula); -// tlacode.addElement("Spec == /\\ " + safetyFormula) ; - int indent = "Spec == /\\ ".length(); - - if (wfNextConj != null) { - addOneLineOfTLA(" /\\ WF_vars(Next)"); -// tlacode.addElement(" /\\ WF_vars(Next)"); - } - for (int i = 0; i < procFairnessFormulas.size(); i++) { - /* - * The original code called format on the fairness formula, which can - * create a string with \n characters embedded. I've just split the - * string into its individual lines and added them to tlacode one at a time. - * However, the current and original code can both produce very long lines - * that could be wrapped. So if this change does make a difference, then - * it would be better to completely rewrite the format method (which is only - * called here). - */ - String str = - " /\\ " + - ((ProcessFairness) procFairnessFormulas.elementAt(i)).format(indent).toString(); - String [] splitStr = str.split("\n"); - for (int j = 0; j < splitStr.length; j++) { - addOneLineOfTLA(splitStr[j]); - } - -// tlacode.addElement( -// " /\\ " + -// ((ProcessFairness) procFairnessFormulas.elementAt(i)).format(indent) -// ); - } - addOneLineOfTLA(""); -// tlacode.addElement(""); - return; - } - - /************************************/ - /* Generate the Termination == */ - /************************************/ - private void GenTermination() - { - // if we're omitting the pc or omitting the stuttering-when-done - // clause of the Next action, then we shouldn't - // generate the Termination definition. - // Check of omitStutteringWhenDone added by LL on 30 Mar 2012. - if (ParseAlgorithm.omitPC || ParseAlgorithm.omitStutteringWhenDone) { - return; - } - StringBuffer sb = new StringBuffer(); - sb.append("Termination == <>("); - if (mp) - sb.append("\\A self \\in ProcSet: pc[self]"); - else - sb.append("pc"); - sb.append(" = \"Done\")"); - addOneLineOfTLA(sb.toString()); - addOneLineOfTLA(""); -// tlacode.addElement(sb.toString()); -// tlacode.addElement(""); - } - - /**********************************************************/ - /* For variables that need subscripts, add the subscript. */ - /* These are pc, stack, procedure variables, procedure */ - /* parameters, and variables defined in process sets. */ - /* Then, add primes to variables that have been changed */ - /* according to c. */ - /* exprn : the original expression. */ - /* sub : the subscript to be added (null if none) */ - /* c : the variables that have been changed (so need to */ - /* be primed. */ - /**********************************************************/ - private TLAExpr AddSubscriptsToExpr(TLAExpr exprn, TLAExpr sub, Changed c) throws PcalTLAGenException - { - /* - * For testing, throw a null pointer exception if the begin/end substitution - * mapping vectors are not properly matching in the returned expression - */ -// int[] depths = new int[1000]; - - int parenDepth = 0; - for (int i = 0; i < exprn.tokens.size(); i++) { - Vector line = (Vector) exprn.tokens.elementAt(i); - for (int j = 0; j < line.size(); j++) { - TLAToken tok = (TLAToken) line.elementAt(j); - parenDepth = parenDepth + tok.getBeginSubst().size() - tok.getEndSubst().size(); - if (parenDepth < 0) { - throw new NullPointerException("argument: begin/end Subst depth negative"); - } - } -// depths[i] = parenDepth; - } - if (parenDepth != 0) { - throw new NullPointerException("argument: Unmatched begin Subst"); - } - /* ------------------ end testing --------------------------*/ - - /* - * We now set stringVec to the sequence of identifiers that occur in exprn - * for which we need to add a subscript or a prime. - */ - Vector exprVec = new Vector(); // the substituting exprs - Vector stringVec = new Vector(); // the substituted ids - TLAExpr expr = exprn.cloneAndNormalize(); // the expression to be returned - - for (int i = 0; i < expr.tokens.size(); i++) - { - Vector tv = (Vector) expr.tokens.elementAt(i); - for (int j = 0; j < tv.size(); j++) - { - TLAToken tok = (TLAToken) tv.elementAt(j); - boolean prime = ((tok.type == TLAToken.IDENT) && c.IsChanged(tok.string)); - boolean subr = (sub != null && (tok.type == TLAToken.ADDED || (mp && (tok.type == TLAToken.IDENT) && (IsProcedureVar(tok.string) || IsProcessSetVar(tok.string))))); - if ((subr || prime) && !InVector(tok.string, stringVec)) - { - stringVec.addElement(tok.string); - TLAExpr exp = new TLAExpr(); - exp.addLine(); - /* - * 15 Dec 2011: The following code added to replace the - * - * exp.addToken(new TLAToken(tok.string, 0, TLAToken.IDENT)); - * - * that is commented out. Note that this change can add a token with - * type ADDED rather than IDENT. I don't think this matters. - */ - TLAToken newTok = tok.Clone() ; - /* - * The new token should inherit nothing from the baggage of tok, whose - * only function is to provide the name - */ - newTok.setBeginSubst(new Vector(2)); - newTok.setEndSubst(new Vector(2)); - newTok.source = null; - newTok.column = 0; - exp.addToken(newTok) ; - // exp.addToken(new TLAToken(tok.string, 0, TLAToken.IDENT)); - if (prime) { - /***************************************************** - * Modified by LL on 30 Aug 2007. The following * - * call to addTokenOffset was originally a call to * - * addToken. See the comments for * - * TLAExpr.addTokenOffset(). * - *****************************************************/ - TLAToken primeTok = new TLAToken("'", 0, TLAToken.BUILTIN, true); - // The following stuff added by LL in Dec 2011 is bogus. The - // token tok is just the first one of many in the exprn with - // the same name for which we want to substitute. The only - // useful data in the token tok is its string. - // - // if (tok.source != null) { - // primeTok.source = - // new Region(tok.source.getEnd(), tok.source.getEnd()); - // } - // if (!subr) { - // primeTok.setEndSubst(tok.getEndSubst()); - // newTok.setEndSubst(new Vector(2)); - // } - exp.addTokenOffset(primeTok, 0); - } - if (subr) - { - TLAExpr subexp = sub.cloneAndNormalize(); - - /* - * We now add the end of the origin of tok to beginSubst - * of the first token of subexp and to endSubst of the - * last token of subexp. This indicates that PCal code - * corresponding to a region of the TLA+ translation that - * includes part of the added subscript and part of the - * original expression is a portion of the source of - * exprn. - */ - // This is bogus, because we are adding the location of where - // the identifier first occurs in exprn to the substitution - // vectors of the expression that's going to be substituted in - // all instances. - // - // if (tok.source != null) { - // PCalLocation endOfTok = tok.source.getEnd(); - // subexp.firstToken().getBeginSubst().add(endOfTok); - // subexp.lastToken().getEndSubst().add(endOfTok); - // } - /* - * However, we do have to move the token's beginSubst and endSubst vectors - * to the first and lasts token of the subscript. Since the - * resulting Parens that they generate should be outside - * the ones generated by the endSubst vectors of the expression, - * we have to add them before that expression's Subst vectors. - * - * No, no! This tok is just giving us the name of the tok that - * we're going to be substituting for in the expression. It is not - * necessarily the one that we're going to substitute for. - */ - // newTok.getEndSubst().addAll(subexp.lastToken().getEndSubst()); - // subexp.lastToken().setEndSubst(newTok.getEndSubst()); - // newTok.setEndSubst(new Vector(2)); - exp.normalize(); - try - { - subexp.prepend(exp, 0); - } catch (TLAExprException e) - { - throw new PcalTLAGenException(e.getMessage()); - } - exp = subexp; - } - /********************************************************** - * Modified by LL on 31 Jan 2006 to comment out the call * - * of MakeExprPretty, since it totally screwed up the * - * formatting when substituting any string containing * - * spaces or multiple lines for a variable. * - **********************************************************/ - // MakeExprPretty(exp); - exprVec.addElement(exp); - } - } - } - if (exprVec.size() > 0) - try - { - expr.substituteForAll(exprVec, stringVec, false); - } catch (TLAExprException e) - { - throw new PcalTLAGenException(e.getMessage()); - } - /* - * For testing, throw a null pointer exception if the begin/end substitution - * mapping vectors are not properly matching in the returned expression - */ -// depths = new int[1000]; - - parenDepth = 0; - for (int i = 0; i < expr.tokens.size(); i++) { - Vector line = (Vector) expr.tokens.elementAt(i); - for (int j = 0; j < line.size(); j++) { - TLAToken tok = (TLAToken) line.elementAt(j); - parenDepth = parenDepth + tok.getBeginSubst().size() - tok.getEndSubst().size(); - if (parenDepth < 0) { - throw new NullPointerException("result: begin/end Subst depth negative"); - } - } -// depths[i] = parenDepth; - } - if (parenDepth != 0) { - throw new NullPointerException("result: Unmatched begin/subst"); - } - /* ------------------ end testing --------------------------*/ - return expr; - } - - /*********************************************************************** - * Given an expression, makes it into a subscript expr. It is called * - * only with argument Self(context), which means that it is called * - * only for a single subscript. * - * * - * If string is null, then returns null. * - * * - * Since this is used only to add "[self]", there is no need to add * - * any REPLACEMENT tokens to the expression. Moreover, the added * - * subscript's tokens have a null source field because they don't come * - * from any PCal code. The new expression is given the same origin as * - * the original one. * - ***********************************************************************/ - private static TLAExpr SubExpr(TLAExpr sub) - { - if (sub != null) - { - TLAExpr expr = sub.cloneAndNormalize(); - // This preserves the origin of the expression - for (int i = 0; i < expr.tokens.size(); i++) { - Vector tokenVec = (Vector) expr.tokens.elementAt(i); - for (int j = 0; j < tokenVec.size(); j++) { - TLAToken tok = (TLAToken) tokenVec.elementAt(j); - tok.column = tok.column + 1; - } - if (i == 0) { - /* - * Set isAppended field of the "[" and "]" to true iff the first - * token of sub has isAppended field true, which should be the - * case iff it is the "self" token. - */ - tokenVec.insertElementAt( - new TLAToken("[", 0, TLAToken.BUILTIN, sub.firstToken().isAppended()), - 0); - } - } - expr.addTokenOffset( - new TLAToken("]", 0, TLAToken.BUILTIN, sub.firstToken().isAppended()), 0); - return expr; - } else { - return null; - } - } - - /*********************************************************/ - /* Gives the string to use when subscripting a variable. */ - /*********************************************************/ - // LL comment: it appears that PcalTLAGen.self is the - // current process id if this is being called in the context - // of a process declared with `process (P = id)'. - private TLAExpr Self(String context) - { - TLAExpr s = null; - if (mp) - { - if (context.equals("procedure")) - s = selfAsExpr(); - else - s = self; - } - return s; - } - - private static TLAExpr selfAsExpr() { - /* - * This is a token that does not correspond to anything in the - * PCal code, so it should have a null source field. - * It has a true isAppended field because it is always appended - * as a subscript to a variable. - */ - TLAToken selfToken = new TLAToken("self", 0, TLAToken.IDENT, true); - Vector tokenVec = new Vector(); - tokenVec.addElement(selfToken); - Vector tokens = new Vector(); - tokens.addElement(tokenVec); - TLAExpr expr = new TLAExpr(tokens); -// expr.anchorTokens = new TLAToken[1]; -// expr.anchorTokens[0] = selfToken; -// expr.anchorTokCol = new int[1]; -// expr.anchorTokCol[0] = 0; - expr.normalize(); - return expr ; - } - /*********************************************************************** - * Comment added by LL: MakeExprPretty should never be called on an * - * expression any part of which was an expression in the input. * - * Fortunately, it is now called only for the expression "[self]", so * - * it is effectively a no-op. * - * Comment added 15 Dec 2011: In fact, it's not called at all. * - ***********************************************************************/ - public static void ObsoleteMakeExprPretty(TLAExpr expr) - { - /********************************************************************* - * Sets columns so this expr looks nice and tight. * - *********************************************************************/ - Vector line; /* Vector of TLAToken */ - boolean spread; - int nextCol = 1; - for (int i = 0; i < expr.tokens.size(); i++) - { - line = (Vector) expr.tokens.elementAt(i); - for (int j = 0; j < line.size(); j++) - { - TLAToken tok = ((TLAToken) line.elementAt(j)); - spread = tok.string.equals("="); - tok.column = nextCol + ((spread) ? 1 : 0); - nextCol = nextCol + tok.getWidth() + ((spread) ? 2 : 0); - } - } - } - - /***********************************************************/ - /* v is a sequence of SingleAssign. Return a vector of the */ - /* same SingleAssign, but sorted in terms of the lhs.var. */ - /***********************************************************/ - private static Vector SortSass(Vector vec) - { - Vector v = (Vector) vec.clone(); - Vector r = new Vector(); // The sorted version of v. - while (v.size() > 0) - { // Good old n^2 insertion sort. - AST.SingleAssign candidate = (AST.SingleAssign) v.elementAt(0); - int indexC = 0; - for (int i = 1; i < v.size(); i++) - { - AST.SingleAssign sass = (AST.SingleAssign) v.elementAt(i); - if (candidate.lhs.var.compareTo(sass.lhs.var) > 0) - { - indexC = i; - candidate = sass; - } - } - r.addElement(candidate); - v.remove(indexC); - } - return r; - } - - /*********************************************************************** - * If vec is a StringVector representing an expression, then this * - * returns the StringVector obtained by parenthesizing the expression * - * if it may need parenthesizing. This is used only to prevent * - * parsing errors when the expression appears immediately to the right * - * of an "=" in the spec. This is a rare situation, so it would be * - * nice to add the parentheses only if really necessary. For now, the * - * parentheses are added if one of the following tokens occur outside * - * parentheses and not inside a string: * - * * - * = * - * # * - * < not followed by < * - * > not followed by > or preceded by = * - * | preceded or followed by - * - * \ not followed by "o" or "X". * - * / followed by "\" * - * * - * Left parentheses are * - * * - * ( [ { << * - * * - * The handling of "\" is a simplifying hack. Lots of operators * - * beginning with "\" like "\/", "\gg" and "\subseteq" have precedence * - * greater than or equal to "=". The only commonly used ones with * - * precedence lower than "=" seem to be "\o" and "\X". It doesn't * - * seem to be worth the bother of checking for the others just to * - * avoid unnecessarily adding the parentheses when those other rare * - * operators are used. * - * * - * Perhaps the one improvement that might be worth making in this * - * procedure is to have it not add parentheses because of "dangerous" * - * operations in an IF clause--for example: * - * * - * IF x < 0 THEN ... * - * * - * This would require considering "IF" to be a left parenthesis and * - * "THEN" to be a right parenthesis. However, that's not trivial to * - * implement because of unlikely things like * - * * - * IFx := 42 ; * - * x := IFx < THENx * - ***********************************************************************/ - private static Vector Parenthesize(Vector vec) - { - /********************************************************************* - * Add the parentheses if necessary. * - *********************************************************************/ - if (NeedsParentheses(vec)) - { - vec.setElementAt("(" + ((String) vec.elementAt(0)), 0); - for (int i = 1; i < vec.size(); i++) - { - vec.setElementAt(" " + ((String) vec.elementAt(i)), i); - } - ; - int curLineNum = vec.size() - 1; - vec.setElementAt(((String) vec.elementAt(curLineNum)) + ")", curLineNum); - } - ; - return vec; - } - - /** - * As part of adding the TLA to PCal translation code, LL removedseparated - * the code that decides if parentheses are needed from the Parenthesize - * method and put it into this method. The Parenthesize method itself - * will not be needed. - */ - public static boolean NeedsParentheses(Vector vec) { - if (vec.size() == 0) - { - return false; - } - ; - /******************************************************************* - * vec shouldn't be empty, but let's not worry about what to do if * - * it is. * - *******************************************************************/ - int curCharNum = 0; - int curLineNum = 0; - int parenDepth = 0; - boolean inString = false; - boolean needParen = false; - while ((curLineNum < vec.size()) && (!needParen)) - { - String curLine = (String) vec.elementAt(0); - while ((curCharNum < curLine.length()) && (!needParen)) - { - char curChar = curLine.charAt(curCharNum); - - if (inString) - { - switch (curChar) { - case '\"': - inString = false; - break; - case '\\': - curCharNum++; - break; - } - ; // end switch - } // end if (inString) - else - { - boolean leftParen = false; - boolean rightParen = false; - boolean mayNeedParen = false; - /*************************************************************** - * Set nextChar to the next character on the line, or ' ' if * - * there is none. * - ***************************************************************/ - char nextChar = ' '; - if (curCharNum < curLine.length() - 1) - { - nextChar = curLine.charAt(curCharNum + 1); - } - switch (curChar) { - case '\"': - inString = true; - break; - case '=': - mayNeedParen = true; - break; - case '#': - mayNeedParen = true; - break; - case '<': - if (nextChar == '<') - { - curCharNum++; - leftParen = true; - } else - { - mayNeedParen = true; - } - ; - break; - case '>': - if (nextChar == '>') - { - curCharNum++; - rightParen = true; - } else - { - mayNeedParen = true; - } - ; - break; - case '|': - if ((nextChar == '-') || ((curCharNum > 0) && (curLine.charAt(curCharNum - 1) == '-'))) - { - mayNeedParen = true; - } - ; - break; - case '\\': - if (!((nextChar == ' ') || (nextChar == 'o') || (nextChar == 'X'))) - { - mayNeedParen = true; - } - ; - break; - case '/': - if (nextChar == '\\') - { - mayNeedParen = true; - } - ; - break; - case '(': - case '[': - case '{': - leftParen = true; - break; - case ')': - case ']': - case '}': - rightParen = true; - break; - } - ; - if (mayNeedParen && (parenDepth == 0)) - { - needParen = true; - } - ; - if (leftParen) - { - parenDepth++; - } - ; - if (rightParen) - { - if (parenDepth == 0) - { - needParen = true; - } - ; - parenDepth--; - } - } - ; // end else ! inString - curCharNum++; - } - ; // end while (curCharNum < curLine.length()) - - if (inString) - { - needParen = true; - } - ; - /***************************************************************** - * If there is an unmatched quote, we might as well stop here. * - *****************************************************************/ - curLineNum++; - curCharNum = 0; - } // end while (curLineNum < vec.size()) - - return needParen; - } - - /* - * The following methods are used to add code to tlacode and add the - * appropriate objects to mappingVector - */ - - /** - * Adds one token to tlacodeNextLine, and adds the appropriate - * Begin/EndTLAToken objects to mappingVectorNextLine. The - * ...TLAToken objects mark a region that excludes beginning and - * ending space characters of token--unless the token has - * only space characters. - * - * @param token - */ - private void addOneTokenToTLA(String token) { - String trimmedToken = token.trim() ; - - int numberOfLeftTrimmedTokens = - (trimmedToken.length() == 0) ? -1 : - token.indexOf(trimmedToken.charAt(0)); - - /** - * Handle a token of only space characters. - */ - if (numberOfLeftTrimmedTokens == -1) { - numberOfLeftTrimmedTokens = 0 ; - trimmedToken = token ; - } - - int objBegin = tlacodeNextLine.length() + numberOfLeftTrimmedTokens; - mappingVectorNextLine.addElement(new MappingObject.BeginTLAToken(objBegin)); - mappingVectorNextLine.addElement(new MappingObject.EndTLAToken(objBegin + trimmedToken.length())); - tlacodeNextLine = tlacodeNextLine + token; - } - - /** - * If region is non-null, then adds string str to the TLA output - * as a SourceToken object with that region. Otherwise, it adds - * it as a TLAToken, with Begin/EndTLAToken objects. - * - * @param str - * @param region - */ - private void addOneSourceTokenToTLA(String str, Region region) { - if (region == null) { - addOneTokenToTLA(str); - return; - } - - int beginCol = tlacodeNextLine.length(); - int endCol = beginCol + str.length(); - mappingVectorNextLine.addElement( - new MappingObject.SourceToken(beginCol, endCol, region)); - tlacodeNextLine = tlacodeNextLine + str; - } - /** - * Adds a complete line of TLA "code" that does not correspond to - * any PlusCal code. Adds Begin/EndTLAToken objects to the mapping - * iff the line does not equal "". - * - * @param line - */ - private void addOneLineOfTLA(String line) { -// temporarily commented out. - if(tlacode.size() != mappingVector.size()) { - PcalDebug.ReportBug("tlacode and mappingVector have different lengths") ; - } -// The following added during testing. -//if(tlacode.size() != mappingVector.size()) { -// System.out.println("tlacode and mappingVector have different lengths"); -//} - endCurrentLineOfTLA(); - if (line.length() == 0) { - mappingVector.addElement(new Vector(2)); - tlacode.addElement(""); - return; - } - addOneTokenToTLA(line); - endCurrentLineOfTLA(); - } - - /** - * If tlacodeNextLine does not equal "", then add it to tlacode - * and add mappingVectorNextLine to mappingVector. If it does equal "", - * don't add any lines, but add any potentially legal leftovers in - * mappingVectorNextLine to the previous line. - */ - private void endCurrentLineOfTLA() { - if (tlacodeNextLine.length() != 0) { - tlacode.addElement(tlacodeNextLine) ; - mappingVector.addElement(mappingVectorNextLine) ; - tlacodeNextLine = ""; - mappingVectorNextLine = new Vector() ; - } - else { - if (mappingVectorNextLine.size() != 0) { - /* - * There's something to go in the mappingVector that doesn't - * accompany any text. It should be one or more RightParen or - * LeftParen objects, (or perhaps, eventually a Break), in which - * case they should be put at the end of the previous mappingVector - * line. Anything else is a mistake. - */ - Vector lastLine = (Vector) mappingVector.elementAt(mappingVector.size()-1); - for (int i = 0; i < mappingVectorNextLine.size(); i++) { - MappingObject obj = (MappingObject) mappingVectorNextLine.elementAt(i); - if (obj.getType() == MappingObject.RIGHT_PAREN || - obj.getType() == MappingObject.LEFT_PAREN|| - obj.getType() == MappingObject.BREAK) { - lastLine.add(obj); - } - else { - PcalDebug.ReportBug("PcalTLAGen.endCurrentLineOfTLA found problem."); - } - mappingVectorNextLine = new Vector() ; - } - } - } - } - - /** - * Adds the expression to tlacode / tlacodeNextLine and its - * mapping to mappingVector / mappingVectorNextLine. It adds - * no space before the expression and leaves the last line of the - * expression (which could be its first line) at the end of - * tlacodeNextLine. - * @param expr - */ - private void addExprToTLA(TLAExpr expr) { - Vector sv = expr.toStringVector() ; - Vector exprMapping = expr.toMappingVector() ; - int indent = tlacodeNextLine.length() ; - int nextLine = 0 ; - if (indent != 0) { - /* - * Need to combine first line of expr with - * tlacodeNextLine. - */ - MappingObject.shiftMappingVector(exprMapping, indent); - tlacodeNextLine = tlacodeNextLine + ((String) sv.elementAt(0)); - mappingVectorNextLine.addAll((Vector) exprMapping.elementAt(0)); - nextLine = 1; - if (sv.size() > 1) { - endCurrentLineOfTLA(); - } - } - if (sv.size() > 1) { - String spaces = NSpaces(indent); - while (nextLine < sv.size()-1) { - tlacode.addElement(spaces + ((String) sv.elementAt(nextLine))); - mappingVector.addElement((Vector) exprMapping.elementAt(nextLine)); - nextLine++ ; - } - tlacodeNextLine = spaces + ((String) sv.elementAt(nextLine)) ; - mappingVectorNextLine = (Vector) exprMapping.elementAt(nextLine); - } - else if (indent == 0){ - /* - * If indent != 0, then we've already added the one-line expression. - */ - tlacodeNextLine = tlacodeNextLine + ((String) sv.elementAt(0)); - mappingVectorNextLine.addAll((Vector) exprMapping.elementAt(0)); - } - } - - /** - * Subroutine of GenInit that adds to the TLA translation the Init conjunct - * corresponding to the VarDecl decl for a global variable and, in a uniprocess - * algorithm for a procedure or process variable It is called with the - * StringBuffer `is' containing the text that precedes the "/\" of the - * conjunct, which will be "Init == " or just spaces. - * - * @param decl - * @param is - */ - private void addVarDeclToTLA(VarDecl decl, StringBuffer is) { - Region origin = decl.getOrigin(); - is.append("/\\ "); - addOneTokenToTLA(is.toString()); - addLeftParen(decl.getOrigin()); -// is.append(decl.var); - is = new StringBuffer(decl.var); - if (decl.isEq) - is.append(" = "); - else - is.append(" \\in "); -// int col2 = is.length(); -// Vector sv = Parenthesize(decl.val.toStringVector()); -// /********************************************************* -// * Call to Parenthesize added by LL on 27 Feb 2008. * -// * See bug_08-02-18. * -// *********************************************************/ -// is.append((String) sv.elementAt(0)); -// for (int v = 1; v < sv.size(); v++) -// { -// tlacode.addElement(is.toString()); -// is = new StringBuffer(NSpaces(col2)); -// is.append((String) sv.elementAt(v)); -// } -// tlacode.addElement(is.toString()); - addOneTokenToTLA(is.toString()); - addLeftParen(decl.val.getOrigin()); - boolean needsParens = NeedsParentheses(decl.val.toStringVector()); - if (needsParens) { - addOneTokenToTLA("("); - } - addExprToTLA(decl.val); - if (needsParens) { - addOneTokenToTLA(")"); - } - addRightParen(decl.val.getOrigin()); - addRightParen(decl.getOrigin()); - endCurrentLineOfTLA(); - } - - /** - * Adds a MappingObject.LeftParen object to the mapping vector - * for the beginning of the Region region, if it's not null. - * @param region - */ - private void addLeftParen(Region region) { - if (region != null) { - mappingVectorNextLine.addElement( - new MappingObject.LeftParen(region.getBegin())); - } - } - - /** - * Adds a MappingObject.LeftParen object to the mapping vector - * for the beginning of the Region region, if it's not null. - * @param region - */ - private void addRightParen(Region region) { - if (region != null) { - mappingVectorNextLine.addElement( - new MappingObject.RightParen(region.getEnd())); - } - } - - /** - * Like addLeftParen(ast.getOrigin()), except that it uses - * loc the location if it is not null. It is called by - * GenLabeledStmt and ast.getOrigin() should never be null. - * However, if it is, then we don't add a Paren; this insures - * that the matching calls of addLeftParenV and addRightParenV - * both either do or don't add a Paren. - * - * @param ast - */ - private void addLeftParenV(AST ast, PCalLocation loc) { - if (ast.getOrigin() == null) { - return; - } - if (loc != null) { - mappingVectorNextLine.addElement( - new MappingObject.LeftParen(loc)); - } - else { - addLeftParen(ast.getOrigin()); - } - } - - /** - * Like addRightParen(ast.getOrigin()), except that it uses - * loc as the location if it is not null. It is called by - * GenLabeledStmt and ast.getOrigin() should never be null. - * However, if it is, then we don't add a Paren; this insures - * that the matching calls of addLeftParenV and addRightParenV - * both either do or don't add a Paren. - * - * @param ast - */ - private void addRightParenV(AST ast, PCalLocation loc) { - if (ast.getOrigin() == null) { - return; - } - if (loc!= null) { - mappingVectorNextLine.addElement( - new MappingObject.RightParen(loc)); - } - else { - addRightParen(ast.getOrigin()); - } - } -/* -------------------------------------------------------------------------- */ - /* - * The following methods and classes of objects are used in GenSpec(). - * See the comments preceding that method above. - */ - - /** - * A FormulaPair should never have wf = null, but might have sf = null. - */ - public static class FormulaPair { - public String wf ; - public String sf ; - - public FormulaPair(String wfVal, String sfVal) { - this.wf = wfVal; - this.sf = sfVal; - } - - /** - * The string wf /\ sf , or just wf if sf is null. - * @return - */ - public String singleLine() { - if (sf == null) { - return wf ; - } - return wf + " /\\ " + sf ; - } - - /** - * The width of the singleLine representation of the - * conjunction of the formlas. - * - * @return - */ - public int singleLineWidth() { - if (sf == null) { - return wf.length() ; - } - return wf.length() + " /\\ ".length() + sf.length() ; - } - - /** - * The representation of the conjunction of the formulas with - * prefix /\s, where the first /\ appears in column col (Java - * numbering), witout any ending "\n" - * - * @return - */ - public String multiLine(int col) { - String val = "/\\ " + wf ; - if (sf == null) { - return val; - } - return val + "\n" + NSpaces(col) + "/\\ " + sf; - } - } - - /** - * Describes a process fairness formula, as described in the comments - * preceding the GetSpec() method above. - * @author lamport - * - */ - public static class ProcessFairness { - public String xf ; // either "WF" or "SF" - public Vector prefix ; - // StringVector either "\A self \in exp : " or - // "LET self == exp \n IN " (note the ending space) or "" - public FormulaPair bodyFormulas ; // fairness conditions for the proc's body - public Vector prcdFormulas ; // fairness conditions for the procedure - - /** - * The constructor - * @param xfVal - * @param prefixVal - * @param bodyWF : can be null if bodySF is also null - * @param bodySF : can be null - * @param prcdVal - */ - public ProcessFairness (String xfVal, Vector prefixVal, String bodyWF, - String bodySF, Vector prcdVal) { - xf = xfVal; - prefix = prefixVal; - bodyFormulas = null ; - if (bodyWF != null) { - bodyFormulas = new FormulaPair(bodyWF, bodySF); - } - prcdFormulas = prcdVal; - } - /** - * The width of the fairness formula written as a "single-line" - * formula. Single-line means that it is not written as a - * conjunction list (with a leading /\). It will actually - * occupy multiple lines if prefix is a multi-line formula. - * - * @return - */ - public int singleLineWidth() { - // Set maxPrefixWidth to length of longest non-final - // line of prefix, width to lenght of final line - int maxPrefixWidth = 0 ; - int width = 0 ; - if (prefix != null && prefix.size() > 0) { - for (int i = 0; i < prefix.size() - 1; i++) { - String line = (String) prefix.elementAt(i); - if (line.length() > maxPrefixWidth) { - maxPrefixWidth = line.length(); - } - String lastLine = (String) prefix.elementAt(prefix.size()-1); - width = lastLine.length(); - } - } - width = width + bodyFormulas.wf.length(); - if (bodyFormulas.sf != null) { - width = width + bodyFormulas.sf.length(); - } - if (prcdFormulas != null) { - for (int i = 0 ; i < prcdFormulas.size(); i++) { - width = width + ((FormulaPair) prcdFormulas.elementAt(i)).singleLineWidth(); - } - } - if (maxPrefixWidth > width) { - return maxPrefixWidth; - } - return width ; - } - - /** - * Returns the prefix as a StringBuffer, assuming it starts - * in column col. That is, all but the first line is indented - * with col spaces, and all but the last line is ended with - * a \n . - * - * @param col - * @return - */ - private StringBuffer prefixAsStringBuffer(int col) { - StringBuffer val = new StringBuffer(); - if (prefix != null && prefix.size() > 0) { - for (int i = 0; i < prefix.size(); i++) { - String line = (String) prefix.elementAt(i); - if (i != 0) { - val.append(NSpaces(col)); - } - val.append(line) ; - if (i != prefix.size()-1) { - val.append("\n") ; - } - } - } - return val; - } - /** - * The process fairness condition written as a single-line formula, - * starting in column col. - * @return - */ - public StringBuffer singleLine(int col) { - StringBuffer val = prefixAsStringBuffer(col); - val.append(bodyFormulas.wf); - if (bodyFormulas.sf != null) { - val.append(" /\\ "); - val.append(bodyFormulas.sf); - } - if (prcdFormulas != null) { - for (int i = 0 ; i < prcdFormulas.size(); i++) { - val.append(" /\\ "); - val.append(((FormulaPair) prcdFormulas.elementAt(i)).singleLine()); - } - } - return val ; - } - - /** - * Returns true iff format(col) should return a single-line version - * of the formula. - * - * @param col - * @return - */ - private boolean fitsAsSingleLine(int col) { - return (col + singleLineWidth() <= PcalTLAGen.wrapColumn) - || (bodyFormulas.sf == null - && (prcdFormulas == null || prcdFormulas.size() == 0)); - } - /** - * The process fairness condition written as a formula that - * begins in column col (Java numbering) and ends with "\n". - * It is formatted to try to extend no further than column - * PcalTLAGen.wrapColumn, but no individual formula is split - * across lines. - * - * @param col - * @return - */ - public StringBuffer format(int col) { - int singleLineWidth = this.singleLineWidth(); - /* - * Return the single-line form if either it fits on the - * line or if it consists of only the wf formula (so it can't - * be put on multiple lines). - */ - if (fitsAsSingleLine(col)) { - return this.singleLine(col); - } - StringBuffer val = prefixAsStringBuffer(col); - int prefixWidth = 0; - if (prefix != null && prefix.size() > 0) { - prefixWidth = ((String) prefix.elementAt(prefix.size()-1)).length(); - } - int curCol = col + prefixWidth; - String line = this.bodyFormulas.singleLine(); - if (curCol + line.length() + 3 <= PcalTLAGen.wrapColumn) { - val.append("/\\ " + line); - } else { - val.append(this.bodyFormulas.multiLine(curCol)); - } - if (prcdFormulas == null) { - return val; - } - for (int i = 0; i < this.prcdFormulas.size(); i++) { - FormulaPair form = (FormulaPair) this.prcdFormulas.elementAt(i) ; - line = form.singleLine(); - // On 2 Apr 2013, LL discovered the following totally bizarre - // line of code, which inserted copies of "THIS_EXTRA_SPACE_INSERTED" into - // the translation, which of course then didn't parse. Apparently some - // change was made and never tested. The conjuncts being inserted here - // seem to be the fairness formulas for procedures in a fair process. - // - //val.append("\nTHIS_EXTRA_SPACE_INSERTED"); - - // One experiment seems to indicate that the following statement is needed - // to put the first of the procedures' liveness formulas where it belongs. - // However, I don't understand the code so I have no idea what actually - // should be done. LL 2 Apr 2013 - // - if (i == 0) { - val.append("\n") ; - } - val.append(NSpaces(curCol)); - if (curCol + line.length() + 3 <= PcalTLAGen.wrapColumn) { - val.append("/\\ " + line + "\n"); - } else { - val.append(form.multiLine(curCol)); - } - } - return val; - } - - - } -} +package pcal; + +import java.util.Vector; + +import pcal.AST.VarDecl; +import pcal.exception.PcalTLAGenException; +import pcal.exception.TLAExprException; +import util.TLAConstants; + +/**************************************************************************** + * Given an exploded and disambiguated AST, generate the equivalent TLA+. + * <br> + * {@link PcalTLAGen#generate(AST, PcalSymTab)} returns a vector of Strings, one entry per line of generated TLA+. + * + * @version $Id$ + * @author Leslie Lamport (modified on Thu 6 March 2008 at 10:16:22 PST) + * (minor change on 9 December 2009) + * @author keith (modified on Mon 3 Oct 2005 at 21:43:09 UT) + * + ****************************************************************************/ +public class PcalTLAGen +{ + // Constants that control formatting + public final static boolean boxUnderCASE = true; /* else [] at end of line */ + + // The following two variables made non-final on 9 Dec 2009 so they can + // be set by options. They are initialized in PcalParams.resetParams(). + public static int wrapColumn ; + /* If the line width will be greater than this, then try to wrap */ + public static int ssWrapColumn ; + // I think that this is used as follows: + // when translating an assignment statement (or multiassignment?) + // to var' = [var EXCEPT ...], it begins the ... on a new line + // iff the ... would begin in a column > ssWrapColumn. + // For the time being, it is set to wrapColumn - 33. We may want + // to do something cleverer or else make it a user option. + + // Private class variables + /** The tlacode field accumulates the translation as it is constructed. It + * should be a vector of separate lines. Keiths original implementation put + * multiple lines in a single element of tlacode in: + * + * GenVarsAndDefs + * GenVarDecl + */ + private Vector tlacode = new Vector(); /* of lines */ + + /** + * The tlacodeNextLine field accumulates characters for the next + * line of tlacode. It is always a string. It is assumed that + * when it equals "", then a new line can be started without + * adding the current string in tlacodeNextLine as a new line. + */ + private String tlacodeNextLine = "" ; + + /** + * mappingVector is a local pointer to {@link TLAtoPCalMapping#mappingVector}, + * which is used to accumulate the TLA+ to PlusCal mapping. It approximately + * reflects the TLA+ that has been inserted in the {@link PcalTLAGen#tlacode} + * vector. It is set in the {@link TLAtoPCalMapping#generate} method. + */ + private Vector mappingVector; + + /** + * mappingVectorNextLine contains the sequence of MappingObject objects + * that correspond to the strings added to tlacodeNextLine. + */ + private Vector mappingVectorNextLine = new Vector() ; + + /** + * The self field is set to "self" by GenProcess when called for a single process + * (rather than a process set) and by GenProcedure for a multiprocess algorithm. + * It is set to the process id by GenProcess when called for a single process. + * selfIsSelf is set to true when self is set to "self", and to false when self is + * set to a process id. The self field never seems to be reset to null. + */ + private TLAExpr self = null; // changed by LL on 22 jan 2011 from: private String self = null; /* for current process */ + private boolean selfIsSelf = false; + + private final Vector<String> vars = new Vector<String>(); /* list of all disambiguated vars */ + private Vector pcV = new Vector(); /* sublist of vars of variables representing + procedure parameters and procedure variables */ + private Vector psV = new Vector(); /* sublist of vars local to a process set */ + private PcalSymTab st = null; /* symbol table */ + private boolean mp = false; /* true if multiprocess, else unip */ + private Vector nextStep = new Vector(); /* unparam actions */ // For multiprocess alg, these are the individual (=) processes + private Vector nextStepSelf = new Vector(); /* param actions */ // These are process sets (\in processes) and procedures + // Following added to keep track of the length of the "lbl... == /\ " + // that precedes all the statements in the definition of a label's action + // because Keith screwed up and handled the assignment to the pc different + // from that of all other variables, forgetting that the subscript exp + // in pc[exp] := ... can be multi-line. + private int kludgeToFixPCHandlingBug ; + /** + * The public method: generate TLA+ as a vector of strings. + * @param ast the AST of the PCal + * @param symtab the symbol table + * @return the vector of strings with TLA+ code + * @throws PcalTLAGenException on any unrecoverable methods + */ + + /* + * The string currentProcName is the name of the current process (for a multiprocess algorithm) + * or "Next" for a uniprocess algorithm. When ParseAlgorithm.omitPC is true, this is used + * instead of the label when generating the process's or the entire algorithm's next-state + * action. Thus, with a single label lbl, this generates + * Next == Xlation + * instead of + * lbl == Xlation + * Next == lbl + */ + private String currentProcName ; + + /** + * Generates the translation. + * + * @param ast The AST produced by parsing and exploding. + * @param symtab The symbol table. + * @param report A vector of strings, containing the reports of renaming. + * @return A vector of strings. + * @throws PcalTLAGenException + */ + public Vector<String> generate(AST ast, PcalSymTab symtab, Vector report) throws PcalTLAGenException + { + TLAtoPCalMapping map = PcalParams.tlaPcalMapping; + mappingVector = new Vector<String>(50); + /* + * Add the reports of renaming to the output. + */ + for (int i = 0; i < report.size(); i++) { + addOneLineOfTLA((String) report.elementAt(i)); + } + + st = symtab; + GenSym(ast, ""); + + /* + * We put at the beginning and end of mappingVector a LeftParen + * and RightParen with location (0, 0), so that location will + * be found by the TLA+ to PCal translation algorithm if the + * user selects the entire algorithm, in which case it will + * return the null region to GotoPCalSourceHandler.execute. + */ + PCalLocation ZeroLocation = new PCalLocation(0, 0); + ((Vector) mappingVector.elementAt(0)). + add(0, new MappingObject.LeftParen(ZeroLocation)); + Vector lastLine = (Vector) mappingVector.elementAt(mappingVector.size()-1); + lastLine.add(lastLine.size(), new MappingObject.RightParen(ZeroLocation)); + + /* + * For testing, throw a null pointer exception if the parentheses are not + * properly matching in mappingVector. + */ + //int[] depths = new int[10000]; + + int parenDepth = 0; + for (int i = 0; i < mappingVector.size(); i++) { + Vector line = (Vector) mappingVector.elementAt(i); + for (int j = 0; j < line.size(); j++) { + MappingObject obj = (MappingObject) line.elementAt(j); + if (obj.getType() == MappingObject.LEFT_PAREN) { + parenDepth++; + } + else if (obj.getType() == MappingObject.RIGHT_PAREN) { + parenDepth--; + if (parenDepth < 0) { + throw new NullPointerException("paren depth < 0"); + } + } + } + // depths[i] = parenDepth; + } + if (parenDepth != 0) { + throw new NullPointerException("Unmatched Left Paren"); + } + /* ------------------ end testing --------------------------*/ + Vector nonredundantMappingVector = + TLAtoPCalMapping.RemoveRedundantParens(mappingVector); + map.makeMapping(nonredundantMappingVector); + +//System.out.println("Original mappingvector:"); +//MappingObject.printMappingVector(mappingVector); +//System.out.println("RemoveRedundantParens(mappingVector)"); +//MappingObject.printMappingVector(TLAtoPCalMapping.RemoveRedundantParens(mappingVector)); +//System.out.println("Should be original mappingvector:"); +//MappingObject.printMappingVector(mappingVector); +// Debugging +//for (int i = 0; i < tlacode.size(); i++) { +//System.out.println("\nline " + i); +//System.out.println((String) tlacode.elementAt(i)) ; +//} +//MappingObject.printMappingVector(mappingVector); + + return tlacode; + } + + /****************************************************************/ + /* Returns whether the string is present in a vector of string. */ + /****************************************************************/ + private static boolean InVector(String var, Vector v) + { + for (int i = 0; i < v.size(); i++) + if (var.equals((String) v.elementAt(i))) + return true; + return false; + } + + /******************************************************/ + /* True if var is in the list of procedure variables. */ + /******************************************************/ + private boolean IsProcedureVar(String var) + { + return InVector(var, pcV); + } + + /****************************************************/ + /* True if var is in the list of process variables. */ + /****************************************************/ + private boolean IsProcessSetVar(String var) + { + return InVector(var, psV); + } + + /**********************************************/ + /* Returns a string of length n of all spaces */ + /**********************************************/ + private static String NSpaces(int n) + { + StringBuffer sb = new StringBuffer(); + AddSpaces(sb, n); + return sb.toString(); + } + + /*********************************************/ + /* Appends n spaces to the string buffer sb. */ + /*********************************************/ + private static void AddSpaces(StringBuffer sb, int num) + { + for (int i = 0; i < num; i++) + sb.append(" "); + } + + /****************************************/ + /* True if expr is an empty expression. */ + /****************************************/ + private static boolean EmptyExpr(TLAExpr expr) + { + if (expr == null) + return true; + if (expr.tokens == null || expr.tokens.size() == 0) + return true; + return false; + } + + /*****************************************************************/ + /* Top level routines. Context is "", "procedure", or "process". */ + /** + ****************************************************************/ + private void GenSym(AST ast, String context) throws PcalTLAGenException + { + if (ast.getClass().equals(AST.UniprocessObj.getClass())) + GenUniprocess((AST.Uniprocess) ast, context); + else if (ast.getClass().equals(AST.MultiprocessObj.getClass())) + GenMultiprocess((AST.Multiprocess) ast, context); + else if (ast.getClass().equals(AST.ProcedureObj.getClass())) + GenProcedure((AST.Procedure) ast, context); + else if (ast.getClass().equals(AST.ProcessObj.getClass())) + GenProcess((AST.Process) ast, context); + else if (ast.getClass().equals(AST.LabeledStmtObj.getClass())) + GenLabeledStmt((AST.LabeledStmt) ast, context); + } + + private void GenUniprocess(AST.Uniprocess ast, String context) throws PcalTLAGenException + { + mp = false; + currentProcName = "Next"; + GenVarsAndDefs(ast.decls, ast.prcds, null, ast.defs); + GenInit(ast.decls, ast.prcds, null); + for (int i = 0; i < ast.prcds.size(); i++) + GenProcedure((AST.Procedure) ast.prcds.elementAt(i), ""); + for (int i = 0; i < ast.body.size(); i++) + { + AST.LabeledStmt ls = (AST.LabeledStmt) ast.body.elementAt(i); + /* Add this step to the disjunct of steps */ + nextStep.addElement(ls.label); + GenLabeledStmt(ls, ""); + } + GenNext(); + GenSpec(); + GenTermination(); + } + + private void GenMultiprocess(AST.Multiprocess ast, String context) throws PcalTLAGenException + { + mp = true; + GenVarsAndDefs(ast.decls, ast.prcds, ast.procs, ast.defs); + GenProcSet(); + GenInit(ast.decls, ast.prcds, ast.procs); + for (int i = 0; i < ast.prcds.size(); i++) + GenProcedure((AST.Procedure) ast.prcds.elementAt(i), ""); + for (int i = 0; i < ast.procs.size(); i++) + GenProcess((AST.Process) ast.procs.elementAt(i), ""); + GenNext(); + GenSpec(); + GenTermination(); + } + + private void GenProcedure(AST.Procedure ast, String context) throws PcalTLAGenException { + /* + * First, generate the body's actions. Must set self and selfIsSelf (?) for + * use by GenLabeledStmt. + */ + if (mp) { + + self = selfAsExpr(); // subscript for variables is "self" + selfIsSelf = true; +// /* Add this step to the disjunct of steps with (self) */ + nextStepSelf.addElement(ast.name + "(self)"); + } else + { + /* Add this step to the disjunct of steps */ + nextStep.addElement(ast.name); + } + for (int i = 0; i < ast.body.size(); i++) { + AST.LabeledStmt stmt = (AST.LabeledStmt) ast.body.elementAt(i); + GenLabeledStmt(stmt, "procedure"); + } + + /* + * Next add the definition of the procedure--e.g., + * + * procedureName(self) == label_1(self) \/ ... \/ label_k(self) + * + * We put Left/RightParens for the entire procedure around the entire + * definition, and Left/RightParens around each disjunction for + * the labeled statement. + */ + addLeftParen(ast.getOrigin()); + String argument = (mp) ? "(self)" : ""; + StringBuffer buf = new StringBuffer(ast.name + argument + " == "); + addOneTokenToTLA(buf.toString()); + String indentSpaces = NSpaces(buf.length() + 2); + for (int i = 0; i < ast.body.size(); i++) { + AST.LabeledStmt stmt = (AST.LabeledStmt) ast.body.elementAt(i); + String disjunct = stmt.label + argument; + if ( i != 0 + && tlacodeNextLine.length() + 7 /* the 7 was obtained empirically */ + + disjunct.length() > wrapColumn) { + endCurrentLineOfTLA(); + } + if (i != 0) { + addOneTokenToTLA(((tlacodeNextLine.length() == 0)? indentSpaces : "") + " \\/ "); + } + addLeftParen(stmt.getOrigin()); + addOneTokenToTLA(disjunct); + addRightParen(stmt.getOrigin()); + } + addRightParen(ast.getOrigin()); + addOneLineOfTLA(""); + +// The previous version was very convoluted just to avoid having to go through the +// list of labeled statements twice. It seemed easier to just reimplement from +// scratch. +// /* ns and nsV accumulate the disjunct of the steps of the procedure, where +// * ns contains the contents of the current line and nsV is the vector of +// * already accumulated lines. +// * +// */ +// StringBuffer ns = new StringBuffer(); +// Vector nsV = new Vector(); +// +// int nsC = ast.name.length() + ((mp) ? "(self)".length() : 0) + " == ".length(); +// if (mp) +// { +// self = selfAsExpr(); // subscript for variables is "self" +// selfIsSelf = true; +// /* Add this step to the disjunct of steps with (self) */ +// nextStepSelf.addElement(ast.name + "(self)"); +// } else +// { +// /* Add this step to the disjunct of steps */ +// nextStep.addElement(ast.name); +// } +// for (int i = 0; i < ast.body.size(); i++) +// { +// AST.LabeledStmt stmt = (AST.LabeledStmt) ast.body.elementAt(i); +// if ((ns.length() + stmt.label.length() + " \\/ ".length() + ((mp) ? "(self)".length() : 0)) > wrapColumn +// - nsC - " \\/ ".length()) +// { +// nsV.addElement(ns.toString()); +// ns = new StringBuffer(); +// } +// if (ns.length() > 0) +// ns.append(" \\/ "); +// ns.append(stmt.label); +// if (mp) +// ns.append("(self)"); +// GenLabeledStmt(stmt, "procedure"); +// } +// nsV.addElement(ns.toString()); +// // Generate definition of procedure steps +// ns = new StringBuffer(); +// ns.append(ast.name); +// if (mp) +// ns.append("(self)"); +// ns.append(" == "); +// ns.append((String) nsV.elementAt(0)); +// tlacode.addElement(ns.toString()); +// for (int i = 1; i < nsV.size(); i++) +// { +// ns = new StringBuffer(NSpaces(nsC + 2)); +// ns.append(" \\/ "); +// ns.append((String) nsV.elementAt(i)); +// tlacode.addElement(ns.toString()); +// } +// tlacode.addElement(""); + } + + private void GenProcess(AST.Process ast, String context) throws PcalTLAGenException + { + currentProcName = ast.name; + + /* + * Generate the body's actions. Must set self and selfIsSelf (?) for + * use by GenLabeledStmt. + */ + boolean isSet = true; + /************************************************************/ + /* Decide if it is a process set or not. If so, set self to */ + /* the string "self"; otherwise set self to the process id. */ + /************************************************************/ + if (ast.isEq) + { + self = ast.id ; + selfIsSelf = false; + isSet = false; + } else { + self = selfAsExpr(); + selfIsSelf = true; + } + + if (isSet) + { + nextStepSelf.addElement(ast.name + "(self)"); + } else + nextStep.addElement(ast.name); + + for (int i = 0; i < ast.body.size(); i++) { + AST.LabeledStmt stmt = (AST.LabeledStmt) ast.body.elementAt(i); + GenLabeledStmt(stmt, "process"); + } + + /* + * Next add the definition of the process--e.g., + * + * processName(self) == label_1(self) \/ ... \/ label_k(self) + * + * We put Left/RightParens for the entire procedure around the entire + * definition, and Left/RightParens around each disjunction for + * the labeled statement. + * + * However, we don't add this definition if we are omitting the pc, + * because we have already defined the process name to equal the + * only label action. + */ + + if (! ParseAlgorithm.omitPC) { + addLeftParen(ast.getOrigin()); + String argument = (isSet) ? "(self)" : ""; + StringBuffer buf = new StringBuffer(ast.name + argument + " == "); + addOneTokenToTLA(buf.toString()); + String indentSpaces = NSpaces(buf.length() + 2); + for (int i = 0; i < ast.body.size(); i++) { + AST.LabeledStmt stmt = (AST.LabeledStmt) ast.body.elementAt(i); + String disjunct = stmt.label + argument; + if ( i != 0 + && tlacodeNextLine.length() + 7 /* the 7 was obtained empirically */ + + disjunct.length() > wrapColumn) { + endCurrentLineOfTLA(); + } + if (i != 0) { + addOneTokenToTLA(((tlacodeNextLine.length() == 0)? indentSpaces : "") + " \\/ "); + } + addLeftParen(stmt.getOrigin()); + addOneTokenToTLA(disjunct); + addRightParen(stmt.getOrigin()); + } + addRightParen(ast.getOrigin()); + addOneLineOfTLA(""); + } + + +// As with GenProcedure, the original implementation was quite convoluted, so +// it was rewritten. The code above was copied with modifications from +// the rewritten GenProcedure code. +// /* ns accumulates the disjunt of the steps of the process */ +// StringBuffer ns = new StringBuffer(); +// Vector nsV = new Vector(); +// boolean isSet = true; +// /************************************************************/ +// /* Decide if it is a process set or not. If so, set self to */ +// /* the string "self"; otherwise set self to the process id. */ +// /************************************************************/ +// if (ast.isEq) +// { +// self = ast.id ; +// selfIsSelf = false; +// isSet = false; +// } else { +// self = selfAsExpr(); +// selfIsSelf = true; +// } +// +// int nsC = ast.name.length() + ((isSet) ? "(self)".length() : 0) + " == ".length(); +// if (isSet) +// { +// nextStepSelf.addElement(ast.name + "(self)"); +// } else +// nextStep.addElement(ast.name); +// for (int i = 0; i < ast.body.size(); i++) +// { +// AST.LabeledStmt stmt = (AST.LabeledStmt) ast.body.elementAt(i); +// if ((ns.length() + stmt.label.length() + " \\/ ".length() + ((isSet) ? "(self)".length() : 0)) > wrapColumn +// - nsC - " \\/ ".length()) +// { +// nsV.addElement(ns.toString()); +// ns = new StringBuffer(); +// } +// if (ns.length() > 0) +// ns.append(" \\/ "); +// ns.append(stmt.label); +// if (isSet) +// ns.append("(self)"); +// GenLabeledStmt(stmt, "process"); +// } +// nsV.addElement(ns.toString()); +// // Generate definition of process steps +// // This apparently defines the process name +// // to equal the disjunction of all the individual +// // label-named actions. If we are omitting +// // the pc, we have already defined the process name +// // to equal the only label action, so we skip +// // this. +// if (! ParseAlgorithm.omitPC) { +// ns = new StringBuffer(); +// ns.append(ast.name); +// if (isSet) +// ns.append("(self)"); +// ns.append(" == "); +// ns.append((String) nsV.elementAt(0)); +// tlacode.addElement(ns.toString()); +// for (int i = 1; i < nsV.size(); i++) +// { +// ns = new StringBuffer(NSpaces(nsC + 2)); +// ns.append(" \\/ "); +// ns.append((String) nsV.elementAt(i)); +// tlacode.addElement(ns.toString()); +// } +// } +// tlacode.addElement(""); + } + + /*****************************************************/ + /* Generates an action with name equal to the label. */ + /** + ****************************************************/ + private void GenLabeledStmt(AST.LabeledStmt ast, String context) throws PcalTLAGenException + { + // Set actionName to the name of the action being defined. + // This is the label, except when we are omitting the PC, + // in which case it is "Next" for a uniprocess algorithm + // and the process name for a multiprocess algorithm. + String actionName = ast.label; + if (ParseAlgorithm.omitPC) { + actionName = currentProcName; + } + StringBuffer sb = new StringBuffer(actionName); + /* c is used to determine which vars are in UNCHANGED. */ + Changed c = new Changed(vars); + if (mp && (context.equals("procedure") || selfIsSelf)) { // self.equals("self"))) + sb.append("(self)"); + } + sb.append(" == "); + int col = sb.length(); + kludgeToFixPCHandlingBug = col; + // There's a problem here. If ast.stmts.size() = 1, then we don't preface + // the statement's translation with "/\". However, that means that if we + // then add an UNCHANGED conjunct, we wind up with + // A + // /\ UNCHANGED ... + // where A is the statement's translation. This looks bad and could wind up + // putting the UNCHANGED inside prefix operator--e.g., + // x' = CHOOSE t : ... + // /\ UNCHANGED ... + // This is seems to be a problem only when omitPC = true, since otherwise the + // testing and setting of pc ensures that there is more than one element in ast.stmts. + // What we do is always add the "/\ ", but remove it afterwards if there's only + // a single statement in ast.stmts and there is no UNCHANGED clause. + // The code for doing this is based on the observation that the contents of + // StringBuffer sb begin the next line added to tlacode. + // + /* if (ast.stmts.size() > 1) { */ + sb.append("/\\ "); + kludgeToFixPCHandlingBug = kludgeToFixPCHandlingBug + 3; + /* } */ + + // We set defStartLine to the index of the next line added to tlacode and + // colAfterAnd to the column position in that line immediately following + // the added "/\ ". This gives us the information needed to remove the + // "/\ " later from tlacode. + int defStartLine = tlacode.size(); + int colAfterAnd = sb.length(); + + /* + * Note: it would make sense for this method to insert sb into the tlacode + * output, but it seems safer to maintain the current structure in which + * each GenX for each statement type X does that. + */ + + /* + * We set macroBeginLeft to the macroOriginBegin field of the first statement + * in ast.stmts with a non-null origin, and macroEndRight to the macroOriginEnd + * field of the last statement in ast.stmts with a non-null origin. + */ + PCalLocation macroBeginLeft = null; + PCalLocation macroEndRight = null; + boolean nonNullNotFound = true; + for (int i = 0 ; i < ast.stmts.size(); i++) { + AST stmt = (AST) ast.stmts.elementAt(i); + if (stmt.getOrigin() != null) { + if (nonNullNotFound) { + nonNullNotFound = false; + macroBeginLeft = stmt.macroOriginBegin; + } + macroEndRight = stmt.macroOriginEnd; + } + } + + /* + * addLeftParenV used instead of addLeftParen because if the first statement + * that came from PlusCal code arose from a macro call, then we want the + * location of the macro call rather than that of the macro's code. + */ + addLeftParenV(ast, macroBeginLeft); + for (int i = 0; i < ast.stmts.size(); i++) + { + GenStmt((AST) ast.stmts.elementAt(i), c, context, sb.toString(), sb.length()); + sb = new StringBuffer(NSpaces(col)); + sb.append("/\\ "); + } + + /* + * Since the UNCHANGED conjunct just consists of TLATokens, with no + * SourceTokens, we can just use the old code, simply replacing each + * tlacode.addElement call with a call of addOneLineOfTLA--except that + * the last one is replaced with a call of addOneTokenToTLA so we can + * put the RightParen object in mappingVector. + */ + Vector unc = c.Unchanged(wrapColumn - col - "/\\ UNCHANGED << ".length()); + if (c.NumUnchanged() > 1) + { + sb = new StringBuffer(NSpaces(col)); + sb.append("/\\ UNCHANGED << "); + int here = sb.length(); + sb.append((String) unc.elementAt(0)); + for (int i = 1; i < unc.size(); i++) + { +// tlacode.addElement(sb.toString()); + addOneLineOfTLA(sb.toString()); + sb = new StringBuffer(NSpaces(here)); + sb.append((String) unc.elementAt(i)); + } + sb.append(" >>"); +// tlacode.addElement(sb.toString()); + addOneTokenToTLA(sb.toString()); + } else if (c.NumUnchanged() == 1) { + // Change made by LL on 16 Mar 2011 so that, if there is a single + // unchanged variable v, it produces v' = v if v is a short variable, + // otherwise it produces UNCHANGED v + if (c.Unchanged().length() > 5) { +// tlacode.addElement(NSpaces(col) + "/\\ UNCHANGED " + c.Unchanged()); + addOneTokenToTLA(NSpaces(col) + "/\\ UNCHANGED " + c.Unchanged()); + } else { +// tlacode.addElement(NSpaces(col) + "/\\ " + c.Unchanged() + "' = " +// + c.Unchanged()); + addOneTokenToTLA(NSpaces(col) + "/\\ " + c.Unchanged() + "' = " + + c.Unchanged()); + } + } else { + // No unchanged. If there was only one conjunction, remove it. + // To do that, we must remove the "/\ " and then remove three spaces + // from all other lines that were added. We must also modify the + // TLAToken objects appropriately in the corresponding lines of + // mappingVector + if (ast.stmts.size() == 1) { + for (int i = defStartLine; i < tlacode.size(); i++) { + String line = (String) tlacode.elementAt(i); + if (i == defStartLine) { + // remove the "/\ " added + tlacode.setElementAt(line.substring(0, colAfterAnd-3) + + line.substring(colAfterAnd, line.length()) , i); + shiftMappingVectorTokensLeft(i, colAfterAnd, 3); + + } else { + // Remove three blanks from any following lines. We test the length + // of the line just in case one or more short (hopefully blank) lines + // have been added. + if (line.length() > 3) { + tlacode.setElementAt(line.substring(3, line.length()) , i); + shiftMappingVectorTokensLeft(i, colAfterAnd, 3); + } + } + } + } + } + /* + * We call addRightParenV rather than addRightParen because it the last statement + * that came from the PCal code arose from the expansion of a macro call, then we + * want the RightParen's location to be the end of the call, not the end of the + * macro's code. + */ + addRightParenV(ast, macroEndRight); + addOneLineOfTLA(""); +// tlacode.addElement(""); + } + + /** + * Adjusts the objects in line lineNum of mappingVector so all column + * numbers starting with startCol are decreased by `shift'. If any Begin/EndTLAToken + * pairs are changed to have a non-positive width, a bug is reported. + * + * Note: It is assumed that there is no aliasing of MappingTokens in mappingVector. + * That is, other than in transient local variables, the only pointer to a + * MappingToken in mappingVector is the single one in its line of mappingVector. + * I can't see how any aliasing of MappingTokens could arise. + * + * This method is called only by GenLabeledStmts. + * + * @param lineNum + * @param startCol + * @param shift + */ + private void shiftMappingVectorTokensLeft(int lineNum, int startCol, int shift) { + boolean lastWasBeginTLAToken = false; + int lastBeginTLATokCol = -777; // to keep the compiler happy. + Vector line = (Vector) mappingVector.elementAt(lineNum); + for (int i = 0; i < line.size(); i++) { + MappingObject obj = (MappingObject) line.elementAt(i); + if (obj.getType() == MappingObject.BEGIN_TLATOKEN) { + MappingObject.BeginTLAToken tobj = (MappingObject.BeginTLAToken) obj; + int col = tobj.getColumn(); + if (col >= startCol) { + tobj.setColumn(col - shift); + } + lastWasBeginTLAToken = true; + lastBeginTLATokCol = tobj.getColumn(); + } + else { + if (obj.getType() == MappingObject.END_TLATOKEN) { + MappingObject.EndTLAToken tobj = (MappingObject.EndTLAToken) obj; + int col = tobj.getColumn(); + if (col >= startCol) { + tobj.setColumn(col - shift); + } + if (lastWasBeginTLAToken && tobj.getColumn() <= lastBeginTLATokCol) { + PcalDebug.ReportBug( + "PcalTLAGen.shiftMappingVectorTokensLeft created a null TLA Token"); + } + } + else if (obj.getType() == MappingObject.SOURCE_TOKEN) { + MappingObject.SourceToken tobj = (MappingObject.SourceToken) obj; + int col = tobj.getBeginColumn(); + if (col >= startCol) { + tobj.setBeginColumn(col - shift); + } + col = tobj.getEndColumn(); + if (col >= startCol) { + tobj.setEndColumn(col - shift); + } + + lastWasBeginTLAToken = false; + } + } + } + + + } + + /*************************************************************************** + * LL Comment added 27 Jan 2006: * + * * + * There is a basic flaw in the way GenStmt works. It now generates the * + * output on the fly. This means that * + * * + * - There is no way to avoid the prefix /\ on a one-element conjunct * + * because GenStmt has no way of knowing if there's another conjunct * + * coming. * + * * + * - The handling of the UNCHANGEDs of the THEN and ELSE clauses of * + * an IF is a kludge, because the UNCHANGED of the THEN clause is * + * output before it can be known. * + * * + * The correct way of doing things is to define GenStmt so it returns a * + * sequence (vector) of string vectors, each string vector being a * + * conjunction expression (without a leading /\ or any leading spaces) and * + * the new Changed object (which it can do as it now does by modifying its * + * Changed object argument). It would also be useful to define a * + * GenStmtSeq that simply calls GenStmt iteratively on a sequence of * + * simple statements. The method that calls GenStmtSeq would then add the * + * Unchanged conjunct and call a method that returns a sequence of * + * conjuncts and a prefix into a string vector containing the prefix and * + * the necessary /\s. * + ***************************************************************************/ + + /*****************************************************************/ + /* General entry for generating the TLA+ for a simple statement. */ + /* Prefix is the prefix of the first line. Col is where to start */ + /* subsequent lines (I think we could replace it with the length */ + /* of prefix). */ + /* */ + /* And what on earth are `c' and `context'? LL */ + /** + ****************************************************************/ + private void GenStmt(AST ast, Changed c, String context, String prefix, int col) throws PcalTLAGenException + { + if (ast.getClass().equals(AST.AssignObj.getClass())) + GenAssign((AST.Assign) ast, c, context, prefix, col); + else if (ast.getClass().equals(AST.IfObj.getClass())) + GenIf((AST.If) ast, c, context, prefix, col); + // Either case added by LL on 27 Jan 2006 + else if (ast.getClass().equals(AST.EitherObj.getClass())) + GenEither((AST.Either) ast, c, context, prefix, col); + else if (ast.getClass().equals(AST.WithObj.getClass())) + GenWith((AST.With) ast, c, context, prefix, col); + else if (ast.getClass().equals(AST.WhenObj.getClass())) + GenWhen((AST.When) ast, c, context, prefix, col); + else if (ast.getClass().equals(AST.PrintSObj.getClass())) + GenPrintS((AST.PrintS) ast, c, context, prefix, col); + else if (ast.getClass().equals(AST.AssertObj.getClass())) + GenAssert((AST.Assert) ast, c, context, prefix, col); + else if (ast.getClass().equals(AST.SkipObj.getClass())) + GenSkip((AST.Skip) ast, c, context, prefix, col); + else + PcalDebug.ReportBug("Unexpected AST type " + ast.toString()); + } + + /*****************************************************************/ + /* Generates a sequence of single assigns. Since all of them are */ + /* executed "at the same time", we accumulate the changes in a */ + /* separate Changed cThis, and use c to determine which vars in */ + /* the right hand side are primed. */ + /** + * ***************************************************************/ + + /** + * @param ast + * @param c + * @param context + * @param prefix + * @param col + * @throws PcalTLAGenException + */ + private void GenAssign(AST.Assign ast, Changed c, String context, String prefix, int col) + throws PcalTLAGenException + { + Changed cThis = new Changed(c); + StringBuffer sb = new StringBuffer(); +// Vector vlines = new Vector(); + /* + * Sort the vector ast.ass so that assignments to the same variable + * follow one another. + */ + ast.ass = SortSass(ast.ass); + + addOneTokenToTLA(prefix); + addLeftParen(ast.getOrigin()); + + int i = 0; + int numAssigns = 0; + /* + * hasMultipleVars set true iff the assignment assigns values to + * more than one variable, and hence the statement's translation + * has multiple conjuncts. + */ + boolean hasMultipleVars = false; + while (i < ast.ass.size()) + { + AST.SingleAssign sF = (AST.SingleAssign) ast.ass.elementAt(i); + + /* + * Added by LL and MK on 16 May 2018: + * Report an error if the variable being assigned is not a + * variable declared in the algorithm (either not declared + * at all or is a constant, bound identifier, ...). + */ + if (!this.vars.contains(sF.lhs.var)) { + throw new PcalTLAGenException("Assignment to undeclared variable " + sF.lhs.var, sF); + } + + int iFirst = i; + int iLast = i; + boolean hasAssignmentWithNoSubscript = false; + boolean lastAssignmentHasNoSubscript = EmptyExpr(sF.lhs.sub); + AST.SingleAssign sL = (AST.SingleAssign) ast.ass.elementAt(i); + while (iLast < ast.ass.size() && sF.lhs.var.equals(sL.lhs.var)) + { + if (lastAssignmentHasNoSubscript) { + hasAssignmentWithNoSubscript = true; + } + iLast = iLast + 1; + if (iLast < ast.ass.size()) { + sL = (AST.SingleAssign) ast.ass.elementAt(iLast); + if (EmptyExpr(sL.lhs.sub)) { + lastAssignmentHasNoSubscript = true; + } + } + } + + /* + * If there are assignments to multiple variables, then this sets + * hasMultiplevars true on the first execution of the outer while loop. + */ + if (iLast != ast.ass.size()) { + hasMultipleVars = true; + } + + iLast = iLast - 1; + // All statements from iFirst to iLast are to the same variable + + /* + * Throws an error if there are multiple assignments to the variable + * in different statements, or if there are multiple assignments to + * the variable in this statement and at least one of them has no + * subscript. + */ + if (cThis.Set(sF.lhs.var) > 1 || + (iLast - iFirst > 0 && hasAssignmentWithNoSubscript)) { + /*********************************************************** + * The following was changed by LL on 3 Mar 06 to use * + * AST.location to properly report the location of an * + * error in a line created by expanding a macro. * + * However, it doesn't work very well otherwise. This * + * should be fixed. * + ***********************************************************/ + throw new PcalTLAGenException("Multiple assignment to " + sF.lhs.var, ast /* sF */); + } + numAssigns = numAssigns + 1; + Vector lines = new Vector(); // For collecting generated lines + + if (hasMultipleVars) { + sb.append("/\\ "); + } + if (iFirst == iLast) + { + /* + * This is a single assignment to the variable. + */ + AST.SingleAssign sass = sF; + + addLeftParen(sass.getOrigin()); + TLAExpr sub = AddSubscriptsToExpr(sass.lhs.sub, SubExpr(Self(context)), c); + TLAExpr rhs = AddSubscriptsToExpr(sass.rhs, SubExpr(Self(context)), c); + if (mp + && (sass.lhs.var.equals("pc") || IsProcedureVar(sass.lhs.var) || IsProcessSetVar(sass.lhs.var) || sass.lhs.var + .equals("stack"))) + { + /* Generate single assignment to variable with self subscript */ + sb.append(sass.lhs.var); + sb.append("' = ["); + int wrapCol = sb.length() + 2; + sb.append(sass.lhs.var); + sb.append(" EXCEPT "); + + Vector selfAsSV = self.toStringVector(); + + // The test for selfAsSV size added by LL on 22 Jan 2011 + // because wrapping screws up the kludgeToFixPCHandlingBug + // hack. + if ( (sb.length() + prefix.length() > ssWrapColumn) + && (selfAsSV.size() == 0)) + { +// lines.addElement(sb.toString()); + addOneLineOfTLA(sb.toString()); + sb = new StringBuffer(NSpaces(wrapCol)); + } + sb.append("!["); + addOneTokenToTLA(sb.toString()); + addLeftParen(self.getOrigin()); + addExprToTLA(self); + addRightParen(self.getOrigin()); + +// +// // following code was modified by LL on 22 Jan 2011 as part of +// // fixing bug 11_01_13, which required modifications to handle +// // the case where self is a multi-line formula, which can happen +// // for a "process (P = exp)" when exp is multi-line. +// int here = sb.length(); +// for (int idx = 0; idx < selfAsSV.size(); idx++) { +// if (idx > 0) { +// sb.append("\n"); +// sb.append(NSpaces(here + kludgeToFixPCHandlingBug)); +// } +// sb.append((String) selfAsSV.elementAt(idx)) ; +// } +//// sb.append(self); +// sb.append("]"); +// here = here + ((String) selfAsSV.elementAt(selfAsSV.size()-1)).length() + 1; + addOneTokenToTLA("]"); + Vector sv = sub.toStringVector(); + /***************************************************** + * Was * + * * + * Vector sv = sass.lhs.sub.toStringVector(); * + * * + * Changed by Chi Ho on 3 Aug 2006 to add * + * subscript. See bug_06_08_03. * + *****************************************************/ + if (sv.size() > 0) + { + addLeftParen(sub.getOrigin()); + addExprToTLA(sub); + addRightParen(sub.getOrigin()); + +// sb.append((String) sv.elementAt(0)); +// for (int v = 1; v < sv.size(); v++) +// { +// lines.addElement(sb.toString()); +// sb = new StringBuffer(NSpaces(here)); +// sb.append((String) sv.elementAt(v)); +// } + } + addOneTokenToTLA(" = ");; + addLeftParen(rhs.getOrigin()); + addExprToTLA(rhs); + addRightParen(rhs.getOrigin()); + addOneTokenToTLA("]"); +// sb.append(" = "); +// here = sb.length(); +// sv = rhs.toStringVector(); +// sb.append((String) sv.elementAt(0)); +// for (int v = 1; v < sv.size(); v++) +// { +// lines.addElement(sb.toString()); +// sb = new StringBuffer(NSpaces(here)); +// sb.append((String) sv.elementAt(v)); +// } +// sb.append("]"); +// lines.addElement(sb.toString()); + sb = new StringBuffer(); + } else if (!EmptyExpr(sass.lhs.sub)) + { + /* + * Generate single assignment to variable with no [self] subscript + * but with an explicit subscript. + */ + sb.append(sass.lhs.var); + sb.append("' = ["); + sb.append(sass.lhs.var); + sb.append(" EXCEPT !"); + addOneTokenToTLA(sb.toString()); + addLeftParen(sub.getOrigin()); + addExprToTLA(sub); + addRightParen(sub.getOrigin()); + addOneTokenToTLA(" = "); + addLeftParen(rhs.getOrigin()); + addExprToTLA(rhs); + addRightParen(rhs.getOrigin()); + addOneTokenToTLA("]"); +// +// int here = sb.length(); +// Vector sv = sub.toStringVector(); +// sb.append((String) sv.elementAt(0)); +// for (int v = 1; v < sv.size(); v++) +// { +// lines.addElement(sb.toString()); +// sb = new StringBuffer(NSpaces(here)); +// sb.append((String) sv.elementAt(v)); +// } +// sb.append(" = "); +// here = sb.length(); +// sv = rhs.toStringVector(); +// sb.append((String) sv.elementAt(0)); +// for (int v = 1; v < sv.size(); v++) +// { +// lines.addElement(sb.toString()); +// sb = new StringBuffer(NSpaces(here)); +// sb.append((String) sv.elementAt(v)); +// } +// sb.append("]"); +// lines.addElement(sb.toString()); + sb = new StringBuffer(); + } else + { + /* + * Generate assignment to a variable with no subscript at all. + */ + sb.append(sass.lhs.var); + sb.append("' = "); +// int here = sb.length(); + boolean needsParens = NeedsParentheses(rhs.toStringVector()); + if (needsParens) { + sb.append("("); + } + addOneTokenToTLA(sb.toString()); + addLeftParen(rhs.getOrigin()); + addExprToTLA(rhs); + addRightParen(rhs.getOrigin()); + if (needsParens) { + addOneTokenToTLA(")"); + } + +// Vector sv = Parenthesize(rhs.toStringVector()); +// /******************************************************* +// * Call of Parenthesize added by LL on 27 Feb 2008. * +// * See bug_08-02-18. * +// *******************************************************/ +// for (int v = 0; v < sv.size(); v++) +// { +// sb.append((String) sv.elementAt(v)); +// lines.addElement(sb.toString()); +// sb = new StringBuffer(NSpaces(here)); +// } +// Debugging +//Vector exprVec = rhs.toMappingVector(); +//MappingObject.shiftMappingVector(exprVec, here); +//MappingObject.printMappingVector(exprVec); +//System.out.println("origin: " + rhs.getOrigin().toString() ); + + sb = new StringBuffer(); + } + addRightParen(sass.getOrigin()); + } else + { + /* + * Multiple assignments to the same variable, which must therefore + * each have a user-specified subscript. + */ + AST.SingleAssign sass = sF; + sb.append(sass.lhs.var); + sb.append("' = ["); + sb.append(sass.lhs.var); + sb.append(" EXCEPT "); + int cc = sb.length(); + /* + * If this the first variable, so i = 0, then sb does not contain + * any spaces to compensate for the missing prefix; otherwise it + * does. + */ + if (i == 0) { + cc = cc + prefix.length(); + } + boolean subscript = (mp && (IsProcedureVar(sass.lhs.var) || IsProcessSetVar(sass.lhs.var))); + while (iFirst <= iLast) + { + sass = (AST.SingleAssign) ast.ass.elementAt(iFirst); + TLAExpr sub = AddSubscriptsToExpr(sass.lhs.sub, SubExpr(Self(context)), c); + TLAExpr rhs = AddSubscriptsToExpr(sass.rhs, SubExpr(Self(context)), c); + addLeftParen(sass.getOrigin()); + sb.append("!"); + + // On 21 Jan 2011, LL moved the following statement to below the if + // to correct part 3 of bug_11_01_13. + // +// int here = sb.length(); + if (subscript) { + /* + * This variable has a "self" subscript in addition to its user-specified + * subscript. + */ + sb.append("["); + addOneTokenToTLA(sb.toString()); + TLAExpr self = Self(context); + addLeftParen(self.getOrigin()); + addExprToTLA(self); + addOneTokenToTLA("]"); + +// Vector selfAsSV = Self(context).toStringVector(); +// for (int idx = 0; idx < selfAsSV.size(); idx++) { +// String start = " "; +// if (idx == 0) { +// sb.append("["); +// } else { +// sb.append("\n"); +// sb.append(NSpaces(here + 1)); +// } +// sb.append((String) selfAsSV.elementAt(idx)); +// } +// sb.append("]"); +// here = here + ((String) selfAsSV.elementAt(selfAsSV.size()-1)).length() + 2; + } + else { + addOneTokenToTLA(sb.toString()); + } + + addLeftParen(sub.getOrigin()); + addExprToTLA(sub); + addRightParen(sub.getOrigin()); + addOneTokenToTLA(" = "); + addLeftParen(rhs.getOrigin()); + addExprToTLA(rhs); + addRightParen(rhs.getOrigin()); + addRightParen(sass.getOrigin()); + addOneTokenToTLA((iFirst == iLast) ? "]" : ","); +// Vector sv = sub.toStringVector(); +// if (sv.size() > 0) +// { +// sb.append((String) sv.elementAt(0)); +// for (int v = 1; v < sv.size(); v++) +// { +// lines.addElement(sb.toString()); +// sb = new StringBuffer(NSpaces(here)); +// sb.append((String) sv.elementAt(v)); +// } +// } +// sb.append(" = "); +// here = sb.length(); +// sv = rhs.toStringVector(); +// sb.append((String) sv.elementAt(0)); +// for (int v = 1; v < sv.size(); v++) +// { +// lines.addElement(sb.toString()); +// sb = new StringBuffer(NSpaces(here)); +// sb.append((String) sv.elementAt(v)); +// } +// sb.append(((iFirst == iLast) ? "]" : ",")); +// lines.addElement(sb.toString()); + sb = new StringBuffer(); + if (iFirst < iLast) { + endCurrentLineOfTLA(); + AddSpaces(sb, cc); + } + iFirst = iFirst + 1; + } + } + +// vlines.addElement(lines); + i = iLast + 1; + if (i < ast.ass.size()) { + endCurrentLineOfTLA(); + AddSpaces(sb, prefix.length()); + } + } + addRightParen(ast.getOrigin()); + endCurrentLineOfTLA(); + + c.Merge(cThis); + // Append generated code to tlacode +// sb = new StringBuffer(prefix); +// col = sb.length(); +// if (numAssigns > 1) +// sb.append("/\\ "); +// if (vlines.size() > 0) +// { +// for (int v1 = 0; v1 < vlines.size(); v1++) +// { +// Vector vl = (Vector) vlines.elementAt(v1); +// for (int v2 = 0; v2 < vl.size(); v2++) +// { +// sb.append((String) vl.elementAt(v2)); +// tlacode.addElement(sb.toString()); +// sb = new StringBuffer(NSpaces(col)); +// if ((v1 > 0 || numAssigns > 1) && (v2 != vl.size() - 1)) +// sb.append(" "); +// } +// sb.append("/\\ "); +// } +// } + } + + /** + * Generate TLA+ for if statement. Each branch has its own + * UNCHANGED that lists variables that were changed in the + * other branch. This is a little difficult since we don't + * know the UNCHANGED for the Then branch until the code + * for the Else branch is generated. So, we fix the + * line in the Then branch after the Else branch is done. + * The corresponding mappingVector line also has to be changed, + * but that's not a problem because the UNCHANGED is a TLA+ + * token with no corresponding source. + */ + private void GenIf(AST.If ast, Changed c, String context, String prefix, int col) throws PcalTLAGenException + { + Changed cThen = new Changed(c); + Changed cElse = new Changed(c); + int lineUncThen; + StringBuffer sb = new StringBuffer(prefix); + TLAExpr test = null; + test = AddSubscriptsToExpr(ast.test, SubExpr(Self(context)), c); +// Vector sv = test.toStringVector(); + sb.append("IF "); + int here = sb.length(); + /************************************************************* + * LL removed a bogus "- 1" here on 31 Jan 2006. * + *************************************************************/ + addLeftParen(ast.getOrigin()); + addOneTokenToTLA(sb.toString()); + addExprToTLA(test); + endCurrentLineOfTLA(); + +// sb.append((String) sv.elementAt(0)); +// for (int v = 1; v < sv.size(); v++) +// { +// tlacode.addElement(sb.toString()); +// sb = new StringBuffer(NSpaces(here)); +// sb.append((String) sv.elementAt(v)); +// } +// tlacode.addElement(sb.toString()); + + sb = new StringBuffer(NSpaces(here)); + sb.append("THEN "); + here = sb.length(); + + sb.append("/\\ "); + for (int i = 0; i < ast.Then.size(); i++) + { + GenStmt((AST) ast.Then.elementAt(i), cThen, context, sb.toString(), + /******************************************************************* + * LL added the +3 on 18 Feb 2006 to take account of the * + * indentation of the "IF ". * + *******************************************************************/ + here + 3); + sb = new StringBuffer(NSpaces(here) + "/\\ "); + } + lineUncThen = tlacode.size(); +// tlacode.addElement(sb.toString()); + addOneLineOfTLA(sb.toString()); + sb = new StringBuffer(NSpaces(here - "THEN ".length()) + "ELSE "); + here = sb.length(); + if (ast.Else.size() == 0) + { + sb.append("/\\ TRUE"); +// tlacode.addElement(sb.toString()); + addOneLineOfTLA(sb.toString()); + + sb = new StringBuffer(NSpaces(here) + "/\\ "); + } else + { + sb.append("/\\ "); + for (int i = 0; i < ast.Else.size(); i++) + { + GenStmt((AST) ast.Else.elementAt(i), cElse, context, sb.toString(), + /******************************************************************* + * LL added the +3 on 18 Feb 2006 to take account of the * + * indentation of the "IF ". * + *******************************************************************/ + here + 3); + sb = new StringBuffer(NSpaces(here) + "/\\ "); + } + } + // Generate UNCHANGED for the ELSE branch + if (cElse.NumUnchanged(cThen) > 1) + { + Vector uncElse = cElse.Unchanged(cThen, wrapColumn - sb.length() - "UNCHANGED << ".length()); + sb.append("UNCHANGED << "); + int cc = sb.length(); + sb.append((String) uncElse.elementAt(0)); + for (int i = 1; i < uncElse.size(); i++) + { +// tlacode.addElement(sb.toString()); + addOneLineOfTLA(sb.toString()); + sb = new StringBuffer(NSpaces(cc)); + sb.append((String) uncElse.elementAt(i)); + } + sb.append(" >>"); +// tlacode.addElement(sb.toString()); + addOneTokenToTLA(sb.toString()); + addRightParen(ast.getOrigin()); + endCurrentLineOfTLA(); + } else if (cElse.NumUnchanged(cThen) == 1) + { // Change made by LL on 16 Mar 2011 so that, if there is a single + // unchanged variable v, it produces v' = v if v is a short variable, + // otherwise it produces UNCHANGED v + // + // sb.append("UNCHANGED " + cElse.Unchanged(cThen)); + String uc = cElse.Unchanged(cThen); + if (uc.length() > 5) { + sb.append("UNCHANGED " + uc); + } else { + sb.append(uc + "' = " + uc); + } +// tlacode.addElement(sb.toString()); + addOneTokenToTLA(sb.toString()); + addRightParen(ast.getOrigin()); + endCurrentLineOfTLA(); + } else + { + /* + * There is no UNCHANGED after the ELSE, so we have to put + * the RightParen for the whole if statement at the end of + * the last line already generated + */ + ((Vector) mappingVector.elementAt(mappingVector.size()-1)) + .add(new MappingObject.RightParen(ast.getOrigin().getEnd())); + } + + // Patch up the UNCHANGED for the THEN branch + sb = new StringBuffer((String) tlacode.elementAt(lineUncThen)); + tlacode.removeElementAt(lineUncThen); + mappingVector.removeElementAt(lineUncThen); + if (cThen.NumUnchanged(cElse) > 1) + { + Vector uncThen = cThen.Unchanged(cElse, wrapColumn - sb.length() - "UNCHANGED << ".length()); + sb.append("UNCHANGED << "); + int cc = sb.length(); + sb.append((String) uncThen.elementAt(0)); + for (int i = 1; i < uncThen.size(); i++) + { + tlacode.insertElementAt(sb.toString(), lineUncThen); + + //set the mappingVector entry + mappingVector.insertElementAt(stringToTLATokens(sb.toString()), lineUncThen); + + lineUncThen = lineUncThen + 1; + sb = new StringBuffer(NSpaces(cc)); + sb.append((String) uncThen.elementAt(i)); + } + sb.append(" >>"); + tlacode.insertElementAt(sb.toString(), lineUncThen); + Vector vec = stringToTLATokens(sb.toString()); + + // The following is bogus because the RightParen for the + // entire procedure is inserted after (or instead of) the + // ELSE's UNCHANGED + // vec.add(new MappingObject.RightParen(ast.getOrigin().getEnd())); + + mappingVector.insertElementAt(vec, lineUncThen); + + } else if (cThen.NumUnchanged(cElse) == 1) + { // Change made by LL on 16 Mar 2011 so that, if there is a single + // unchanged variable v, it produces v' = v if v is a short variable, + // otherwise it produces UNCHANGED v + // + // sb.append("UNCHANGED "); + // sb.append(cThen.Unchanged(cElse)); + String uc = cThen.Unchanged(cElse); + if (uc.length() > 5) { + sb.append("UNCHANGED " + uc); + } else { + sb.append(uc + "' = " + uc); + } + tlacode.insertElementAt(sb.toString(), lineUncThen); + Vector vec = stringToTLATokens(sb.toString()); + // The following is bogus because the RightParen for the + // entire procedure is inserted after (or instead of) the + // ELSE's UNCHANGED + // vec.add(new MappingObject.RightParen(ast.getOrigin().getEnd())); + mappingVector.insertElementAt(vec, lineUncThen); + } + + // Merge the change lists together + c.Merge(cThen); + c.Merge(cElse); + } + + /** + * Returns the vector of MappingObjects containing the BeginTLAToken and + * EndTLAToken that are put in the mappingVector by a call of addOneLineOfTLA. + * The code was essentially copied from addOneTokenToTLA. + * + * @param token + * @return + */ + private Vector stringToTLATokens(String token) { + Vector result = new Vector(3); + + String trimmedToken = token.trim() ; + + int numberOfLeftTrimmedTokens = + (trimmedToken.length() == 0) ? -1 : + token.indexOf(trimmedToken.charAt(0)); + + /** + * Handle a token of only space characters. + */ + if (numberOfLeftTrimmedTokens == -1) { + numberOfLeftTrimmedTokens = 0 ; + trimmedToken = token ; + } + + int objBegin = numberOfLeftTrimmedTokens; + result.addElement(new MappingObject.BeginTLAToken(objBegin)); + result.addElement(new MappingObject.EndTLAToken(objBegin + trimmedToken.length())); + return result; + } + + /*********************************************************************** + * Added by LL on 30 Jan 2006. * + * * + * Generate TLA+ for the `either' statement. This performs the same * + * sort of hackery as for the `if' statement, necessitated by the * + * design flaw commented on above. + ** + ***********************************************************************/ + private void GenEither(AST.Either ast, Changed c, String context, String prefix, int col) + throws PcalTLAGenException + { + Changed allC = new Changed(c); + /******************************************************************* + * Accumulates the variable changes of all the clauses. * + *******************************************************************/ + Changed[] cOrs = new Changed[ast.ors.size()]; + /******************************************************************* + * cOrs[i] is the Changed vector for the i-th `or' clause. * + *******************************************************************/ + int[] ucLocs = new int[ast.ors.size()]; // location of unchangeds. + /****************************************************************** + * tlaout.elementAt(ucLocs[i]) is the UNCHANGED clause for the * + * i-th `or' clause. * + ******************************************************************/ + StringBuffer sb = new StringBuffer(prefix); + int prefixIndent = sb.length(); + sb.append("\\/ "); + int here = sb.length(); + /******************************************************************* + * The number of columns to the left of the code generated for * + * each `or' clause. * + *******************************************************************/ + + /* + * Add the left paren for the statement. + */ + addLeftParen(ast.getOrigin()); + /********************************************************************* + * Produce the output for the clauses, but with a dummy line in * + * place of the UNCHANGED clause, and compute allC, cOrs, and * + * ucLocs. * + *********************************************************************/ + for (int i = 0; i < ast.ors.size(); i++) + { + if (i != 0) + { + sb = new StringBuffer(NSpaces(prefixIndent) + "\\/ "); + } + ; + sb.append("/\\ "); + Vector orClause = (Vector) ast.ors.elementAt(i); + Changed cC = new Changed(c); + for (int j = 0; j < orClause.size(); j++) + { + /*********************************************************** + * On 6 Jun 2010, LL added the "+3" in the following call * + * of GenStmt. This seems to fix a bug which caused * + * * + * either when \/ A * + * \/ B * + * or ... * + * * + * to produce * + * \/ /\ \/ A * + * \/ B * + * \/ ... * + ***********************************************************/ + GenStmt((AST) orClause.elementAt(j), cC, context, sb.toString(), here + 3); + sb = new StringBuffer(NSpaces(here) + "/\\ "); + } + ; + cOrs[i] = cC; + allC.Merge(cC); + ucLocs[i] = tlacode.size(); +// tlacode.addElement("Replace by UNCHANGED"); // + addOneLineOfTLA("Replace by UNCHANGED"); + } + ; // End of for i + + /********************************************************************** + * Insert real UNCHANGED clauses. Note that we have to go through * + * loop backwards since we will remove a line of output for each `or' * + * clause that doesn't get an UNCHANGED. * + **********************************************************************/ + int i = ast.ors.size(); + while (i > 0) + { + i = i - 1; + tlacode.removeElementAt(ucLocs[i]); + mappingVector.removeElementAt(ucLocs[i]); + int numUnchanged = cOrs[i].NumUnchanged(allC); + String NotChanged = cOrs[i].Unchanged(allC); + if (numUnchanged > 1) + { + /* + * The line should be wrapped if it's too long. + */ + String line = NSpaces(here) + "/\\ UNCHANGED <<" + NotChanged + ">>"; + tlacode.insertElementAt(line, ucLocs[i]); + mappingVector.insertElementAt(stringToTLATokens(line), ucLocs[i]); + } else if (numUnchanged == 1) + { // Change made by LL on 16 Mar 2011 so that, if there is a single + // unchanged variable v, it produces v' = v if v is a short variable, + // otherwise it produces UNCHANGED v + // + // tlacode.insertElementAt(NSpaces(here) + "/\\ UNCHANGED " + NotChanged, ucLocs[i]); + if (NotChanged.length() > 5) { + String line = NSpaces(here) + "/\\ UNCHANGED " + NotChanged; + tlacode.insertElementAt(line, ucLocs[i]); + mappingVector.insertElementAt(stringToTLATokens(line), ucLocs[i]); +// tlacode.insertElementAt(NSpaces(here) + "/\\ UNCHANGED " + NotChanged, ucLocs[i]); + } else { + String line = NSpaces(here) + "/\\ " + NotChanged + "' = " + NotChanged; + tlacode.insertElementAt(line, ucLocs[i]); + mappingVector.insertElementAt(stringToTLATokens(line), ucLocs[i]); +// tlacode.insertElementAt(NSpaces(here) + "/\\ " + NotChanged + "' = " +// + NotChanged, ucLocs[i]); + } + } + } + ; + /* + * Add the right paren for the entire statement. + */ + ((Vector) mappingVector.elementAt(mappingVector.size()-1)) + .add(new MappingObject.RightParen(ast.getOrigin().getEnd())); + /********************************************************************** + * Add the statement's unchangeds to c. * + **********************************************************************/ + c.Merge(allC); + } + + private void GenWith(AST.With ast, Changed c, String context, String prefix, int col) throws PcalTLAGenException + { + addLeftParen(ast.getOrigin()); + StringBuffer sb = new StringBuffer(prefix); + TLAExpr exp = AddSubscriptsToExpr(ast.exp, SubExpr(Self(context)), c); +// Vector sv = exp.toStringVector(); + if (ast.isEq) + { + /* generate LET statement */ + sb.append("LET "); + sb.append(ast.var); + sb.append(" == "); + addOneTokenToTLA(sb.toString()); + addLeftParen(exp.getOrigin()); + addExprToTLA(exp); + addRightParen(exp.getOrigin()); +// int here = sb.length(); +// sb.append((String) sv.elementAt(0)); +// for (int v = 1; v < sv.size(); v++) +// { +// tlacode.addElement(sb.toString()); +// sb = new StringBuffer(NSpaces(here)); +// sb.append((String) sv.elementAt(v)); +// } + addOneTokenToTLA(" IN"); + endCurrentLineOfTLA(); +// sb.append(" IN"); +// tlacode.addElement(sb.toString()); + sb = new StringBuffer(NSpaces(col + 2)); + /************************************************************* + * LL changed "col + 4" to "col + 2" here to correct an * + * alignment problem on 31 Jan 2006. * + *************************************************************/ + if (ast.Do.size() > 1) + sb.append("/\\ "); + } else + { + /* generate \E statement */ + sb.append("\\E "); + sb.append(ast.var); + sb.append(" \\in "); + addOneTokenToTLA(sb.toString()); + addLeftParen(exp.getOrigin()); + addExprToTLA(exp); + addRightParen(exp.getOrigin()); +// int here = sb.le + addOneTokenToTLA(":"); + endCurrentLineOfTLA(); +// sb.append(":"); +// tlacode.addElement(sb.toString()); + sb = new StringBuffer(NSpaces(col + 2)); + if (ast.Do.size() > 1) + sb.append("/\\ "); + } + for (int i = 0; i < ast.Do.size(); i++) + { + GenStmt((AST) ast.Do.elementAt(i), c, context, sb.toString(), sb.length()); + sb = new StringBuffer(NSpaces(col + 2) + "/\\ "); + } + // tlacode.addElement(NSpaces(col) + ")"); + + /* + * Add the right paren for the entire statement. + */ + ((Vector) mappingVector.elementAt(mappingVector.size()-1)) + .add(new MappingObject.RightParen(ast.getOrigin().getEnd())); + } + + private void GenWhen(AST.When ast, Changed c, String context, String prefix, int col) throws PcalTLAGenException + { + addOneTokenToTLA(prefix); + +// StringBuffer sb = new StringBuffer(prefix); + TLAExpr exp = AddSubscriptsToExpr(ast.exp, SubExpr(Self(context)), c); + addLeftParen(exp.getOrigin()); + addExprToTLA(exp); + addRightParen(exp.getOrigin()); + endCurrentLineOfTLA(); +// Vector sv = exp.toStringVector(); +// +//// Debugging +////Vector vec = exp.toMappingVector(); +////System.out.println("Original vec:"); +////MappingObject.printMappingVector(vec); +////System.out.println("RemoveRedundantParens(vec)"); +////MappingObject.printMappingVector(TLAtoPCalMapping.RemoveRedundantParens(vec)); +////System.out.println("Should be original mappingvector:"); +////MappingObject.printMappingVector(vec); +// +// sb.append((String) sv.elementAt(0)); +// for (int v = 1; v < sv.size(); v++) +// { +// tlacode.addElement(sb.toString()); +// sb = new StringBuffer(NSpaces(col)); +// sb.append((String) sv.elementAt(v)); +// } +// tlacode.addElement(sb.toString()); + } + + private void GenPrintS(AST.PrintS ast, Changed c, String context, String prefix, int col) + throws PcalTLAGenException + { + StringBuffer sb = new StringBuffer(prefix); + TLAExpr exp = AddSubscriptsToExpr(ast.exp, SubExpr(Self(context)), c); + addLeftParen(ast.getOrigin()); + addOneTokenToTLA(prefix + "PrintT("); + addExprToTLA(exp); + addOneTokenToTLA(")"); + addRightParen(ast.getOrigin()); + endCurrentLineOfTLA(); + +// Vector sv = exp.toStringVector(); +// // The following modified 19 Nov 05 by LL to use PrintT instead of Print +// sb.append("PrintT("); +// sb.append((String) sv.elementAt(0)); +// for (int v = 1; v < sv.size(); v++) +// { +// tlacode.addElement(sb.toString()); +// sb = new StringBuffer(NSpaces(col + "PrintT(".length())); +// sb.append((String) sv.elementAt(v)); +// } +// sb.append(")"); +// tlacode.addElement(sb.toString()); + } + + /********************************************************/ + /* Assert(ast.expr, "Failure of assertion at... ") */ + /** + *******************************************************/ + private void GenAssert(AST.Assert ast, Changed c, String context, String prefix, int col) + throws PcalTLAGenException + { + addLeftParen(ast.getOrigin()); + StringBuffer sb = new StringBuffer(prefix); + StringBuffer sc = new StringBuffer(); + TLAExpr exp = AddSubscriptsToExpr(ast.exp, SubExpr(Self(context)), c); +// Vector sv = exp.toStringVector(); + sb.append("Assert("); + addOneTokenToTLA(sb.toString()); + addLeftParen(exp.getOrigin()); + addExprToTLA(exp); + addRightParen(exp.getOrigin()); + int here = sb.length(); +// sb.append((String) sv.elementAt(0)); +// for (int v = 1; v < sv.size(); v++) +// { +// tlacode.addElement(sb.toString()); +// sb = new StringBuffer(NSpaces(col + "Assert(".length())); +// sb.append((String) sv.elementAt(v)); +// } +// sb.append(", "); + sb = new StringBuffer(", "); + sc.append("\"Failure of assertion at "); + sc.append(ast.location()); + // modified on 23 Mar 2006 by LL to use location() instead of + // ast.line and ast.col + sc.append(".\")"); + if (tlacodeNextLine.length() + sb.length() + sc.length() < wrapColumn) { + addOneTokenToTLA(sb.toString() + sc.toString()); + } +// if (sb.length() + sc.length() < wrapColumn) +// tlacode.addElement(sb.toString() + sc.toString()); + else + { + addOneTokenToTLA(sb.toString()); + endCurrentLineOfTLA(); + addOneTokenToTLA(NSpaces(here) + sc.toString()); +// tlacode.addElement(sb.toString()); +// tlacode.addElement(NSpaces(here) + sc.toString()); + } + addRightParen(ast.getOrigin()); + endCurrentLineOfTLA(); + } + + /********************************************************/ + /* I generate a TRUE conjunct, which is useless, but so */ + /* is a skip statement. */ + /********************************************************/ + private void GenSkip(AST.Skip ast, Changed c, String context, String prefix, int col) + { +// tlacode.addElement(prefix + "TRUE"); + addOneTokenToTLA(prefix); + addLeftParen(ast.getOrigin()); + addOneTokenToTLA("TRUE"); + addRightParen(ast.getOrigin()); + endCurrentLineOfTLA(); + } + + /*********************************************************************** + * Generate the VARIABLES declaration(s), output the TLA+ "code" from * + * a `define' statement, if any, and generate the definition of * + * `vars'. * + * * + * Method renamed from GenVars and given the defs argument by LL on * + * 25 Jan 2006 to handle the `define' statement. * + ***********************************************************************/ + private void GenVarsAndDefs(Vector globals, Vector procs, Vector processes, TLAExpr defs) + throws PcalTLAGenException + { + /******************************************************************* + * lVars and gVars are vectors of strings, each element being a * + * variable name. They hold the local and global variables, * + * respectively. * + *******************************************************************/ + Vector lVars = new Vector(); + Vector gVars = new Vector(); + + /** + * lVarsSource and gVarsSource are vectors of AST.VarDecl objects that + * generated the elements of lVars and gVars, where PVarDecl objects + * are converted to VarDecl objects. + */ + Vector lVarsSource = new Vector(); + Vector gVarsSource = new Vector(); + + /******************************************************************* + * Set gVars to the global variables, including pc and `stack' if * + * there are procedures, and add these variables to vars. * + *******************************************************************/ + if (globals != null) + for (int i = 0; i < globals.size(); i++) + { + AST.VarDecl decl = (AST.VarDecl) globals.elementAt(i); + gVars.addElement(decl.var); + gVarsSource.addElement(decl); + vars.addElement(decl.var); + } + if (! ParseAlgorithm.omitPC) { + gVars.addElement("pc"); + /** + * For added variables, create a VarDecl with null origin. + */ + AST.VarDecl pcVarDecl = new AST.VarDecl(); + pcVarDecl.var = "pc"; + gVarsSource.addElement(pcVarDecl); + vars.addElement("pc"); + } + if (procs != null && procs.size() > 0) + { + gVars.addElement("stack"); + /** + * For added variables, create a VarDecl with null origin. + */ + AST.VarDecl pcVarDecl = new AST.VarDecl(); + pcVarDecl.var = "stack"; + gVarsSource.addElement(pcVarDecl); + vars.addElement("stack"); + } + /******************************************************************* + * Add local procedure variables to lVars, vars, and pcV. * + *******************************************************************/ + if (procs != null) + for (int i = 0; i < procs.size(); i++) + { + AST.Procedure proc = (AST.Procedure) procs.elementAt(i); + if (proc.params != null) + for (int p = 0; p < proc.params.size(); p++) + { + AST.PVarDecl decl = (AST.PVarDecl) proc.params.elementAt(p); + lVars.addElement(decl.var); + lVarsSource.addElement(decl.toVarDecl()) ; + vars.addElement(decl.var); + pcV.addElement(decl.var); + } + if (proc.decls != null) + for (int p = 0; p < proc.decls.size(); p++) + { + AST.PVarDecl decl = (AST.PVarDecl) proc.decls.elementAt(p); + lVars.addElement(decl.var); + lVarsSource.addElement(decl.toVarDecl()) ; + vars.addElement(decl.var); + pcV.addElement(decl.var); + } + } + + /******************************************************************* + * Add local process variables to lVars, vars, and psV for * + * variables local to process sets. * + *******************************************************************/ + if (processes != null) + for (int i = 0; i < processes.size(); i++) + { + AST.Process proc = (AST.Process) processes.elementAt(i); + if (proc.decls != null) + for (int p = 0; p < proc.decls.size(); p++) + { + AST.VarDecl decl = (AST.VarDecl) proc.decls.elementAt(p); + lVars.addElement(decl.var); + lVarsSource.addElement(decl); + vars.addElement(decl.var); + if (!proc.isEq) + psV.addElement(decl.var); + } + } + + /******************************************************************** + * Add a declaration of the constant defaultInitValue if it is * + * used. (Added by LL on 22 Aug 2007.) * + ********************************************************************/ + if (ParseAlgorithm.hasDefaultInitialization) + { + addOneLineOfTLA("CONSTANT defaultInitValue"); + } + ; + + if (EmptyExpr(defs)) + { + /****************************************************************** + * There is no `define' statement. In this case, generate a * + * single VARIABLES statement and set gVars to vector of all * + * variables. * + ******************************************************************/ + gVars.addAll(lVars); + gVarsSource.addAll(lVarsSource) ; + GenVarDecl(gVars, gVarsSource); + } else + { + /****************************************************************** + * There is a `define' statement. In this case, must declare * + * global and local variables separately. Also, set gVars to * + * vector of all variables. * + ******************************************************************/ + GenVarDecl(gVars, gVarsSource); + addOneLineOfTLA(""); + addOneLineOfTLA("(* define statement *)"); + addExprToTLA(defs); +// Vector sv = defs.toStringVector(); +// for (int i = 0; i < sv.size(); i++) +// { +// tlacode.addElement((String) sv.elementAt(i)); +// } + ; + addOneLineOfTLA(""); + GenVarDecl(lVars, lVarsSource); // to be fixed + gVars.addAll(lVars); + gVarsSource.addAll(lVarsSource); + } + ; + addOneLineOfTLA(""); + + /* + * We check for the unlikely case in which there are no variables. + * Without this check, the Init is not generated but appears in + * the definition of Spec. + */ + if (gVars.size() == 0) { + throw new PcalTLAGenException("The algorithm has no variables."); + } + /******************************************************************* + * Generate definition of var. * + *******************************************************************/ +// StringBuffer var = new StringBuffer("vars == << "); +// StringBuffer curLine = new StringBuffer("vars == << "); + addOneTokenToTLA("vars == << ") ; + int indent = tlacodeNextLine.length(); + for (int i = 0; i < gVars.size(); i++) + { + if (i > 0) + { +// var.append(", "); +// curLine.append(", "); +// tlacodeNextLine = tlacodeNextLine + ", "; + addOneTokenToTLA(", "); + } + ; + String vbl = (String) gVars.elementAt(i); + AST.VarDecl vblDecl = (AST.VarDecl) gVarsSource.elementAt(i); + Region vblOrigin = vblDecl.getOrigin(); +// if (curLine.length() + vbl.length() + 1 > wrapColumn) + if (tlacodeNextLine.length() + vbl.length() + 1 > wrapColumn) + { +// curLine = new StringBuffer("vars == << "); +// var.append("\n" + NSpaces("vars == << ".length())); + endCurrentLineOfTLA(); + tlacodeNextLine = NSpaces(indent); + } +// var.append(vbl); +// curLine.append(vbl); + addOneSourceTokenToTLA(vbl, vblOrigin); + } +// if (curLine.length() + " >>".length() + 1 > wrapColumn) + if (tlacodeNextLine.length() + " >>".length() + 1 > wrapColumn) + { +// var.append("\n" + NSpaces("vars ==".length())); + endCurrentLineOfTLA() ; + tlacodeNextLine = NSpaces("vars ==".length()); + } + ; +// var.append(" >>"); +// tlacodeNextLine = tlacodeNextLine + " >>"; + addOneTokenToTLA(" >>"); +// tlacode.addElement(var.toString()); + addOneLineOfTLA(""); + } + + /** + * Generate a VARIABLE(S) declarations. The varVec argument is a vector of + * strings that are the variables to be declared. It does nothing if + * the vector has length 0. The varVecSource argument is a vector + * of the same size as varVec that contains the AST.VarDecl objects. + * <p> + * Method added by LL on 25 Jan 2006. + * + * Modified 16 Dec 2011 to add varVecSource argument and generate TLA to + * PCal mapping. + * + * @param varVec A vector of strings. + * + * @param varVecSource A vector of AST.VarDecl objects. + */ + public void GenVarDecl(Vector varVec, Vector varVecSource) + { +// StringBuffer res = new StringBuffer(); +// StringBuffer curLine = new StringBuffer("VARIABLES "); + // for measuring length + if (varVec.size() == 0) + { + return; + } + ; + if (varVec.size() > 1) + { +// res.append("VARIABLES "); + addOneTokenToTLA("VARIABLES "); + } else + { +// res.append("VARIABLE "); + addOneTokenToTLA(TLAConstants.KeyWords.VARIABLE + " "); + } + ; + for (int i = 0; i < varVec.size(); i++) + { + if (i > 0) + { +// res.append(", "); +// curLine.append(", "); + addOneTokenToTLA(", "); + } + ; + String vbl = (String) varVec.elementAt(i); + AST vblsource = (AST) varVecSource.elementAt(i); +// if (curLine.length() + vbl.length() + 1 > wrapColumn) + if (tlacodeNextLine.length() + vbl.length() + 1 > wrapColumn) + { +// curLine = new String + endCurrentLineOfTLA(); + if (varVec.size() > 1) + { +// res.append(NSpaces("VARIABLES ".length())); + tlacodeNextLine = tlacodeNextLine + NSpaces("VARIABLES ".length()); + } else + { +// res.append(NSpaces("VARIABLE ".length())); + tlacodeNextLine = tlacodeNextLine + NSpaces("VARIABLE ".length()); + } + ; + } + ; +// res.append(vbl); +// curLine.append(vbl); + addOneSourceTokenToTLA(vbl, vblsource.getOrigin()); + } + ; +// tlacode.addElement(res.toString()); + endCurrentLineOfTLA(); + } + + /** + * Generates the "ProcSet == ..." output. It is just a union of all the + * process sets, all on one line (except if a process set is a multi-line + * expression). It wouldn't be too hard to break long lines, but that + * should be done later, if desired, after the TLA to PCal translation + * is finished. + */ + public void GenProcSet() + { + StringBuffer ps = new StringBuffer(); + if (st.processes == null || st.processes.size() == 0) + return; +// ps.append("ProcSet == "); + addOneTokenToTLA("ProcSet == "); + for (int i = 0; i < st.processes.size(); i++) + { + PcalSymTab.ProcessEntry proc = (PcalSymTab.ProcessEntry) st.processes.elementAt(i); +// Vector sv = proc.id.toStringVector(); + if (i > 0) { +// ps.append(" \\cup "); + addOneTokenToTLA(" \\cup "); + } + addLeftParen(proc.id.getOrigin()); + if (proc.isEq) { +// ps.append("{"); + addOneTokenToTLA("{"); + } + else { +// ps.append("("); + addOneTokenToTLA("("); + } + int col = ps.length(); +// ps.append((String) sv.elementAt(0)); +// for (int v = 1; v < sv.size(); v++) +// { +// tlacode.addElement(ps.toString()); +// ps = new StringBuffer(NSpaces(col)); +// ps.append((String) sv.elementAt(v)); +// } + addExprToTLA(proc.id); + if (proc.isEq) { +// ps.append("}"); + addOneTokenToTLA("}"); + } + else { +// ps.append(")"); + addOneTokenToTLA(")"); + } + addRightParen(proc.id.getOrigin()); + } +// tlacode.addElement(ps.toString()); +// tlacode.addElement(""); + endCurrentLineOfTLA(); + addOneLineOfTLA(""); + } + + /***********************************/ + /* Generate the Init == statement. */ + /** + **********************************/ + private void GenInit(Vector globals, Vector procs, Vector processes) throws PcalTLAGenException + { + int col = "Init == ".length(); + StringBuffer is = new StringBuffer(); + is.append("Init == "); + + /* Global variables */ + if (globals != null && globals.size() > 0) + { + is.append("(* Global variables *)"); +// tlacode.addElement(is.toString()); + addOneLineOfTLA(is.toString()) ; + is = new StringBuffer(NSpaces(col)); + for (int i = 0; i < globals.size(); i++) + { + AST.VarDecl decl = (AST.VarDecl) globals.elementAt(i); + addVarDeclToTLA(decl, is); + is = new StringBuffer(NSpaces(col)); + } + } + if (procs != null && procs.size() > 0) + { + /* Procedure variables and parameters */ + for (int i = 0; i < procs.size(); i++) + { + AST.Procedure proc = (AST.Procedure) procs.elementAt(i); + if (proc.params.size() == 0 && proc.decls.size() == 0) + // No parameters or procedure variables in this procedure + continue; + is.append("(* Procedure "); + is.append(proc.name); + is.append(" *)"); +// tlacode.addElement(is.toString()); + addOneLineOfTLA(is.toString()); + is = new StringBuffer(NSpaces(col)); + for (int p = 0; p < proc.params.size(); p++) + { + AST.PVarDecl decl = (AST.PVarDecl) proc.params.elementAt(p); + if (!mp) { + addVarDeclToTLA(decl.toVarDecl(), is); + } + else { + is.append("/\\ "); + addOneTokenToTLA(is.toString()); + addLeftParen(decl.getOrigin()); +// is.append(decl.var); + is = new StringBuffer(decl.var); + /******************************************************* + * Modified on 31 Jan 2006 by LL to add subscripts to * + * initialization expression if needed. Also replaced * + * test for "\\in" with assertion that it can't occur, * + * since it's forbidden by the grammar. * + *******************************************************/ + PcalDebug.Assert(decl.isEq); + is.append(" = "); + +// Vector sv; +// if (mp) +// { +// sv = AddSubscriptsToExpr(decl.val, +// SubExpr(Self("procedure")), new Changed(new Vector())) +// .toStringVector(); +// } else +// { +// sv = Parenthesize(decl.val.toStringVector()); +// /************************************************* +// * Call to Parenthesize added by LL on 27 Feb 2008. * +// * See bug_08-02-18. * +// *************************************************/ +// } +// ; +// if (mp) +// { + is.append("[ self \\in ProcSet |-> "); +// } + addOneTokenToTLA(is.toString()); + addLeftParen(decl.val.getOrigin()); + addExprToTLA( + AddSubscriptsToExpr(decl.val, + SubExpr(Self("procedure")), + new Changed(new Vector()))); + addRightParen(decl.val.getOrigin()); + addOneTokenToTLA("]"); + addRightParen(decl.getOrigin()); + endCurrentLineOfTLA(); + +// int col2 = is.length(); +// is.append((String) sv.elementAt(0)); +// for (int v = 1; v < sv.size(); v++) +// { +// tlacode.addElement(is.toString()); +// is = new StringBuffer(NSpaces(col2)); +// is.append((String) sv.elementAt(v)); +// } +// if (mp) +// is.append("]"); +// tlacode.addElement(is.toString()); + } + is = new StringBuffer(NSpaces(col)); + } + for (int p = 0; p < proc.decls.size(); p++) + { + /* + * Note: the following code is identical to the for loop + * code above for procedure variables. (Well done, Keith!) + * I realized this too late to feel like procedurizing it. + */ + AST.PVarDecl decl = (AST.PVarDecl) proc.decls.elementAt(p); + if (!mp) { + addVarDeclToTLA(decl.toVarDecl(), is); + } + else { + is.append("/\\ "); + addOneTokenToTLA(is.toString()); + addLeftParen(decl.getOrigin()); +// is.append(decl.var); + is = new StringBuffer(decl.var); +// is.append("/\\ "); +// is.append(decl.var); + + /******************************************************* + * Modified on 31 Jan 2006 by LL to add subscripts to * + * initialization expression if needed. Also replaced * + * test for "\\in" with assertion that it can't occur, * + * since it's forbidden by the grammar. * + *******************************************************/ + PcalDebug.Assert(decl.isEq); + is.append(" = "); +// Vector sv; +// if (mp) +// { +// sv = AddSubscriptsToExpr(decl.val, SubExpr(Self("procedure")), new Changed(new Vector())) +// .toStringVector(); +// } else +// { +// sv = Parenthesize(decl.val.toStringVector()); +// /************************************************* +// * Call to Parenthesize added by LL on * +// * 27 Feb 2008. See bug_08-02-18. * +// *************************************************/ +// } +// ; +// if (mp) +// { + is.append("[ self \\in ProcSet |-> "); +// } + addOneTokenToTLA(is.toString()); + addLeftParen(decl.val.getOrigin()); + addExprToTLA(AddSubscriptsToExpr( + decl.val, + SubExpr(Self("procedure")), + new Changed(new Vector()))); + addRightParen(decl.val.getOrigin()); + addOneTokenToTLA("]"); + addRightParen(decl.getOrigin()); + endCurrentLineOfTLA(); + +// int col2 = is.length(); +// is.append((String) sv.elementAt(0)); +// for (int v = 1; v < sv.size(); v++) +// { +// tlacode.addElement(is.toString()); +// is = new StringBuffer(NSpaces(col2)); +// is.append((String) sv.elementAt(v)); +// } +// if (mp) +// is.append("]"); +// tlacode.addElement(is.toString()); + } + is = new StringBuffer(NSpaces(col)); + } + } + } + if (processes != null && processes.size() > 0) + { + /* Process variables */ + for (int i = 0; i < processes.size(); i++) + { + AST.Process proc = (AST.Process) processes.elementAt(i); + if (proc.decls.size() == 0) // No variables in this procedure + continue; + is.append("(* Process "); + is.append(proc.name); + is.append(" *)"); +// tlacode.addElement(is.toString()); + addOneLineOfTLA(is.toString()); + is = new StringBuffer(NSpaces(col)); + for (int p = 0; p < proc.decls.size(); p++) + { + /* + * In the comments below, (( and )) represent + * MappingObject.LeftParen and MappingObject.RightParen + * objects. + */ + AST.VarDecl decl = (AST.VarDecl) proc.decls.elementAt(p); + is.append("/\\ "); + /* + * The following adds /\ (( to the TLA+ output. + */ + addOneTokenToTLA(is.toString()); + addLeftParen(decl.getOrigin()); + + + if (proc.isEq) { + /* + * The source is + * + * process (P = S) variables ... v @@ Val + * + * where @@ is either "=" or "\in". The TLA+ output is + * + * /\ (( v @@ (( Val )) )) + */ + is = new StringBuffer(decl.var); + if (decl.isEq) { + is.append(" = "); + } + else { + is.append(" \\in "); + } + addOneTokenToTLA(is.toString()); + addLeftParen(decl.val.getOrigin()); + addExprToTLA(decl.val); + addRightParen(decl.val.getOrigin()); + } + else { + if (decl.isEq) { + /* + * The source is + * + * process (P \in S) variables ... v = Val + * + * The TLA+ output is + * + * /\ (( v = [self \in (( S )) |-> (( ValBar )) ] )) + * + * where ValBar obtained from Val by replacing each + * variable w of the process with w[self]. + */ + is = new StringBuffer(decl.var); + is.append(" = [self \\in "); + addOneTokenToTLA(is.toString()); + addLeftParen(proc.id.getOrigin()); + addExprToTLA(proc.id); + addRightParen(proc.id.getOrigin()); + addOneTokenToTLA(TLAConstants.RECORD_ARROW); + addLeftParen(decl.val.getOrigin()); + addExprToTLA(AddSubscriptsToExpr( + decl.val, + SubExpr(Self("procedure")), + new Changed(new Vector()))); + addRightParen(decl.val.getOrigin()); + addOneTokenToTLA("]"); + + } + else { + /* + * The source is + * + * process (P \in S) variables ... v \in Val + * + * The TLA+ output is + * + * /\ (( v \in [ (( S )) -> (( ValBar )) ] )) + * + * where ValBar is obtained from Val by replacing each + * variable w of the process with + * + * w[CHOOSE self \in S : TRUE] + * + * We first set expr to the TLAExpr "CHOOSE self \in S : TRUE". + * (This is Keith's original code.) + */ + TLAExpr subexpr = proc.id.cloneAndNormalize(); + TLAExpr expr = new TLAExpr(); + expr.addLine(); + expr.addToken(new TLAToken("[", 0, TLAToken.BUILTIN)); + expr.addToken(new TLAToken("CHOOSE", 1, TLAToken.BUILTIN)); + expr.addToken(new TLAToken("self", 8, TLAToken.IDENT)); + expr.addToken(new TLAToken("\\in ", 13, TLAToken.BUILTIN)); + expr.normalize(); + expr.setOrigin(subexpr.getOrigin()); // see what this does. + try + { + subexpr.prepend(expr, 1); + expr = new TLAExpr(); + expr.addLine(); + expr.addToken(new TLAToken(":", 0, TLAToken.BUILTIN)); + expr.addToken(new TLAToken("TRUE", 2, TLAToken.BUILTIN)); + expr.addToken(new TLAToken("]", 6, TLAToken.BUILTIN)); + expr.prepend(subexpr, 1); + } catch (TLAExprException e) + { + throw new PcalTLAGenException(e.getMessage()); + } + + /* + * Now we output the TLA+ code. + */ + is = new StringBuffer(decl.var); + is.append(" \\in ["); + addOneTokenToTLA(is.toString()); + addLeftParen(proc.id.getOrigin()); + addExprToTLA(proc.id); + addRightParen(proc.id.getOrigin()); + addOneTokenToTLA(" -> " ); + addLeftParen(decl.val.getOrigin()); + addExprToTLA(AddSubscriptsToExpr( + decl.val, expr, new Changed(new Vector())) ); + addRightParen(decl.val.getOrigin()); + addOneTokenToTLA("]"); + } + } + /* + * This adds the final )) . + */ + addRightParen(decl.getOrigin()); + endCurrentLineOfTLA(); + is = new StringBuffer(NSpaces(col)); + +// everything from here down to the end of the for p loop should be commented out +// is.append(decl.var); +// is = new StringBuffer(decl.var); +// if (decl.isEq) +// is.append(" = "); +// else +// is.append(" \\in "); +// /******************************************************* +// * Modified on 31 Jan 2006 by LL to add subscripts to * +// * initialization expression for process set. Note * +// * tricky subscript that is added in expr for * +// * declaration of form "v \in expr". * +// * * +// * Also modified the whole method of producing the * +// * variable declaration because the original destroyed * +// * the formatting of the expression proc.id, leading * +// * to bad or incorrect formatting if the process id * +// * set expression was not trivial. * +// *******************************************************/ +// Vector sv; +// TLAExpr sve; +// if (proc.isEq) +// { +// /*************************************************** +// * No substitution unless it's a process set. * +// ***************************************************/ +// sve = decl.val; // .toStringVector(); +// } else +// { +// if (decl.isEq) +// { +// /*********************************************** +// * For declaration "v = ...", add subscript * +// * "[self]". * +// ***********************************************/ +// sve = AddSubscriptsToExpr(decl.val, SubExpr(Self("procedure")), new Changed(new Vector())); +// } else +// { +// /************************************************ +// * For declaration "v \in ...", add subscript * +// * "[CHOOSE self \in Process Id Set : TRUE]". * +// * * +// * This weird subscript is needed in the * +// * following weird case: * +// * * +// * process (P \in S) * +// * variable v \in T, w \in R(v) * +// * * +// * This produces the following conjunct in * +// * the initial predicate for w: * +// * * +// * w \in [S -> R(v[CHOOSE self \in S : TRUE])] * +// ************************************************/ +// TLAExpr subexpr = proc.id.cloneAndNormalize(); +// +// TLAExpr expr = new TLAExpr(); +// expr.addLine(); +// expr.addToken(new TLAToken("[", 0, TLAToken.BUILTIN)); +// expr.addToken(new TLAToken("CHOOSE", 1, TLAToken.BUILTIN)); +// expr.addToken(new TLAToken("self", 8, TLAToken.IDENT)); +// expr.addToken(new TLAToken("\\in ", 13, TLAToken.BUILTIN)); +// expr.normalize(); +// +// try +// { +// subexpr.prepend(expr, 1); +// expr = new TLAExpr(); +// expr.addLine(); +// expr.addToken(new TLAToken(":", 0, TLAToken.BUILTIN)); +// expr.addToken(new TLAToken("TRUE", 2, TLAToken.BUILTIN)); +// expr.addToken(new TLAToken("]", 6, TLAToken.BUILTIN)); +// expr.prepend(subexpr, 1); +// } catch (TLAExprException e) +// { +// throw new PcalTLAGenException(e.getMessage()); +// } +// +// sve = AddSubscriptsToExpr(decl.val, expr, new Changed(new Vector())); +// } +// ; +// } +// ; +// TLAExpr expr = new TLAExpr(); +// expr.addLine(); +// if (!proc.isEq) +// { +// expr.addToken(new TLAToken("[", 0, TLAToken.BUILTIN)); +// if (decl.isEq) +// { +// expr.addToken(new TLAToken("self", 1, TLAToken.IDENT)); +// expr.addToken(new TLAToken("\\in ", 6, TLAToken.BUILTIN)); +// } +// ; +// expr.normalize(); +// TLAExpr expr2 = proc.id.cloneAndNormalize(); +// try +// { +// expr2.prepend(expr, 0); +// expr = new TLAExpr(); +// expr.addLine(); +// if (decl.isEq) +// { +// expr.addToken(new TLAToken("|->", 0, TLAToken.BUILTIN)); +// } else +// { +// expr.addToken(new TLAToken("->", 0, TLAToken.BUILTIN)); +// } +// ; +// expr.prepend(expr2, 1); +// sve.prepend(expr, 1); +// } catch (TLAExprException e) +// { +// throw new PcalTLAGenException(e.getMessage()); +// } +// } +// ; +// sv = sve.toStringVector(); +// if (proc.isEq) +// { +// sv = Parenthesize(sv); +// } +// ; +// /***************************************************** +// * Call to Parenthesize added by LL on 27 Feb 2008. * +// * See bug_08-02-18. * +// *****************************************************/ +// int col2 = is.length(); +// is.append((String) sv.elementAt(0)); +// for (int v = 1; v < sv.size(); v++) +// { +// tlacode.addElement(is.toString()); +// is = new StringBuffer(NSpaces(col2)); +// is.append((String) sv.elementAt(v)); +// } +// if (!proc.isEq) +// is.append("]"); +// tlacode.addElement(is.toString()); +// is = new StringBuffer(NSpaces(col)); +// end of section to be commented out + } // end of for p loop. + } + } + + /* stack initial value */ + if (procs != null && procs.size() > 0) + { + if (mp) + is.append("/\\ stack = [self \\in ProcSet |-> << >>]"); + else + is.append("/\\ stack = << >>"); +// tlacode.addElement(is.toString()); + addOneLineOfTLA(is.toString()); + is = new StringBuffer(NSpaces(col)); + } + /* pc initial value */ + if (! ParseAlgorithm.omitPC) { + if (mp) + { + // On 4 May 2012, LL added useCase flag to inhibit adding of CASE for + // a single process or process set. + boolean useCase = st.processes.size() != 1; + if (useCase) { + is.append("/\\ pc = [self \\in ProcSet |-> CASE "); + } else { + is.append("/\\ pc = [self \\in ProcSet |-> "); + } + int colPC = is.length(); + if (boxUnderCASE) + colPC = colPC - 3; + for (int p = 0; p < st.processes.size(); p++) + { + PcalSymTab.ProcessEntry pe = (PcalSymTab.ProcessEntry) st.processes.elementAt(p); + if (useCase) { + is.append("self "); + if (pe.isEq) { + is.append("= "); + // int colExpr = is.length(); + addOneTokenToTLA(is.toString()); + addLeftParen(pe.id.getOrigin()); + addExprToTLA(pe.id); + addRightParen(pe.id.getOrigin()); + + // Vector sv = pe.id.toStringVector(); + // is.append((String) sv.elementAt(0)); + // for (int v = 1; v < sv.size(); v++) + // { + // addOneLineOfTLA(is.toString()); + // // tlacode.addElement(is.toString()); + // is = new StringBuffer(NSpaces(colExpr)); + // is.append((String) sv.elementAt(v)); + // } + } else { + is.append("\\in "); + // int colExpr = is.length(); + // Vector sv = pe.id.toStringVector(); + // is.append((String) sv.elementAt(0)); + // for (int v = 1; v < sv.size(); v++) + // { + // tlacode.addElement(is.toString()); + // is = new StringBuffer(NSpaces(colExpr)); + // is.append((String) sv.elementAt(v)); + // } + addOneTokenToTLA(is.toString()); + addLeftParen(pe.id.getOrigin()); + addExprToTLA(pe.id); + addRightParen(pe.id.getOrigin()); + + } + // is.append(" -> \""); + is = new StringBuffer(" -> \""); + is.append(pe.iPC); + if (p == st.processes.size() - 1) + is.append("\"]"); + else if (!boxUnderCASE) + is.append("\" []"); + else + is.append("\""); + } // end if (useCase) + else { + is.append("\"" + pe.iPC + "\"]"); + } +// tlacode.addElement(is.toString()); + addOneTokenToTLA(is.toString()); + endCurrentLineOfTLA(); + is = new StringBuffer(NSpaces(colPC)); + if (boxUnderCASE && p < st.processes.size() - 1) + is.append("[] "); + } + } else + { + is.append("/\\ pc = \"" + st.iPC + "\""); +// tlacode.addElement(is.toString()); + addOneLineOfTLA(is.toString()); + } + } +// tlacode.addElement(""); + addOneLineOfTLA(""); + } + + /************************************/ + /* Generate the Next == definition. */ + /************************************/ + private void GenNext() + { + // It we are omitting pc and this is a uniprocess + // algorithm, then the definition of Next has + // already been added. + if (ParseAlgorithm.omitPC && !mp) { + return; + } + Vector nextS = new Vector(); + StringBuffer sb = new StringBuffer(); + int max, col; + + if (! (PcalParams.NoDoneDisjunct || ParseAlgorithm.omitStutteringWhenDone)) + { +// tlacode.addElement(sb.toString()); + sb.append("(* Allow infinite stuttering to prevent deadlock on termination. *)"); + addOneLineOfTLA(sb.toString()); + + sb = new StringBuffer("Terminating == "); + if (mp) { + /************************************************************ + * Bug fix by LL on 6 Sep 2007. Added parentheses to * + * change * + * * + * (*) \A self \in ProcSet: ... /\ UNCHANGED vars * + * * + * to * + * * + * (**) (\A self \in ProcSet: ...) /\ UNCHANGED vars * + * * + * thus moving the UNCHANGED vars outside the quantifier. * + * Since self does not appear in UNCHANGED vars, the two * + * expressions are equivalent except when ProcSet is the * + * empty set, in which case (*) equals TRUE and (**) equals * + * UNCHANGED vars. * + ************************************************************/ + /************************************************************ + * Changed by MK on 19 Jun 2019 into a conjunct list * + * after modifying GenNext to generate an explicit * + * Terminating action before Next instead of the old * + * implicit disjunct-to-prevent-deadlock-on-termination. * + * This change also entailed to copy this if-block from the * + * end of GenNext here modifying the original one to * + * generate a call to Terminating. * + * * + * The rational for this change results from the recently * + * introduced TLC profiler. The profiler reports the number * + * of distinct successor states per action. * + * A terminating PlusCal algorithm has the implicit * + * disjunct-to-prevent-deadlock-on-termination sub-action of * + * the Next next-state action. Since the sub-action has no * + * identifier, the profiler has to report Next as generating * + * no successor states (which is bogus). With this change, * + * the profiler will report the Terminating sub-action to * + * generate no (distinct) successor states instead, which * + * is perfectly correct and easy to understand. * + ************************************************************/ + sb.append("/\\ \\A self \\in ProcSet: pc[self] = \"Done\""); + addOneLineOfTLA(sb.toString()); + sb = new StringBuffer(NSpaces("Terminating == ".length())); + sb.append("/\\ UNCHANGED vars"); + } else { + sb.append("pc = \"Done\" /\\ UNCHANGED vars"); +// tlacode.addElement(sb.toString()); + } + addOneLineOfTLA(sb.toString()); + addOneLineOfTLA(""); + } ; + sb = new StringBuffer(); + + // Steps with no parameter + max = wrapColumn - ("Next == \\/ ".length()); + for (int i = 0; i < nextStep.size(); i++) + { + String a = (String) nextStep.elementAt(i); + if (a.length() + " \\/ ".length() + sb.length() > max) + { + nextS.addElement(sb.toString()); + sb = new StringBuffer(); + } + if (sb.length() > 0) + sb.append(" \\/ "); + sb.append(a); + } + if (sb.length() > 0) + nextS.addElement(sb.toString()); + + // Steps with (self) from ProcSet + // These are procedures in a multiprocess algorithm + Vector nextSS = new Vector(); + String nextSSstart = "(\\E self \\in ProcSet: "; + sb = new StringBuffer(); + max = wrapColumn - ("Next == \\/ (\\E self \\in ProcSet: \\/ ".length()); + if (mp && st.procs.size() > 0) + { + for (int i = 0; i < st.procs.size(); i++) + { + PcalSymTab.ProcedureEntry p = (PcalSymTab.ProcedureEntry) st.procs.elementAt(i); + if ((p.name.length() + "(self) \\/ ".length() + sb.length()) > max) + { + nextSS.addElement(sb.toString()); + sb = new StringBuffer(); + } + if (sb.length() > 0) + sb.append(" \\/ "); + sb.append(p.name); + sb.append("(self)"); + } + if (sb.length() > 0) + nextSS.addElement(sb.toString() + ")"); + } + + // Steps with (self) from a set + // These are process sets + Vector nextSSP = new Vector(); // of Vector + if (mp && st.processes.size() > 0) + for (int i = 0; i < st.processes.size(); i++) + { + PcalSymTab.ProcessEntry p = (PcalSymTab.ProcessEntry) st.processes.elementAt(i); + if (p.isEq) + continue; + Vector vec = new Vector(); + sb = new StringBuffer(); + sb.append("(\\E self \\in "); + Vector sv = p.id.toStringVector(); + col = sb.length(); + sb.append((String) sv.elementAt(0)); + for (int j = 1; j < sv.size(); j++) + { + vec.addElement(sb.toString()); + sb = new StringBuffer(NSpaces(col)); + sb.append((String) sv.elementAt(j)); + } + sb.append(": "); + sb.append(p.name); + sb.append("(self))"); + vec.addElement(sb.toString()); + nextSSP.addElement(vec); + } + + // assemble the line from the pieces + sb = new StringBuffer("Next == "); + col = sb.length() + 2; + for (int i = 0; i < nextS.size(); i++) + { + sb.append((String) nextS.elementAt(i)); + addOneLineOfTLA(sb.toString()); +// tlacode.addElement(sb.toString()); + sb = new StringBuffer(NSpaces(col) + " \\/ "); + } + if (nextSS.size() > 0) + { + sb.append(nextSSstart); + int col2 = sb.length(); + if (nextSS.size() > 1) + sb.append(" \\/ "); + for (int i = 0; i < nextSS.size(); i++) + { + sb.append((String) nextSS.elementAt(i)); + addOneLineOfTLA(sb.toString()); +// tlacode.addElement(sb.toString()); + sb = new StringBuffer(NSpaces(col2) + " \\/ "); + } + sb = new StringBuffer(NSpaces(col) + " \\/ "); + } + if (nextSSP.size() > 0) + for (int i = 0; i < nextSSP.size(); i++) + { + Vector v = (Vector) nextSSP.elementAt(i); + for (int j = 0; j < v.size(); j++) + { + String line = (String) v.elementAt(j); + sb.append(line); + addOneLineOfTLA(sb.toString()); +// tlacode.addElement(sb.toString()); + + // The following if case was added by LL on 22 Jan 2011 + // to correct part 1 of bug bug_11_01_13. This bug occurs + // when an "\in" process's set is multi-line and that + // process's next-state action comes immediately after + // the Next == ..., with no " \/ " preceding it. To fix the + // problem, we must add 6 fewer spaces to all lines after + // the first in that process's set than in other such sets. + if ((nextS.size() == 0) && (nextSS.size() == 0) && (i == 0)) { + sb = new StringBuffer(NSpaces(col - 2)); + } else { + sb = new StringBuffer(NSpaces(col + 4)); + } + } + sb = new StringBuffer(NSpaces(col) + " \\/ "); + } + if (! (PcalParams.NoDoneDisjunct || ParseAlgorithm.omitStutteringWhenDone)) + { + addOneLineOfTLA(sb.append("Terminating").toString()); + } + addOneLineOfTLA(""); +// tlacode.addElement(""); + } + + /****************************************/ + /* Generate the Spec == ... definition. */ + /****************************************/ + /*********************************************************************** + * The spec can contain the following conjuncts * + * * + * 1. Init /\ [][Next]_vars * + * Always present * + * * + * 2. WF_var(Next) * + * present if (a) The wfNext option is specified, or * + * (b) It is a uniprocess algorithm and one of the * + * options -wf, -sf, -termination is specified. * + * * + * 3. A sequence of process fairness formulas, containing * + * one for each process for which there is a fairness condition. * + * * + * A process has * + * (a) a WF fairness condition iff * + * (i) it is preceded by the keyword "fair" and the -nof * + * option is not specified, or * + * (ii) the -wf option is specified. * + * (b) an SF fairness condition iff it is not preceded * + * by the keyword "fair" and the -sf option is specified. * + * * + * Let P be a process specified by either * + * * + * [fair] process (P = exp) ... * + * [fair] process (P \in exp) ... * + * * + * In the first case we say that P is a single process, in the second * + * that it is a process set. Let * + * * + * - p_1, ... , p_Np be the labels of P modified by "+" * + * * + * - m_1, ... , m_Nm be the set of labels of P modified by "-" * + * * + * - pSelf = IF P is a single process THEN "pname" * + * ELSE "self" * + * * + * - qSelf = IF /\ P is a single process * + * /\ pSelf a multi-line formula * + * THEN "self" * + * ELSE pSelf * + * * + * - pName = IF P is a single process THEN "P" * + * ELSE "P(self)" * + * * + * A process fairness formula is described by the following: * + * * + * XF: * + * either WF or SF depending on P's fairness condition * + * * + * prefix: * + * IF P is a single process * + * THEN IF pSelf a multi-line formula * + * THEN "LET self = pSelf IN" * + * ELSE "" * + * ELSE "\A self \in exp" * + * * + * wfFormula: * + * "XF( (pc[qSelf] \notin {"m_1", ... , "m_Np"}) /\ pName )" * + * * + * sfFormula: * + * if XF = SF * + * then null * + * else if P a single process * + * then "SF_vars(p_1) /\ ... /\ SF_vars(p_Np)" * + * else "SF_vars(p_1(self)) /\ ... * + * /\ SF_vars(p_Np(self))" * + * * + * prcdFormulas: * + * A sequence consisting of the following two formulas for each * + * procedure D called within P. Let * + * * + * - pd_1, ... , pd_Npd be the labels of D modified by "+" * + * - md_1, ... , md_Nmd be the labels of D modified by "-" * + * * + * in the formulas: * + * * + * wfFormula: * + * "XF( (pc[qSelf] \notin {"md_1", ... , "md_Nmd"}) * + * /\ D(qSelf) )" * + * * + * sfFormula: * + * if XF = SF * + * then null * + * else "SF_vars(pd_1(qSelf)) /\ ... * + * /\ SF_vars(pd_Npd(qSelf))" * + * * + * ------- * + * * + * If there is at least one fairness formula, the definition of Spec * + * will be formatted in one of two ways. If there is either a * + * WF_vars(Next) condition or a process fairness formula, then it may * + * be formatted as: * + * * + * Spec == Init /\ [][Next]_vars * + * * + * otherwise, it will be formatted as * + * * + * Spec == /\ Init /\ [][Next]_vars * + * [/\ WF_vars(Next)] * + * /\ F_1 * + * ... * + * /\ F_n * + * * + * where each F_i is a process fairness formulas. * + ***********************************************************************/ + private void GenSpec() + { String safetyFormula = "Init /\\ [][Next]_vars" ; + + if ( PcalParams.FairnessOption.equals("nof") + || (!mp && PcalParams.FairnessOption.equals(""))) { + addOneLineOfTLA("Spec == " + safetyFormula); + addOneLineOfTLA(""); +// tlacode.addElement("Spec == " + safetyFormula ); +// tlacode.addElement(""); + return; + } +//System.out.println("foo |-> " + st.UseThis(PcalSymTab.PROCEDURE, "foo", "")); +//int to = st.FindProc("foo"); +//PcalSymTab.ProcedureEntry pe = +// (PcalSymTab.ProcedureEntry) st.procs.elementAt(to); +//AST.Procedure procAst = pe.ast; + + StringBuffer sb = new StringBuffer("Spec == "); + // Generate the requested fairness conjuncts + + // wfNextConj is either null or " /\ WF_(Next)" + String wfNextConj = null; + if ( PcalParams.FairnessOption.equals("wfNext") + || PcalParams.FairAlgorithm + || (!mp && ( PcalParams.FairnessOption.equals("wf") + || PcalParams.FairnessOption.equals("sf")))) + { + // If uniprocess then wf and sf are the same as wfNext + wfNextConj = " /\\ WF_vars(Next)"; + } + + // Now compute procFairnessFormulas to equal the processes' fairness + // formulas, which is never null but may have zero length. + Vector procFairnessFormulas = new Vector() ; + if (mp) { + for (int i = 0; i < st.processes.size(); i++) { + PcalSymTab.ProcessEntry p = (PcalSymTab.ProcessEntry) st.processes.elementAt(i); + AST.Process pAst = p.ast ; + int fairness = pAst.fairness; + if (fairness != AST.UNFAIR_PROC) { + String xf = (fairness == AST.WF_PROC) ? "WF" : "SF"; + + Vector pSelf = p.id.toStringVector(); + + // makeLetIn is true iff prefix will be LET self == ... IN + boolean makeLetIn = false ; + + String qSelf = "self"; + if (p.isEq) { + if (pSelf.size() > 1) { + makeLetIn = true ; + } else { + qSelf = (String) pSelf.elementAt(0); + } + } + + Vector prefix = new Vector(); + if (makeLetIn || !p.isEq) { + int prefixSize = pSelf.size(); + String prefixBegin; + String prefixEnd; + if (p.isEq) { + prefixBegin = "LET self == "; + prefixEnd = ""; + } else { + prefixBegin = "\\A self \\in "; + prefixEnd = " : "; + } + String padding = NSpaces(prefixBegin.length()); + for (int j = 0; j < prefixSize; j++) { + String line = (String) pSelf.elementAt(j); + if (j == 0) { + line = prefixBegin + line; + } else { + line = padding + line; + } + if (j == prefixSize - 1) { + line = line + prefixEnd; + } + prefix.addElement(line); + } + if (makeLetIn) { + prefix.addElement("IN "); + } + } // end if (makeLetIn || !p.isEq) + + StringBuffer wfSB = new StringBuffer(xf + "_vars("); + if (pAst.minusLabels != null && pAst.minusLabels.size() > 0) { + wfSB.append("(pc["); + wfSB.append(qSelf); + if (pAst.minusLabels.size() == 1) { + wfSB.append("] # \""); + wfSB.append(pAst.minusLabels.elementAt(0)); + wfSB.append("\""); + } else { + wfSB.append("] \\notin {\""); + for (int j = 0; j < pAst.minusLabels.size(); j++) { + wfSB.append(pAst.minusLabels.elementAt(j)); + if (j == pAst.minusLabels.size() - 1) { + wfSB.append("\"}"); + } else { + wfSB.append("\", \""); + } + } + } + wfSB.append(") /\\ "); + } + + String pName = p.name; + if (!p.isEq) { + pName = p.name + "(self)"; + } + wfSB.append(pName); + wfSB.append(")"); + + StringBuffer sfSB = null ; + if ( xf.equals("WF") + && (pAst.plusLabels != null) + && (pAst.plusLabels.size() != 0)) { + sfSB = new StringBuffer() ; + for (int j = 0; j < pAst.plusLabels.size(); j++) { + if (j != 0) { + sfSB.append(" /\\ "); + } + sfSB.append("SF_vars("); + sfSB.append(pAst.plusLabels.elementAt(j)); + if (!p.isEq) { + sfSB.append("(self)"); + } + sfSB.append(")"); + } + } + + Vector prcdFormulas = new Vector(); + Vector procedures = pAst.proceduresCalled; + for (int k = 0; k < procedures.size(); k++) { + String originalName = (String) procedures.elementAt(k); + String name = st.UseThis(PcalSymTab.PROCEDURE, originalName, ""); + int procedureIndex = st.FindProc(name); + PcalSymTab.ProcedureEntry pe = + (PcalSymTab.ProcedureEntry) st.procs.elementAt(procedureIndex); + AST.Procedure prcAst = pe.ast; + + StringBuffer wfPrcSB = new StringBuffer(xf + "_vars("); + if (prcAst.minusLabels != null && prcAst.minusLabels.size() > 0) { + wfPrcSB.append("(pc["); + wfPrcSB.append(qSelf); + if (prcAst.minusLabels.size() == 1) { + wfPrcSB.append("] # \""); + wfPrcSB.append(prcAst.minusLabels.elementAt(0)); + wfPrcSB.append("\""); + } else { + wfPrcSB.append("] \\notin {\""); + for (int j = 0; j < prcAst.minusLabels.size(); j++) { + wfPrcSB.append(prcAst.minusLabels.elementAt(j)); + if (j == prcAst.minusLabels.size() - 1) { + wfPrcSB.append("\"}"); + } else { + wfPrcSB.append("\", \""); + } + } + } + wfPrcSB.append(") /\\ "); + } + + String prcName = pe.name + "(" + qSelf + ")"; + wfPrcSB.append(prcName); + wfPrcSB.append(")"); + + StringBuffer sfPrcSB = null; + if ( xf.equals("WF") + && (prcAst.plusLabels != null) + && (prcAst.plusLabels.size() != 0)) { + sfPrcSB = new StringBuffer() ; + for (int j = 0; j < prcAst.plusLabels.size(); j++) { + if (j != 0) { + sfPrcSB.append(" /\\ "); + } + sfPrcSB.append("SF_vars("); + sfPrcSB.append(prcAst.plusLabels.elementAt(j)); + sfPrcSB.append("(" + qSelf + ")") ; + sfPrcSB.append(")"); + } + } + prcdFormulas.addElement( + new FormulaPair( + wfPrcSB.toString(), + (sfPrcSB == null) ? null : sfPrcSB.toString()) + ) ; + } // end construction of prcdFormulas + + procFairnessFormulas.addElement( + new ProcessFairness( + xf, + prefix, + wfSB.toString(), + (sfSB == null) ? null : sfSB.toString(), + prcdFormulas) + ) ; + } // end if (fairness != AST.UNFAIR_PROC) + } + } // ends construction of procFairnessFormulas + + if (wfNextConj == null && procFairnessFormulas.size() == 0) { + addOneLineOfTLA("Spec == " + safetyFormula); + addOneLineOfTLA(""); +// tlacode.addElement("Spec == " + safetyFormula); +// tlacode.addElement(""); + return; + } + addOneLineOfTLA("Spec == /\\ " + safetyFormula); +// tlacode.addElement("Spec == /\\ " + safetyFormula) ; + int indent = "Spec == /\\ ".length(); + + if (wfNextConj != null) { + addOneLineOfTLA(" /\\ WF_vars(Next)"); +// tlacode.addElement(" /\\ WF_vars(Next)"); + } + for (int i = 0; i < procFairnessFormulas.size(); i++) { + /* + * The original code called format on the fairness formula, which can + * create a string with \n characters embedded. I've just split the + * string into its individual lines and added them to tlacode one at a time. + * However, the current and original code can both produce very long lines + * that could be wrapped. So if this change does make a difference, then + * it would be better to completely rewrite the format method (which is only + * called here). + */ + String str = + " /\\ " + + ((ProcessFairness) procFairnessFormulas.elementAt(i)).format(indent).toString(); + String [] splitStr = str.split("\n"); + for (int j = 0; j < splitStr.length; j++) { + addOneLineOfTLA(splitStr[j]); + } + +// tlacode.addElement( +// " /\\ " + +// ((ProcessFairness) procFairnessFormulas.elementAt(i)).format(indent) +// ); + } + addOneLineOfTLA(""); +// tlacode.addElement(""); + return; + } + + /************************************/ + /* Generate the Termination == */ + /************************************/ + private void GenTermination() + { + // if we're omitting the pc or omitting the stuttering-when-done + // clause of the Next action, then we shouldn't + // generate the Termination definition. + // Check of omitStutteringWhenDone added by LL on 30 Mar 2012. + if (ParseAlgorithm.omitPC || ParseAlgorithm.omitStutteringWhenDone) { + return; + } + StringBuffer sb = new StringBuffer(); + sb.append("Termination == <>("); + if (mp) + sb.append("\\A self \\in ProcSet: pc[self]"); + else + sb.append("pc"); + sb.append(" = \"Done\")"); + addOneLineOfTLA(sb.toString()); + addOneLineOfTLA(""); +// tlacode.addElement(sb.toString()); +// tlacode.addElement(""); + } + + /**********************************************************/ + /* For variables that need subscripts, add the subscript. */ + /* These are pc, stack, procedure variables, procedure */ + /* parameters, and variables defined in process sets. */ + /* Then, add primes to variables that have been changed */ + /* according to c. */ + /* exprn : the original expression. */ + /* sub : the subscript to be added (null if none) */ + /* c : the variables that have been changed (so need to */ + /* be primed. */ + /**********************************************************/ + private TLAExpr AddSubscriptsToExpr(TLAExpr exprn, TLAExpr sub, Changed c) throws PcalTLAGenException + { + /* + * For testing, throw a null pointer exception if the begin/end substitution + * mapping vectors are not properly matching in the returned expression + */ +// int[] depths = new int[1000]; + + int parenDepth = 0; + for (int i = 0; i < exprn.tokens.size(); i++) { + Vector line = (Vector) exprn.tokens.elementAt(i); + for (int j = 0; j < line.size(); j++) { + TLAToken tok = (TLAToken) line.elementAt(j); + parenDepth = parenDepth + tok.getBeginSubst().size() - tok.getEndSubst().size(); + if (parenDepth < 0) { + throw new NullPointerException("argument: begin/end Subst depth negative"); + } + } +// depths[i] = parenDepth; + } + if (parenDepth != 0) { + throw new NullPointerException("argument: Unmatched begin Subst"); + } + /* ------------------ end testing --------------------------*/ + + /* + * We now set stringVec to the sequence of identifiers that occur in exprn + * for which we need to add a subscript or a prime. + */ + Vector exprVec = new Vector(); // the substituting exprs + Vector stringVec = new Vector(); // the substituted ids + TLAExpr expr = exprn.cloneAndNormalize(); // the expression to be returned + + for (int i = 0; i < expr.tokens.size(); i++) + { + Vector tv = (Vector) expr.tokens.elementAt(i); + for (int j = 0; j < tv.size(); j++) + { + TLAToken tok = (TLAToken) tv.elementAt(j); + boolean prime = ((tok.type == TLAToken.IDENT) && c.IsChanged(tok.string)); + boolean subr = (sub != null && (tok.type == TLAToken.ADDED || (mp && (tok.type == TLAToken.IDENT) && (IsProcedureVar(tok.string) || IsProcessSetVar(tok.string))))); + if ((subr || prime) && !InVector(tok.string, stringVec)) + { + stringVec.addElement(tok.string); + TLAExpr exp = new TLAExpr(); + exp.addLine(); + /* + * 15 Dec 2011: The following code added to replace the + * + * exp.addToken(new TLAToken(tok.string, 0, TLAToken.IDENT)); + * + * that is commented out. Note that this change can add a token with + * type ADDED rather than IDENT. I don't think this matters. + */ + TLAToken newTok = tok.Clone() ; + /* + * The new token should inherit nothing from the baggage of tok, whose + * only function is to provide the name + */ + newTok.setBeginSubst(new Vector(2)); + newTok.setEndSubst(new Vector(2)); + newTok.source = null; + newTok.column = 0; + exp.addToken(newTok) ; + // exp.addToken(new TLAToken(tok.string, 0, TLAToken.IDENT)); + if (prime) { + /***************************************************** + * Modified by LL on 30 Aug 2007. The following * + * call to addTokenOffset was originally a call to * + * addToken. See the comments for * + * TLAExpr.addTokenOffset(). * + *****************************************************/ + TLAToken primeTok = new TLAToken("'", 0, TLAToken.BUILTIN, true); + // The following stuff added by LL in Dec 2011 is bogus. The + // token tok is just the first one of many in the exprn with + // the same name for which we want to substitute. The only + // useful data in the token tok is its string. + // + // if (tok.source != null) { + // primeTok.source = + // new Region(tok.source.getEnd(), tok.source.getEnd()); + // } + // if (!subr) { + // primeTok.setEndSubst(tok.getEndSubst()); + // newTok.setEndSubst(new Vector(2)); + // } + exp.addTokenOffset(primeTok, 0); + } + if (subr) + { + TLAExpr subexp = sub.cloneAndNormalize(); + + /* + * We now add the end of the origin of tok to beginSubst + * of the first token of subexp and to endSubst of the + * last token of subexp. This indicates that PCal code + * corresponding to a region of the TLA+ translation that + * includes part of the added subscript and part of the + * original expression is a portion of the source of + * exprn. + */ + // This is bogus, because we are adding the location of where + // the identifier first occurs in exprn to the substitution + // vectors of the expression that's going to be substituted in + // all instances. + // + // if (tok.source != null) { + // PCalLocation endOfTok = tok.source.getEnd(); + // subexp.firstToken().getBeginSubst().add(endOfTok); + // subexp.lastToken().getEndSubst().add(endOfTok); + // } + /* + * However, we do have to move the token's beginSubst and endSubst vectors + * to the first and lasts token of the subscript. Since the + * resulting Parens that they generate should be outside + * the ones generated by the endSubst vectors of the expression, + * we have to add them before that expression's Subst vectors. + * + * No, no! This tok is just giving us the name of the tok that + * we're going to be substituting for in the expression. It is not + * necessarily the one that we're going to substitute for. + */ + // newTok.getEndSubst().addAll(subexp.lastToken().getEndSubst()); + // subexp.lastToken().setEndSubst(newTok.getEndSubst()); + // newTok.setEndSubst(new Vector(2)); + exp.normalize(); + try + { + subexp.prepend(exp, 0); + } catch (TLAExprException e) + { + throw new PcalTLAGenException(e.getMessage()); + } + exp = subexp; + } + /********************************************************** + * Modified by LL on 31 Jan 2006 to comment out the call * + * of MakeExprPretty, since it totally screwed up the * + * formatting when substituting any string containing * + * spaces or multiple lines for a variable. * + **********************************************************/ + // MakeExprPretty(exp); + exprVec.addElement(exp); + } + } + } + if (exprVec.size() > 0) + try + { + expr.substituteForAll(exprVec, stringVec, false); + } catch (TLAExprException e) + { + throw new PcalTLAGenException(e.getMessage()); + } + /* + * For testing, throw a null pointer exception if the begin/end substitution + * mapping vectors are not properly matching in the returned expression + */ +// depths = new int[1000]; + + parenDepth = 0; + for (int i = 0; i < expr.tokens.size(); i++) { + Vector line = (Vector) expr.tokens.elementAt(i); + for (int j = 0; j < line.size(); j++) { + TLAToken tok = (TLAToken) line.elementAt(j); + parenDepth = parenDepth + tok.getBeginSubst().size() - tok.getEndSubst().size(); + if (parenDepth < 0) { + throw new NullPointerException("result: begin/end Subst depth negative"); + } + } +// depths[i] = parenDepth; + } + if (parenDepth != 0) { + throw new NullPointerException("result: Unmatched begin/subst"); + } + /* ------------------ end testing --------------------------*/ + return expr; + } + + /*********************************************************************** + * Given an expression, makes it into a subscript expr. It is called * + * only with argument Self(context), which means that it is called * + * only for a single subscript. * + * * + * If string is null, then returns null. * + * * + * Since this is used only to add "[self]", there is no need to add * + * any REPLACEMENT tokens to the expression. Moreover, the added * + * subscript's tokens have a null source field because they don't come * + * from any PCal code. The new expression is given the same origin as * + * the original one. * + ***********************************************************************/ + private static TLAExpr SubExpr(TLAExpr sub) + { + if (sub != null) + { + TLAExpr expr = sub.cloneAndNormalize(); + // This preserves the origin of the expression + for (int i = 0; i < expr.tokens.size(); i++) { + Vector tokenVec = (Vector) expr.tokens.elementAt(i); + for (int j = 0; j < tokenVec.size(); j++) { + TLAToken tok = (TLAToken) tokenVec.elementAt(j); + tok.column = tok.column + 1; + } + if (i == 0) { + /* + * Set isAppended field of the "[" and "]" to true iff the first + * token of sub has isAppended field true, which should be the + * case iff it is the "self" token. + */ + tokenVec.insertElementAt( + new TLAToken("[", 0, TLAToken.BUILTIN, sub.firstToken().isAppended()), + 0); + } + } + expr.addTokenOffset( + new TLAToken("]", 0, TLAToken.BUILTIN, sub.firstToken().isAppended()), 0); + return expr; + } else { + return null; + } + } + + /*********************************************************/ + /* Gives the string to use when subscripting a variable. */ + /*********************************************************/ + // LL comment: it appears that PcalTLAGen.self is the + // current process id if this is being called in the context + // of a process declared with `process (P = id)'. + private TLAExpr Self(String context) + { + TLAExpr s = null; + if (mp) + { + if (context.equals("procedure")) + s = selfAsExpr(); + else + s = self; + } + return s; + } + + private static TLAExpr selfAsExpr() { + /* + * This is a token that does not correspond to anything in the + * PCal code, so it should have a null source field. + * It has a true isAppended field because it is always appended + * as a subscript to a variable. + */ + TLAToken selfToken = new TLAToken("self", 0, TLAToken.IDENT, true); + Vector tokenVec = new Vector(); + tokenVec.addElement(selfToken); + Vector tokens = new Vector(); + tokens.addElement(tokenVec); + TLAExpr expr = new TLAExpr(tokens); +// expr.anchorTokens = new TLAToken[1]; +// expr.anchorTokens[0] = selfToken; +// expr.anchorTokCol = new int[1]; +// expr.anchorTokCol[0] = 0; + expr.normalize(); + return expr ; + } + /*********************************************************************** + * Comment added by LL: MakeExprPretty should never be called on an * + * expression any part of which was an expression in the input. * + * Fortunately, it is now called only for the expression "[self]", so * + * it is effectively a no-op. * + * Comment added 15 Dec 2011: In fact, it's not called at all. * + ***********************************************************************/ + public static void ObsoleteMakeExprPretty(TLAExpr expr) + { + /********************************************************************* + * Sets columns so this expr looks nice and tight. * + *********************************************************************/ + Vector line; /* Vector of TLAToken */ + boolean spread; + int nextCol = 1; + for (int i = 0; i < expr.tokens.size(); i++) + { + line = (Vector) expr.tokens.elementAt(i); + for (int j = 0; j < line.size(); j++) + { + TLAToken tok = ((TLAToken) line.elementAt(j)); + spread = tok.string.equals("="); + tok.column = nextCol + ((spread) ? 1 : 0); + nextCol = nextCol + tok.getWidth() + ((spread) ? 2 : 0); + } + } + } + + /***********************************************************/ + /* v is a sequence of SingleAssign. Return a vector of the */ + /* same SingleAssign, but sorted in terms of the lhs.var. */ + /***********************************************************/ + private static Vector SortSass(Vector vec) + { + Vector v = (Vector) vec.clone(); + Vector r = new Vector(); // The sorted version of v. + while (v.size() > 0) + { // Good old n^2 insertion sort. + AST.SingleAssign candidate = (AST.SingleAssign) v.elementAt(0); + int indexC = 0; + for (int i = 1; i < v.size(); i++) + { + AST.SingleAssign sass = (AST.SingleAssign) v.elementAt(i); + if (candidate.lhs.var.compareTo(sass.lhs.var) > 0) + { + indexC = i; + candidate = sass; + } + } + r.addElement(candidate); + v.remove(indexC); + } + return r; + } + + /*********************************************************************** + * If vec is a StringVector representing an expression, then this * + * returns the StringVector obtained by parenthesizing the expression * + * if it may need parenthesizing. This is used only to prevent * + * parsing errors when the expression appears immediately to the right * + * of an "=" in the spec. This is a rare situation, so it would be * + * nice to add the parentheses only if really necessary. For now, the * + * parentheses are added if one of the following tokens occur outside * + * parentheses and not inside a string: * + * * + * = * + * # * + * < not followed by < * + * > not followed by > or preceded by = * + * | preceded or followed by - * + * \ not followed by "o" or "X". * + * / followed by "\" * + * * + * Left parentheses are * + * * + * ( [ { << * + * * + * The handling of "\" is a simplifying hack. Lots of operators * + * beginning with "\" like "\/", "\gg" and "\subseteq" have precedence * + * greater than or equal to "=". The only commonly used ones with * + * precedence lower than "=" seem to be "\o" and "\X". It doesn't * + * seem to be worth the bother of checking for the others just to * + * avoid unnecessarily adding the parentheses when those other rare * + * operators are used. * + * * + * Perhaps the one improvement that might be worth making in this * + * procedure is to have it not add parentheses because of "dangerous" * + * operations in an IF clause--for example: * + * * + * IF x < 0 THEN ... * + * * + * This would require considering "IF" to be a left parenthesis and * + * "THEN" to be a right parenthesis. However, that's not trivial to * + * implement because of unlikely things like * + * * + * IFx := 42 ; * + * x := IFx < THENx * + ***********************************************************************/ + private static Vector Parenthesize(Vector vec) + { + /********************************************************************* + * Add the parentheses if necessary. * + *********************************************************************/ + if (NeedsParentheses(vec)) + { + vec.setElementAt("(" + ((String) vec.elementAt(0)), 0); + for (int i = 1; i < vec.size(); i++) + { + vec.setElementAt(" " + ((String) vec.elementAt(i)), i); + } + ; + int curLineNum = vec.size() - 1; + vec.setElementAt(((String) vec.elementAt(curLineNum)) + ")", curLineNum); + } + ; + return vec; + } + + /** + * As part of adding the TLA to PCal translation code, LL removedseparated + * the code that decides if parentheses are needed from the Parenthesize + * method and put it into this method. The Parenthesize method itself + * will not be needed. + */ + public static boolean NeedsParentheses(Vector vec) { + if (vec.size() == 0) + { + return false; + } + ; + /******************************************************************* + * vec shouldn't be empty, but let's not worry about what to do if * + * it is. * + *******************************************************************/ + int curCharNum = 0; + int curLineNum = 0; + int parenDepth = 0; + boolean inString = false; + boolean needParen = false; + while ((curLineNum < vec.size()) && (!needParen)) + { + String curLine = (String) vec.elementAt(0); + while ((curCharNum < curLine.length()) && (!needParen)) + { + char curChar = curLine.charAt(curCharNum); + + if (inString) + { + switch (curChar) { + case '\"': + inString = false; + break; + case '\\': + curCharNum++; + break; + } + ; // end switch + } // end if (inString) + else + { + boolean leftParen = false; + boolean rightParen = false; + boolean mayNeedParen = false; + /*************************************************************** + * Set nextChar to the next character on the line, or ' ' if * + * there is none. * + ***************************************************************/ + char nextChar = ' '; + if (curCharNum < curLine.length() - 1) + { + nextChar = curLine.charAt(curCharNum + 1); + } + switch (curChar) { + case '\"': + inString = true; + break; + case '=': + mayNeedParen = true; + break; + case '#': + mayNeedParen = true; + break; + case '<': + if (nextChar == '<') + { + curCharNum++; + leftParen = true; + } else + { + mayNeedParen = true; + } + ; + break; + case '>': + if (nextChar == '>') + { + curCharNum++; + rightParen = true; + } else + { + mayNeedParen = true; + } + ; + break; + case '|': + if ((nextChar == '-') || ((curCharNum > 0) && (curLine.charAt(curCharNum - 1) == '-'))) + { + mayNeedParen = true; + } + ; + break; + case '\\': + if (!((nextChar == ' ') || (nextChar == 'o') || (nextChar == 'X'))) + { + mayNeedParen = true; + } + ; + break; + case '/': + if (nextChar == '\\') + { + mayNeedParen = true; + } + ; + break; + case '(': + case '[': + case '{': + leftParen = true; + break; + case ')': + case ']': + case '}': + rightParen = true; + break; + } + ; + if (mayNeedParen && (parenDepth == 0)) + { + needParen = true; + } + ; + if (leftParen) + { + parenDepth++; + } + ; + if (rightParen) + { + if (parenDepth == 0) + { + needParen = true; + } + ; + parenDepth--; + } + } + ; // end else ! inString + curCharNum++; + } + ; // end while (curCharNum < curLine.length()) + + if (inString) + { + needParen = true; + } + ; + /***************************************************************** + * If there is an unmatched quote, we might as well stop here. * + *****************************************************************/ + curLineNum++; + curCharNum = 0; + } // end while (curLineNum < vec.size()) + + return needParen; + } + + /* + * The following methods are used to add code to tlacode and add the + * appropriate objects to mappingVector + */ + + /** + * Adds one token to tlacodeNextLine, and adds the appropriate + * Begin/EndTLAToken objects to mappingVectorNextLine. The + * ...TLAToken objects mark a region that excludes beginning and + * ending space characters of token--unless the token has + * only space characters. + * + * @param token + */ + private void addOneTokenToTLA(String token) { + String trimmedToken = token.trim() ; + + int numberOfLeftTrimmedTokens = + (trimmedToken.length() == 0) ? -1 : + token.indexOf(trimmedToken.charAt(0)); + + /** + * Handle a token of only space characters. + */ + if (numberOfLeftTrimmedTokens == -1) { + numberOfLeftTrimmedTokens = 0 ; + trimmedToken = token ; + } + + int objBegin = tlacodeNextLine.length() + numberOfLeftTrimmedTokens; + mappingVectorNextLine.addElement(new MappingObject.BeginTLAToken(objBegin)); + mappingVectorNextLine.addElement(new MappingObject.EndTLAToken(objBegin + trimmedToken.length())); + tlacodeNextLine = tlacodeNextLine + token; + } + + /** + * If region is non-null, then adds string str to the TLA output + * as a SourceToken object with that region. Otherwise, it adds + * it as a TLAToken, with Begin/EndTLAToken objects. + * + * @param str + * @param region + */ + private void addOneSourceTokenToTLA(String str, Region region) { + if (region == null) { + addOneTokenToTLA(str); + return; + } + + int beginCol = tlacodeNextLine.length(); + int endCol = beginCol + str.length(); + mappingVectorNextLine.addElement( + new MappingObject.SourceToken(beginCol, endCol, region)); + tlacodeNextLine = tlacodeNextLine + str; + } + /** + * Adds a complete line of TLA "code" that does not correspond to + * any PlusCal code. Adds Begin/EndTLAToken objects to the mapping + * iff the line does not equal "". + * + * @param line + */ + private void addOneLineOfTLA(String line) { +// temporarily commented out. + if(tlacode.size() != mappingVector.size()) { + PcalDebug.ReportBug("tlacode and mappingVector have different lengths") ; + } +// The following added during testing. +//if(tlacode.size() != mappingVector.size()) { +// System.out.println("tlacode and mappingVector have different lengths"); +//} + endCurrentLineOfTLA(); + if (line.length() == 0) { + mappingVector.addElement(new Vector(2)); + tlacode.addElement(""); + return; + } + addOneTokenToTLA(line); + endCurrentLineOfTLA(); + } + + /** + * If tlacodeNextLine does not equal "", then add it to tlacode + * and add mappingVectorNextLine to mappingVector. If it does equal "", + * don't add any lines, but add any potentially legal leftovers in + * mappingVectorNextLine to the previous line. + */ + private void endCurrentLineOfTLA() { + if (tlacodeNextLine.length() != 0) { + tlacode.addElement(tlacodeNextLine) ; + mappingVector.addElement(mappingVectorNextLine) ; + tlacodeNextLine = ""; + mappingVectorNextLine = new Vector() ; + } + else { + if (mappingVectorNextLine.size() != 0) { + /* + * There's something to go in the mappingVector that doesn't + * accompany any text. It should be one or more RightParen or + * LeftParen objects, (or perhaps, eventually a Break), in which + * case they should be put at the end of the previous mappingVector + * line. Anything else is a mistake. + */ + Vector lastLine = (Vector) mappingVector.elementAt(mappingVector.size()-1); + for (int i = 0; i < mappingVectorNextLine.size(); i++) { + MappingObject obj = (MappingObject) mappingVectorNextLine.elementAt(i); + if (obj.getType() == MappingObject.RIGHT_PAREN || + obj.getType() == MappingObject.LEFT_PAREN|| + obj.getType() == MappingObject.BREAK) { + lastLine.add(obj); + } + else { + PcalDebug.ReportBug("PcalTLAGen.endCurrentLineOfTLA found problem."); + } + mappingVectorNextLine = new Vector() ; + } + } + } + } + + /** + * Adds the expression to tlacode / tlacodeNextLine and its + * mapping to mappingVector / mappingVectorNextLine. It adds + * no space before the expression and leaves the last line of the + * expression (which could be its first line) at the end of + * tlacodeNextLine. + * @param expr + */ + private void addExprToTLA(TLAExpr expr) { + Vector sv = expr.toStringVector() ; + Vector exprMapping = expr.toMappingVector() ; + int indent = tlacodeNextLine.length() ; + int nextLine = 0 ; + if (indent != 0) { + /* + * Need to combine first line of expr with + * tlacodeNextLine. + */ + MappingObject.shiftMappingVector(exprMapping, indent); + tlacodeNextLine = tlacodeNextLine + ((String) sv.elementAt(0)); + mappingVectorNextLine.addAll((Vector) exprMapping.elementAt(0)); + nextLine = 1; + if (sv.size() > 1) { + endCurrentLineOfTLA(); + } + } + if (sv.size() > 1) { + String spaces = NSpaces(indent); + while (nextLine < sv.size()-1) { + tlacode.addElement(spaces + ((String) sv.elementAt(nextLine))); + mappingVector.addElement((Vector) exprMapping.elementAt(nextLine)); + nextLine++ ; + } + tlacodeNextLine = spaces + ((String) sv.elementAt(nextLine)) ; + mappingVectorNextLine = (Vector) exprMapping.elementAt(nextLine); + } + else if (indent == 0){ + /* + * If indent != 0, then we've already added the one-line expression. + */ + tlacodeNextLine = tlacodeNextLine + ((String) sv.elementAt(0)); + mappingVectorNextLine.addAll((Vector) exprMapping.elementAt(0)); + } + } + + /** + * Subroutine of GenInit that adds to the TLA translation the Init conjunct + * corresponding to the VarDecl decl for a global variable and, in a uniprocess + * algorithm for a procedure or process variable It is called with the + * StringBuffer `is' containing the text that precedes the "/\" of the + * conjunct, which will be "Init == " or just spaces. + * + * @param decl + * @param is + */ + private void addVarDeclToTLA(VarDecl decl, StringBuffer is) { + Region origin = decl.getOrigin(); + is.append("/\\ "); + addOneTokenToTLA(is.toString()); + addLeftParen(decl.getOrigin()); +// is.append(decl.var); + is = new StringBuffer(decl.var); + if (decl.isEq) + is.append(" = "); + else + is.append(" \\in "); +// int col2 = is.length(); +// Vector sv = Parenthesize(decl.val.toStringVector()); +// /********************************************************* +// * Call to Parenthesize added by LL on 27 Feb 2008. * +// * See bug_08-02-18. * +// *********************************************************/ +// is.append((String) sv.elementAt(0)); +// for (int v = 1; v < sv.size(); v++) +// { +// tlacode.addElement(is.toString()); +// is = new StringBuffer(NSpaces(col2)); +// is.append((String) sv.elementAt(v)); +// } +// tlacode.addElement(is.toString()); + addOneTokenToTLA(is.toString()); + addLeftParen(decl.val.getOrigin()); + boolean needsParens = NeedsParentheses(decl.val.toStringVector()); + if (needsParens) { + addOneTokenToTLA("("); + } + addExprToTLA(decl.val); + if (needsParens) { + addOneTokenToTLA(")"); + } + addRightParen(decl.val.getOrigin()); + addRightParen(decl.getOrigin()); + endCurrentLineOfTLA(); + } + + /** + * Adds a MappingObject.LeftParen object to the mapping vector + * for the beginning of the Region region, if it's not null. + * @param region + */ + private void addLeftParen(Region region) { + if (region != null) { + mappingVectorNextLine.addElement( + new MappingObject.LeftParen(region.getBegin())); + } + } + + /** + * Adds a MappingObject.LeftParen object to the mapping vector + * for the beginning of the Region region, if it's not null. + * @param region + */ + private void addRightParen(Region region) { + if (region != null) { + mappingVectorNextLine.addElement( + new MappingObject.RightParen(region.getEnd())); + } + } + + /** + * Like addLeftParen(ast.getOrigin()), except that it uses + * loc the location if it is not null. It is called by + * GenLabeledStmt and ast.getOrigin() should never be null. + * However, if it is, then we don't add a Paren; this insures + * that the matching calls of addLeftParenV and addRightParenV + * both either do or don't add a Paren. + * + * @param ast + */ + private void addLeftParenV(AST ast, PCalLocation loc) { + if (ast.getOrigin() == null) { + return; + } + if (loc != null) { + mappingVectorNextLine.addElement( + new MappingObject.LeftParen(loc)); + } + else { + addLeftParen(ast.getOrigin()); + } + } + + /** + * Like addRightParen(ast.getOrigin()), except that it uses + * loc as the location if it is not null. It is called by + * GenLabeledStmt and ast.getOrigin() should never be null. + * However, if it is, then we don't add a Paren; this insures + * that the matching calls of addLeftParenV and addRightParenV + * both either do or don't add a Paren. + * + * @param ast + */ + private void addRightParenV(AST ast, PCalLocation loc) { + if (ast.getOrigin() == null) { + return; + } + if (loc!= null) { + mappingVectorNextLine.addElement( + new MappingObject.RightParen(loc)); + } + else { + addRightParen(ast.getOrigin()); + } + } +/* -------------------------------------------------------------------------- */ + /* + * The following methods and classes of objects are used in GenSpec(). + * See the comments preceding that method above. + */ + + /** + * A FormulaPair should never have wf = null, but might have sf = null. + */ + public static class FormulaPair { + public String wf ; + public String sf ; + + public FormulaPair(String wfVal, String sfVal) { + this.wf = wfVal; + this.sf = sfVal; + } + + /** + * The string wf /\ sf , or just wf if sf is null. + * @return + */ + public String singleLine() { + if (sf == null) { + return wf ; + } + return wf + " /\\ " + sf ; + } + + /** + * The width of the singleLine representation of the + * conjunction of the formlas. + * + * @return + */ + public int singleLineWidth() { + if (sf == null) { + return wf.length() ; + } + return wf.length() + " /\\ ".length() + sf.length() ; + } + + /** + * The representation of the conjunction of the formulas with + * prefix /\s, where the first /\ appears in column col (Java + * numbering), witout any ending "\n" + * + * @return + */ + public String multiLine(int col) { + String val = "/\\ " + wf ; + if (sf == null) { + return val; + } + return val + "\n" + NSpaces(col) + "/\\ " + sf; + } + } + + /** + * Describes a process fairness formula, as described in the comments + * preceding the GetSpec() method above. + * @author lamport + * + */ + public static class ProcessFairness { + public String xf ; // either "WF" or "SF" + public Vector prefix ; + // StringVector either "\A self \in exp : " or + // "LET self == exp \n IN " (note the ending space) or "" + public FormulaPair bodyFormulas ; // fairness conditions for the proc's body + public Vector prcdFormulas ; // fairness conditions for the procedure + + /** + * The constructor + * @param xfVal + * @param prefixVal + * @param bodyWF : can be null if bodySF is also null + * @param bodySF : can be null + * @param prcdVal + */ + public ProcessFairness (String xfVal, Vector prefixVal, String bodyWF, + String bodySF, Vector prcdVal) { + xf = xfVal; + prefix = prefixVal; + bodyFormulas = null ; + if (bodyWF != null) { + bodyFormulas = new FormulaPair(bodyWF, bodySF); + } + prcdFormulas = prcdVal; + } + /** + * The width of the fairness formula written as a "single-line" + * formula. Single-line means that it is not written as a + * conjunction list (with a leading /\). It will actually + * occupy multiple lines if prefix is a multi-line formula. + * + * @return + */ + public int singleLineWidth() { + // Set maxPrefixWidth to length of longest non-final + // line of prefix, width to lenght of final line + int maxPrefixWidth = 0 ; + int width = 0 ; + if (prefix != null && prefix.size() > 0) { + for (int i = 0; i < prefix.size() - 1; i++) { + String line = (String) prefix.elementAt(i); + if (line.length() > maxPrefixWidth) { + maxPrefixWidth = line.length(); + } + String lastLine = (String) prefix.elementAt(prefix.size()-1); + width = lastLine.length(); + } + } + width = width + bodyFormulas.wf.length(); + if (bodyFormulas.sf != null) { + width = width + bodyFormulas.sf.length(); + } + if (prcdFormulas != null) { + for (int i = 0 ; i < prcdFormulas.size(); i++) { + width = width + ((FormulaPair) prcdFormulas.elementAt(i)).singleLineWidth(); + } + } + if (maxPrefixWidth > width) { + return maxPrefixWidth; + } + return width ; + } + + /** + * Returns the prefix as a StringBuffer, assuming it starts + * in column col. That is, all but the first line is indented + * with col spaces, and all but the last line is ended with + * a \n . + * + * @param col + * @return + */ + private StringBuffer prefixAsStringBuffer(int col) { + StringBuffer val = new StringBuffer(); + if (prefix != null && prefix.size() > 0) { + for (int i = 0; i < prefix.size(); i++) { + String line = (String) prefix.elementAt(i); + if (i != 0) { + val.append(NSpaces(col)); + } + val.append(line) ; + if (i != prefix.size()-1) { + val.append("\n") ; + } + } + } + return val; + } + /** + * The process fairness condition written as a single-line formula, + * starting in column col. + * @return + */ + public StringBuffer singleLine(int col) { + StringBuffer val = prefixAsStringBuffer(col); + val.append(bodyFormulas.wf); + if (bodyFormulas.sf != null) { + val.append(" /\\ "); + val.append(bodyFormulas.sf); + } + if (prcdFormulas != null) { + for (int i = 0 ; i < prcdFormulas.size(); i++) { + val.append(" /\\ "); + val.append(((FormulaPair) prcdFormulas.elementAt(i)).singleLine()); + } + } + return val ; + } + + /** + * Returns true iff format(col) should return a single-line version + * of the formula. + * + * @param col + * @return + */ + private boolean fitsAsSingleLine(int col) { + return (col + singleLineWidth() <= PcalTLAGen.wrapColumn) + || (bodyFormulas.sf == null + && (prcdFormulas == null || prcdFormulas.size() == 0)); + } + /** + * The process fairness condition written as a formula that + * begins in column col (Java numbering) and ends with "\n". + * It is formatted to try to extend no further than column + * PcalTLAGen.wrapColumn, but no individual formula is split + * across lines. + * + * @param col + * @return + */ + public StringBuffer format(int col) { + int singleLineWidth = this.singleLineWidth(); + /* + * Return the single-line form if either it fits on the + * line or if it consists of only the wf formula (so it can't + * be put on multiple lines). + */ + if (fitsAsSingleLine(col)) { + return this.singleLine(col); + } + StringBuffer val = prefixAsStringBuffer(col); + int prefixWidth = 0; + if (prefix != null && prefix.size() > 0) { + prefixWidth = ((String) prefix.elementAt(prefix.size()-1)).length(); + } + int curCol = col + prefixWidth; + String line = this.bodyFormulas.singleLine(); + if (curCol + line.length() + 3 <= PcalTLAGen.wrapColumn) { + val.append("/\\ " + line); + } else { + val.append(this.bodyFormulas.multiLine(curCol)); + } + if (prcdFormulas == null) { + return val; + } + for (int i = 0; i < this.prcdFormulas.size(); i++) { + FormulaPair form = (FormulaPair) this.prcdFormulas.elementAt(i) ; + line = form.singleLine(); + // On 2 Apr 2013, LL discovered the following totally bizarre + // line of code, which inserted copies of "THIS_EXTRA_SPACE_INSERTED" into + // the translation, which of course then didn't parse. Apparently some + // change was made and never tested. The conjuncts being inserted here + // seem to be the fairness formulas for procedures in a fair process. + // + //val.append("\nTHIS_EXTRA_SPACE_INSERTED"); + + // One experiment seems to indicate that the following statement is needed + // to put the first of the procedures' liveness formulas where it belongs. + // However, I don't understand the code so I have no idea what actually + // should be done. LL 2 Apr 2013 + // + if (i == 0) { + val.append("\n") ; + } + val.append(NSpaces(curCol)); + if (curCol + line.length() + 3 <= PcalTLAGen.wrapColumn) { + val.append("/\\ " + line + "\n"); + } else { + val.append(form.multiLine(curCol)); + } + } + return val; + } + + + } +} diff --git a/tlatools/src/pcal/PcalTranslate.java b/tlatools/src/pcal/PcalTranslate.java index f77ae608e5b6b8a59a3d769c8c12d68049c0e89d..8a586a35d2352826283c0d12875685846506315e 100644 --- a/tlatools/src/pcal/PcalTranslate.java +++ b/tlatools/src/pcal/PcalTranslate.java @@ -556,6 +556,10 @@ public class PcalTranslate { result1.removeElementAt(result1.size()-1); result1.addAll(ExplodeCallReturn((AST.CallReturn) last, next)); } + else if (last.getClass().equals(AST.CallGotoObj.getClass())) { + result1.removeElementAt(result1.size()-1); + result1.addAll(ExplodeCallGoto((AST.CallGoto) last, next)); + } else if (last.getClass().equals(AST.IfObj.getClass())) { AST.If If = (AST.If) last; Vector p1 = CopyAndExplodeLastStmt(If.Then, next); @@ -1479,4 +1483,18 @@ public class PcalTranslate { result.addElement(UpdatePC(peTo.iPC)); return result; } + + /*********************************************************************** + * Generate sequence of statements corresponding to call followed by a * + * goto. * + ***********************************************************************/ + private static Vector ExplodeCallGoto(AST.CallGoto ast, String next) throws PcalTranslateException { + AST.Call call = new AST.Call(); + call.to = ast.to; + call.args = ast.args; + call.line = ast.line; + call.col = ast.col; + call.setOrigin(ast.getOrigin()); + return ExplodeCall(call, ast.after); + } } diff --git a/tlatools/src/pcal/Region.java b/tlatools/src/pcal/Region.java index 548681978b01fba42469c74408e11ec4b5c58a17..5089e3c441c400a19770e6ebac4a75d8f39f6be2 100644 --- a/tlatools/src/pcal/Region.java +++ b/tlatools/src/pcal/Region.java @@ -55,11 +55,6 @@ public class Region implements Serializable { } public String toString() { -// return "[begin |-> " + begin.toString() + ", end |-> " -// + end.toString() + "]"; - if (this == null) { - return "null"; - } return "[" + begin.toString() + "-" + end.toString() + "]"; } diff --git a/tlatools/src/pcal/Tokenize.java b/tlatools/src/pcal/Tokenize.java index d81c982c210c75e442a98c31648261b607e29861..45a071c408c8fc51e59deb99459437af583c1218 100644 --- a/tlatools/src/pcal/Tokenize.java +++ b/tlatools/src/pcal/Tokenize.java @@ -814,6 +814,7 @@ public class Tokenize || tok.equals("procedure") || tok.equals("define") || tok.equals("process") + || tok.equals("fair") ) ; } diff --git a/tlatools/src/pcal/Translator.java b/tlatools/src/pcal/Translator.java index cc67c5e367341782adf444ac9e95cf9abef90247..b9a880530b8e338c588c4a907e31a4a090bc9102 100644 --- a/tlatools/src/pcal/Translator.java +++ b/tlatools/src/pcal/Translator.java @@ -1,64 +1,166 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Simon Zambrowski - initial API and implementation + * Markus Alexander Kuppe - Refactoring + ******************************************************************************/ package pcal; +import java.util.Arrays; import java.util.List; import java.util.Vector; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import util.ToolIO; /** * Launcher for the PCal Translator for running out-of-the-tool * @author Simon Zambrovski - * @version $Id$ */ public class Translator { - /** - * Resets the Tool IO - */ - public void init() - { - ToolIO.reset(); - ToolIO.setMode(ToolIO.TOOL); + private String output; + + private final String input; + + public Translator(final String anInput, final String[] args) { + this.input = anInput; + ToolIO.reset(); + ToolIO.setMode(ToolIO.TOOL); + PcalParams.resetParams(); + PcalParams.tlaPcalMapping = new TLAtoPCalMapping(); + trans.parseAndProcessArguments(args); + } + + public Translator(final String anInput, final List<String> args) { + this(anInput, args.toArray(new String[args.size()])); } /** * delegates the call to the {@link trans#main()} * @param args + * @return */ -// public int runTranslation(String[] args) - public TLAtoPCalMapping runTranslation(String[] args) - { - init(); -// int status = trans.runMe(args); - TLAtoPCalMapping status = trans.runMe(args); - return status; - } + public boolean translate(final ValidationCallBack cb) { + // The input .tla file might have unix or windows line ending. If we fail to + // properly split the input (a line per array cell), the pcal translator will + // silently fail as well. + final String[] lines = input.split("\\r?\\n"); + final List<String> in = Arrays.asList(lines); + + final List<String> out = trans.performTranslation(in, cb); + if (out != null) { + final StringBuilder buf = new StringBuilder(); + final String lineSeparator = System.getProperty("line.separator"); + for (final String line : out) { + buf.append(line); + // The output .tla file will use the OS's line ending which is in line with the + // the translator's legacy behavior. + buf.append(lineSeparator); + } + output = buf.toString(); + } + + return output != null && PcalParams.tlaPcalMapping != null; + } + + public String getOutput() { + return output; + } + + public boolean hasChanged() { + return !input.equals(output); + } + + public TLAtoPCalMapping getMapping() { + return PcalParams.tlaPcalMapping; + } /** * Retrieves the errors recorded during the execution * @return */ - public List getErrorMessages() - { - String[] messages = ToolIO.getAllMessages(); - Vector errorMessages = new Vector(); - System.out.println("Found " + messages.length + " messages"); - int position; - String cleanMessage = null; - for (int i = 0; i < messages.length; i++) - { - position = messages[i].indexOf(PcalDebug.UNRECOVERABLE_ERROR); - if (position != -1) - { - cleanMessage = messages[i].substring(position, messages[i].length() - PcalDebug.ERROR_POSTFIX.length()); - errorMessages.add(cleanMessage); - } else - { - cleanMessage = messages[i]; - // unknown error format - System.out.println(cleanMessage); - } - } - return errorMessages; - } + public List<Error> getErrors() { + final String[] messages = ToolIO.getAllMessages(); + final Vector<Error> errorMessages = new Vector<Error>(); + for (int i = 0; i < messages.length; i++) { + int position = messages[i].indexOf(PcalDebug.UNRECOVERABLE_ERROR); + if (position != -1) { + errorMessages.add(new Error(messages[i].substring(position, + messages[i].length() - PcalDebug.ERROR_POSTFIX.length()))); + } + } + return errorMessages; + } + + public static class Error { + + private static final String LINE = "line "; + private static final String COLUMN = ", column "; + + private final String error; + + public Error(String anError) { + this.error = anError; + } + + /* (non-Javadoc) + * @see java.lang.Object#toString() + */ + public String toString() { + return error; + } + + public int[] getLocation() { + final int lineStarts = error.indexOf(LINE); + final int lineEnds = error.indexOf(COLUMN); + if (lineStarts != -1 && lineEnds != -1) { + final String line = error.substring(lineStarts + LINE.length(), lineEnds); + /* + * afterColumnString is the substring of message that comes after the first + * occurance of ", column" in message. + */ + final String afterColumnString = error.substring(lineEnds + COLUMN.length()); + // match any number of white spaces followed by the first string of digits. + final Matcher matcher = Pattern.compile("\\s*\\d+").matcher(afterColumnString); + matcher.find(); + // the column string that should be a parsable int + final String column = matcher.group().trim(); + + int lineNumber = -1; + int columnNumber = -1; + try { + lineNumber = Integer.parseInt(line); + } catch (final NumberFormatException e) { + } + try { + columnNumber = Integer.parseInt(column); + } catch (final NumberFormatException e) { + } + return new int[] { lineNumber, columnNumber, lineNumber, columnNumber + 1 }; + } + return new int[] { -1, -1, -1, -1 }; + } + } } diff --git a/tlatools/src/pcal/ValidationCallBack.java b/tlatools/src/pcal/ValidationCallBack.java new file mode 100644 index 0000000000000000000000000000000000000000..91c8558039998c133aa0a367d0179c3926c997e1 --- /dev/null +++ b/tlatools/src/pcal/ValidationCallBack.java @@ -0,0 +1,49 @@ +/******************************************************************************* + * Copyright (c) 2020 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package pcal; + +public interface ValidationCallBack { + + public enum Generate { + NOT_NOW, IGNORE, DO_IT; + } + + public class Noop implements ValidationCallBack { + @Override + public boolean shouldCancel() { + return false; + } + + @Override + public Generate shouldGenerate() { + return Generate.NOT_NOW; + } + } + + boolean shouldCancel(); + + Generate shouldGenerate(); +} diff --git a/tlatools/src/pcal/Validator.java b/tlatools/src/pcal/Validator.java new file mode 100644 index 0000000000000000000000000000000000000000..2c3660e8c2c3b9d51a510891393fd702b17f0549 --- /dev/null +++ b/tlatools/src/pcal/Validator.java @@ -0,0 +1,297 @@ +package pcal; + +import java.io.BufferedInputStream; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.Vector; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.zip.CRC32; + +import pcal.exception.ParseAlgorithmException; +import pcal.exception.RemoveNameConflictsException; +import tla2sany.modanalyzer.ParseUnit; +import tla2sany.st.Location; +import util.TLAConstants; + +/** + * This class validates the recorded checksums generated from an input specification's PlusCal and translated PlusCal + * blocks. + */ +public class Validator { + + public enum ValidationResult { + /** No PlusCal algorithm exists in the specification */ + NO_PLUSCAL_EXISTS, + + /** No translation exists - BUT THERE IS PLUSCAL IN THE SPECIFICATION! */ + NO_TRANSLATION_EXISTS, + + NO_TLA_CHECKSUMS_EXIST, + NO_PCAL_CHECKSUMS_EXIST, + + TLA_DIVERGENCE_EXISTS, + PCAL_DIVERGENCE_EXISTS, + + /** A Java error was encountered while attempting to validate. */ + ERROR_ENCOUNTERED, + + /** Everything checks out. */ + NO_DIVERGENCE; + } + + static final String PCAL_CHECKSUM = "pcalchecksum"; + static final String TLA_CHECKSUM = "tlachecksum"; + + static final String CHECKSUM_TEMPLATE_IGNORE = "(chksum(pcal) \\in STRING /\\ chksum(tla) \\in STRING)"; + static final String CHECKSUM_TEMPLATE = "(chksum(pcal) = \"%s\" /\\ chksum(tla) = \"%s\")"; + + static final Pattern CHECKSUM_PATTERN = Pattern + .compile("\\\\\\* BEGIN TRANSLATION\\s+\\(\\s*((?i)ch(ec)?ksum\\(p(lus)?cal\\)(?-i))\\s*(=\\s*\\\"(?<" + + PCAL_CHECKSUM + ">[0-9A-Fa-f]*)\\\"|\\\\in\\s*" + + "STRING)\\s*\\/\\\\\\s*((?i)ch(ec)?ksum\\(tla\\+?\\)(?-i))\\s*(=\\s*\\\"(?<" + TLA_CHECKSUM + + ">[0-9A-Fa-f]*)\\\"|\\\\in\\s*STRING)\\s*\\)"); + + private static final Pattern MODULE_CLOSING_PATTERN = Pattern.compile(TLAConstants.MODULE_CLOSING_REGEX); + + public static Set<ValidationResult> validate(ParseUnit parseUnit, InputStream inputStream) + throws IOException { + final BufferedReader reader = new BufferedReader( + new InputStreamReader((inputStream instanceof BufferedInputStream) ? (BufferedInputStream) inputStream + : new BufferedInputStream(inputStream))); + return validate(parseUnit, reader); + } + + public static Set<ValidationResult> validate(final ParseUnit parseUnit, final BufferedReader reader) throws IOException { + // Instead of matching the module start and end markers, whe while loop use SANY's + // parse tree that has the location of the start and end markers. + final Location loc = parseUnit.getParseTree().getLocation(); + + // Pre-allocate the number of lines we will read below. + final List<String> lines = new ArrayList<>(loc.endLine() - loc.beginLine()); + + // TODO It would be faster to let SANY look for a PlusCal algorithm when it + // parses the TLA+ spec (although SANY probably doesn't parse comments, it + // could look for the "--algorithm" or "--fair algorithm" tokens). + boolean seenAlgo = false; + int cnt = 1; // Location is 1-indexed. + String line; + while ((line = reader.readLine()) != null) { + // Skip lines that are not within the range given by location. + // This implies that this loop will miss "pluscal options" before + // or after the module's start and end markers. While it is legal to + // put options there, I've decided we don't want to pay the price + // of parsing the lines preceding or after a module. Users can + // put the options into a comment inside the module, which I + // believe to be the choice for most users anyway. + if (loc.beginLine() <= cnt && cnt <= loc.endLine()) { + if (line.indexOf(PcalParams.BeginAlg.substring(2)) > 0) { + seenAlgo = true; + } + lines.add(line); + } else if (cnt >= loc.endLine()) { + break; + } + cnt++; + } + + if (!seenAlgo) { + // No "algorithm" string in the input => No PlusCal algorithm. + // The appearance of "algorithm", however, might be a false + // positive of which validate will take care of (I don't have + // time to move the logic up here). + return setOf(ValidationResult.NO_PLUSCAL_EXISTS); + } + return validate(lines); + } + + /** + * There is some redundancy between this and {@link trans#performTranslation(List)} - it would be nice to make a + * common method, extended by each. + * + * @param specificationText the entire specification, line by line - for historical reasons. + * @return the result of the validation, as enumerated by the inner enum of this class. + */ + private static Set<ValidationResult> validate(final List<String> specificationText) { + final Vector<String> deTabbedSpecification = trans.removeTabs(specificationText); + + final IntPair searchLoc = new IntPair(0, 0); + boolean notDone = true; + while (notDone) { + try { + /* + * As mentioned in #413, this is a performance bottleneck point; unfortunately we need process the + * options as it affects the production of the AST and we base the checksum on the AST. + * We have addressed a use case in which there is a long run of line prefacing the module specification + * in the {@link #validate(InputStream)} method, but that doesn't address a long spec. + * If we wanted to devote more time to this, we should examine the performance difference between + * the character-by-character marching done in the ParseAlgorithm code versus using a + * regex matcher to split apart lines. + */ + ParseAlgorithm.FindToken("PlusCal", deTabbedSpecification, searchLoc, ""); + final String line = ParseAlgorithm.GotoNextNonSpace(deTabbedSpecification, searchLoc); + final String restOfLine = line.substring(searchLoc.two); + if (restOfLine.startsWith("options")) { + // The first string after "PlusCal" not starting with a + // space character is "options" + if (ParseAlgorithm.NextNonIdChar(restOfLine, 0) == 7) { + // The "options" should begin an options line + PcalParams.optionsInFile = true; + ParseAlgorithm.ProcessOptions(deTabbedSpecification, searchLoc); + notDone = false; + } + } + } catch (ParseAlgorithmException e) { + // The token "PlusCal" not found. + notDone = false; + } + } + + int algLine = 0; + int algCol = -1; + // Search for "--algorithm" or "--fair". + // If found set algLine and algCol right after the last char, + // set foundBegin true, and set foundFairBegin true iff it + // was "--fair". Otherwise, set foundBegin false. + boolean foundBegin = false; + boolean foundFairBegin = false; + while ((algLine < deTabbedSpecification.size()) && !foundBegin) { + final String line = deTabbedSpecification.elementAt(algLine); + final Matcher m = MODULE_CLOSING_PATTERN.matcher(line); + if (m.matches()) { + break; + } + + algCol = line.indexOf(PcalParams.BeginAlg); + if (algCol != -1) { + algCol = algCol + PcalParams.BeginAlg.length(); + foundBegin = true; + } else { + algCol = line.indexOf(PcalParams.BeginFairAlg); + if (algCol != -1) { + // Found the "--fair". The more structurally nice thing to + // do here would be to move past the following "algorithm". + // However, it's easier to pass a parameter to the ParseAlgorithm + // class's GetAlgorithm method that tells it to go past the + // "algorithm" token. + algCol = algCol + PcalParams.BeginFairAlg.length(); + foundBegin = true; + foundFairBegin = true; + + } else { + algLine = algLine + 1; + } + } + } + + final Set<ValidationResult> res = new HashSet<>(); + + + // TLA+ translation + final int translationLine = trans.findTokenPair(deTabbedSpecification, 0, + PcalParams.BeginXlation1, PcalParams.BeginXlation2); + if (translationLine == -1) { + res.add(ValidationResult.NO_TRANSLATION_EXISTS); + } + + final int endTranslationLine = trans.findTokenPair(deTabbedSpecification, translationLine + 1, + PcalParams.EndXlation1, PcalParams.EndXlation2); + if (endTranslationLine == -1) { + res.add(ValidationResult.NO_TRANSLATION_EXISTS); + } + if (translationLine == -1 && endTranslationLine == -1) { + return res; + } + + // PlusCal algorithm + if (!foundBegin) { + res.add(ValidationResult.NO_PLUSCAL_EXISTS); + } else { + + // Get the PlusCal AST by parsing it. + try { + ParseAlgorithm.uncomment(deTabbedSpecification, algLine, algCol); + } catch (ParseAlgorithmException e) { + PcalDebug.reportError(e); + return setOf(ValidationResult.ERROR_ENCOUNTERED); + } + + final TLAtoPCalMapping mapping = new TLAtoPCalMapping() ; + mapping.algColumn = algCol; + mapping.algLine = algLine; + PcalParams.tlaPcalMapping = mapping; + + AST ast = new AST(); + try { + final PcalCharReader reader = new PcalCharReader(deTabbedSpecification, algLine, algCol, + specificationText.size(), 0); + ast = ParseAlgorithm.getAlgorithm(reader, foundFairBegin); + + // The call translate mutates the ast in pcal.PcalTranslate.Explode(AST, PcalSymTab). + // I'm ignoring the PcalParams.tlcTranslation() alternative in trans.java + // because I doubt the "-spec" feature is used widely (if at all). + final PCalTLAGenerator pcalTLAGenerator = new PCalTLAGenerator(ast); + pcalTLAGenerator.removeNameConflicts(); + pcalTLAGenerator.translate(); + } catch (ParseAlgorithmException | RemoveNameConflictsException e) { + PcalDebug.reportError(e); + // The PlusCal algorithm doesn't parse because of a syntax error. This indicates + // that the algorithm has been modified since it wouldn't have been possible to + // calculate a checksum earlier. ast will be empty string and, thus, not match below. + } + + // Calculate the checksum value for the PlusCal's AST and compare unless no + // checksum to compare it with is given (or it has been disabled). + final Matcher m = Validator.CHECKSUM_PATTERN.matcher(deTabbedSpecification.get(translationLine)); + if (m.find()) { + if (m.group(Validator.PCAL_CHECKSUM) != null) { + + final String chksumPCalAST = Validator.checksum(ast.toString()); + if (!m.group(Validator.PCAL_CHECKSUM).equals(chksumPCalAST)) { + // Mismatch between the PlusCal algorithm and its checksum. + res.add(ValidationResult.PCAL_DIVERGENCE_EXISTS); + } + } + if (m.group(Validator.TLA_CHECKSUM) != null) { + final Vector<String> translation = new Vector<>( + specificationText.subList((translationLine + 1), endTranslationLine)); + final String chksumTLATranslation = Validator.checksum(translation); + + if (!m.group(Validator.TLA_CHECKSUM).equals(chksumTLATranslation)) { + // Mismatch between the TLA+ translation and its checksum. + res.add(ValidationResult.TLA_DIVERGENCE_EXISTS); + } + } + } else { + res.add(ValidationResult.NO_PCAL_CHECKSUMS_EXIST); + } + } + return res.isEmpty() ? setOf(ValidationResult.NO_DIVERGENCE) : res; + } + + public static String checksum(final Vector<String> lines) { + final StringBuilder sb = new StringBuilder(); + for (final String str : lines) { + sb.append(str); + } + + return checksum(sb.toString()); + } + + static String checksum(final String string) { + final CRC32 crc32 = new CRC32(); + crc32.update(string.getBytes()); + return Long.toHexString(crc32.getValue()); + } + + private static Set<ValidationResult> setOf(ValidationResult... res) { + return new HashSet<>(Arrays.asList(res)); + } +} diff --git a/tlatools/src/pcal/trans.java b/tlatools/src/pcal/trans.java index 37f7237776d3bf62301115a8e7fb531cc89700fd..428dbcda55863f27945be154b49e9ac8b6d99dad 100644 --- a/tlatools/src/pcal/trans.java +++ b/tlatools/src/pcal/trans.java @@ -1,1970 +1,2021 @@ -package pcal; - -import java.io.BufferedReader; -import java.io.BufferedWriter; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.FileWriter; -import java.io.IOException; -import java.io.InputStreamReader; -import java.util.Vector; - -import pcal.MappingObject.Break; -import pcal.exception.FileToStringVectorException; -import pcal.exception.ParseAlgorithmException; -import pcal.exception.PcalResourceFileReaderException; -import pcal.exception.RemoveNameConflictsException; -import pcal.exception.StringVectorToFileException; -import pcal.exception.TLCTranslationException; -import pcal.exception.UnrecoverableException; -import tla2tex.Debug; -import util.ToolIO; - -/*************************************************************************** -* <pre> -* CLASS trans * -* * -* BUGS: * -* Accepts if (...) {...} ; else {...} * -* Generates code as if the ";" were not there. * -* ----------------------------------------------------------------- * -* History: * -* Version 1.0: Original release. * -* * -* Version 1.1: (March 2006) * -* Introduced ability to have the translator * -* add missing labels. * -* * -* Version 1.2: (August 2007) * -* Introduced defaultInitValue so liveness can be * -* checked even with "uninitialized" variables. * -* * -* Version 1.3: (February 2008) * -* Added "await" as a synonym for "when" * -* * -* Version 1.4: (June 2010) * -* Added the options statement and the -lineWidth option. * -* * -* Version 1.5: (Jan 2011) * -* 1. Added the -noDoneDisjunct option. * -* 2. Added the new way of specifying fairness, with * -* the "fair" keyword with "+" modifier, and the "+" * -* and "-" label modifiers. * -* 3. Automatically removes the stuttering-on-termination * -* disjunction if all processes are "while (TRUE)" * -* statements with no gotos to "Done". * -* 4. In addition, if the "while (TRUE)"s have no * -* internal labels, it removes the pc variable. * -* 5. Changed the representation of Process and Procedure * -* nodes in the abstract syntax tree produced by * -* the -writeAST option and used when TLC is producing * -* the translation. * -* The changes 3-5 are not implemented when the -version * -* option specifies an earlier version. * -* * -* Version 1.6: (July 2011) * -* - Added the "--fair algorithm" syntax for specifying * -* weak fairness of the next-state action. (This * -* changes the way fairness of a uniprocess algorithm was * -* specified in Version 1.5. All legal Version 1.5 * -* algorithms should work with the "version 1.5" option.) * -* - Permitted previously defined macros to be called * -* inside a macro definition. * -* * -* Version 1.7: (19 January 2012) * -* - Translator adds "BEGIN/END TRANSLATION" if needed. * -* - Added support for Toolbox Goto PCal Source command. * -* * -* Version 1.8: (30 Mar 2012) * -* - Changed translation to remove stuttering-on- * -* termination disjunction if some process is * -* "while (TRUE)". * -* - Omitted the Termination definition if the stuttering- * -* on-termination disjunction is removed, since that * -* implies Termination is always FALSE. * -* (4 May 2012) * -* - Removed the unnecessary CASE in the pc = ... clause of * -* the Init predicate when there is only a single process * -* statement. * -* ----------------------------------------------------------------- * -* * -* This is the main method of the +CAL to TLA+ translation program. * -* the program has the following command-line options. Only the ones * -* marked with ** besides them can be specified in the file's * -* options statement. The "-" can be omitted when the option is in * -* the options statement. * -* * -* -help : Type a help file instead of running the program. * -* * -* -spec name : Uses TLC and the TLA+ specification name.tla to do * -* the translation. The files name.tla and name.cfg * -* are copied from the java/ directory to the current * -* directory; the file AST.tla that defines `fairness' * -* to equal the fairness option and `ast' to equal * -* the the AST data structure representing the algorithm * -* is written to the current directory; and TLC is run * -* on name.tla to produce the translation. * -* * -* -myspec name : Like -spec, except it finds the files names.tla * -* and names.cfg in the current directory, instead * -* of writing them there. * -* * -* -spec2 name * -* -myspec2 name : Like -spec and -myspec, except they use TLC2 * -* instead of TLC (aka TLC1). * -* * -* -writeAST : Writes the AST file as in the -spec option, but does * -* not perform the translation. * -* * -* -debug : Run in debugging mode, whatever that means. For the * -* parser, it just means that the toString() methods * -* output the line and column number along with the * -* node. * -* * -* -unixEOL : Forces the use of Unix end-of-line convention, regardless * -* of the system's default. Without this, when run on * -* Windows, the files it writes seem to have a "^M" at the * -* end of every line when viewed with Emacs. * -* * -*** -wf : Conjoin to formula Spec weak fairness of each process's * -* next-state action * -* * -*** -sf : Conjoin to formula Spec strong fairness of each process's * -* next-state action * -* * -*** -wfNext : Conjoin to formula Spec weak fairness of the entire * -* next-state action * -* * -*** -nof : Conjoin no fairness formula to Spec. This is the default, * -* except when the -termination option is chosen. * -* * -*** -termination : Add to the .cfg file the command * -* PROPERTY Termination * -* With this option, the default fairness option * -* becomes -wf. * -* * -* -nocfg : Suppress writing of the .cfg file. * -* * -*** -noDoneDisjunct : Suppress the disjunct of the next-state * -* relation that describes stuttering steps taken * -* when the algorithm has halted. * -* * -*** -label : Tells the translator to add missing labels. This is * -* the default only for a uniprocess algorithm in which * -* the user has typed no labels. * -* * -* -reportLabels : True if the translator should print the names * -* and locations of all labels it adds. Like * -* -label, it tells the translator to add missing * -* labels. * -* * -*** -labelRoot name : If the translator adds missing labels, it * -* names them name1, name2, etc. Default value * -* is "Lbl_". * -* * -* THE FOLLOWING OPTION ADDED IN VERSION 1.31 * -* * -*** -lineWidth : The translation tries to perform the translation so * -* lines have this maximum width. (It will often * -* fail.) Default is 78, minimum value is 60. * -* * -* THE FOLLOWING OPTIONS ADDED IN VERSION 1.4 * -* * -*** -lineWidth : The translation tries to perform the translation so * -* lines have this maximum width. (It will often * -* fail.) Default is 78, minimum value is 60. * -* * -*** -version : The version of PlusCal for which this algorithm is * -* written. If the language is ever changed to make * -* algorithms written for earlier versions no longer * -* legal, then the translator should do the appropriate * -* thing when the earlier version number is specified. * -* ------------------------------------------------------------------------ * -* * -* The program uses vector objects from the Vector class to implement * -* sequences (lists). This generates a compiler warning. * -* * -* In Java data structures like arrays and Vectors, numbering starts with * -* 0. Unlike programmers, human beings count from 1. I use the term "Java * -* ordinal" to refer a number that denotes a position that represents the * -* first item as 0, and the term "human ordinal" to refer to an ordinary * -* ordinal that counts the first item as 1. * -* * -* * -* NOTE: * -* * -* One process should be able to read the pc or stack of another. There * -* is no logical reason to forbid this. However, the definition of * -* Translation in PlusCal.tla does not distinguish between instances of pc * -* in the original algorithm and ones inserted by the translation. The * -* latter instances must be subscripted--that is replaced by something * -* like pc[self]. Therefore, the Translation operator subscripts the * -* instances of pc from the original algorithm. The Java Translate method * -* must not do this, but must subscript (and prime) only the instances of * -* pc and stack introduced during the translation process. * -* * -* * -* The following bugs should all have been fixed by the addition of * -* ParseAlgorithm.Uncommment by LL on 18 Feb 2006. * -* * -* - There cannot be a comment between a label and the * -* following ":". * -* * -* - There cannot be a comment immediately before the ")" that ends * -* the list of arguments in a call statement. * -* * -* - The code for reporting the location of an error has the * -* following problem. If the token where the error occurs is * -* preceded by a comment, then the position reported is that of the * -* beginning of the comment rather than that of the token. * -* * -* TENTATIVE CHANGE MADE * -* * -* The following change was made along with a modification to the parser * -* to allow semi-colons to be omitted when they're obviously unnecessary. * -* * -* The parser does not parse expressions in the +CAL algorithm; it just * -* scans ahead to the first token that it can determine is not part of the * -* expression. To make this work, the following tokens that are legal in * -* a TLA+ expression are illegal in a +CAL expression: * -* * -* variable variables begin do then := || * -* * -* Making additional tokens illegal might help the parser find errors * -* sooner. For example, example, if one forgets the ";" and writes * -* * -* x := x + 1 * -* if x > y + 17 then ... * -* * -* the parser takes everything up to the "then" to be the right-hand side * -* of the "x :=" assignment. Making "if" illegal in an expression would * -* allow the parser to catch the error at the "if". * -* </pre> -***************************************************************************/ -class trans -{ - /** Status indicating no errors and successful process */ - static final int STATUS_OK = 1; - /** Status of no errors, but abort of the translation */ - private static final int STATUS_EXIT_WITHOUT_ERROR = 0; - /** Status of present errors and abort of the translation */ - static final int STATUS_EXIT_WITH_ERRORS = -1; - - /** - * Main function called from the command line - * @param args, command line arguments - */ - public static void main(String[] args) - { - runMe(args); - } - - /** - * The main translator method - * @return one of {@link trans#STATUS_OK}, {@link trans#STATUS_EXIT_WITH_ERRORS}, - * {@link trans#STATUS_EXIT_WITH_ERRORS} - * indicating the status - * - * Modified by LL on 16 Dec 2011. Changed the return value to the - * TLAtoPCalMapping object for the translation. (The status return - * value was not being used.) If the translation fails, it returns - * null. - */ - /** - * @param args - * @return - */ - /** - * @param args - * @return - */ -// public static int runMe(String[] args) - public static TLAtoPCalMapping runMe(String[] args) // added for testing - { - /********************************************************************* - * Get and print version number. * - *********************************************************************/ - // String lastModified = - // "last modified on Wed 11 March 2009 at 14:52:58 PST by lamport"; - /******************************************************************* - * This string is inserted by an Emacs macro when a new version is * - * saved. Unfortunately, Eclipse isn't Emacs, so the modification * - * date must be entered manually in the PcalParams module. * - *******************************************************************/ - - if (ToolIO.getMode() == ToolIO.SYSTEM) - { - PcalDebug.reportInfo("pcal.trans Version " + PcalParams.version + " of " + PcalParams.modDate); - } - - // SZ Mar 9, 2009: - /* - * This method is called in order to make sure, that the - * parameters are not sticky because these are could have been initialized - * by the previous run - */ - PcalParams.resetParams(); - /********************************************************************* - * Get and process arguments. - *********************************************************************/ - - /** - * Create the new TLAtoPCalMapping object, call it mapping - * here and set PcalParams.tlaPcalMapping to point to it. - */ - TLAtoPCalMapping mapping = new TLAtoPCalMapping() ; - PcalParams.tlaPcalMapping = mapping; - - int status = parseAndProcessArguments(args); - - if (status != STATUS_OK) - { -// return exitWithStatus(status); - return new TLAtoPCalMapping() ; // added for testing - } - - /********************************************************************* - * Read the input file, and set the Vector inputVec equal to its * - * contents, where inputVec[i] is the string containing the contents * - * of line i+1 of the input file. * - *********************************************************************/ - Vector inputVec = null; - try - { - inputVec = fileToStringVector(PcalParams.TLAInputFile + /* (PcalParams.fromPcalFile ? ".pcal" : */".tla" /*)*/); - } catch (FileToStringVectorException e) - { - PcalDebug.reportError(e); -// return exitWithStatus(STATUS_EXIT_WITH_ERRORS); - return null ; // added for testing - } - - /********************************************************************* - * outputVec is an alias for inputVec if the input is a .tla file, * - * which was not always the case in the aborted version 1.31. * - *********************************************************************/ - // Vector outputVec = PcalParams.fromPcalFile ? new Vector() : inputVec; - Vector outputVec = inputVec; - - /********************************************************************* - * Set untabInputVec to be the vector of strings obtained from * - * inputVec by replacing tabs with spaces. * - * * - * Tabs are date from the days when memory cost $1 per bit and are a * - * stupid anachronism. They should be banned. Although the various * - * methods taken from TLATeX should deal with tabs, there are * - * undoubtedly corner cases that don't work right. In particular, I * - * think there's one case where PcalCharReader.backspace() might be * - * called to backspace over a tab. It's easier to simply get rid of * - * the tabs than to try to make it work. * - * * - * Since the user might be evil enough to prefer tabs, with tla-file * - * input, the parts of the output file that are not produced by the * - * translator are copied from inputVec, so any tabs the user wants * - * are kept. * - *********************************************************************/ - Vector untabInputVec = removeTabs(inputVec); - - /** - * Look through the file for PlusCal options. They are put anywhere - * in the file (either before or after the module or in a comment) - * with the following sequence - * PlusCal <optional white space> options <optional white space> - * ( <options> ) - * - * where <options> has the same format as options on the command - * line. - */ - IntPair searchLoc = new IntPair(0, 0); - boolean notDone = true; - while (notDone) - { - try - { - ParseAlgorithm.FindToken("PlusCal", untabInputVec, searchLoc, ""); - String line = ParseAlgorithm.GotoNextNonSpace(untabInputVec, searchLoc); - String restOfLine = line.substring(searchLoc.two); - if (restOfLine.startsWith("options")) - { - // The first string after "PlusCal" not starting with a - // space character is "options" - if (ParseAlgorithm.NextNonIdChar(restOfLine, 0) == 7) - { - // The "options" should begin an options line - PcalParams.optionsInFile = true; - ParseAlgorithm.ProcessOptions(untabInputVec, searchLoc); - notDone = false; - } - } - } catch (ParseAlgorithmException e) - { - // The token "PlusCal" not found. - notDone = false; - } - } - - /** - * translationLine is set to the line of the output file at which - * the \* BEGIN TRANSLATION appears--whether it is inserted into the - * tla-file input by the user, or inserted into the output by the - * translator for pcal-file input. - */ - int translationLine = 0; - - /********************************************************************* - * Set algLine, algCol to the line and column just after the string * - * [--]algorithm that begins the algorithm. (These are Java * - * ordinals, in which counting begins at 0.) * - * * - * Modified by LL on 18 Feb 2006 to use untabInputVec instead of * - * inputVec, to correct bug that occurred when tabs preceded the * - * "--algorithm". * - * * - * For the code to handle pcal-input, I introduced the use of * - * IntPair objects to hold <line, column> Java coordinates (counting * - * from zero) in a file (or an image of a file in a String Vector). * - * For methods that advance through the file, the IntPair object is * - * passed as an argument and is advanced by the method. This is * - * what I should have been doing from the start, but I wasn't smart * - * enough The IntPair curLoc is the main one used in the part of the * - * following code that handles pcal-file input. * - *********************************************************************/ - int algLine = 0; - int algCol = -1; - /******************************************************************* - * If the BEGIN/END TRANSLATION region exists, then set * - * translationLine to the number of the line after which the * - * translation is to be inserted and delete the previous version * - * of the translation (if it exists) from inputVec. (Line * - * numbering is by Java ordinals.) If the region doesn't exist, * - * set translationLine to -1. * - * * - * Note: we remove the previous version from inputVec, because * - * that's where the translated output is going to go, and also * - * from untabInputVec, because we will then detect if the begin * - * and end translation lines contain part of the algorithm within * - * them. * - *******************************************************************/ - translationLine = findTokenPair(untabInputVec, 0, PcalParams.BeginXlation1, PcalParams.BeginXlation2); - if (translationLine != -1) - { - - - int endTranslationLine = findTokenPair(untabInputVec, translationLine + 1, PcalParams.EndXlation1, - PcalParams.EndXlation2); - if (endTranslationLine == -1) - { - PcalDebug.reportError("No line containing `" + PcalParams.EndXlation1 + " " + PcalParams.EndXlation2); -// return exitWithStatus(STATUS_EXIT_WITH_ERRORS); - return null; - } - - endTranslationLine = endTranslationLine - 1; - while (translationLine < endTranslationLine) - { - inputVec.remove(endTranslationLine); - untabInputVec.remove(endTranslationLine); - endTranslationLine = endTranslationLine - 1; - } - } - - // Search for "--algorithm" or "--fair". - // If found set algLine and algCol right after the last char, - // set foundBegin true, and set foundFairBegin true iff it - // was "--fair". Otherwise, set foundBegin false. - boolean foundBegin = false; - boolean foundFairBegin = false; - while ((algLine < untabInputVec.size()) && !foundBegin) - { - String line = (String) untabInputVec.elementAt(algLine); - algCol = line.indexOf(PcalParams.BeginAlg); - if (algCol != -1) - { - algCol = algCol + PcalParams.BeginAlg.length(); - foundBegin = true; - } else - { - algCol = line.indexOf(PcalParams.BeginFairAlg); - if (algCol != -1) { - // Found the "--fair". The more structurally nice thing to - // do here would be to move past the following "algorithm". - // However, it's easier to pass a parameter to the ParseAlgorithm - // class's GetAlgorithm method that tells it to go past the - // "algorithm" token. - algCol = algCol + PcalParams.BeginFairAlg.length(); - foundBegin = true; - foundFairBegin = true; - - } else { - algLine = algLine + 1; - } - } - ; - } - ; - if (!foundBegin) - { - PcalDebug.reportError("Beginning of algorithm string " + PcalParams.BeginAlg + " not found."); -// return exitWithStatus(STATUS_EXIT_WITH_ERRORS); - return null ; // added for testing - } - ; - - /* - * Set the algColumn and algLine fields of the mapping object. - */ - mapping.algColumn = algCol; - mapping.algLine = algLine; - - if (translationLine == -1) - { - /**************************************************************** - * Insert BEGIN/END TRANSLATION comments immediately after the * - * end of the comment that contains the beginning of the * - * algorithm. Set translationLine to the (Java) line number of * - * the BEGIN TRANSLATION. * - ****************************************************************/ - - // Set ecLine, ecCol to the position immediately after the - // *) that closes the current comment. - int depth = 1 ; - int ecLine = algLine ; - int ecCol = algCol ; - boolean notFound = true ; - while (notFound && ecLine < untabInputVec.size()) { - char[] line = ((String) untabInputVec.elementAt(ecLine)).toCharArray(); - - // check current line - while (notFound && ecCol < line.length-1) { - char ch = line[ecCol] ; - char ch2 = line[ecCol+1] ; - -// The following code isn't needed because the algorithm is inside a comment, and -// quotes and \* have no effect in determining where the comment ends. -// -// if (ch == '"') { -// // gobble string -// ch = ch2 ; -// ecCol++ ; -// while (ch != '"') { -// if (ch == '\\') { -// ecCol = ecCol + 2; -// } -// else { -// ecCol++ ; -// } ; -// if (ecCol < line.length - 1) { -// ch = line[ecCol] ; -// } -// else { -// ch = '"' ; -// } -// } ; -// ecCol++ ; -// } -// -// if (ch == '\\' && ch2 == '*' ) { -// // end of line comment, skip to end of line -// ecCol = 214748364; // a very large int -// } - if (ch == '(' && ch2 == '*') { - // left comment delimiter - depth++; - ecCol = ecCol + 2; - } - else if (ch == '*' && ch2 == ')') { - // right comment delimiter - depth--; - ecCol = ecCol + 2; - if (depth == 0) { - notFound = false ; - } - } - else { - // not an interesting character - ecCol++ ; - } - } - - // if not found, go to next line - if (notFound) { - ecLine++ ; - ecCol = 0; - } - } - - if (notFound) { - PcalDebug.reportError("Algorithm not in properly terminated comment"); -// return exitWithStatus(STATUS_EXIT_WITH_ERRORS); - return null ; // added for testing - } - - // Report an error if there's something else on the line that doesn't begin with "\*". This is probably - - String endStuff = ((String) untabInputVec.elementAt(ecLine)).substring(ecCol).trim() ; - - if (!endStuff.equals("") && !endStuff.startsWith("\\*")) { - PcalDebug.reportError("Text on same line following `*)' that ends the \n comment containing the algorithm."); -// return exitWithStatus(STATUS_EXIT_WITH_ERRORS); - return null ; // added for testing - } ; - - inputVec.insertElementAt("\\* BEGIN TRANSLATION", ecLine+1) ; - untabInputVec.insertElementAt("\\* BEGIN TRANSLATION", ecLine+1) ; - inputVec.insertElementAt("\\* END TRANSLATION", ecLine+2) ; - untabInputVec.insertElementAt("\\* END TRANSLATION", ecLine+2) ; - - translationLine = ecLine + 1; -//System.out.println(ecLine + ", " + ecCol); -//Debug.printVector(inputVec, "foo"); - } - - /* - * Set the mappings start line. - */ - mapping.tlaStartLine = translationLine + 1; - - /********************************************************************* - * Added by LL on 18 Feb 2006 to fix bugs related to handling of * - * comments. * - * * - * Remove all comments from the algorithm in untabInputVec, * - * replacing (* *) comments by spaces to keep the algorithm tokens * - * in the same positions for error reporting. * - *********************************************************************/ - try - { - ParseAlgorithm.uncomment(untabInputVec, algLine, algCol); - } catch (ParseAlgorithmException e) - { - PcalDebug.reportError(e); -// return exitWithStatus(STATUS_EXIT_WITH_ERRORS); - return null ; // added for testing - } - // } // end else of if (PcalParams.fromPcalFile) -- i.e., end processing - // of .tla input file. - - /********************************************************************* - * Set reader to a PcalCharReader for the input file (with tabs and * - * the previous translation removed), starting right after the * - * PcalParams.BeginAlg string. * - *********************************************************************/ - PcalCharReader reader = new PcalCharReader(untabInputVec, algLine, algCol, inputVec.size(), 0); - - /********************************************************************* - * Set ast to the AST node representing the entire algorithm. * - *********************************************************************/ - AST ast = null; - try - { - ast = ParseAlgorithm.getAlgorithm(reader, foundFairBegin); -// System.out.println(ast.toString()); -// For testing, we print out when the new code for eliminating the -// suttering-on-done and pc is used. -// if (ParseAlgorithm.omitPC || ParseAlgorithm.omitStutteringWhenDone) { -// System.out.println("omit pc = " + ParseAlgorithm.omitPC + -// ", omitStutteringWhenDone = " + ParseAlgorithm.omitStutteringWhenDone); -// } - - } catch (ParseAlgorithmException e) - { - PcalDebug.reportError(e); -// return exitWithStatus(STATUS_EXIT_WITH_ERRORS); - return null ; // added for testing - } - PcalDebug.reportInfo("Parsing completed."); -// tla-pcal debugging -//System.out.println("Translation Output:"); -//System.out.println(ast.toString()); - /********************************************************************* - * For -writeAST option, just write the file AST.tla and halt. * - *********************************************************************/ - if (PcalParams.WriteASTFlag) - { - WriteAST(ast); -// return exitWithStatus(STATUS_EXIT_WITHOUT_ERROR); - return null ; // added for testing - } - ; - - /********************************************************************* - * Rename algorithm variables to eliminate name conflicts--for * - * example, if the same variable is declared inside different * - * procedures, if a variable name and a label are the same, or if * - * the same label is used in to different procedures. This should * - * also report an error and terminate if it discovers a conflict * - * between the variable of a `with' statement and some other * - * identifier in the algorithm. It should also detect other * - * conflicts--for example, if there is a variable named "Init" or * - * "TRUE". However, there are conflicts that the translator can't * - * spot--for example, if a variable name is the same as the name of * - * some operator defined elsewhere in the TLA+ module. So it's not * - * worth going overboard in this checking. * - *********************************************************************/ - - // SZ February.15 2009: made non-static to make PCal stateless for tool runs - PCalTLAGenerator pcalTLAGenerator = new PCalTLAGenerator(ast); - try - { - pcalTLAGenerator.removeNameConflicts(); - } catch (RemoveNameConflictsException e1) - { - PcalDebug.reportError(e1); -// return exitWithStatus(STATUS_EXIT_WITH_ERRORS); - return null ; // added for testing - } - - /********************************************************************* - * Set the vector `translation' to the translation of the algorithm * - * represented by the AST ast. If called with the -spec option, * - * do the translation by calling TLC. Otherwise, call the ordinary * - * Translate method. * - *********************************************************************/ - Vector translation = null; - boolean tlcTranslation = PcalParams.SpecOption || PcalParams.MyspecOption || PcalParams.Spec2Option - || PcalParams.Myspec2Option; - - if (tlcTranslation) - { - try - { - translation = TLCTranslate(ast); - } catch (TLCTranslationException e) - { - PcalDebug.reportError(e); -// return exitWithStatus(STATUS_EXIT_WITH_ERRORS); - return null ; // added for testing - } - } else - { - try - { - translation = pcalTLAGenerator.translate(); - } catch (RemoveNameConflictsException e) - { - PcalDebug.reportError(e); -// return exitWithStatus(STATUS_EXIT_WITH_ERRORS); - return null ; // added for testing - } - } - ; - - PcalDebug.reportInfo("Translation completed."); -// tla-pcal Debugging -//System.exit(0); - /********************************************************************* - * For .tla input: * - * Rename the old file by changing its extension from "tla" to "old". * - *********************************************************************/ - // if (!PcalParams.fromPcalFile) - // { - File file; - try - { - file = new File(PcalParams.TLAInputFile + ".old"); - if (file.exists()) - { - file.delete(); - } - ; - file = new File(PcalParams.TLAInputFile + ".tla"); - file.renameTo(new File(PcalParams.TLAInputFile + ".old")); - } catch (Exception e) - { - PcalDebug.reportError("Could not rename input file " + PcalParams.TLAInputFile + ".tla" + " to " - + PcalParams.TLAInputFile + ".old"); -// return exitWithStatus(STATUS_EXIT_WITH_ERRORS); - return null ; // added for testing - } - ; - // } - - /********************************************************************* - * Add the translation to outputVec. * - *********************************************************************/ - int i = 0; - while (i < translation.size()) - { - outputVec.insertElementAt(translation.elementAt(i), i + translationLine + 1); - i = i + 1; - } - - /********************************************************************* - * Code from aborted version 1.31. * - * For .pcal input, set outputSuffixLoc and add the rest of the * - * input file to the output. * - *********************************************************************/ - // if (PcalParams.fromPcalFile) - // { - // PcalParams.outputSuffixLoc = new IntPair(outputVec.size(), 0); - // // if there's stuff in the suffix on the same line with the - // // end of the algorithm, write it on a separate line. - // IntPair curLoc = new IntPair(PcalParams.inputSuffixLoc.one, PcalParams.inputSuffixLoc.two); - // if (curLoc.one < untabInputVec.size()) - // { - // String lastLine = (String) untabInputVec.elementAt(curLoc.one); - // if (curLoc.two < lastLine.length()) - // { - // outputVec.addElement(lastLine.substring(curLoc.two)); - // } - // curLoc.one++; - // } - // // Copy the rest of the input file into the output file. - // for (int ii = curLoc.one; ii < untabInputVec.size(); ii++) - // { - // outputVec.addElement((String) untabInputVec.elementAt(ii)); - // } - // } - /********************************************************************* - * Write the output file. * - *********************************************************************/ - try - { - WriteStringVectorToFile(outputVec, PcalParams.TLAInputFile + ".tla"); - } catch (StringVectorToFileException e) - { - PcalDebug.reportError(e); -// return exitWithStatus(STATUS_EXIT_WITH_ERRORS); - return null ; // added for testing - } - - PcalDebug.reportInfo("New file " + PcalParams.TLAInputFile + ".tla" + " written."); - - /********************************************************************* - * Write the cfg file, unless the -nocfg option is used. * - *********************************************************************/ - File cfgFile = new File(PcalParams.TLAInputFile + ".cfg"); - Vector cfg = null; - boolean writeCfg = !PcalParams.Nocfg; - if (writeCfg && cfgFile.exists()) - { - if (cfgFile.canRead()) - { - try - { - cfg = fileToStringVector(PcalParams.TLAInputFile + ".cfg"); - } catch (FileToStringVectorException e) - { - PcalDebug.reportError(e); -// return exitWithStatus(STATUS_EXIT_WITH_ERRORS); - return null ; // added for testing - } - } else - { - /************************************************************* - * cfg file is read-only. * - *************************************************************/ - writeCfg = false; - PcalDebug.reportInfo("File " + PcalParams.TLAInputFile + ".cfg is read only, new version not written."); - } - } else - { - cfg = new Vector(); - cfg.addElement(PcalParams.CfgFileDelimiter); - } - ; - - /********************************************************************* - * Delete previously written part of cfg file. * - *********************************************************************/ - if (writeCfg) - { - i = 0; - boolean done = false; - while ((!done) && (cfg.size() > i)) - { - if (((String) cfg.elementAt(i)).indexOf(PcalParams.CfgFileDelimiter) == -1) - { - i = i + 1; - } else - { - done = true; - } - } - if (done) - { - /************************************************************* - * Delete all lines before the delimiting comment string. * - *************************************************************/ - while (i > 0) - { - cfg.removeElementAt(0); - i = i - 1; - } - } else - { - /************************************************************* - * The delimiting comment string written by the translator * - * not found in the cfg file, so presumably the user created * - * the cfg file before running the translator on the input * - * file. We insert the delimiter. * - *************************************************************/ - cfg.add(0, PcalParams.CfgFileDelimiter); - } - ; - - /****************************************************************** - * If defaultInitValue is used, add a CONSTANT statement setting * - * it to a model value of the same name. * - * (Added 22 Aug 2007 by LL.) * - ******************************************************************/ - if (tlcTranslation || ParseAlgorithm.hasDefaultInitialization) - { - cfg.add(0, "CONSTANT defaultInitValue = defaultInitValue"); - } - ; - /****************************************************************** - * Insert the `PROPERTY Termination' line if requested. * - ******************************************************************/ - if (PcalParams.CheckTermination) - { - cfg.add(0, "PROPERTY Termination"); - } - ; - - /****************************************************************** - * Insert the SPECIFICATION line if there isn't already one. * - ******************************************************************/ - i = 0; - boolean hasSpec = false; - while (i < cfg.size()) - { - String thisLine = (String) cfg.elementAt(i); - if ((thisLine.indexOf("SPECIFICATION") != -1) - && ((thisLine.indexOf("\\*") == -1) || (thisLine.indexOf("\\*") > thisLine - .indexOf("SPECIFICATION")))) - { - hasSpec = true; - } - ; - i = i + 1; - } - ; - if (hasSpec) - { - PcalDebug.reportInfo("File " + PcalParams.TLAInputFile - + ".cfg already contains SPECIFICATION statement," + "\n so new one not written."); - } else - { - cfg.add(0, "SPECIFICATION Spec"); - } - ; - try - { - WriteStringVectorToFile(cfg, PcalParams.TLAInputFile + ".cfg"); - } catch (StringVectorToFileException e) - { - PcalDebug.reportError(e); -// return exitWithStatus(STATUS_EXIT_WITH_ERRORS); - return null ; // added for testing - } - PcalDebug.reportInfo("New file " + PcalParams.TLAInputFile + ".cfg" + " written."); - } - ; - -// return exitWithStatus(STATUS_EXIT_WITHOUT_ERROR); - return PcalParams.tlaPcalMapping ; // added for testing - } // END main - - /** - * If run in the system mode, exits the program, in tool mode returns the status - * @param status - */ - private static int exitWithStatus(int status) - { - if (ToolIO.getMode() == ToolIO.SYSTEM) - { - // return exit status in system mode - System.exit(status); - } - - // just exit the function in tool mode - return status; - } - - /********************** Writing the AST ************************************/ - private static boolean WriteAST(AST ast) - { - Vector astFile = new Vector(); - astFile.addElement("------ MODULE AST -------"); - astFile.addElement("EXTENDS TLC"); - astFile.addElement("fairness == \"" + PcalParams.FairnessOption + "\""); - astFile.addElement(" "); - astFile.addElement("ast == "); - astFile.addElement(ast.toString()); - astFile.addElement("=========================="); - try - { - WriteStringVectorToFile(astFile, "AST.tla"); - } catch (StringVectorToFileException e) - { - PcalDebug.reportError(e); - return false; - } - PcalDebug.reportInfo("Wrote file AST.tla."); - return true; - } - - /************************* THE TLC TRANSLATION *****************************/ - - private static Vector TLCTranslate(AST ast) throws TLCTranslationException - /*********************************************************************** - * The result is a translation of the algorithm represented by ast * - * obtained by using TLC to execute the definition of Translation(ast) * - * in the TLA+ module PlusCal. It equals a vector with a single * - * element, which is the entire translation as a single string. * - * * - * This method relies on a bug in TLC's pretty-print routine that * - * causes it not to work properly on the output produced by the TLA * - * spec. Instead of prettyprinting the output as * - * * - * << "VARIABLES ...", * - * "vars == ... ", * - * ... * - * >> * - * * - * it prints the entire translation on a single line as * - * * - * << "VARIABLES ...", "vars == ... ", ... >> * - * * - * This allows the method to find the entire translation as the single * - * line that begins with "<<". If this TLC bug is fixed, then the * - * TLCTranslate method will have to be modified to read the spec as a * - * sequence of lines. This will probably require the TLA module that * - * translates the spec to print a special marker line to indicate the * - * end of the translation. * - ***********************************************************************/ - { - /********************************************************************* - * Write the file AST.tla that contains * - *********************************************************************/ - WriteAST(ast); - - /********************************************************************* - * For the -spec (rather than -myspec) option, copy the * - * specification's .tla and .cfg files PlusCal.tla to current * - * directory. * - *********************************************************************/ - if (PcalParams.SpecOption || PcalParams.Spec2Option) - { - try - { - Vector parseFile = PcalResourceFileReader.ResourceFileToStringVector(PcalParams.SpecFile + ".tla"); - - WriteStringVectorToFile(parseFile, PcalParams.SpecFile + ".tla"); - parseFile = PcalResourceFileReader.ResourceFileToStringVector(PcalParams.SpecFile + ".cfg"); - WriteStringVectorToFile(parseFile, PcalParams.SpecFile + ".cfg"); - - PcalDebug - .reportInfo("Wrote files " + PcalParams.SpecFile + ".tla and " + PcalParams.SpecFile + ".cfg."); - - } catch (UnrecoverableException e) - { - throw new TLCTranslationException(e.getMessage()); - } - - } - ; - /********************************************************************* - * Run TLC on the specification file and set tlcOut to TLC's output. * - *********************************************************************/ - String javaInvocation; - if (PcalParams.SpecOption || PcalParams.MyspecOption) - { - // Modified on 29 May 2010 by LL so tlc2 is run in - // all cases. - PcalDebug.reportInfo("Running TLC2."); - javaInvocation = "java -Xss1m tlc2.TLC "; - } else - { - PcalDebug.reportInfo("Running TLC2."); - javaInvocation = "java -Xss1m tlc2.TLC "; - } - ; - String tlcOut = " "; - Runtime rt = Runtime.getRuntime(); - try - { - // Modified on 29 May 2010 by LL to replace getErrorStream() with - // getInputStream(), which by Java logic gets standard out. (And no, - // getErrorStream() did not get standard non-error.) Apparently, - // TLC has been changed to put its output on stdout. - BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(rt.exec( - javaInvocation + PcalParams.SpecFile).getInputStream())); - while (tlcOut.indexOf("<<") == -1) - { - tlcOut = bufferedReader.readLine(); - } - ; - bufferedReader.close(); - } catch (Exception e) - { - throw new TLCTranslationException("Error reading output of TLC"); - } - ; - - /********************************************************************* - * Test if the translation failed and reported an error message, * - * bracketed by "@Error@" and "@EndError@" strings. If it did, * - * report the error and halt. If not, set tlcOut to the value of * - * Translation(ast) with the outermost "<<" and ">>" removed. * - *********************************************************************/ - if (tlcOut.indexOf("@Error@") != -1) - { - throw new TLCTranslationException("TLC's translation of the parsed algorithm failed with\n Error: " - + tlcOut.substring(tlcOut.indexOf("@Error@") + 7, tlcOut.indexOf("@EndError@"))); - } - ; - tlcOut = tlcOut.substring(2, tlcOut.lastIndexOf(">>")) + " "; - PcalDebug.reportInfo("Read TLC output."); - - /********************************************************************* - * Set transl to the string obtained by converting tlcOut, which is * - * a comma-separated sequence of strings, to the single string that * - * they represent. See PlusCal.tla for an explanation of the * - * encoding of TLA+ statements as sequences of strings. * - *********************************************************************/ - int i = 0; - String transl = ""; - while (i < tlcOut.length()) - { - if (tlcOut.charAt(i) == '"') - { - i = i + 1; - if ((tlcOut.charAt(i) == '\\') && (tlcOut.charAt(i + 1) == '"')) - /******************************************************* - * This is a quoted string. * - *******************************************************/ - { - if (tlcOut.charAt(i + 2) != '"') - { - throw new TLCTranslationException("I'm confused"); - - } - ; - i = i + 3; - while (tlcOut.charAt(i) != '"') - { - i = i + 1; - } - i = i + 1; - transl = transl + "\""; - while (tlcOut.charAt(i) != '"') // " - { - if (tlcOut.charAt(i) == '\\') - { - /*********************************************** - * Get special character. * - ***********************************************/ - transl = transl + tlcOut.substring(i, i + 2); - i = i + 2; - } else - { - transl = transl + tlcOut.substring(i, i + 1); - i = i + 1; - } - ; - } - ; - i = i + 8; - transl = transl + "\""; - } else - { - while (tlcOut.charAt(i) != '"') - { - if ((tlcOut.charAt(i) == '\\') && (tlcOut.charAt(i + 1) == '\\')) - { - i = i + 1; - } - ; - transl = transl + tlcOut.substring(i, i + 1); - i = i + 1; - } - ; - i = i + 1; - } - ; - } // END if (tlcOut.charAt(i) == '"') - else if (tlcOut.charAt(i) == ',') - { - i = i + 1; - } else - { - if (tlcOut.charAt(i) != ' ') - { - throw new TLCTranslationException("Expected space but found `" + tlcOut.charAt(i) + "'"); - } - ; - transl = transl + tlcOut.substring(i, i + 1); - i = i + 1; - } - ; - } - ; // END while - /* ****************************************************************** - * Wrap the translated string into approximately 80 character lines * - *******************************************************************/ - transl = WrapString(transl, 78); - Vector result = new Vector(); - result.addElement(transl); - return result; - } - - /***************** METHODS FOR READING AND WRITING FILES *****************/ - - private static void WriteStringVectorToFile(Vector inputVec, String fileName) throws StringVectorToFileException - /*********************************************************************** - * Writes the Vector of strings inputVec to file named fileName, with * - * each element of inputVec written on a new line. * - ***********************************************************************/ - { - try - { - // I have no idea what Java does if you try to write a new version - // of a read-only file. On Windows, it's happy to write it. Who - // the hell knows what it does on other operating systems? So, something - // like the following code could be necessary. However, the setWritable() - // method was introduced in Java 1.6, and in December 2009, that version - // isn't available on the Mac. And I can't find out how to set a file - // to be writable in any earlier version of Java. On the web, the advice - // is to copy the file, delete the old version, and rename the copy. - // But the File method's documentation actually says that delete may or - // may not delete the read-only file, depending on the OS. - // - // File file = new File(fileName); - // if (! file.canWrite()) { - // file.setWritable(true); - // } - BufferedWriter fileW = new BufferedWriter(new FileWriter(fileName)); - int lineNum = 0; - while (lineNum < inputVec.size()) - { - fileW.write((String) inputVec.elementAt(lineNum)); - fileW.newLine(); - lineNum = lineNum + 1; - } - ; - fileW.close(); - } catch (Exception e) - { - throw new StringVectorToFileException("Could not write file " + fileName); - } - ; - - } - - private static Vector fileToStringVector(String fileName) throws FileToStringVectorException - /*********************************************************************** - * Reads file fileName into a StringVector, a vector in which each * - * element is a line of the file. * - ***********************************************************************/ - { - Vector inputVec = new Vector(100); - try - { - BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(new FileInputStream(fileName))); - try - { - String nextLine = bufferedReader.readLine(); - while (nextLine != null) - { - inputVec.addElement(nextLine); - nextLine = bufferedReader.readLine(); - } - ; - bufferedReader.close(); - } catch (IOException e) - { - /********************************************************* - * Error while reading input file. * - *********************************************************/ - throw new FileToStringVectorException("Error reading file " + fileName + "."); - } - } - - catch (FileNotFoundException e) - { - /************************************************************** - * Input file could not be found. * - **************************************************************/ - throw new FileToStringVectorException("Input file " + fileName + " not found."); - } - ; - return inputVec; - } - - /********************* PROCESSING THE COMMAND LINE ***********************/ - - /** - * Processes the command line arguments - * - * This method changes values of public static variables of the {@link PcalParams} - * - * SZ: This will cause problems, due to the fact that the PCalParams are static. - * Any initialization should overwrite the previous, which is currently NOT the case - * Should be re-factored to non-static access to the properties - * - * @return status of processing. - * the status {@link trans#STATUS_OK} indicates that no errors has been detected. - * the status {@link trans#STATUS_EXIT_WITHOUT_ERROR} indicates that no errors has been found but translation - * should not be started (e.G -help call) - * the status {@link trans#STATUS_EXIT_WITH_ERRORS} indicates errors - * - * Change made on 9 December 2009 for pcal-file input. This procedure is - * called a second time if there is pcal-file input with an options statement. - * It will be the second call iff {@link PcalParams#optionsInFile} equals true. - * The second call should have a dummy extra argument in place of the - * command-line's file-name argument. When pcal files were eliminated, this - * kludgy mechanism was kept and used to indicate if the method is being called - * for options specified inside the module. - */ - static int parseAndProcessArguments(String[] args) - { - - /** ******************************************************************* - *<pre> - * Get the command-line arguments and set the appropriate parameters. * - * The following command line arguments are handled. Only the ones * - * marked with ** besides them can be specified in the module file's * - * options statement. The "-" can be omitted when the option is in * - * the module file's options statement. * - * * - * -help : Type a help file instead of running the program. * - * * - *** -spec name : Uses TLC and the TLA+ specification name.tla to do * - * the translation. The files name.tla and name.cfg * - * are copied from the java/ directory to the current * - * directory; the file AST.tla that defines `fairness' * - * to equal the fairness option and `ast' to equal * - * the the AST data structure representing the * - * algorithm is written to the current directory; and * - * TLC is run on name.tla to produce the translation. * - * * - *** -myspec name : Like -spec, except it finds the files names.tla * - * and names.cfg in the current directory, instead * - * of writing them there. * - * * - * -spec2 name * - * -myspec2 name : Like -spec and -myspec, except they use TLC2 * - * instead of TLC (aka TLC1). * - * * - * -writeAST : Writes the AST file as in the -spec option, but does * - * not perform the translation. * - * * - * -debug : Run in debugging mode, whatever that means. For the * - * parser, it just means that the toString() methods * - * output the line and column number along with the * - * node. * - * * - * -unixEOL : Forces the use of Unix end-of-line convention, * - * regardless of the system's default. Without this, * - * when run on Windows, the files it writes seem to have * - * a "^M" at the end of every line when viewed with * - * Emacs. * - * * - *** -wf : Conjoin to formula Spec weak fairness of each process's * - * next-state action * - * * - *** -sf : Conjoin to formula Spec strong fairness of each process's * - * next-state action * - * * - *** -wfNext : Conjoin to formula Spec weak fairness of the entire * - * next-state action * - * * - *** -nof : Conjoin no fairness formula to Spec. This is the default, * - * except when the -termination option is chosen. * - * * - *** -termination : Add to the .cfg file the command * - * PROPERTY Termination * - * * - * -nocfg : Suppress writing of the .cfg file. * - * * - * * - *** -noDoneDisjunct : Suppress the disjunct of the next-state * - * relation that describes stuttering steps taken * - * when the algorithm has halted. * - * * - *** -label : Tells the translator to add missing labels. This is * - * the default only for a uniprocess algorithm in which * - * the user has typed no labels. * - * * - * -reportLabels : True if the translator should print the names * - * and locations of all labels it adds. Like * - * -label, it tells the translator to add missing * - * labels. * - * * - *** -labelRoot name : If the translator adds missing labels, it * - * names them name1, name2, etc. Default value * - * is "Lbl_". * - * * - * THE FOLLOWING OPTIONS ADDED IN VERSION 1.4 * - * * - *** -lineWidth : The translation tries to perform the translation so * - * lines have this maximum width. (It will often * - * fail.) Default is 78, minimum value is 60. * - * * - *** -version : The version of PlusCal for which this algorithm is * - * written. If the language is ever changed to make * - * algorithms written for earlier versions no longer * - * legal, then the translator should do the appropriate * - * thing when the earlier version number is specified. * - *</pre> - ********************************************************************* */ - boolean inFile = PcalParams.optionsInFile; - boolean notInFile = !inFile; - // Just convenient abbreviations - boolean firstFairness = inFile; - // Used to allow a fairness property specified by a command-line - // option to be overridden by one in the pcal-file's options statement. - // It is set false when the first fairness property is set from - // the options statement. - boolean explicitNof = false; - // Set true when the "nof" fairness option is set by an explicit - // user request, rather than by default. It was added to fix - // a bug in -termination introduced in version 1.4 by having - // the options statement in the file. I think option processing - // can be simplified to eliminate this, but it's easier to add - // this kludge. - int nextArg = 0; - /****************************************************************** - * The number of the argument being processed. * - ******************************************************************/ - int maxArg = args.length - 1; - /****************************************************************** - * The number of option arguments. (For processing command-line * - * arguments, the last element of args is the input-file name.) * - ******************************************************************/ - if (maxArg < 0) - { - return CommandLineError("No arguments specified"); - } - - if (notInFile && (args[maxArg].length() != 0) && (args[maxArg].charAt(0) == '-')) - /****************************************************************** - * If the last argument begins with "-", then no file has been * - * specified. This should mean that the user has typed "-help", * - * but it could be a mistake. But let's just assume she typed * - * "-help", since she either wants or needs help. * - ******************************************************************/ - { - if (OutputHelpMessage()) - { - return STATUS_EXIT_WITHOUT_ERROR; - - } else - { - return STATUS_EXIT_WITH_ERRORS; - } - } - - while (nextArg < maxArg) - /******************************************************************* - * Process all the arguments, except for the input-file name. * - *******************************************************************/ - { - String option = args[nextArg]; - if (notInFile && option.equals("-help")) - { - if (OutputHelpMessage()) - { - return STATUS_EXIT_WITHOUT_ERROR; - } else - { - return STATUS_EXIT_WITH_ERRORS; - } - } else if (notInFile && option.equals("-writeAST")) - { - PcalParams.WriteASTFlag = true; - if (CheckForConflictingSpecOptions()) - { - return STATUS_EXIT_WITH_ERRORS; - } - } else if (option.equals("-spec") || - (inFile && option.equals("spec"))) - { - PcalParams.SpecOption = true; - if (CheckForConflictingSpecOptions()) - { - return STATUS_EXIT_WITH_ERRORS; - } - nextArg = nextArg + 1; - if (nextArg == maxArg) - { - return CommandLineError("Specification name must follow `-spec' option"); - } - PcalParams.SpecFile = args[nextArg]; - } else if (option.equals("-myspec") || - (inFile && option.equals("myspec"))) - { - PcalParams.MyspecOption = true; - if (CheckForConflictingSpecOptions()) - { - return STATUS_EXIT_WITH_ERRORS; - } - nextArg = nextArg + 1; - if (nextArg == maxArg) - { - return CommandLineError("Specification name must follow `-myspec' option"); - } - PcalParams.SpecFile = args[nextArg]; - } else if (notInFile && option.equals("-spec2")) - { - PcalParams.Spec2Option = true; - if (CheckForConflictingSpecOptions()) - { - return STATUS_EXIT_WITH_ERRORS; - } - ; - nextArg = nextArg + 1; - if (nextArg == maxArg) - { - return CommandLineError("Specification name must follow `-spec' option"); - } - PcalParams.SpecFile = args[nextArg]; - } else if (notInFile && option.equals("-myspec2")) - { - PcalParams.Myspec2Option = true; - if (CheckForConflictingSpecOptions()) - { - return STATUS_EXIT_WITH_ERRORS; - } - ; - nextArg = nextArg + 1; - if (nextArg == maxArg) - { - return CommandLineError("Specification name must follow `-myspec' option"); - } - PcalParams.SpecFile = args[nextArg]; - } else if (notInFile && option.equals("-debug")) - { - PcalParams.Debug = true; - } else if (notInFile && option.equals("-unixEOL")) - { - System.setProperty("line.separator", "\n"); - } else if (option.equals("-termination") || (inFile && option.equals("termination"))) - { - PcalParams.CheckTermination = true; - } else if (option.equals("-nocfg")) - { - PcalParams.Nocfg = true; - } else if (option.equals("-noDoneDisjunct") || (inFile && option.equals("noDoneDisjunct"))) - { - PcalParams.NoDoneDisjunct = true; - } else if (option.equals("-wf") || (inFile && option.equals("wf"))) - { - if (firstFairness) - { - PcalParams.FairnessOption = ""; - firstFairness = false; - } - if (!PcalParams.FairnessOption.equals("")) - { - return CommandLineError("Can only have one of -wf, -sf, -wfNext, " + "and -nof options"); - } - PcalParams.FairnessOption = "wf"; - } else if (option.equals("-sf") || (inFile && option.equals("sf"))) - { - if (firstFairness) - { - PcalParams.FairnessOption = ""; - firstFairness = false; - } - if (!PcalParams.FairnessOption.equals("")) - { - return CommandLineError("Can only have one of -wf, -sf, -wfNext, " + "and -nof options"); - } - PcalParams.FairnessOption = "sf"; - } else if (option.equals("-wfNext") || (inFile && option.equals("wfNext"))) - { - if (firstFairness) - { - PcalParams.FairnessOption = ""; - firstFairness = false; - } - if (!PcalParams.FairnessOption.equals("")) - { - return CommandLineError("Can only have one of -wf, -sf, -wfNext, " + "and -nof options"); - } - PcalParams.FairnessOption = "wfNext"; - } else if (option.equals("-nof") || (inFile && option.equals("nof"))) - { - if (firstFairness) - { - PcalParams.FairnessOption = ""; - firstFairness = false; - } - if (!PcalParams.FairnessOption.equals("")) - { - return CommandLineError("Can only have one of -wf, -sf, -wfNext, " + "and -nof options"); - } - PcalParams.FairnessOption = "nof"; - explicitNof = true; - } else if (option.equals("-label") || (inFile && option.equals("label"))) - { - PcalParams.LabelFlag = true; - } else if (notInFile && option.equals("-reportLabels")) - { - PcalParams.ReportLabelsFlag = true; - PcalParams.LabelFlag = true; - } else if (option.equals("-labelRoot") || (inFile && option.equals("labelRoot"))) - { - nextArg = nextArg + 1; - if (nextArg == maxArg) - { - return CommandLineError("Label root must follow `-labelRoot' option"); - } - PcalParams.LabelRoot = args[nextArg]; - } - // else if (option.equals("-readOnly") || (pcal && option.equals("readOnly"))) { - // PcalParams.readOnly = true; - // } - // else if (option.equals("-writable") || (pcal && option.equals("writable"))) { - // PcalParams.readOnly = false; - // } - else if (option.equals("-version") || (inFile && option.equals("version"))) - { - nextArg = nextArg + 1; - if (nextArg == maxArg) - { - return CommandLineError("Version number must follow `-version' option"); - } - if (!PcalParams.ProcessVersion(args[nextArg])) - { - return CommandLineError("Bad version number"); - } - - } else if (option.equals("-lineWidth")) - { - nextArg = nextArg + 1; - try - { - if (nextArg == maxArg) - { - throw new NumberFormatException(); - } - int a = new Integer(args[nextArg]).intValue(); - if (a < 60) - { - throw new NumberFormatException(); - } - PcalTLAGen.wrapColumn = a; - PcalTLAGen.ssWrapColumn = a - 33; - } catch (Exception e) - { - return CommandLineError("Integer value at least 60 must follow `-lineWidth' option"); - } - - } else - { - if (notInFile) - { - return CommandLineError("Unknown command-line option: " + option); - } else - { - return CommandLineError("Unknown or illegal option in options statement: " + option); - } - } - ; - nextArg = nextArg + 1; - } // END while (nextArg < maxArg) - - if (nextArg > maxArg) - /****************************************************************** - * The last option took an argument that was the last * - * command-line argument. * - ******************************************************************/ - { - return CommandLineError("No input file specified"); - } - - // SZ 02.16.2009: since this is a modification of the parameters, moved - // to the parameter handling method - if (PcalParams.FairnessOption.equals("-nof")) - { - PcalParams.FairnessOption = ""; - } - if (PcalParams.CheckTermination && PcalParams.FairnessOption.equals("") && !explicitNof) - { - PcalParams.FairnessOption = "wf"; - - } - - /******************************************************************** - * If we are processing the command-line arguments, we need to get * - * the input-file name. Otherwise, we're done. * - *******************************************************************/ - if (inFile) - { - return STATUS_OK; - } - - /******************************************************************** - * Set PcalParams.TLAInputFile to the last argument, removing a * - * "tla" extension if it has one. * - ********************************************************************/ - /* - int dotIndex = args[maxArg].lastIndexOf(".") ; - if (dotIndex == -1) - { - PcalParams.TLAInputFile = args[maxArg]; - } - else if (args[maxArg].substring(dotIndex).equals(".tla")) - { - PcalParams.TLAInputFile = args[maxArg].substring(0, dotIndex); - } - else - { - return CommandLineError("Input file has extension other than tla"); - } - */ - - // SZ 02.16.2009: check for correct file extension (ignoring case) - // and file existence. also handles dots in the pathname - File file = new File(args[maxArg]); - boolean hasExtension = false; - if (file.getName().lastIndexOf(".") == -1) - { - // no extension - PcalParams.TLAInputFile = file.getPath(); - } else - { - // extension present - if (file.getName().toLowerCase().endsWith(".tla")) - { - hasExtension = true; - } - // Aborted version 1.31 code - // else if (file.getName().toLowerCase().endsWith(".pcal")){ - // hasExtension = true; - // PcalParams.fromPcalFile = true; - // } - else - { - return CommandLineError("Input file has extension other than " /* pcal or */+ "tla"); - } - } - if (hasExtension) - { - // cut the extension - PcalParams.TLAInputFile = file.getPath().substring(0, file.getPath().lastIndexOf(".")); - if (!file.exists()) - { - return CommandLineError("Input file " + file.getPath() + " does not exist."); - } - } else - { - // aborted version 1.31 code - // file = new File(PcalParams.TLAInputFile + ".pcal"); - // if (file.exists()) - // { - // PcalParams.fromPcalFile = true; - // } else - // { - file = new File(PcalParams.TLAInputFile + ".tla"); - if (!file.exists()) - { - return CommandLineError("Input file " + PcalParams.TLAInputFile + ".pcal and " + file.getPath() - + ".tla not found"); - } - // } - } - // file = new File(PcalParams.TLAInputFile + (PcalParams.fromPcalFile?".pcal":".tla")); - // if (!file.exists()) - // { - // return CommandLineError("Input file " + file.getPath() + " not found"); - // } - - return STATUS_OK; - } - - /** - * Prints out the help message - * @return status if it has been successfully printed - */ - private static boolean OutputHelpMessage() - { - Vector helpVec = null; - try - { - helpVec = PcalResourceFileReader.ResourceFileToStringVector("help.txt"); - } catch (PcalResourceFileReaderException e) - { - PcalDebug.reportError(e); - return false; - } - int i = 0; - while (i < helpVec.size()) - { - ToolIO.out.println((String) helpVec.elementAt(i)); - i = i + 1; - } - - return true; - } - - /** - * Returns if the options are conflicting - * @return true if the provided options are conflicting, false otherwise - */ - private static boolean CheckForConflictingSpecOptions() - { - if ((PcalParams.SpecOption ? 1 : 0) + (PcalParams.MyspecOption ? 1 : 0) + (PcalParams.Spec2Option ? 1 : 0) - + (PcalParams.Myspec2Option ? 1 : 0) + (PcalParams.WriteASTFlag ? 1 : 0) > 1) - { - CommandLineError("\nCan have at most one of the options " + "-spec, -myspec, -spec2, -myspec2, writeAST"); - return true; - } - ; - return false; - } - - private static int CommandLineError(String msg) - /********************************************************************* - * Announce a command line error with the string indicating the * - * explanation and halt. * - *********************************************************************/ - { - PcalDebug.reportError("Command-line error: " + msg + "."); -// ToolIO.out.println("Command-line error: " + msg + "."); -// ToolIO.out.println("Use -help option for more information."); - return STATUS_EXIT_WITH_ERRORS; - } - - private static int findTokenPair(Vector vec, int lineNum, String tok1, String tok2) - /********************************************************************* - * Returns the number of the first line at or after lineNum in the * - * vector of strings vec containing tok1 followed by 1 or more * - * spaces followed by tok2. Returns -1 if such a line is not found. * - *********************************************************************/ - { - int i = lineNum; - while (i < vec.size()) - { - String line = (String) vec.elementAt(i); - int col = line.indexOf(tok1); - int nextcol = col + tok1.length(); - if (col != -1) - { - while ((nextcol < line.length()) && (line.charAt(nextcol) == ' ')) - { - nextcol = nextcol + 1; - } - ; - if ((nextcol < line.length()) && (nextcol == line.indexOf(tok2))) - { - return i; - } - } - ; - i = i + 1; - } - ; - return -1; - } - - /************************** RemoveTabs *********************************/ - - public static Vector removeTabs(Vector vec) - { - /******************************************************************** - * Returns a string vector obtained from the string vector vec by * - * replacing any evil tabs with the appropriate number of spaces, * - * where "appropriate" means adding from 1 to 8 spaces in order to * - * make the next character fall on a column with Java column * - * number (counting from 0) congruent to 0 mod 8. This is what * - * Emacs does when told to remove tabs, which makes it good enough * - * for me. * - ********************************************************************/ - Vector newVec = new Vector(); - int i = 0; - while (i < vec.size()) - { - String oldline = (String) vec.elementAt(i); - String newline = ""; - int next = 0; - while (next < oldline.length()) - { - if (oldline.charAt(next) == '\t') - { - int toAdd = 8 - (newline.length() % 8); - while (toAdd > 0) - { - newline = newline + " "; - toAdd = toAdd - 1; - } - } else - { - newline = newline + oldline.substring(next, next + 1); - } - ; - next = next + 1; - } - newVec.addElement(newline); - i = i + 1; - } - ; - return newVec; - } - - /********************* STRING UTILITY FUNCTIONS ***********************/ - - private static int NextSpace(String s, int cur) - /******************************************************************** - * Returns the first space in s at or after col. If there is none, * - * return the index of the last character in s. Spaces in strings * - * are not treated as spaces. Assumes s[cur] is not in a string. * - ********************************************************************/ - { - int i = cur; - boolean inString = false; - while ((i < s.length()) && ((s.charAt(i) != ' ') || inString)) - { - if ((s.charAt(i) == '"') && ((i == 0) || (s.charAt(i - 1) != '\\'))) - inString = !inString; - i = i + 1; - } - if (i == s.length()) - return i - 1; - else - return i; - } - - private static String WrapString(String inString, int col) - /********************************************************************* - * Returns the string inString with lines wrapped approximately at * - * col, taking care not to wrap in a string. * - *********************************************************************/ - { - int i = 0; - int ccol = 1; - StringBuffer sb = new StringBuffer(); - while (i < inString.length()) - { - if (inString.charAt(i) == ' ') // An initial space or a space - { - sb.append(' '); // that follows a space. It - i = i + 1; // can always be appended to a line. - ccol = ccol + 1; - } else - // Find next word, which starts at i. - { - int j = NextSpace(inString, i); - if (ccol + (j - i + 1) > col) - { - sb.append('\n'); - ccol = 1; - } - while (i <= j) // If this overflows col, then the word - { - sb.append(inString.charAt(i)); - // is longer than col. - i = i + 1; - ccol = ccol + 1; - } - } - } - return sb.toString(); - } - -} +package pcal; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileWriter; +import java.io.IOException; +import java.io.InputStreamReader; +import java.util.ArrayList; +import java.util.List; +import java.util.Vector; +import java.util.regex.Matcher; + +import pcal.ValidationCallBack.Generate; +import pcal.exception.FileToStringVectorException; +import pcal.exception.ParseAlgorithmException; +import pcal.exception.PcalResourceFileReaderException; +import pcal.exception.RemoveNameConflictsException; +import pcal.exception.StringVectorToFileException; +import pcal.exception.TLCTranslationException; +import pcal.exception.UnrecoverableException; +import util.TLAConstants; +import util.ToolIO; + +/*************************************************************************** +* <pre> +* CLASS trans * +* * +* BUGS: * +* - Interprets * +* * +* if (...) call f(...) ; return * +* * +* as if it were * +* * +* if (...) {call f(...) ; return } * +* * +* See 2 Dec 2015 Tlaplus Google group post by Jaak Ristioja * +* * +* - Accepts if (...) {...} ; else {...} * +* Generates code as if the ";" were not there. * +* +* - When the body of a macro contains a statement with(v \in ...) +* and v is a macro parameter, the argument is not being substituted +* for v. +* +* - I came across a "TLAExpr.renormalize() found anchor has moved to left" +* error, apparently caused by a substitution of an expression longer +* than the macro parameter it is instantiating in some weird case. +* * +* POSSIBLE FEATURE: * +* Adds the pc variable if a label has a + or - modifier. It's * +* not needed for a + modifier (and all the other conditions * +* for eliminating pc hold). The solution is not to try to handle * +* this case, but to add a "strongly fair process" construct, since * +* the pc can be eliminated only when there's just a single label. * +* This would be easy to implement if + modifiers are ignored or * +* cause an error and - modifiers mean no fairness. Then "strongly * +* fair" would act like "fair" except that + modifiers do nothing * +* and an SF instead of a WF is produced. * +* ----------------------------------------------------------------- * +* History: * +* Version 1.0: Original release. * +* * +* Version 1.1: (March 2006) * +* Introduced ability to have the translator * +* add missing labels. * +* * +* Version 1.2: (August 2007) * +* Introduced defaultInitValue so liveness can be * +* checked even with "uninitialized" variables. * +* * +* Version 1.3: (February 2008) * +* Added "await" as a synonym for "when" * +* * +* Version 1.4: (June 2010) * +* Added the options statement and the -lineWidth option. * +* * +* Version 1.5: (Jan 2011) * +* 1. Added the -noDoneDisjunct option. * +* 2. Added the new way of specifying fairness, with * +* the "fair" keyword with "+" modifier, and the "+" * +* and "-" label modifiers. * +* 3. Automatically removes the stuttering-on-termination * +* disjunction if all processes are "while (TRUE)" * +* statements with no gotos to "Done". * +* 4. In addition, if the "while (TRUE)"s have no * +* internal labels, it removes the pc variable. * +* 5. Changed the representation of Process and Procedure * +* nodes in the abstract syntax tree produced by * +* the -writeAST option and used when TLC is producing * +* the translation. * +* The changes 3-5 are not implemented when the -version * +* option specifies an earlier version. * +* * +* Version 1.6: (July 2011) * +* - Added the "--fair algorithm" syntax for specifying * +* weak fairness of the next-state action. (This * +* changes the way fairness of a uniprocess algorithm was * +* specified in Version 1.5. All legal Version 1.5 * +* algorithms should work with the "version 1.5" option.) * +* - Permitted previously defined macros to be called * +* inside a macro definition. * +* * +* Version 1.7: (19 January 2012) * +* - Translator adds "BEGIN/END TRANSLATION" if needed. * +* - Added support for Toolbox Goto PCal Source command. * +* * +* Version 1.8: (30 Mar 2012) * +* - Changed translation to remove stuttering-on- * +* termination disjunction if some process is * +* "while (TRUE)". * +* - Omitted the Termination definition if the stuttering- * +* on-termination disjunction is removed, since that * +* implies Termination is always FALSE. * +* (4 May 2012) * +* - Removed the unnecessary CASE in the pc = ... clause of * +* the Init predicate when there is only a single process * +* statement. * +* Version 1.9: (10 July 2019) * +* - Change translation to generate an explicit * +* Terminating action instead of the implicit one which * +* is the disjunct of the next-state relation. * +* - Support in-memory only translation for better * +* Toolbox integration (old file based translation was * +* racy). * +* - Minor changes related to better error reporting. * +* ----------------------------------------------------------------- * +* * +* This is the main method of the +CAL to TLA+ translation program. * +* the program has the following command-line options. Only the ones * +* marked with ** besides them can be specified in the file's * +* options statement. The "-" can be omitted when the option is in * +* the options statement. * +* * +* -help : Type a help file instead of running the program. * +* * +* -spec name : Uses TLC and the TLA+ specification name.tla to do * +* the translation. The files name.tla and name.cfg * +* are copied from the java/ directory to the current * +* directory; the file AST.tla that defines `fairness' * +* to equal the fairness option and `ast' to equal * +* the the AST data structure representing the algorithm * +* is written to the current directory; and TLC is run * +* on name.tla to produce the translation. * +* * +* -myspec name : Like -spec, except it finds the files names.tla * +* and names.cfg in the current directory, instead * +* of writing them there. * +* * +* -spec2 name * +* -myspec2 name : Like -spec and -myspec, except they use TLC2 * +* instead of TLC (aka TLC1). * +* * +* -writeAST : Writes the AST file as in the -spec option, but does * +* not perform the translation. * +* * +* -debug : Run in debugging mode, whatever that means. For the * +* parser, it just means that the toString() methods * +* output the line and column number along with the * +* node. * +* * +* -unixEOL : Forces the use of Unix end-of-line convention, regardless * +* of the system's default. Without this, when run on * +* Windows, the files it writes seem to have a "^M" at the * +* end of every line when viewed with Emacs. * +* * +*** -wf : Conjoin to formula Spec weak fairness of each process's * +* next-state action * +* * +*** -sf : Conjoin to formula Spec strong fairness of each process's * +* next-state action * +* * +*** -wfNext : Conjoin to formula Spec weak fairness of the entire * +* next-state action * +* * +*** -nof : Conjoin no fairness formula to Spec. This is the default, * +* except when the -termination option is chosen. * +* * +*** -termination : Add to the .cfg file the command * +* PROPERTY Termination * +* With this option, the default fairness option * +* becomes -wf. * +* * +* -nocfg : Suppress writing of the .cfg file. * +* * +*** -noDoneDisjunct : Suppress the disjunct of the next-state * +* relation that describes stuttering steps taken * +* when the algorithm has halted. * +* * +*** -label : Tells the translator to add missing labels. This is * +* the default only for a uniprocess algorithm in which * +* the user has typed no labels. * +* * +* -reportLabels : True if the translator should print the names * +* and locations of all labels it adds. Like * +* -label, it tells the translator to add missing * +* labels. * +* * +*** -labelRoot name : If the translator adds missing labels, it * +* names them name1, name2, etc. Default value * +* is "Lbl_". * +* * +* THE FOLLOWING OPTION ADDED IN VERSION 1.31 * +* * +*** -lineWidth : The translation tries to perform the translation so * +* lines have this maximum width. (It will often * +* fail.) Default is 78, minimum value is 60. * +* * +* THE FOLLOWING OPTIONS ADDED IN VERSION 1.4 * +* * +*** -lineWidth : The translation tries to perform the translation so * +* lines have this maximum width. (It will often * +* fail.) Default is 78, minimum value is 60. * +* * +*** -version : The version of PlusCal for which this algorithm is * +* written. If the language is ever changed to make * +* algorithms written for earlier versions no longer * +* legal, then the translator should do the appropriate * +* thing when the earlier version number is specified. * +* ------------------------------------------------------------------------ * +* * +* The program uses vector objects from the Vector class to implement * +* sequences (lists). This generates a compiler warning. * +* * +* In Java data structures like arrays and Vectors, numbering starts with * +* 0. Unlike programmers, human beings count from 1. I use the term "Java * +* ordinal" to refer a number that denotes a position that represents the * +* first item as 0, and the term "human ordinal" to refer to an ordinary * +* ordinal that counts the first item as 1. * +* * +* * +* NOTE: * +* * +* One process should be able to read the pc or stack of another. There * +* is no logical reason to forbid this. However, the definition of * +* Translation in PlusCal.tla does not distinguish between instances of pc * +* in the original algorithm and ones inserted by the translation. The * +* latter instances must be subscripted--that is replaced by something * +* like pc[self]. Therefore, the Translation operator subscripts the * +* instances of pc from the original algorithm. The Java Translate method * +* must not do this, but must subscript (and prime) only the instances of * +* pc and stack introduced during the translation process. * +* * +* * +* The following bugs should all have been fixed by the addition of * +* ParseAlgorithm.Uncommment by LL on 18 Feb 2006. * +* * +* - There cannot be a comment between a label and the * +* following ":". * +* * +* - There cannot be a comment immediately before the ")" that ends * +* the list of arguments in a call statement. * +* * +* - The code for reporting the location of an error has the * +* following problem. If the token where the error occurs is * +* preceded by a comment, then the position reported is that of the * +* beginning of the comment rather than that of the token. * +* * +* TENTATIVE CHANGE MADE * +* * +* The following change was made along with a modification to the parser * +* to allow semi-colons to be omitted when they're obviously unnecessary. * +* * +* The parser does not parse expressions in the +CAL algorithm; it just * +* scans ahead to the first token that it can determine is not part of the * +* expression. To make this work, the following tokens that are legal in * +* a TLA+ expression are illegal in a +CAL expression: * +* * +* variable variables begin do then := || * +* * +* Making additional tokens illegal might help the parser find errors * +* sooner. For example, example, if one forgets the ";" and writes * +* * +* x := x + 1 * +* if x > y + 17 then ... * +* * +* the parser takes everything up to the "then" to be the right-hand side * +* of the "x :=" assignment. Making "if" illegal in an expression would * +* allow the parser to catch the error at the "if". * +* </pre> +***************************************************************************/ +class trans { + /** Status indicating no errors and successful process */ + static final int STATUS_OK = 1; + /** Status of no errors, but abort of the translation */ + static final int STATUS_EXIT_WITHOUT_ERROR = 0; + /** Status of present errors and abort of the translation */ + static final int STATUS_EXIT_WITH_ERRORS = -1; + + private static final String PCAL_TRANSLATION_COMMENT_LINE_PREFIX + = "\\* " + PcalParams.BeginXlation1 + " " + PcalParams.BeginXlation2; + private static final String TLA_TRANSLATION_COMMENT_LINE_PREFIX + = "\\* " + PcalParams.EndXlation1 + " " + PcalParams.EndXlation2; + + + /** + * Main function called from the command line + * @param args, command line arguments + */ + public static void main(String[] args) + { + runMe(args); + } + + /** + * The main translator method + * @return one of {@link trans#STATUS_OK}, {@link trans#STATUS_EXIT_WITH_ERRORS}, + * {@link trans#STATUS_EXIT_WITH_ERRORS} + * indicating the status + * + * Modified by LL on 16 Dec 2011. Changed the return value to the + * TLAtoPCalMapping object for the translation. (The status return + * value was not being used.) If the translation fails, it returns + * null. + */ + public static int runMe(String[] args) + { + /********************************************************************* + * Get and print version number. * + *********************************************************************/ + // String lastModified = + // "last modified on Wed 11 March 2009 at 14:52:58 PST by lamport"; + /******************************************************************* + * This string is inserted by an Emacs macro when a new version is * + * saved. Unfortunately, Eclipse isn't Emacs, so the modification * + * date must be entered manually in the PcalParams module. * + *******************************************************************/ + + if (ToolIO.getMode() == ToolIO.SYSTEM) + { + PcalDebug.reportInfo("pcal.trans Version " + PcalParams.version + " of " + PcalParams.modDate); + } + + // SZ Mar 9, 2009: + /* + * This method is called in order to make sure, that the + * parameters are not sticky because these are could have been initialized + * by the previous run + */ + PcalParams.resetParams(); + /********************************************************************* + * Get and process arguments. + *********************************************************************/ + int status = parseAndProcessArguments(args); + + if (status != STATUS_OK) + { + return exitWithStatus(status); + } + + /********************************************************************* + * Read the input file, and set the Vector inputVec equal to its * + * contents, where inputVec[i] is the string containing the contents * + * of line i+1 of the input file. * + *********************************************************************/ + List<String> inputVec = null; + try + { + inputVec = fileToStringVector(PcalParams.TLAInputFile + /* (PcalParams.fromPcalFile ? ".pcal" : */TLAConstants.Files.TLA_EXTENSION /*)*/); + } catch (FileToStringVectorException e) + { + PcalDebug.reportError(e); + return exitWithStatus(STATUS_EXIT_WITH_ERRORS); + } + + /********************************************************************* + * outputVec is an alias for inputVec if the input is a .tla file, * + * which was not always the case in the aborted version 1.31. * + *********************************************************************/ + // Vector outputVec = PcalParams.fromPcalFile ? new Vector() : inputVec; + final List<String> outputVec = performTranslation(inputVec); + if (outputVec == null) { + return exitWithStatus(STATUS_EXIT_WITH_ERRORS); + } + + /********************************************************************* + * For .tla input: * + * Rename the old file by changing its extension from "tla" to "old". * + *********************************************************************/ + // if (!PcalParams.fromPcalFile) + // { + File file; + try + { + file = new File(PcalParams.TLAInputFile + ".old"); + if (file.exists()) + { + file.delete(); + } + ; + file = new File(PcalParams.TLAInputFile + TLAConstants.Files.TLA_EXTENSION); + file.renameTo(new File(PcalParams.TLAInputFile + ".old")); + } catch (Exception e) + { + PcalDebug.reportError("Could not rename input file " + PcalParams.TLAInputFile + TLAConstants.Files.TLA_EXTENSION + " to " + + PcalParams.TLAInputFile + ".old"); + return exitWithStatus(STATUS_EXIT_WITH_ERRORS); + } + ; + // } + + /********************************************************************* + * Code from aborted version 1.31. * + * For .pcal input, set outputSuffixLoc and add the rest of the * + * input file to the output. * + *********************************************************************/ + // if (PcalParams.fromPcalFile) + // { + // PcalParams.outputSuffixLoc = new IntPair(outputVec.size(), 0); + // // if there's stuff in the suffix on the same line with the + // // end of the algorithm, write it on a separate line. + // IntPair curLoc = new IntPair(PcalParams.inputSuffixLoc.one, PcalParams.inputSuffixLoc.two); + // if (curLoc.one < untabInputVec.size()) + // { + // String lastLine = (String) untabInputVec.elementAt(curLoc.one); + // if (curLoc.two < lastLine.length()) + // { + // outputVec.addElement(lastLine.substring(curLoc.two)); + // } + // curLoc.one++; + // } + // // Copy the rest of the input file into the output file. + // for (int ii = curLoc.one; ii < untabInputVec.size(); ii++) + // { + // outputVec.addElement((String) untabInputVec.elementAt(ii)); + // } + // } + /********************************************************************* + * Write the output file. * + *********************************************************************/ + try + { + WriteStringVectorToFile(outputVec, PcalParams.TLAInputFile + TLAConstants.Files.TLA_EXTENSION); + } catch (StringVectorToFileException e) + { + PcalDebug.reportError(e); + return exitWithStatus(STATUS_EXIT_WITH_ERRORS); + } + + PcalDebug.reportInfo("New file " + PcalParams.TLAInputFile + TLAConstants.Files.TLA_EXTENSION + " written."); + + /********************************************************************* + * Write the cfg file, unless the -nocfg option is used. * + *********************************************************************/ + final File cfgFile = new File(PcalParams.TLAInputFile + TLAConstants.Files.CONFIG_EXTENSION); + List<String> cfg = null; + boolean writeCfg = !PcalParams.Nocfg; + if (writeCfg && cfgFile.exists()) + { + if (cfgFile.canRead()) + { + try + { + cfg = fileToStringVector(PcalParams.TLAInputFile + TLAConstants.Files.CONFIG_EXTENSION); + } catch (FileToStringVectorException e) + { + PcalDebug.reportError(e); + return exitWithStatus(STATUS_EXIT_WITH_ERRORS); + } + } else + { + /************************************************************* + * cfg file is read-only. * + *************************************************************/ + writeCfg = false; + PcalDebug.reportInfo("File " + PcalParams.TLAInputFile + ".cfg is read only, new version not written."); + } + } else + { + cfg = new ArrayList<>(); + cfg.add(PcalParams.CfgFileDelimiter); + } + + /********************************************************************* + * Delete previously written part of cfg file. * + *********************************************************************/ + if (writeCfg) + { + int j = 0; + boolean done = false; + while ((!done) && (cfg.size() > j)) + { + if (((String) cfg.get(j)).indexOf(PcalParams.CfgFileDelimiter) == -1) + { + j = j + 1; + } else + { + done = true; + } + } + if (done) + { + /************************************************************* + * Delete all lines before the delimiting comment string. * + *************************************************************/ + while (j > 0) + { + cfg.remove(0); + j = j - 1; + } + } else + { + /************************************************************* + * The delimiting comment string written by the translator * + * not found in the cfg file, so presumably the user created * + * the cfg file before running the translator on the input * + * file. We insert the delimiter. * + *************************************************************/ + cfg.add(0, PcalParams.CfgFileDelimiter); + } + + /****************************************************************** + * If defaultInitValue is used, add a CONSTANT statement setting * + * it to a model value of the same name. * + * (Added 22 Aug 2007 by LL.) * + ******************************************************************/ + if (PcalParams.tlcTranslation() || ParseAlgorithm.hasDefaultInitialization) + { + cfg.add(0, "CONSTANT defaultInitValue = defaultInitValue"); + } + + /****************************************************************** + * Insert the `PROPERTY Termination' line if requested. * + ******************************************************************/ + if (PcalParams.CheckTermination) + { + cfg.add(0, "PROPERTY Termination"); + } + + /****************************************************************** + * Insert the SPECIFICATION line if there isn't already one. * + ******************************************************************/ + boolean hasSpec = false; + for (final String thisLine : cfg) { + if ((thisLine.indexOf(TLAConstants.KeyWords.SPECIFICATION) != -1) + && ((thisLine.indexOf("\\*") == -1) || (thisLine.indexOf("\\*") > thisLine + .indexOf(TLAConstants.KeyWords.SPECIFICATION)))) { + hasSpec = true; + break; + } + } + + if (hasSpec) + { + PcalDebug.reportInfo("File " + PcalParams.TLAInputFile + + ".cfg already contains " + TLAConstants.KeyWords.SPECIFICATION + + " statement," + "\n so new one not written."); + } else + { + cfg.add(0, TLAConstants.KeyWords.SPECIFICATION + " Spec"); + } + + try + { + WriteStringVectorToFile(cfg, PcalParams.TLAInputFile + TLAConstants.Files.CONFIG_EXTENSION); + } catch (StringVectorToFileException e) + { + PcalDebug.reportError(e); + return exitWithStatus(STATUS_EXIT_WITH_ERRORS); + } + PcalDebug.reportInfo("New file " + PcalParams.TLAInputFile + TLAConstants.Files.CONFIG_EXTENSION + " written."); + } + + return exitWithStatus(STATUS_EXIT_WITHOUT_ERROR); + } // END main + + // This is called from the main-invoked {@link #runMe(String[])} + // For some reason this method used to both mutate the argument, and then also returns that argument... ? + // Now we copy the argument, mutate the copy, and return that. + public static List<String> performTranslation(final List<String> specificationText) { + return performTranslation(specificationText, new ValidationCallBack.Noop()); + } + + public static List<String> performTranslation(final List<String> specificationText, ValidationCallBack cb) { + /** + * Create the new TLAtoPCalMapping object, call it mapping + * here and set PcalParams.tlaPcalMapping to point to it. + */ + final TLAtoPCalMapping mapping = new TLAtoPCalMapping() ; + PcalParams.tlaPcalMapping = mapping; + + /********************************************************************* + * Set untabInputVec to be the vector of strings obtained from * + * inputVec by replacing tabs with spaces. * + * * + * Tabs are date from the days when memory cost $1 per bit and are a * + * stupid anachronism. They should be banned. Although the various * + * methods taken from TLATeX should deal with tabs, there are * + * undoubtedly corner cases that don't work right. In particular, I * + * think there's one case where PcalCharReader.backspace() might be * + * called to backspace over a tab. It's easier to simply get rid of * + * the tabs than to try to make it work. * + * * + * Since the user might be evil enough to prefer tabs, with tla-file * + * input, the parts of the output file that are not produced by the * + * translator are copied from inputVec, so any tabs the user wants * + * are kept. * + *********************************************************************/ + final Vector<String> untabInputVec = removeTabs(specificationText); + + /** + * Look through the file for PlusCal options. They are put anywhere + * in the file (either before or after the module or in a comment) + * with the following sequence + * PlusCal <optional white space> options <optional white space> + * ( <options> ) + * + * where <options> has the same format as options on the command + * line. + */ + IntPair searchLoc = new IntPair(0, 0); + boolean notDone = true; + while (notDone) + { + try + { + ParseAlgorithm.FindToken("PlusCal", untabInputVec, searchLoc, ""); + String line = ParseAlgorithm.GotoNextNonSpace(untabInputVec, searchLoc); + String restOfLine = line.substring(searchLoc.two); + if (restOfLine.startsWith("options")) + { + // The first string after "PlusCal" not starting with a + // space character is "options" + if (ParseAlgorithm.NextNonIdChar(restOfLine, 0) == 7) + { + // The "options" should begin an options line + PcalParams.optionsInFile = true; + ParseAlgorithm.ProcessOptions(untabInputVec, searchLoc); + notDone = false; + } + } + } catch (ParseAlgorithmException e) + { + // The token "PlusCal" not found. + notDone = false; + } + } + + /** + * translationLine is set to the line of the output file at which + * the \* BEGIN TRANSLATION appears--whether it is inserted into the + * tla-file input by the user, or inserted into the output by the + * translator for pcal-file input. + */ + int translationLine = 0; + + /********************************************************************* + * Set algLine, algCol to the line and column just after the string * + * [--]algorithm that begins the algorithm. (These are Java * + * ordinals, in which counting begins at 0.) * + * * + * Modified by LL on 18 Feb 2006 to use untabInputVec instead of * + * inputVec, to correct bug that occurred when tabs preceded the * + * "--algorithm". * + * * + * For the code to handle pcal-input, I introduced the use of * + * IntPair objects to hold <line, column> Java coordinates (counting * + * from zero) in a file (or an image of a file in a String Vector). * + * For methods that advance through the file, the IntPair object is * + * passed as an argument and is advanced by the method. This is * + * what I should have been doing from the start, but I wasn't smart * + * enough The IntPair curLoc is the main one used in the part of the * + * following code that handles pcal-file input. * + *********************************************************************/ + int algLine = 0; + int algCol = -1; + /******************************************************************* + * If the BEGIN/END TRANSLATION region exists, then set * + * translationLine to the number of the line after which the * + * translation is to be inserted and delete the previous version * + * of the translation (if it exists) from inputVec. (Line * + * numbering is by Java ordinals.) If the region doesn't exist, * + * set translationLine to -1. * + * * + * Note: we remove the previous version from inputVec, because * + * that's where the translated output is going to go, and also * + * from untabInputVec, because we will then detect if the begin * + * and end translation lines contain part of the algorithm within * + * them. * + *******************************************************************/ + final ArrayList<String> output = new ArrayList<>(specificationText); + + translationLine = findTokenPair(untabInputVec, 0, PcalParams.BeginXlation1, PcalParams.BeginXlation2); + int endTranslationLine = -1; + if (translationLine != -1) + { + endTranslationLine = findTokenPair(untabInputVec, translationLine + 1, + PcalParams.EndXlation1, PcalParams.EndXlation2); + if (endTranslationLine == -1) + { + PcalDebug.reportError("No line containing `" + PcalParams.EndXlation1 + " " + PcalParams.EndXlation2); + return null; + } + + + int etl = endTranslationLine - 1; + while (translationLine < etl) + { + output.remove(etl); + untabInputVec.remove(etl); + etl--; + } + } + + // Search for "--algorithm" or "--fair". + // If found set algLine and algCol right after the last char, + // set foundBegin true, and set foundFairBegin true iff it + // was "--fair". Otherwise, set foundBegin false. + boolean foundBegin = false; + boolean foundFairBegin = false; + while ((algLine < untabInputVec.size()) && !foundBegin) + { + String line = untabInputVec.elementAt(algLine); + algCol = line.indexOf(PcalParams.BeginAlg); + if (algCol != -1) + { + algCol = algCol + PcalParams.BeginAlg.length(); + foundBegin = true; + } else + { + algCol = line.indexOf(PcalParams.BeginFairAlg); + if (algCol != -1) { + // Found the "--fair". The more structurally nice thing to + // do here would be to move past the following "algorithm". + // However, it's easier to pass a parameter to the ParseAlgorithm + // class's GetAlgorithm method that tells it to go past the + // "algorithm" token. + algCol = algCol + PcalParams.BeginFairAlg.length(); + foundBegin = true; + foundFairBegin = true; + + } else { + algLine = algLine + 1; + } + } + ; + } + ; + if (!foundBegin) + { + PcalDebug.reportError("Beginning of algorithm string " + PcalParams.BeginAlg + " not found."); + return null; + } + ; + + /* + * Set the algColumn and algLine fields of the mapping object. + */ + mapping.algColumn = algCol; + mapping.algLine = algLine; + + if (translationLine == -1) + { + /**************************************************************** + * Insert BEGIN/END TRANSLATION comments immediately after the * + * end of the comment that contains the beginning of the * + * algorithm. Set translationLine to the (Java) line number of * + * the BEGIN TRANSLATION. * + ****************************************************************/ + + // Set ecLine, ecCol to the position immediately after the + // *) that closes the current comment. + int depth = 1 ; + int ecLine = algLine ; + int ecCol = algCol ; + boolean notFound = true ; + while (notFound && ecLine < untabInputVec.size()) { + char[] line = ((String) untabInputVec.elementAt(ecLine)).toCharArray(); + + // check current line + while (notFound && ecCol < line.length-1) { + char ch = line[ecCol] ; + char ch2 = line[ecCol+1] ; + +// The following code isn't needed because the algorithm is inside a comment, and +// quotes and \* have no effect in determining where the comment ends. +// +// if (ch == '"') { +// // gobble string +// ch = ch2 ; +// ecCol++ ; +// while (ch != '"') { +// if (ch == '\\') { +// ecCol = ecCol + 2; +// } +// else { +// ecCol++ ; +// } ; +// if (ecCol < line.length - 1) { +// ch = line[ecCol] ; +// } +// else { +// ch = '"' ; +// } +// } ; +// ecCol++ ; +// } +// +// if (ch == '\\' && ch2 == '*' ) { +// // end of line comment, skip to end of line +// ecCol = 214748364; // a very large int +// } + if (ch == '(' && ch2 == '*') { + // left comment delimiter + depth++; + ecCol = ecCol + 2; + } + else if (ch == '*' && ch2 == ')') { + // right comment delimiter + depth--; + ecCol = ecCol + 2; + if (depth == 0) { + notFound = false ; + } + } + else { + // not an interesting character + ecCol++ ; + } + } + + // if not found, go to next line + if (notFound) { + ecLine++ ; + ecCol = 0; + } + } + + if (notFound) { + PcalDebug.reportError("Algorithm not in properly terminated comment"); + return null; + } + + // Report an error if there's something else on the line that doesn't begin with "\*". This is probably + + String endStuff = ((String) untabInputVec.elementAt(ecLine)).substring(ecCol).trim() ; + + if (!endStuff.equals("") && !endStuff.startsWith("\\*")) { + PcalDebug.reportError("Text on same line following `*)' that ends the \n comment containing the algorithm."); + return null ; + } ; + + output.add((ecLine + 1), (PCAL_TRANSLATION_COMMENT_LINE_PREFIX + " " + + String.format(Validator.CHECKSUM_TEMPLATE, "ffffffff", "ffffffff"))); + untabInputVec.insertElementAt(PCAL_TRANSLATION_COMMENT_LINE_PREFIX, (ecLine + 1)); + output.add((ecLine + 2), (TLA_TRANSLATION_COMMENT_LINE_PREFIX + " ")); + untabInputVec.insertElementAt(TLA_TRANSLATION_COMMENT_LINE_PREFIX, (ecLine + 2)); + + translationLine = ecLine + 1; +//System.out.println(ecLine + ", " + ecCol); +//Debug.printVector(inputVec, "foo"); + } + else { + // Check if the existing TLA+ translation has been modified by the user and + // raise a warning (via cb) if translation should be cancelled to not + // lose/overwrite the user changes. + final Matcher m = Validator.CHECKSUM_PATTERN.matcher(output.get(translationLine)); + if (m.find() && m.group(Validator.TLA_CHECKSUM) != null) { + final String checksumTLATranslation = Validator + .checksum(new Vector<>(specificationText.subList((translationLine + 1), endTranslationLine))); + if (!m.group(Validator.TLA_CHECKSUM).equals(checksumTLATranslation) && cb.shouldCancel()) { + return null; + } + } + } + + /* + * Set the mappings start line. + */ + mapping.tlaStartLine = translationLine + 1; + + /********************************************************************* + * Added by LL on 18 Feb 2006 to fix bugs related to handling of * + * comments. * + * * + * Remove all comments from the algorithm in untabInputVec, * + * replacing (* *) comments by spaces to keep the algorithm tokens * + * in the same positions for error reporting. * + *********************************************************************/ + try + { + ParseAlgorithm.uncomment(untabInputVec, algLine, algCol); + } catch (ParseAlgorithmException e) + { + PcalDebug.reportError(e); +// return exitWithStatus(STATUS_EXIT_WITH_ERRORS); + return null ; // added for testing + } + // } // end else of if (PcalParams.fromPcalFile) -- i.e., end processing + // of .tla input file. + + /********************************************************************* + * Set reader to a PcalCharReader for the input file (with tabs and * + * the previous translation removed), starting right after the * + * PcalParams.BeginAlg string. * + *********************************************************************/ + PcalCharReader reader = new PcalCharReader(untabInputVec, algLine, algCol, output.size(), 0); + + /********************************************************************* + * Set ast to the AST node representing the entire algorithm. * + *********************************************************************/ + AST ast = null; + try + { + ast = ParseAlgorithm.getAlgorithm(reader, foundFairBegin); +// System.out.println(ast.toString()); +// For testing, we print out when the new code for eliminating the +// suttering-on-done and pc is used. +// if (ParseAlgorithm.omitPC || ParseAlgorithm.omitStutteringWhenDone) { +// System.out.println("omit pc = " + ParseAlgorithm.omitPC + +// ", omitStutteringWhenDone = " + ParseAlgorithm.omitStutteringWhenDone); +// } + + } catch (ParseAlgorithmException e) + { + PcalDebug.reportError(e); +// return exitWithStatus(STATUS_EXIT_WITH_ERRORS); + return null ; // added for testing + } + PcalDebug.reportInfo("Parsing completed."); + +// tla-pcal debugging +//System.out.println("Translation Output:"); +//System.out.println(ast.toString()); + /********************************************************************* + * For -writeAST option, just write the file AST.tla and halt. * + *********************************************************************/ + if (PcalParams.WriteASTFlag) + { + WriteAST(ast); +// return exitWithStatus(STATUS_EXIT_WITHOUT_ERROR); + return null ; // added for testing + } + ; + + /********************************************************************* + * Rename algorithm variables to eliminate name conflicts--for * + * example, if the same variable is declared inside different * + * procedures, if a variable name and a label are the same, or if * + * the same label is used in to different procedures. This should * + * also report an error and terminate if it discovers a conflict * + * between the variable of a `with' statement and some other * + * identifier in the algorithm. It should also detect other * + * conflicts--for example, if there is a variable named "Init" or * + * "TRUE". However, there are conflicts that the translator can't * + * spot--for example, if a variable name is the same as the name of * + * some operator defined elsewhere in the TLA+ module. So it's not * + * worth going overboard in this checking. * + *********************************************************************/ + + // SZ February.15 2009: made non-static to make PCal stateless for tool runs + PCalTLAGenerator pcalTLAGenerator = new PCalTLAGenerator(ast); + try + { + pcalTLAGenerator.removeNameConflicts(); + } catch (RemoveNameConflictsException e1) + { + PcalDebug.reportError(e1); +// return exitWithStatus(STATUS_EXIT_WITH_ERRORS); + return null ; // added for testing + } + + /********************************************************************* + * Set the vector `translation' to the translation of the algorithm * + * represented by the AST ast. If called with the -spec option, * + * do the translation by calling TLC. Otherwise, call the ordinary * + * Translate method. * + *********************************************************************/ + Vector<String> translation = null; + + if (PcalParams.tlcTranslation()) + { + try + { + translation = TLCTranslate(ast); + } catch (TLCTranslationException e) + { + PcalDebug.reportError(e); +// return exitWithStatus(STATUS_EXIT_WITH_ERRORS); + return null ; // added for testing + } + } else + { + try + { + translation = pcalTLAGenerator.translate(); + } catch (RemoveNameConflictsException e) + { + PcalDebug.reportError(e); +// return exitWithStatus(STATUS_EXIT_WITH_ERRORS); + return null ; // added for testing + } + } + + final Matcher m = Validator.CHECKSUM_PATTERN.matcher(output.get(mapping.tlaStartLine - 1)); + ValidationCallBack.Generate g = null; + if (m.find()) { + // Do TLA_CHECKSUM first because doing PCAL_CHECKSUM (at the front of the + // string) invalidates start end enf of the TLA_CHECKSUM match. + if (m.group(Validator.TLA_CHECKSUM) != null) { + output.set(mapping.tlaStartLine - 1, + new StringBuilder(output.get(mapping.tlaStartLine - 1)).replace(m.start(Validator.TLA_CHECKSUM), + m.end(Validator.TLA_CHECKSUM), Validator.checksum(translation)).toString()); + } + if (m.group(Validator.PCAL_CHECKSUM) != null) { + output.set(mapping.tlaStartLine - 1, + new StringBuilder(output.get(mapping.tlaStartLine - 1)) + .replace(m.start(Validator.PCAL_CHECKSUM), m.end(Validator.PCAL_CHECKSUM), + Validator.checksum(ast.toString())) + .toString()); + } + } else if ((g = cb.shouldGenerate()) != Generate.NOT_NOW) { + if (g == Generate.DO_IT) { + output.set(mapping.tlaStartLine - 1, + output.get(mapping.tlaStartLine - 1) + " " + String.format(Validator.CHECKSUM_TEMPLATE, + Validator.checksum(ast.toString()), Validator.checksum(translation))); + } else { + output.set(mapping.tlaStartLine - 1, + output.get(mapping.tlaStartLine - 1) + " " + Validator.CHECKSUM_TEMPLATE_IGNORE); + } + } + + /********************************************************************* + * Add the translation to outputVec. * + *********************************************************************/ + int i = 0; + while (i < translation.size()) + { + output.add((i + translationLine + 1), translation.elementAt(i)); + i = i + 1; + } + + PcalDebug.reportInfo("Translation completed."); + return output; +// tla-pcal Debugging +//System.exit(0); + } + + /** + * If run in the system mode, exits the program, in tool mode returns the status + * @param status + */ + private static int exitWithStatus(int status) + { + if (ToolIO.getMode() == ToolIO.SYSTEM) + { + // return exit status in system mode + System.exit(status); + } + + // just exit the function in tool mode + return status; + } + + /********************** Writing the AST ************************************/ + private static boolean WriteAST(AST ast) + { + Vector<String> astFile = new Vector<String>(); + astFile.addElement("------ MODULE AST -------"); + astFile.addElement("EXTENDS TLC"); + astFile.addElement("fairness == \"" + PcalParams.FairnessOption + "\""); + astFile.addElement(" "); + astFile.addElement("ast == "); + astFile.addElement(ast.toString()); + astFile.addElement("=========================="); + try + { + WriteStringVectorToFile(astFile, "AST.tla"); + } catch (StringVectorToFileException e) + { + PcalDebug.reportError(e); + return false; + } + PcalDebug.reportInfo("Wrote file AST.tla."); + return true; + } + + /************************* THE TLC TRANSLATION *****************************/ + + private static Vector<String> TLCTranslate(AST ast) throws TLCTranslationException + /*********************************************************************** + * The result is a translation of the algorithm represented by ast * + * obtained by using TLC to execute the definition of Translation(ast) * + * in the TLA+ module PlusCal. It equals a vector with a single * + * element, which is the entire translation as a single string. * + * * + * This method relies on a bug in TLC's pretty-print routine that * + * causes it not to work properly on the output produced by the TLA * + * spec. Instead of prettyprinting the output as * + * * + * << "VARIABLES ...", * + * "vars == ... ", * + * ... * + * >> * + * * + * it prints the entire translation on a single line as * + * * + * << "VARIABLES ...", "vars == ... ", ... >> * + * * + * This allows the method to find the entire translation as the single * + * line that begins with "<<". If this TLC bug is fixed, then the * + * TLCTranslate method will have to be modified to read the spec as a * + * sequence of lines. This will probably require the TLA module that * + * translates the spec to print a special marker line to indicate the * + * end of the translation. * + ***********************************************************************/ + { + /********************************************************************* + * Write the file AST.tla that contains * + *********************************************************************/ + WriteAST(ast); + + /********************************************************************* + * For the -spec (rather than -myspec) option, copy the * + * specification's .tla and .cfg files PlusCal.tla to current * + * directory. * + *********************************************************************/ + if (PcalParams.SpecOption || PcalParams.Spec2Option) + { + try + { + Vector<String> parseFile = PcalResourceFileReader + .ResourceFileToStringVector(PcalParams.SpecFile + TLAConstants.Files.TLA_EXTENSION); + + WriteStringVectorToFile(parseFile, PcalParams.SpecFile + TLAConstants.Files.TLA_EXTENSION); + parseFile = PcalResourceFileReader + .ResourceFileToStringVector(PcalParams.SpecFile + TLAConstants.Files.CONFIG_EXTENSION); + WriteStringVectorToFile(parseFile, PcalParams.SpecFile + TLAConstants.Files.CONFIG_EXTENSION); + + PcalDebug.reportInfo("Wrote files " + PcalParams.SpecFile + TLAConstants.Files.TLA_EXTENSION + " and " + + PcalParams.SpecFile + TLAConstants.Files.CONFIG_EXTENSION + "."); + } catch (UnrecoverableException e) + { + throw new TLCTranslationException(e.getMessage()); + } + + } + ; + /********************************************************************* + * Run TLC on the specification file and set tlcOut to TLC's output. * + *********************************************************************/ + String javaInvocation; + if (PcalParams.SpecOption || PcalParams.MyspecOption) + { + // Modified on 29 May 2010 by LL so tlc2 is run in + // all cases. + PcalDebug.reportInfo("Running TLC2."); + javaInvocation = "java -Xss1m tlc2.TLC "; + } else + { + PcalDebug.reportInfo("Running TLC2."); + javaInvocation = "java -Xss1m tlc2.TLC "; + } + ; + String tlcOut = " "; + Runtime rt = Runtime.getRuntime(); + try + { + // Modified on 29 May 2010 by LL to replace getErrorStream() with + // getInputStream(), which by Java logic gets standard out. (And no, + // getErrorStream() did not get standard non-error.) Apparently, + // TLC has been changed to put its output on stdout. + BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(rt.exec( + javaInvocation + PcalParams.SpecFile).getInputStream())); + while (tlcOut.indexOf("<<") == -1) + { + tlcOut = bufferedReader.readLine(); + } + ; + bufferedReader.close(); + } catch (Exception e) + { + throw new TLCTranslationException("Error reading output of TLC"); + } + ; + + /********************************************************************* + * Test if the translation failed and reported an error message, * + * bracketed by "@Error@" and "@EndError@" strings. If it did, * + * report the error and halt. If not, set tlcOut to the value of * + * Translation(ast) with the outermost "<<" and ">>" removed. * + *********************************************************************/ + if (tlcOut.indexOf("@Error@") != -1) + { + throw new TLCTranslationException("TLC's translation of the parsed algorithm failed with\n Error: " + + tlcOut.substring(tlcOut.indexOf("@Error@") + 7, tlcOut.indexOf("@EndError@"))); + } + ; + tlcOut = tlcOut.substring(2, tlcOut.lastIndexOf(">>")) + " "; + PcalDebug.reportInfo("Read TLC output."); + + /********************************************************************* + * Set transl to the string obtained by converting tlcOut, which is * + * a comma-separated sequence of strings, to the single string that * + * they represent. See PlusCal.tla for an explanation of the * + * encoding of TLA+ statements as sequences of strings. * + *********************************************************************/ + int i = 0; + String transl = ""; + while (i < tlcOut.length()) + { + if (tlcOut.charAt(i) == '"') + { + i = i + 1; + if ((tlcOut.charAt(i) == '\\') && (tlcOut.charAt(i + 1) == '"')) + /******************************************************* + * This is a quoted string. * + *******************************************************/ + { + if (tlcOut.charAt(i + 2) != '"') + { + throw new TLCTranslationException("I'm confused"); + + } + ; + i = i + 3; + while (tlcOut.charAt(i) != '"') + { + i = i + 1; + } + i = i + 1; + transl = transl + "\""; + while (tlcOut.charAt(i) != '"') // " + { + if (tlcOut.charAt(i) == '\\') + { + /*********************************************** + * Get special character. * + ***********************************************/ + transl = transl + tlcOut.substring(i, i + 2); + i = i + 2; + } else + { + transl = transl + tlcOut.substring(i, i + 1); + i = i + 1; + } + ; + } + ; + i = i + 8; + transl = transl + "\""; + } else + { + while (tlcOut.charAt(i) != '"') + { + if ((tlcOut.charAt(i) == '\\') && (tlcOut.charAt(i + 1) == '\\')) + { + i = i + 1; + } + ; + transl = transl + tlcOut.substring(i, i + 1); + i = i + 1; + } + ; + i = i + 1; + } + ; + } // END if (tlcOut.charAt(i) == '"') + else if (tlcOut.charAt(i) == ',') + { + i = i + 1; + } else + { + if (tlcOut.charAt(i) != ' ') + { + throw new TLCTranslationException("Expected space but found `" + tlcOut.charAt(i) + "'"); + } + ; + transl = transl + tlcOut.substring(i, i + 1); + i = i + 1; + } + ; + } + ; // END while + /* ****************************************************************** + * Wrap the translated string into approximately 80 character lines * + *******************************************************************/ + transl = WrapString(transl, 78); + Vector<String> result = new Vector<String>(); + result.addElement(transl); + return result; + } + + /***************** METHODS FOR READING AND WRITING FILES *****************/ + + private static void WriteStringVectorToFile(final List<String> inputVec, String fileName) throws StringVectorToFileException + /*********************************************************************** + * Writes the List of strings inputVec to file named fileName, with * + * each element of inputVec written on a new line. * + ***********************************************************************/ + { + // TODO use Apache Commons for this + try (final BufferedWriter fileW = new BufferedWriter(new FileWriter(fileName))) { + // I have no idea what Java does if you try to write a new version + // of a read-only file. On Windows, it's happy to write it. Who + // the hell knows what it does on other operating systems? So, something + // like the following code could be necessary. However, the setWritable() + // method was introduced in Java 1.6, and in December 2009, that version + // isn't available on the Mac. And I can't find out how to set a file + // to be writable in any earlier version of Java. On the web, the advice + // is to copy the file, delete the old version, and rename the copy. + // But the File method's documentation actually says that delete may or + // may not delete the read-only file, depending on the OS. + // + // File file = new File(fileName); + // if (! file.canWrite()) { + // file.setWritable(true); + // } + for (final String line : inputVec) { + fileW.write(line); + fileW.newLine(); + } + } catch (Exception e) { + throw new StringVectorToFileException("Could not write file " + fileName); + } + } + + private static List<String> fileToStringVector(String fileName) throws FileToStringVectorException + /*********************************************************************** + * Reads file fileName into a StringVector, a vector in which each * + * element is a line of the file. * + ***********************************************************************/ + { + final List<String> inputVec = new ArrayList<>(100); + // TODO use Apache Commons for this + try (final BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(new FileInputStream(fileName)))) { + String nextLine = bufferedReader.readLine(); + while (nextLine != null) { + inputVec.add(nextLine); + nextLine = bufferedReader.readLine(); + } + } catch (FileNotFoundException e) { + /************************************************************** + * Input file could not be found. * + **************************************************************/ + throw new FileToStringVectorException("Input file " + fileName + " not found."); + } catch (IOException e) { + /********************************************************* + * Error while reading input file. * + *********************************************************/ + throw new FileToStringVectorException("Error reading file " + fileName + "."); + } + + return inputVec; + } + + /********************* PROCESSING THE COMMAND LINE ***********************/ + + /** + * Processes the command line arguments + * + * This method changes values of public static variables of the {@link PcalParams} + * + * SZ: This will cause problems, due to the fact that the PCalParams are static. + * Any initialization should overwrite the previous, which is currently NOT the case + * Should be re-factored to non-static access to the properties + * + * @return status of processing. + * the status {@link trans#STATUS_OK} indicates that no errors has been detected. + * the status {@link trans#STATUS_EXIT_WITHOUT_ERROR} indicates that no errors has been found but translation + * should not be started (e.G -help call) + * the status {@link trans#STATUS_EXIT_WITH_ERRORS} indicates errors + * + * Change made on 9 December 2009 for pcal-file input. This procedure is + * called a second time if there is pcal-file input with an options statement. + * It will be the second call iff {@link PcalParams#optionsInFile} equals true. + * The second call should have a dummy extra argument in place of the + * command-line's file-name argument. When pcal files were eliminated, this + * kludgy mechanism was kept and used to indicate if the method is being called + * for options specified inside the module. + */ + static int parseAndProcessArguments(String[] args) + { + + /** ******************************************************************* + *<pre> + * Get the command-line arguments and set the appropriate parameters. * + * The following command line arguments are handled. Only the ones * + * marked with ** besides them can be specified in the module file's * + * options statement. The "-" can be omitted when the option is in * + * the module file's options statement. * + * * + * -help : Type a help file instead of running the program. * + * * + *** -spec name : Uses TLC and the TLA+ specification name.tla to do * + * the translation. The files name.tla and name.cfg * + * are copied from the java/ directory to the current * + * directory; the file AST.tla that defines `fairness' * + * to equal the fairness option and `ast' to equal * + * the the AST data structure representing the * + * algorithm is written to the current directory; and * + * TLC is run on name.tla to produce the translation. * + * * + *** -myspec name : Like -spec, except it finds the files names.tla * + * and names.cfg in the current directory, instead * + * of writing them there. * + * * + * -spec2 name * + * -myspec2 name : Like -spec and -myspec, except they use TLC2 * + * instead of TLC (aka TLC1). * + * * + * -writeAST : Writes the AST file as in the -spec option, but does * + * not perform the translation. * + * * + * -debug : Run in debugging mode, whatever that means. For the * + * parser, it just means that the toString() methods * + * output the line and column number along with the * + * node. * + * * + * -unixEOL : Forces the use of Unix end-of-line convention, * + * regardless of the system's default. Without this, * + * when run on Windows, the files it writes seem to have * + * a "^M" at the end of every line when viewed with * + * Emacs. * + * * + *** -wf : Conjoin to formula Spec weak fairness of each process's * + * next-state action * + * * + *** -sf : Conjoin to formula Spec strong fairness of each process's * + * next-state action * + * * + *** -wfNext : Conjoin to formula Spec weak fairness of the entire * + * next-state action * + * * + *** -nof : Conjoin no fairness formula to Spec. This is the default, * + * except when the -termination option is chosen. * + * * + *** -termination : Add to the .cfg file the command * + * PROPERTY Termination * + * * + * -nocfg : Suppress writing of the .cfg file. * + * * + * * + *** -noDoneDisjunct : Suppress the disjunct of the next-state * + * relation that describes stuttering steps taken * + * when the algorithm has halted. * + * * + *** -label : Tells the translator to add missing labels. This is * + * the default only for a uniprocess algorithm in which * + * the user has typed no labels. * + * * + * -reportLabels : True if the translator should print the names * + * and locations of all labels it adds. Like * + * -label, it tells the translator to add missing * + * labels. * + * * + *** -labelRoot name : If the translator adds missing labels, it * + * names them name1, name2, etc. Default value * + * is "Lbl_". * + * * + * THE FOLLOWING OPTIONS ADDED IN VERSION 1.4 * + * * + *** -lineWidth : The translation tries to perform the translation so * + * lines have this maximum width. (It will often * + * fail.) Default is 78, minimum value is 60. * + * * + *** -version : The version of PlusCal for which this algorithm is * + * written. If the language is ever changed to make * + * algorithms written for earlier versions no longer * + * legal, then the translator should do the appropriate * + * thing when the earlier version number is specified. * + *</pre> + ********************************************************************* */ + boolean inFile = PcalParams.optionsInFile; + boolean notInFile = !inFile; + // Just convenient abbreviations + boolean firstFairness = inFile; + // Used to allow a fairness property specified by a command-line + // option to be overridden by one in the pcal-file's options statement. + // It is set false when the first fairness property is set from + // the options statement. + boolean explicitNof = false; + // Set true when the "nof" fairness option is set by an explicit + // user request, rather than by default. It was added to fix + // a bug in -termination introduced in version 1.4 by having + // the options statement in the file. I think option processing + // can be simplified to eliminate this, but it's easier to add + // this kludge. + int nextArg = 0; + /****************************************************************** + * The number of the argument being processed. * + ******************************************************************/ + int maxArg = args.length - 1; + /****************************************************************** + * The number of option arguments. (For processing command-line * + * arguments, the last element of args is the input-file name.) * + ******************************************************************/ + if (maxArg < 0) + { + return CommandLineError("No arguments specified"); + } + + if (notInFile && (args[maxArg].length() != 0) && (args[maxArg].charAt(0) == '-')) + /****************************************************************** + * If the last argument begins with "-", then no file has been * + * specified. This should mean that the user has typed "-help", * + * but it could be a mistake. But let's just assume she typed * + * "-help", since she either wants or needs help. * + ******************************************************************/ + { + if (OutputHelpMessage()) + { + return STATUS_EXIT_WITHOUT_ERROR; + + } else + { + return STATUS_EXIT_WITH_ERRORS; + } + } + + while (nextArg < maxArg) + /******************************************************************* + * Process all the arguments, except for the input-file name. * + *******************************************************************/ + { + String option = args[nextArg]; + if (notInFile && option.equals("-help")) + { + if (OutputHelpMessage()) + { + return STATUS_EXIT_WITHOUT_ERROR; + } else + { + return STATUS_EXIT_WITH_ERRORS; + } + } else if (notInFile && option.equals("-writeAST")) + { + PcalParams.WriteASTFlag = true; + if (CheckForConflictingSpecOptions()) + { + return STATUS_EXIT_WITH_ERRORS; + } + } else if (option.equals("-spec") || + (inFile && option.equals("spec"))) + { + PcalParams.SpecOption = true; + if (CheckForConflictingSpecOptions()) + { + return STATUS_EXIT_WITH_ERRORS; + } + nextArg = nextArg + 1; + if (nextArg == maxArg) + { + return CommandLineError("Specification name must follow `-spec' option"); + } + PcalParams.SpecFile = args[nextArg]; + } else if (option.equals("-myspec") || + (inFile && option.equals("myspec"))) + { + PcalParams.MyspecOption = true; + if (CheckForConflictingSpecOptions()) + { + return STATUS_EXIT_WITH_ERRORS; + } + nextArg = nextArg + 1; + if (nextArg == maxArg) + { + return CommandLineError("Specification name must follow `-myspec' option"); + } + PcalParams.SpecFile = args[nextArg]; + } else if (notInFile && option.equals("-spec2")) + { + PcalParams.Spec2Option = true; + if (CheckForConflictingSpecOptions()) + { + return STATUS_EXIT_WITH_ERRORS; + } + ; + nextArg = nextArg + 1; + if (nextArg == maxArg) + { + return CommandLineError("Specification name must follow `-spec' option"); + } + PcalParams.SpecFile = args[nextArg]; + } else if (notInFile && option.equals("-myspec2")) + { + PcalParams.Myspec2Option = true; + if (CheckForConflictingSpecOptions()) + { + return STATUS_EXIT_WITH_ERRORS; + } + ; + nextArg = nextArg + 1; + if (nextArg == maxArg) + { + return CommandLineError("Specification name must follow `-myspec' option"); + } + PcalParams.SpecFile = args[nextArg]; + } else if (notInFile && option.equals("-debug")) + { + PcalParams.Debug = true; + } else if (notInFile && option.equals("-unixEOL")) + { + System.setProperty("line.separator", "\n"); + } else if (option.equals("-termination") || (inFile && option.equals("termination"))) + { + PcalParams.CheckTermination = true; + } else if (option.equals("-nocfg")) + { + PcalParams.Nocfg = true; + } else if (option.equals("-noDoneDisjunct") || (inFile && option.equals("noDoneDisjunct"))) + { + PcalParams.NoDoneDisjunct = true; + } else if (option.equals("-wf") || (inFile && option.equals("wf"))) + { + if (firstFairness) + { + PcalParams.FairnessOption = ""; + firstFairness = false; + } + if (!PcalParams.FairnessOption.equals("")) + { + return CommandLineError("Can only have one of -wf, -sf, -wfNext, " + "and -nof options"); + } + PcalParams.FairnessOption = "wf"; + } else if (option.equals("-sf") || (inFile && option.equals("sf"))) + { + if (firstFairness) + { + PcalParams.FairnessOption = ""; + firstFairness = false; + } + if (!PcalParams.FairnessOption.equals("")) + { + return CommandLineError("Can only have one of -wf, -sf, -wfNext, " + "and -nof options"); + } + PcalParams.FairnessOption = "sf"; + } else if (option.equals("-wfNext") || (inFile && option.equals("wfNext"))) + { + if (firstFairness) + { + PcalParams.FairnessOption = ""; + firstFairness = false; + } + if (!PcalParams.FairnessOption.equals("")) + { + return CommandLineError("Can only have one of -wf, -sf, -wfNext, " + "and -nof options"); + } + PcalParams.FairnessOption = "wfNext"; + } else if (option.equals("-nof") || (inFile && option.equals("nof"))) + { + if (firstFairness) + { + PcalParams.FairnessOption = ""; + firstFairness = false; + } + if (!PcalParams.FairnessOption.equals("")) + { + return CommandLineError("Can only have one of -wf, -sf, -wfNext, " + "and -nof options"); + } + PcalParams.FairnessOption = "nof"; + explicitNof = true; + } else if (option.equals("-label") || (inFile && option.equals("label"))) + { + PcalParams.LabelFlag = true; + } else if (notInFile && option.equals("-reportLabels")) + { + PcalParams.ReportLabelsFlag = true; + PcalParams.LabelFlag = true; + } else if (option.equals("-labelRoot") || (inFile && option.equals("labelRoot"))) + { + nextArg = nextArg + 1; + if (nextArg == maxArg) + { + return CommandLineError("Label root must follow `-labelRoot' option"); + } + PcalParams.LabelRoot = args[nextArg]; + } + // else if (option.equals("-readOnly") || (pcal && option.equals("readOnly"))) { + // PcalParams.readOnly = true; + // } + // else if (option.equals("-writable") || (pcal && option.equals("writable"))) { + // PcalParams.readOnly = false; + // } + else if (option.equals("-version") || (inFile && option.equals("version"))) + { + nextArg = nextArg + 1; + if (nextArg == maxArg) + { + return CommandLineError("Version number must follow `-version' option"); + } + if (!PcalParams.ProcessVersion(args[nextArg])) + { + return CommandLineError("Bad version number"); + } + + } else if (option.equals("-lineWidth")) + { + nextArg = nextArg + 1; + try + { + if (nextArg == maxArg) + { + throw new NumberFormatException(); + } + int a = new Integer(args[nextArg]).intValue(); + if (a < 60) + { + throw new NumberFormatException(); + } + PcalTLAGen.wrapColumn = a; + PcalTLAGen.ssWrapColumn = a - 33; + } catch (Exception e) + { + return CommandLineError("Integer value at least 60 must follow `-lineWidth' option"); + } + + } else + { + if (notInFile) + { + return CommandLineError("Unknown command-line option: " + option); + } else + { + return CommandLineError("Unknown or illegal option in options statement: " + option); + } + } + ; + nextArg = nextArg + 1; + } // END while (nextArg < maxArg) + + if (nextArg > maxArg) + /****************************************************************** + * The last option took an argument that was the last * + * command-line argument. * + ******************************************************************/ + { + return CommandLineError("No input file specified"); + } + + // SZ 02.16.2009: since this is a modification of the parameters, moved + // to the parameter handling method + if (PcalParams.FairnessOption.equals("-nof")) + { + PcalParams.FairnessOption = ""; + } + if (PcalParams.CheckTermination && PcalParams.FairnessOption.equals("") && !explicitNof) + { + PcalParams.FairnessOption = "wf"; + + } + + /******************************************************************** + * If we are processing the command-line arguments, we need to get * + * the input-file name. Otherwise, we're done. * + *******************************************************************/ + if (inFile) + { + return STATUS_OK; + } + + /******************************************************************** + * Set PcalParams.TLAInputFile to the last argument, removing a * + * "tla" extension if it has one. * + ********************************************************************/ + /* + int dotIndex = args[maxArg].lastIndexOf(".") ; + if (dotIndex == -1) + { + PcalParams.TLAInputFile = args[maxArg]; + } + else if (args[maxArg].substring(dotIndex).equals(TLAConstants.FILE_TLA_EXTENSION)) + { + PcalParams.TLAInputFile = args[maxArg].substring(0, dotIndex); + } + else + { + return CommandLineError("Input file has extension other than tla"); + } + */ + + // SZ 02.16.2009: check for correct file extension (ignoring case) + // and file existence. also handles dots in the pathname + File file = new File(args[maxArg]); + boolean hasExtension = false; + if (file.getName().lastIndexOf(".") == -1) + { + // no extension + PcalParams.TLAInputFile = file.getPath(); + } else + { + // extension present + if (file.getName().toLowerCase().endsWith(TLAConstants.Files.TLA_EXTENSION)) + { + hasExtension = true; + } + // Aborted version 1.31 code + // else if (file.getName().toLowerCase().endsWith(".pcal")){ + // hasExtension = true; + // PcalParams.fromPcalFile = true; + // } + else + { + return CommandLineError("Input file has extension other than " /* pcal or */+ "tla"); + } + } + if (hasExtension) + { + // cut the extension + PcalParams.TLAInputFile = file.getPath().substring(0, file.getPath().lastIndexOf(".")); + if (!file.exists()) + { + return CommandLineError("Input file " + file.getPath() + " does not exist."); + } + } else + { + // aborted version 1.31 code + // file = new File(PcalParams.TLAInputFile + ".pcal"); + // if (file.exists()) + // { + // PcalParams.fromPcalFile = true; + // } else + // { + file = new File(PcalParams.TLAInputFile + TLAConstants.Files.TLA_EXTENSION); + if (!file.exists()) + { + return CommandLineError("Input file " + PcalParams.TLAInputFile + ".pcal and " + file.getPath() + + ".tla not found"); + } + // } + } + // file = new File(PcalParams.TLAInputFile + (PcalParams.fromPcalFile?".pcal":TLAConstants.FILE_TLA_EXTENSION)); + // if (!file.exists()) + // { + // return CommandLineError("Input file " + file.getPath() + " not found"); + // } + + return STATUS_OK; + } + + /** + * Prints out the help message + * @return status if it has been successfully printed + */ + private static boolean OutputHelpMessage() + { + Vector<String> helpVec = null; + try + { + helpVec = PcalResourceFileReader.ResourceFileToStringVector("help.txt"); + } catch (PcalResourceFileReaderException e) + { + PcalDebug.reportError(e); + return false; + } + int i = 0; + while (i < helpVec.size()) + { + ToolIO.out.println((String) helpVec.elementAt(i)); + i = i + 1; + } + + return true; + } + + /** + * Returns if the options are conflicting + * @return true if the provided options are conflicting, false otherwise + */ + private static boolean CheckForConflictingSpecOptions() + { + if ((PcalParams.SpecOption ? 1 : 0) + (PcalParams.MyspecOption ? 1 : 0) + (PcalParams.Spec2Option ? 1 : 0) + + (PcalParams.Myspec2Option ? 1 : 0) + (PcalParams.WriteASTFlag ? 1 : 0) > 1) + { + CommandLineError("\nCan have at most one of the options " + "-spec, -myspec, -spec2, -myspec2, writeAST"); + return true; + } + ; + return false; + } + + private static int CommandLineError(String msg) + /********************************************************************* + * Announce a command line error with the string indicating the * + * explanation and halt. * + *********************************************************************/ + { + PcalDebug.reportError("Command-line error: " + msg + "."); +// ToolIO.out.println("Command-line error: " + msg + "."); +// ToolIO.out.println("Use -help option for more information."); + return STATUS_EXIT_WITH_ERRORS; + } + + static int findTokenPair(Vector<String> vec, int lineNum, String tok1, String tok2) + /********************************************************************* + * Returns the number of the first line at or after lineNum in the * + * vector of strings vec containing tok1 followed by 1 or more * + * spaces followed by tok2. Returns -1 if such a line is not found. * + *********************************************************************/ + { + int i = lineNum; + while (i < vec.size()) + { + String line = vec.elementAt(i); + int col = line.indexOf(tok1); + int nextcol = col + tok1.length(); + if (col != -1) + { + while ((nextcol < line.length()) && (line.charAt(nextcol) == ' ')) + { + nextcol = nextcol + 1; + } + ; + if ((nextcol < line.length()) && (nextcol == line.indexOf(tok2))) + { + return i; + } + } + ; + i = i + 1; + } + ; + return -1; + } + + /************************** RemoveTabs *********************************/ + + public static Vector<String> removeTabs(List<String> input) { + /******************************************************************** + * Returns a string vector obtained from the string vector vec by * + * replacing any evil tabs with the appropriate number of spaces, * + * where "appropriate" means adding from 1 to 8 spaces in order to * + * make the next character fall on a column with Java column * + * number (counting from 0) congruent to 0 mod 8. This is what * + * Emacs does when told to remove tabs, which makes it good enough * + * for me. * + ********************************************************************/ + final Vector<String> newVec = new Vector<>(); + for (final String oldLine : input) { + String newLine = ""; + int next = 0; + while (next < oldLine.length()) { + if (oldLine.charAt(next) == '\t') { + int toAdd = 8 - (newLine.length() % 8); + while (toAdd > 0) { + newLine = newLine + " "; + toAdd = toAdd - 1; + } + } else { + newLine = newLine + oldLine.substring(next, next + 1); + } + next = next + 1; + } + // The following line is a hack to eliminate a rare bug that caused + // the translation to loop forever if a line ended with a symbol + // that is the prefix of a legal BUILT_IN token and that is not + // a legal token but has a prefix that is a legal token--for + // example "(+" and "::" (since ::= is a legal operator). + // It was added by LL on 13 May 2020 + newLine = newLine + " "; + + newVec.add(newLine); + } + + return newVec; + } + + /********************* STRING UTILITY FUNCTIONS ***********************/ + + private static int NextSpace(String s, int cur) + /******************************************************************** + * Returns the first space in s at or after col. If there is none, * + * return the index of the last character in s. Spaces in strings * + * are not treated as spaces. Assumes s[cur] is not in a string. * + ********************************************************************/ + { + int i = cur; + boolean inString = false; + while ((i < s.length()) && ((s.charAt(i) != ' ') || inString)) + { + if ((s.charAt(i) == '"') && ((i == 0) || (s.charAt(i - 1) != '\\'))) + inString = !inString; + i = i + 1; + } + if (i == s.length()) + return i - 1; + else + return i; + } + + private static String WrapString(String inString, int col) + /********************************************************************* + * Returns the string inString with lines wrapped approximately at * + * col, taking care not to wrap in a string. * + *********************************************************************/ + { + int i = 0; + int ccol = 1; + StringBuffer sb = new StringBuffer(); + while (i < inString.length()) + { + if (inString.charAt(i) == ' ') // An initial space or a space + { + sb.append(' '); // that follows a space. It + i = i + 1; // can always be appended to a line. + ccol = ccol + 1; + } else + // Find next word, which starts at i. + { + int j = NextSpace(inString, i); + if (ccol + (j - i + 1) > col) + { + sb.append('\n'); + ccol = 1; + } + while (i <= j) // If this overflows col, then the word + { + sb.append(inString.charAt(i)); + // is longer than col. + i = i + 1; + ccol = ccol + 1; + } + } + } + return sb.toString(); + } + +} diff --git a/tlatools/src/tla2sany/StandardModules/Bags.tla b/tlatools/src/tla2sany/StandardModules/Bags.tla index 7be7a4420403141009471a0c2c3804cf12a28615..265045e3a0842f467419ca02b51cecba5e1aac85 100644 --- a/tlatools/src/tla2sany/StandardModules/Bags.tla +++ b/tlatools/src/tla2sany/StandardModules/Bags.tla @@ -8,6 +8,13 @@ (* subset of the positive integers. An element e belongs to bag B iff e *) (* is in the domain of B, in which case bag B contains B[e] copies of e. *) (**************************************************************************) +(**************************************************************************) +(* All these definitions other than SubBag are overridden by TLC in the *) +(* Java class tlc2.module.Bags. Each operator is overridden by the Java *) +(* method with the same name, except that the mapping for TLA+ infix *) +(* operators is defined in the static block at the beginning of the Java *) +(* class. *) +(**************************************************************************) EXTENDS TLC LOCAL INSTANCE Naturals @@ -22,7 +29,7 @@ BagToSet(B) == DOMAIN B (* The set of elements at least one copy of which is in B. *) (************************************************************************) -SetToBag(S) == [e \in S |-> 1] +SetToBag(S) == [e \in S |-> 1] (************************************************************************) (* The bag that contains one copy of every element of the set S. *) (************************************************************************) @@ -42,7 +49,7 @@ B1 (+) B2 == (IF e \in DOMAIN B1 THEN B1[e] ELSE 0) + (IF e \in DOMAIN B2 THEN B2[e] ELSE 0) ] -B1 (-) B2 == +B1 (-) B2 == (************************************************************************) (* The bag B1 with the elements of B2 removed--that is, with one copy *) (* of an element removed from B1 for each copy of the same element in *) diff --git a/tlatools/src/tla2sany/StandardModules/FiniteSets.tla b/tlatools/src/tla2sany/StandardModules/FiniteSets.tla index 57ac40235075b64119e4ab2ddf92abeab9120a5a..601ab16beeab8c7fea1f137f8b626771ee262753 100644 --- a/tlatools/src/tla2sany/StandardModules/FiniteSets.tla +++ b/tlatools/src/tla2sany/StandardModules/FiniteSets.tla @@ -1,4 +1,9 @@ ---------------------------- MODULE FiniteSets ----------------------------- +(***************************************************************************) +(* The two definitions in this standard module are overridden by TLC in *) +(* the Java class tlc2.module.FiniteSets. Each operator is overridden by *) +(* the Java method with the same name. *) +(***************************************************************************) LOCAL INSTANCE Naturals LOCAL INSTANCE Sequences (*************************************************************************) diff --git a/tlatools/src/tla2sany/StandardModules/Integers.tla b/tlatools/src/tla2sany/StandardModules/Integers.tla index 2df47a87a219821e9bbd243defdc826992636431..2dd1cc9daa3b2ca327ac098604793293eef4e070 100644 --- a/tlatools/src/tla2sany/StandardModules/Integers.tla +++ b/tlatools/src/tla2sany/StandardModules/Integers.tla @@ -1,10 +1,23 @@ -------------------------------- MODULE Integers ---------------------------- (***************************************************************************) -(* A dummy module that declares the operators that are defined in the *) -(* real Integers module. *) +(* This module provides dummy definitions of the operators that are *) +(* defined by the real Integers module. It is expected that any tool will *) +(* provide its own implementations of these operators. See the book *) +(* "Specifying Systems" for the real Integers module. *) +(***************************************************************************) +(***************************************************************************) +(* The two definitions here and the definitions imported from the Naturals *) +(* module are overridden by TLC in the Java class tlc2.module.Integers. *) +(* Each operator is overridden by the Java method with the same name, *) +(* except that the mappings for the prefix - operator and the TLA+ infix *) +(* operators are defined in the static block at the beginning of the Java *) +(* class. *) (***************************************************************************) EXTENDS Naturals Int == { } +(***************************************************************************) +(* This defines the prefix - operator. *) +(***************************************************************************) -. a == 0 - a ============================================================================= diff --git a/tlatools/src/tla2sany/StandardModules/Naturals.tla b/tlatools/src/tla2sany/StandardModules/Naturals.tla index 6dc13fea6ae556e21f74d4f0322d0f7751c6a448..4c4de1defdba8624d2f296f839d810fe3faab92a 100644 --- a/tlatools/src/tla2sany/StandardModules/Naturals.tla +++ b/tlatools/src/tla2sany/StandardModules/Naturals.tla @@ -1,18 +1,35 @@ -------------------------------- MODULE Naturals ---------------------------- (***************************************************************************) -(* A dummy module that defines the operators that are defined by the *) -(* real Naturals module. *) +(* This module provides dummy definitions of the operators that are *) +(* defined by the real Naturals module. It is expected that any tool will *) +(* provide its own implementations of these operators. See the book *) +(* "Specifying Systems" for the real Naturals module. *) +(***************************************************************************) +(***************************************************************************) +(* These definitions are all overridden by TLC in the Java class *) +(* tlc2.module.Naturals. Each operator is overridden by the Java method *) +(* with the same name, except that the mapping for TLA+ infix operators *) +(* is defined in the static block at the beginning of the Java class. *) (***************************************************************************) - Nat == { } a+b == {a, b} -a-b == {a, b} -a*b == {a, b} + +a-b == CHOOSE n : b + n = a +a*b == TRUE a^b == {a, b} a<b == a = b a>b == a = b a \leq b == a = b a \geq b == a = b +(***************************************************************************) +(* a .. b is defined to equal {i \in Int : (a \leq i) /\ (i \leq b)} *) +(* where Int is the set of all integers. *) +(* *) +(* a % b and a \div b are defined so that for any integers a and b *) +(* with b > 0 , the following formula is true: *) +(* *) +(* a = b * (a \div b) + (a % b) *) +(***************************************************************************) a % b == {a, b} a \div b == {a, b} a .. b == {a, b} diff --git a/tlatools/src/tla2sany/StandardModules/Randomization.tla b/tlatools/src/tla2sany/StandardModules/Randomization.tla new file mode 100644 index 0000000000000000000000000000000000000000..a1266fee4f6067a0b8c857f3dd2a31298f3bec71 --- /dev/null +++ b/tlatools/src/tla2sany/StandardModules/Randomization.tla @@ -0,0 +1,64 @@ +-------------------------- MODULE Randomization------------------------------ +(***************************************************************************) +(* This module defines operators for choosing pseudo-random subsets of a *) +(* set. It is useful for inductive invariance checking, where the *) +(* operators appear only in the initial predicate. However, it may have *) +(* other uses. *) +(* *) +(* In breadth-first search model checking, the pseudo-random choices made *) +(* when computing possible steps satisfying the next-state relation are *) +(* determined by the first state of the step. Thus, the choices made for *) +(* a particular state will be the same in successive runs of TLC. This is *) +(* done to permit TLC to generate an error trace if an error is found. *) +(* This applies only when TLC is run in breadth-first search mode. In *) +(* particular, the choices made in simulation mode are independent of the *) +(* state for which they are made. *) +(***************************************************************************) + +(***************************************************************************) +(* Except for TestRandomSetOfSubsets, all these definitions are overridden *) +(* by TLC in the Java class tlc2.module.Randomization. Each operator is *) +(* overridden by the Java method with the same name. *) +(***************************************************************************) +LOCAL INSTANCE Naturals +LOCAL INSTANCE FiniteSets + +(***************************************************************************) +(* RandomSubset(k, S) equals a randomly chosen subset of S containing k *) +(* elements, where 0 < k < Cardinality(S). *) +(***************************************************************************) +RandomSubset(k, S) == CHOOSE T \in SUBSET S : Cardinality(T) = T + +(***************************************************************************) +(* RandomSetOfSubsets(k, n, S) equals a pseudo-randomly chosen set of *) +(* subsets of S -- that is, a randomly chosen subset of SUBSET S . Thus, *) +(* each element T of this set is a subset of S. Each such T is chosen so *) +(* that each element of S has a probability n / Cardinality(S) of being in *) +(* T. Thus, the average number of elements in each chosen subset T is n. *) +(* The set RandomSetOfSubsets(k, n, S) is obtained by making k such *) +(* choices of T . Because this can produce duplicate choices, the number *) +(* of elements T in this set may be less than k. The average number of *) +(* elements in RandomSetOfSubsets(k, n, S) seems to be difficult to *) +(* compute in general. However, there is very little variation in the *) +(* actual number of elements in the chosen set for fixed values of k, n, *) +(* and Cardinality(S). You can therefore use the operator *) +(* TestRandomSetOfSubsets defined below to find out approximately how *) +(* close to k the cardinality of the chosen set of subsets is. *) +(***************************************************************************) +RandomSetOfSubsets(k, n, S) == CHOOSE T \in SUBSET SUBSET S : + Cardinality(T) \leq k + +(***************************************************************************) +(* The value of TestRandomSetOfSubsets(k, n, S) is a sequence of five *) +(* values that are the cardinality of the set of subsets produced by five *) +(* executions of RandomSetOfSubsets(k, n, S). For constant values of k, *) +(* n, and S, you can enter TestRandomSetOfSubsets(k, n, S) in the Evaluate *) +(* Constant Expression section of a TLC model in the TLA+ Toolbox. *) +(* Running TLC will then tell you the approximate number of elements in *) +(* the set of subsets produced by RandomSetOfSubsets for these parameters. *) +(* You can then choose k to obtain a set of the desired size. *) +(***************************************************************************) +TestRandomSetOfSubsets(k, n, S) == + [i \in 1..5 |-> Cardinality(RandomSetOfSubsets(k, n, S))] + +============================================================================= diff --git a/tlatools/src/tla2sany/StandardModules/RealTime.tla b/tlatools/src/tla2sany/StandardModules/RealTime.tla index 1026c66a4b853a896a3b6f3374b02292f0562753..c326bfa0a0075428864f425f6c741fc13ac11810 100644 --- a/tlatools/src/tla2sany/StandardModules/RealTime.tla +++ b/tlatools/src/tla2sany/StandardModules/RealTime.tla @@ -1,4 +1,9 @@ ----------------------------- MODULE RealTime ------------------------------- +(***************************************************************************) +(* This standard module is described in Chapter 9 of the book "Specifying *) +(* Systems". The type of specification described there cannot be handled *) +(* by the TLC model checker. *) +(***************************************************************************) EXTENDS Reals VARIABLE now diff --git a/tlatools/src/tla2sany/StandardModules/Reals.tla b/tlatools/src/tla2sany/StandardModules/Reals.tla index f7aa44677fcb75da6c0717f71188d65886a8fa84..c63cd93dbea6a35ca6152cf24aba396fc2c90fe3 100644 --- a/tlatools/src/tla2sany/StandardModules/Reals.tla +++ b/tlatools/src/tla2sany/StandardModules/Reals.tla @@ -1,8 +1,14 @@ -------------------------------- MODULE Reals ------------------------------- (***************************************************************************) -(* A dummy module that declares the operators that are defined in the *) -(* real Reals module. It should produce an error if TLC tries to *) -(* evaluate these operators when it shouldn't. *) +(* This module provides dummy definitions of the operators that are *) +(* defined by the real Reals module. It is expected that any tool that *) +(* handles specifications that use real numbers will provide its own *) +(* implementations of these operators. TLC currently does not handle real *) +(* numbers and produces an error if a specification requires TLC to *) +(* evaluate the operators defined here or to evaluate any operator defined *) +(* in the Integers module that is applied to non-integer real numbers. *) +(* *) +(* See the book "Specifying Systems" for the real Reals module. *) (***************************************************************************) EXTENDS Integers diff --git a/tlatools/src/tla2sany/StandardModules/Sequences.tla b/tlatools/src/tla2sany/StandardModules/Sequences.tla index df77a97facdf6d62ce44aa6d4dd304894742fc1d..d179cf8a59913d24c0c56dbe61493fd892a11a1d 100644 --- a/tlatools/src/tla2sany/StandardModules/Sequences.tla +++ b/tlatools/src/tla2sany/StandardModules/Sequences.tla @@ -5,10 +5,16 @@ (* {1, 2, ... , n}). This is also how TLA+ defines an n-tuple, so *) (* tuples are sequences. *) (***************************************************************************) +(***************************************************************************) +(* These definitions are all overridden by TLC in the Java class *) +(* tlc2.module.Sequences. Each operator is overridden by the Java method *) +(* with the same name, except that the mapping for TLA+ infix operators *) +(* is defined in the static block at the beginning of the Java class. *) +(***************************************************************************) LOCAL INSTANCE Naturals (*************************************************************************) - (* Imports the definitions from Naturals, but don't export them. *) + (* Imports the definitions from Naturals, but doesn't export them. *) (*************************************************************************) Seq(S) == UNION {[1..n -> S] : n \in Nat} @@ -51,7 +57,7 @@ SelectSeq(s, Test(_)) == (*************************************************************************) LET F[i \in 0..Len(s)] == (*******************************************************************) - (* F[i] equals SelectSeq(SubSeq(s, 1, i), Test] *) + (* F[i] equals SelectSeq(SubSeq(s, 1, i), Test) . *) (*******************************************************************) IF i = 0 THEN << >> ELSE IF Test(s[i]) THEN Append(F[i-1], s[i]) diff --git a/tlatools/src/tla2sany/StandardModules/TLC.tla b/tlatools/src/tla2sany/StandardModules/TLC.tla index a9567ac5272fc5fcd620e58e13a9f1652510b3bd..0384c039761a1a517955481c153b2f5ea7f301bf 100644 --- a/tlatools/src/tla2sany/StandardModules/TLC.tla +++ b/tlatools/src/tla2sany/StandardModules/TLC.tla @@ -1,12 +1,77 @@ ------------------------------- MODULE TLC ---------------------------------- +(***************************************************************************) +(* This is a standard module for use with the TLC model checker. *) +(* Operators not explained by comments here are explained in the book *) +(* "Specifying Systems". *) +(* *) +(* The definitions of all the operators in this module are overridden by *) +(* TLC with methods defined in the Java class tlc2.module.TLC. Each *) +(* definition is overridden with the method of the same name, except that *) +(* the mapping from infix operators to Java methods is specified in the *) +(* static block at the beginning of the Java class. *) +(***************************************************************************) LOCAL INSTANCE Naturals LOCAL INSTANCE Sequences +LOCAL INSTANCE FiniteSets ----------------------------------------------------------------------------- Print(out, val) == val PrintT(out) == TRUE + (************************************************************************) + (* This expression equals TRUE, but evaluating it causes TLC to print *) + (* the value of out. *) + (************************************************************************) + Assert(val, out) == IF val = TRUE THEN TRUE ELSE CHOOSE v : TRUE JavaTime == CHOOSE n : n \in Nat + +(***************************************************************************) +(* TLC can read and set a special list of values while evaluating *) +(* expressions using the operators TLCSet and TLCGet. When TLC evaluates *) +(* TLCSet(i,v), for any positive integer i and arbitrary value v, it *) +(* obtains the value TRUE and sets element number i of the list to v. *) +(* When TLC evaluates TLCGet(i), the value it obtains is the current value *) +(* of the element number i of this list. *) +(* *) +(* One use of this feature is to check TLC's progress during long *) +(* computations. For example, suppose TLC is evaluating a formula *) +(* \A x \in S : P where S is a large set, so it evaluates P many times. *) +(* You can use TLCGet, TLCSet, and Print to print something after every *) +(* 1000 times TLC evaluates P. *) +(* *) +(* As explained in the description of the TLCEval operator below, you may *) +(* also want to use this feature to count how many times TLC is evaluating *) +(* an expression e. To use value number i as the counter, just replace e *) +(* by *) +(* *) +(* IF TLCSet(i, TLCGet(i)+1) THEN e ELSE 42 *) +(* *) +(* (The ELSE expression is never evaluated.) *) +(* *) +(* TLCGet accepts some pre-defined string values to query TLC state. The *) +(* values are as follows: *) +(* *) +(* - TLCGet("distinct") evaluates to the total number of distinct states *) +(* found by TLC so far, globally. *) +(* - TLCGet("queue") evaluates to the number of states currently in the *) +(* queue to be checked. *) +(* - TLCGet("duration") evaluates to the number of seconds elapsed since *) +(* model checking began. *) +(* - TLCGet("diameter") evaluates to the length of the longest behavior *) +(* found by TLC so far, globally (equals one in the initial predicate).*) +(* - TLCGet("level") is the length of the current behavior (equals zero *) +(* in the evaluation of the initial predicate). *) +(* *) +(* For reasons of efficiency, TLCGet and TLCSet behave somewhat strangely *) +(* when TLC is run with multiple worker threads. Each worker thread *) +(* maintains its own individual copy of the list of values on which it *) +(* evaluates TLCGet and TLCSet. The worker threads are activated only *) +(* after the computation and invariance checking of the initial states. *) +(* Before then, evaluating TLCSet(i,v) sets the element i of the list *) +(* maintained by all threads. Thus, the lists of all the worker threads *) +(* can be initialized by putting the appropriate TLCSet expression in an *) +(* ASSUME expression or in the initial predicate. *) +(***************************************************************************) TLCGet(i) == CHOOSE n : TRUE TLCSet(i, v) == TRUE ----------------------------------------------------------------------------- @@ -26,11 +91,57 @@ SortSeq(s, Op(_, _)) == (i < j) => Op(s[p[i]], s[p[j]]) \/ (s[p[i]] = s[p[j]]) IN [i \in 1..Len(s) |-> s[Perm[i]]] +(***************************************************************************) +(* TLC evaluates RandomElement(S) to be a pseudo-randomly chosen element *) +(* of the set S, where each element of S is chosen with equal probability. *) +(* This feature was added to enable the computation of statistical *) +(* properties of a specification's executions by running TLC in simulation *) +(* mode. We don't know if anyone has ever done this. *) +(* *) +(* In breadth-first search model checking, the pseudo-random choices made *) +(* when computing possible steps satisfying the next-state relation are *) +(* determined by the first state of the step. Thus, the choices made for *) +(* a particular state will be the same in successive runs of TLC. This is *) +(* done to permit TLC to generate an error trace if an error is found. *) +(* This applies only when TLC is run in breadth-first search mode. The *) +(* random choices made in simulation mode are independent of the state for *) +(* which they are made. *) +(***************************************************************************) RandomElement(s) == CHOOSE x \in s : TRUE +(***************************************************************************) +(* The constant value Any has the special property that, for any value v, *) +(* TLC evaluates the expression v \in Any to equal TRUE. The special *) +(* value Any was introduced because TLA+ originally allowed only functions *) +(* to be defined recursively, and it was sometimes convenient to define a *) +(* function with domain Any. It is retained for backwards compatibility. *) +(***************************************************************************) Any == CHOOSE x : TRUE ToString(v) == (CHOOSE x \in [a : v, b : STRING] : TRUE).b + (************************************************************************) + (* This equals a string that is the TLA+ representation of the value *) + (* that TLC obtains by evaluating v. *) + (************************************************************************) +(***************************************************************************) +(* TLC often uses lazy evaluation. For example, it may not enumerate the *) +(* elements of a set of the form {x \in T : P(x)} unless it has to; and it *) +(* doesn't have to if it only needs to check if an element e is in that *) +(* set. (TLC can do that by evaluating x \in T and P(e).) TLC uses *) +(* heuristics to determine when it should completely evaluate an *) +(* expression. Those heuristics work well most of the time. However, *) +(* sometimes lazy evaluation can result in the expression ultimately being *) +(* evaluated multiple times instead of just once. This can especially be *) +(* a problem when evaluating a recursively defined operator. You can get *) +(* TLC to fully evaluate an expression exp and not use lazy evaluation by *) +(* replacing exp with TLCEval(exp). *) +(* *) +(* If TLC is taking a long time to evaluate something, you can check if *) +(* lazy evaluation is the source of the problem by using the TLCSet and *) +(* TLCGet operators to count how many times expressions are being *) +(* evaluated. *) +(***************************************************************************) TLCEval(v) == v -============================================================================= + +============================================================================= \ No newline at end of file diff --git a/tlatools/src/tla2sany/StandardModules/Toolbox.tla b/tlatools/src/tla2sany/StandardModules/Toolbox.tla new file mode 100644 index 0000000000000000000000000000000000000000..113eb3b690bda01e066ca6dd906e03acc272b968 --- /dev/null +++ b/tlatools/src/tla2sany/StandardModules/Toolbox.tla @@ -0,0 +1,23 @@ +----------------------------- MODULE Toolbox -------------------------------- +(***************************************************************************) +(* *) +(***************************************************************************) +LOCAL INSTANCE Naturals \* a + b +LOCAL INSTANCE FiniteSets \* Cardinality +LOCAL INSTANCE TLC \* TLCGet + +----------------------------------------------------------------------------- + +(* Trace Exploration *) + +_TETrace == + CHOOSE f \in [ (Nat \ {0}) -> STRING ] : TRUE + +LOCAL _TETraceLength == Cardinality(DOMAIN _TETrace) + +_TEPosition == + IF TLCGet("level") >= _TETraceLength + THEN _TETraceLength + ELSE TLCGet("level") + 1 + +============================================================================= diff --git a/tlatools/src/tla2sany/configuration/ASCII_CharStream.java b/tlatools/src/tla2sany/configuration/ASCII_CharStream.java index 3bbbdc33c62fe04d8607cddbcb981a66c6ae4c7a..71e03c7b21f9c766d9afad93ec142b73cc8f1bb7 100644 --- a/tlatools/src/tla2sany/configuration/ASCII_CharStream.java +++ b/tlatools/src/tla2sany/configuration/ASCII_CharStream.java @@ -284,7 +284,7 @@ public final class ASCII_CharStream public ASCII_CharStream(java.io.InputStream dstream, int startline, int startcolumn, int buffersize) { - this(new java.io.InputStreamReader(dstream), startline, startcolumn, 4096); + this(new java.io.InputStreamReader(dstream), startline, startcolumn, buffersize); } public ASCII_CharStream(java.io.InputStream dstream, int startline, @@ -296,7 +296,7 @@ public final class ASCII_CharStream static public void ReInit(java.io.InputStream dstream, int startline, int startcolumn, int buffersize) { - ReInit(new java.io.InputStreamReader(dstream), startline, startcolumn, 4096); + ReInit(new java.io.InputStreamReader(dstream), startline, startcolumn, buffersize); } static public void ReInit(java.io.InputStream dstream, int startline, diff --git a/tlatools/src/tla2sany/drivers/FrontEndException.java b/tlatools/src/tla2sany/drivers/FrontEndException.java index 609ab4413c130db9e1ee0fbc3d9e198f6cb03426..e8804206b23545f562a602697c3e97c05b8cffdf 100644 --- a/tlatools/src/tla2sany/drivers/FrontEndException.java +++ b/tlatools/src/tla2sany/drivers/FrontEndException.java @@ -4,16 +4,17 @@ package tla2sany.drivers; /** * Exception thrown by SANY on errors * @author Leslie Lamport - * @version $Id$ */ public class FrontEndException extends Exception { - // SZ Feb 20, 2009: fixed the exception chaining // instead of having the cause as a member, the // exception now has it in trace // also changed the visibility to public - public FrontEndException(Exception cause) - { - super(cause); + public FrontEndException(final Exception cause) { + super(cause); + } + + public FrontEndException(final String msg) { + super(msg); } } diff --git a/tlatools/src/tla2sany/drivers/SANY.java b/tlatools/src/tla2sany/drivers/SANY.java index 9ce89d8765e1d429b7a557732907bc11ec47af8e..2ae22d90d45f955d61217e07cf3752cc9cfe06fc 100644 --- a/tlatools/src/tla2sany/drivers/SANY.java +++ b/tlatools/src/tla2sany/drivers/SANY.java @@ -78,7 +78,7 @@ public class SANY { // should be reported /** - * The TLAFrontEnd.frontEndMain method Processes an entire TLA+ spec + * The SANY.frontEndMain method Processes an entire TLA+ spec * that starts in the file named in "filename", including all files * it refers to directly or indirectly in EXTENDS or INSTANCE * constructs. "fileName" is relative to the current directory. It @@ -114,7 +114,7 @@ public class SANY { * processing--those are reported in the returned specification * object, which must be checked as described above. */ - public static final void frontEndMain( + public static final int frontEndMain( SpecObj spec, String fileName, PrintStream syserr) throws FrontEndException { @@ -130,20 +130,20 @@ public class SANY { {frontEndSemanticAnalysis(spec, syserr, doLevelChecking);} ; } catch (InitException ie) { - return; + return -1; } catch (ParseException pe) { - return; + return -1; } catch (SemanticException se) { - return; + return -1; } catch (Exception e) { // e.printStackTrace(syserr); syserr.println(e.toString()); throw new FrontEndException(e); } - return; + return 0; } /** @@ -197,6 +197,10 @@ public class SANY { // Parse all of the files referred to by the top-level file in specification public static void frontEndParse(SpecObj spec, PrintStream syserr) + throws ParseException { + frontEndParse(spec, syserr, true); + } + public static void frontEndParse(SpecObj spec, PrintStream syserr, boolean validatePCalTranslation) throws ParseException { /*********************************************************************** * Modified on 12 May 2008 by LL to remove "throws AbortException", * @@ -206,7 +210,7 @@ public class SANY { try { // Actual parsing method called from inside loadSpec() - if (!spec.loadSpec(spec.getFileName(), spec.parseErrors)) + if (!spec.loadSpec(spec.getFileName(), spec.parseErrors, validatePCalTranslation)) { // dead code SZ 02. Aug 2009 /* @@ -389,7 +393,7 @@ public class SANY { } /** - * Main driver method for maintainers and debuggers of the TLAFrontEnd. + * Main driver method for maintainers and debuggers of SANY. * * Calls frontEndMain for each file named on the command line and then, * barring errors too severe, calls the Explorer tool with the resulting @@ -436,13 +440,16 @@ public class SANY { if (FileUtil.createNamedInputStream(args[i], spec.getResolver()) != null) { try { - frontEndMain(spec, args[i], ToolIO.out); + int ret = frontEndMain(spec, args[i], ToolIO.out); + if (ret != 0) { + System.exit(ret); + } } catch (FrontEndException fe) { // For debugging fe.printStackTrace(); ToolIO.out.println(fe); - return; + System.exit(-1); } // Compile operator usage stats @@ -452,13 +459,14 @@ public class SANY { // Run the Semantic Graph Exploration tool Explorer explorer = new Explorer(spec.getExternalModuleTable()); try { - explorer.main(); + explorer.main(args); } catch (ExplorerQuitException e) { /*do nothing*/ } } } else { ToolIO.out.println("Cannot find the specified file " + args[i] + "."); + System.exit(-1); } } } diff --git a/tlatools/src/tla2sany/explorer/DotExplorerVisitor.java b/tlatools/src/tla2sany/explorer/DotExplorerVisitor.java new file mode 100644 index 0000000000000000000000000000000000000000..dfd828dc4732f5ed78ed3630fe8fb22e58543041 --- /dev/null +++ b/tlatools/src/tla2sany/explorer/DotExplorerVisitor.java @@ -0,0 +1,178 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tla2sany.explorer; + +import java.io.FileNotFoundException; +import java.io.PrintWriter; +import java.util.ArrayDeque; +import java.util.Deque; +import java.util.HashMap; +import java.util.Hashtable; +import java.util.Map; + +import tla2sany.semantic.Context; +import tla2sany.semantic.FormalParamNode; +import tla2sany.semantic.LetInNode; +import tla2sany.semantic.ModuleNode; +import tla2sany.semantic.OpApplNode; +import tla2sany.semantic.OpDeclNode; +import tla2sany.semantic.OpDefNode; +import tla2sany.semantic.SemanticNode; +import tla2sany.semantic.Subst; +import util.FileUtil; + +public class DotExplorerVisitor extends ExplorerVisitor { + + private static final Map<Class<? extends SemanticNode>, String> type2format = new HashMap<>(); + + static { + type2format.put(OpDefNode.class, " [style=filled,shape=diamond,fillcolor=\"red\","); + type2format.put(OpApplNode.class, " [color=\"green\","); + type2format.put(OpDeclNode.class, " [shape=square,color=\"yellow\","); + type2format.put(LetInNode.class, " [color=\"orange\","); + } + + private final ModuleNode rootModule; + private final Hashtable<Integer, ExploreNode> table; + private final PrintWriter writer; + private final Deque<ExploreNode> stack = new ArrayDeque<>(); + private final boolean includeLineNumbers = Boolean.getBoolean(DotExplorerVisitor.class.getName() + ".includeLineNumbers"); + + public DotExplorerVisitor(final ModuleNode rootModule) { + this.rootModule = rootModule; + this.table = new NoopTable<>(); + try { + this.writer = new PrintWriter(FileUtil.newBFOS(rootModule.getName() + ".dot")); + } catch (FileNotFoundException e) { + throw new RuntimeException(e); + } + this.writer.append("strict digraph DiskGraph {\n"); // strict removes redundant edges + } + + @Override + public void preVisit(ExploreNode exploreNode) { + if (skipNode(exploreNode)) { + return; + } + + final ExploreNode parent = stack.peek(); + if (exploreNode == this.rootModule) { + assert parent == null; + + final ModuleNode mn = (ModuleNode) exploreNode; + this.writer.append(Integer.toString(mn.hashCode())); + this.writer.append(" [label=\""); + this.writer.append(mn.getName().toString()); + this.writer.append("\",style = filled]"); + this.writer.append(";\n"); + + stack.push(exploreNode); + } else { + final SemanticNode sn = (SemanticNode) exploreNode; + this.writer.append(Integer.toString(sn.hashCode())); + this.writer.append(type2format.getOrDefault(exploreNode.getClass(), " [") + "label=\""); + if (exploreNode instanceof OpDefNode) { + this.writer.append(toDot(((OpDefNode) sn).getName().toString())); + } else { + this.writer.append(toDot(sn.getTreeNode().getHumanReadableImage())); + } + if (includeLineNumbers) { + // Wrap location for more compact nodes in dot output. + final String loc = sn.getLocation().toString(); + this.writer.append("\n"); + this.writer.append(loc.replace("of module", "\n")); + } + this.writer.append("\"]"); + this.writer.append(";\n"); + + this.writer.append(Integer.toString(parent.hashCode())); + this.writer.append(" -> "); + this.writer.append(Integer.toString(sn.hashCode())); + this.writer.append("\n"); + + stack.push(sn); + } + } + + @Override + public void postVisit(final ExploreNode exploreNode) { + if (skipNode(exploreNode)) { + return; + } + final ExploreNode pop = stack.pop(); + assert pop == exploreNode; + } + + public void done() { + this.writer.append("}"); + this.writer.close(); + } + + public Hashtable<Integer, ExploreNode> getTable() { + return table; + } + + private static String toDot(final String sn) { + return sn.replace("\\", "\\\\").replace("\"", "\\\"").trim().replace("\n", "\\n"); + } + + private static boolean skipNode(final ExploreNode exploreNode) { + if (exploreNode instanceof Context || exploreNode instanceof FormalParamNode) { + return true; + } + if (exploreNode instanceof Subst) { + return true; + } + if (Context.isBuiltIn(exploreNode)) { + return true; + } + if (exploreNode instanceof SemanticNode) { + return ((SemanticNode) exploreNode).isStandardModule(); + } + return false; + } + + @SuppressWarnings("serial") + private class NoopTable<K, V> extends Hashtable<K, V> { + @Override + public V get(Object key) { + // Return null here to visit an OpDefNode D multiple times if D is "called" from + // multiple OpApplNodes. However, stop endless recursion if D is a RECURSIVE + // operator. + final V v = super.get(key); + if (v instanceof OpDefNode) { + final OpDefNode odn = (OpDefNode) v; + if (odn.getInRecursive()) { + if (stack.contains(odn)) { + // RECURSIVE operators + return v; + } + } + } + return null; + } + } +} diff --git a/tlatools/src/tla2sany/explorer/ExploreNode.java b/tlatools/src/tla2sany/explorer/ExploreNode.java index 81df5d3aa8594d943c39ab8434fb432b3b61331e..f3eb734631ebc0ed9ae9f6cf46a91a6b88cfdc74 100644 --- a/tlatools/src/tla2sany/explorer/ExploreNode.java +++ b/tlatools/src/tla2sany/explorer/ExploreNode.java @@ -21,13 +21,14 @@ public interface ExploreNode { * descendant foo. * ***********************************************************************/ public String levelDataToString(); - public void walkGraph(Hashtable semNodesTable); + + public void walkGraph(Hashtable<Integer, ExploreNode> semNodesTable, final ExplorerVisitor visitor); /*********************************************************************** * This method is apparently supposed to insert an entry in * * semNodesTable for itself and every descendant in the semantic tree * * by executing * * * - * Integer uid = new Integer(myUID); * + * Integer uid = Integer.valueOf(myUID); * * if (semNodesTable.get(uid) != null) return; * * semNodesTable.put(uid, this); * * * diff --git a/tlatools/src/tla2sany/explorer/Explorer.java b/tlatools/src/tla2sany/explorer/Explorer.java index 6fec0b6fc9946f0c3c467203c97f56f121b94885..04d2e7a28585ac79ad14213d2853417f9a6da7f1 100644 --- a/tlatools/src/tla2sany/explorer/Explorer.java +++ b/tlatools/src/tla2sany/explorer/Explorer.java @@ -4,15 +4,16 @@ * 2 Mar 2007: enum <- Enum * ***************************************************************************/ - package tla2sany.explorer; import java.io.EOFException; import java.io.IOException; import java.io.InputStreamReader; +import java.util.Arrays; import java.util.Enumeration; import java.util.Hashtable; import java.util.Iterator; +import java.util.List; import java.util.StringTokenizer; import tla2sany.semantic.ExternalModuleTable; @@ -38,383 +39,399 @@ import util.UniqueString; public class Explorer { - public Generator generator; - - // Next three vars used in reading commands from keyboard - private InputStreamReader inStream = new InputStreamReader(System.in); - private final int inCapacity = 100; - private StringBuffer input = new StringBuffer(inCapacity); - private int lineLength; - - // semNodesTable contains various nodes in the semantic graph keyed - // by their UIDs - private Hashtable semNodesTable = new Hashtable(); - - // variables used in parsing commands - private int ntokens; - private StringTokenizer inputTokens; - private String inputString; - private String firstToken,secondToken; - private Integer icmd,icmd2 = null; - private ExploreNode obj; - - private ExternalModuleTable mt; + public Generator generator; + // Next three vars used in reading commands from keyboard + private final InputStreamReader inStream = new InputStreamReader(System.in); + private final int inCapacity = 100; + private StringBuffer input = new StringBuffer(inCapacity); + private int lineLength; - // Constructor - public Explorer (ExternalModuleTable mtarg) { + // semNodesTable contains various nodes in the semantic graph keyed + // by their UIDs + private final Hashtable<Integer, ExploreNode> semNodesTable = new Hashtable<>(); - mt = mtarg; + // variables used in parsing commands + private int ntokens; + private StringTokenizer inputTokens; + private String firstToken, secondToken; + private Integer icmd, icmd2 = null; + private ExploreNode obj; - } + private ExternalModuleTable mt; + // Constructor + public Explorer(ExternalModuleTable mtarg) { - /* Reads one line from "inStream" into "input". - Returns "false" if there is an EOF; "true" otherwise - */ + mt = mtarg; - private boolean getLine() { - try { - lineLength=0; - input.setLength(inCapacity); - do { - input.setCharAt(lineLength,(char)inStream.read()); - lineLength++; } - while (input.charAt(lineLength-1) != '\n' & lineLength < inCapacity ); - input.setLength(lineLength); - } - catch (EOFException e) { - return false; - } - catch (IOException e) { - System.out.println("***I/O exception on keyboard input; " + e); - System.exit(-1); - } - if (lineLength >= inCapacity) { - System.out.println("Input line too long; line limited to " + inCapacity - + " chars. Line ignored."); - input.setLength(0); - } - - return true; - } - - - - // Integer command - private void printNode(int depth) { - - // See if the object requested is already in the table - if ((obj = (ExploreNode)semNodesTable.get(icmd)) != null) { - //Print tree to depth of icmd2 - System.out.println(((ExploreNode)obj).toString(depth)); - System.out.print("\n" + ((ExploreNode)obj).levelDataToString()); - } else { - // object requested is not in semNodesTable - System.out.println("No such node encountered yet"); - } - } // end method - - - private void lookUpAndPrintSyntaxTree(String symbName) { - - Vector symbolVect = new Vector(8); // Initial room for 8 symbols with same name - - - // Collect in Vector symbols all SymbolNodes in the semNodesTable whose name == symbName - - for ( Enumeration Enum = semNodesTable.elements(); Enum.hasMoreElements(); ) { - - Object semNode = Enum.nextElement(); - - if ( semNode instanceof SymbolNode && - ((SymbolNode)semNode).getName() == UniqueString.uniqueStringOf(symbName) ) { - - symbolVect.addElement(semNode); - } - } - - // Print them all - for (int i = 0; i < symbolVect.size(); i++ ) { - SymbolNode sym = (SymbolNode)(symbolVect.elementAt(i)); - ((SemanticNode)(sym)).getTreeNode().printST(0); - System.out.println(); - } + /* + * Reads one line from "inStream" into "input". Returns "false" if there is an + * EOF; "true" otherwise + */ + + private boolean getLine() { + try { + lineLength = 0; + input.setLength(inCapacity); + do { + input.setCharAt(lineLength, (char) inStream.read()); + lineLength++; + } while (input.charAt(lineLength - 1) != '\n' & lineLength < inCapacity); + input.setLength(lineLength); + } catch (EOFException e) { + return false; + } catch (IOException e) { + System.out.println("***I/O exception on keyboard input; " + e); + System.exit(-1); + } + if (lineLength >= inCapacity) { + System.out.println("Input line too long; line limited to " + inCapacity + " chars. Line ignored."); + input.setLength(0); + } + + return true; + } - } + // Integer command + private void printNode(int depth) { + // See if the object requested is already in the table + if ((obj = (ExploreNode) semNodesTable.get(icmd)) != null) { + // Print tree to depth of icmd2 + System.out.println(((ExploreNode) obj).toString(depth)); + System.out.print("\n" + ((ExploreNode) obj).levelDataToString()); + } else { + // object requested is not in semNodesTable + System.out.println("No such node encountered yet"); + } + } // end method - private void lookUpAndPrintDef(String symbName) { + private void lookUpAndPrintSyntaxTree(String symbName) { - Vector symbolVect = new Vector(8); // Initial room for 8 symbols with same name + Vector<SymbolNode> symbolVect = new Vector<>(8); // Initial room for 8 symbols with same name + // Collect in Vector symbols all SymbolNodes in the semNodesTable whose name == + // symbName - // Collect in Vector symbols all SymbolNodes in the semNodesTable whose name == symbName + for (Enumeration<ExploreNode> Enum = semNodesTable.elements(); Enum.hasMoreElements();) { - for ( Enumeration Enum = semNodesTable.elements(); Enum.hasMoreElements(); ) { + ExploreNode semNode = Enum.nextElement(); - Object semNode = Enum.nextElement(); + if (semNode instanceof SymbolNode + && ((SymbolNode) semNode).getName() == UniqueString.uniqueStringOf(symbName)) { - if ( semNode instanceof SymbolNode && - ((SymbolNode)semNode).getName() == UniqueString.uniqueStringOf(symbName) ) { + symbolVect.addElement((SymbolNode) semNode); - symbolVect.addElement(semNode); + } + } - } - } + // Print them all + for (int i = 0; i < symbolVect.size(); i++) { + SymbolNode sym = (SymbolNode) (symbolVect.elementAt(i)); + ((SemanticNode) (sym)).getTreeNode().printST(0); + System.out.println(); + } - // Print them all - for (int i = 0; i < symbolVect.size(); i++ ) { - SymbolNode sym = (SymbolNode)(symbolVect.elementAt(i)); - if (sym instanceof OpDefOrDeclNode) { - if ( ((OpDefOrDeclNode)sym).getOriginallyDefinedInModuleNode() != null ) { - System.out.print( "Module: " + ((OpDefOrDeclNode)sym).getOriginallyDefinedInModuleNode().getName() + "\n" ); - } else { - System.out.print( "Module: " + "null" + "\n"); - } - } else if (sym instanceof FormalParamNode) { - System.out.print( "Module: " + ((FormalParamNode)sym).getModuleNode().getName() ); } - System.out.println( ((ExploreNode)(symbolVect.elementAt(i))).toString(100) ); - System.out.println(); - } - - } - private void levelDataPrint(String symbName) { + private void lookUpAndPrintDef(String symbName) { - Vector symbolVect = new Vector(8); // Initial room for 8 symbols with same name + Vector<SymbolNode> symbolVect = new Vector<>(8); // Initial room for 8 symbols with same name + // Collect in Vector symbols all SymbolNodes in the semNodesTable whose name == + // symbName - // Collect in Vector symbols all SymbolNodes in the semNodesTable whose name == symbName + for (Enumeration<ExploreNode> Enum = semNodesTable.elements(); Enum.hasMoreElements();) { - for ( Enumeration Enum = semNodesTable.elements(); Enum.hasMoreElements(); ) { + ExploreNode semNode = Enum.nextElement(); - Object semNode = Enum.nextElement(); + if (semNode instanceof SymbolNode + && ((SymbolNode) semNode).getName() == UniqueString.uniqueStringOf(symbName)) { - if ( semNode instanceof SymbolNode && - ((SymbolNode)semNode).getName() == UniqueString.uniqueStringOf(symbName) ) { + symbolVect.addElement((SymbolNode) semNode); - symbolVect.addElement(semNode); + } + } - } - } + // Print them all + for (int i = 0; i < symbolVect.size(); i++) { + SymbolNode sym = (SymbolNode) (symbolVect.elementAt(i)); + if (sym instanceof OpDefOrDeclNode) { + if (((OpDefOrDeclNode) sym).getOriginallyDefinedInModuleNode() != null) { + System.out.print( + "Module: " + ((OpDefOrDeclNode) sym).getOriginallyDefinedInModuleNode().getName() + "\n"); + } else { + System.out.print("Module: " + "null" + "\n"); + } + } else if (sym instanceof FormalParamNode) { + System.out.print("Module: " + ((FormalParamNode) sym).getModuleNode().getName()); + } + System.out.println(((ExploreNode) (symbolVect.elementAt(i))).toString(100)); + System.out.println(); + } - // Print them all - for (int i = 0; i < symbolVect.size(); i++ ) { - SymbolNode sym = (SymbolNode)(symbolVect.elementAt(i)); - if (sym instanceof OpDefOrDeclNode) { - if ( ((OpDefOrDeclNode)sym).getOriginallyDefinedInModuleNode() != null ) { - System.out.print( "Module: " + ((OpDefOrDeclNode)sym).getOriginallyDefinedInModuleNode().getName() + "\n" ); - } else { - System.out.print( "Module: " + "null" + "\n" ); - } - } else if (sym instanceof FormalParamNode) { - System.out.print( "Module: " + ((FormalParamNode)sym).getModuleNode().getName() + "\n" ); } - System.out.println( ((ExploreNode)(sym)).levelDataToString() ); - System.out.println(); - } - - } - - - private void executeCommand() throws ExplorerQuitException { - - // At this point icmd (firsToken) may be null, but icmd2 - // (second token) is always non-null - - // Integers as commands start printing at the node having icmd == UID; - // non-integer commands do something else - - if (icmd != null) { // first token is an integer - printNode(icmd2.intValue()); + private void levelDataPrint(String symbName) { - } else { // the first token is not an integer - - // non-integer commands - if (firstToken.toLowerCase().startsWith("qu")) { - // "quit" command - throw new ExplorerQuitException(); - - } else if (firstToken.toLowerCase().equals("mt")) { - - // Print the semantic graph, rooted in the Module Table - // excluding built-ins and ops defined in module Naturals - if (icmd2 != null) { - mt.printExternalModuleTable(icmd2.intValue(),false); - } else { - mt.printExternalModuleTable(2, false); - } + Vector<SymbolNode> symbolVect = new Vector<>(8); // Initial room for 8 symbols with same name - } else if (firstToken.toLowerCase().equals("mt*")) { + // Collect in Vector symbols all SymbolNodes in the semNodesTable whose name == + // symbName - // Print the semantic graph, rooted in the Module Table - // including builtins and ops defined in Naturals - if (icmd2 != null) { - mt.printExternalModuleTable(icmd2.intValue(),true); - } else { - mt.printExternalModuleTable(2,true); - } + for (Enumeration<ExploreNode> Enum = semNodesTable.elements(); Enum.hasMoreElements();) { - } else if (firstToken.toLowerCase().startsWith("cst")) { - printSyntaxTree(); + ExploreNode semNode = Enum.nextElement(); - } else if (firstToken.toLowerCase().startsWith("s")) { - if (secondToken != null) { - lookUpAndPrintSyntaxTree(secondToken); - } else { - System.out.println("***Error: You must indicate what name you want to print the syntax tree of."); - } + if (semNode instanceof SymbolNode + && ((SymbolNode) semNode).getName() == UniqueString.uniqueStringOf(symbName)) { - } else if (firstToken.toLowerCase().startsWith("d")) { - if (secondToken != null) { - lookUpAndPrintDef(secondToken); - } else { - System.out.println("***Error: You must indicate what name you want to print the definition of."); - } + symbolVect.addElement((SymbolNode) semNode); - } else if (firstToken.toLowerCase().startsWith("l")) { - if (secondToken != null) { - levelDataPrint(secondToken); - } else { - System.out.println("***Error: You must indicate what name you want to print the level data of."); - } + } + } - } else { - // unknown command - System.out.println("Unknown command: " + firstToken.toString()); - return; - } + // Print them all + for (int i = 0; i < symbolVect.size(); i++) { + SymbolNode sym = (SymbolNode) (symbolVect.elementAt(i)); + if (sym instanceof OpDefOrDeclNode) { + if (((OpDefOrDeclNode) sym).getOriginallyDefinedInModuleNode() != null) { + System.out.print( + "Module: " + ((OpDefOrDeclNode) sym).getOriginallyDefinedInModuleNode().getName() + "\n"); + } else { + System.out.print("Module: " + "null" + "\n"); + } + } else if (sym instanceof FormalParamNode) { + System.out.print("Module: " + ((FormalParamNode) sym).getModuleNode().getName() + "\n"); + } + System.out.println(((ExploreNode) (sym)).levelDataToString()); + System.out.println(); + } - } // end else - - } - - - private void parseAndExecuteCommand() throws ExplorerQuitException { - - icmd = null; - icmd2 = null; - ntokens = 0; - - // Do nothing if cmd line contains no tokens - if (!inputTokens.hasMoreElements()) return; + } - // Process first token - ntokens++; - firstToken = (String)(inputTokens.nextElement()); + private void executeCommand() throws ExplorerQuitException { - // Try parsing first token as an Integer - try { - icmd = Integer.valueOf(firstToken); - } - catch (Exception e) { } + // At this point icmd (firsToken) may be null, but icmd2 + // (second token) is always non-null - //Process second token (if present) - if (inputTokens.hasMoreElements()) { - ntokens++; - secondToken = (String)(inputTokens.nextElement()); + // Integers as commands start printing at the node having icmd == UID; + // non-integer commands do something else - // Try parsing second token as an Integer - try { - icmd2 = Integer.valueOf(secondToken); - } - catch (Exception e) { } - } + if (icmd != null) { // first token is an integer - // A single token command defaults the depth to 20, except for - // "mt" command, which defaults to 2 - if (ntokens < 2 || (icmd2 != null && icmd2.intValue() < 0)) { - if (firstToken.toLowerCase().startsWith("mt")) { - icmd2 = new Integer(2); - } else { - icmd2 = new Integer(4); - } - } + printNode(icmd2.intValue()); - if (inputTokens.hasMoreElements()) { - System.out.println("Command has too many tokens"); - return; - } + } else { // the first token is not an integer - executeCommand(); + // non-integer commands + if (firstToken.toLowerCase().startsWith("qu")) { + // "quit" command + throw new ExplorerQuitException(); - } // end method + } else if (firstToken.toLowerCase().equals("mt")) { + // Print the semantic graph, rooted in the Module Table + // excluding built-ins and ops defined in module Naturals + if (icmd2 != null) { + mt.printExternalModuleTable(icmd2.intValue(), false); + } else { + mt.printExternalModuleTable(2, false); + } - public void printSyntaxTree () { + } else if (firstToken.toLowerCase().equals("mt*")) { - Integer key; + // Print the semantic graph, rooted in the Module Table + // including builtins and ops defined in Naturals + if (icmd2 != null) { + mt.printExternalModuleTable(icmd2.intValue(), true); + } else { + mt.printExternalModuleTable(2, true); + } - // Prepare to iterate over ExternalModuleTable entries - Iterator modules = mt.moduleHashTable.values().iterator(); - ExternalModuleTable.ExternalModuleTableEntry mte; + } else if (firstToken.equalsIgnoreCase("dot")) { + dotSemanticGraph(); + } else if (firstToken.toLowerCase().startsWith("cst")) { + printSyntaxTree(); - // For each entry ExternalModuleTableEntry mte in the ExternalModuleTable mt ... - while (modules.hasNext()) { - key = new Integer(-1); + } else if (firstToken.toLowerCase().startsWith("s")) { + if (secondToken != null) { + lookUpAndPrintSyntaxTree(secondToken); + } else { + System.out.println("***Error: You must indicate what name you want to print the syntax tree of."); + } - mte = (ExternalModuleTable.ExternalModuleTableEntry)(modules.next()); + } else if (firstToken.toLowerCase().startsWith("d")) { + if (secondToken != null) { + lookUpAndPrintDef(secondToken); + } else { + System.out.println("***Error: You must indicate what name you want to print the definition of."); + } - // Did the module parse correctly? - if (mte != null) { - if (mte.getModuleNode() != null ) { - key = new Integer(mte.getModuleNode().getUid()); + } else if (firstToken.toLowerCase().startsWith("l")) { + if (secondToken != null) { + levelDataPrint(secondToken); + } else { + System.out.println("***Error: You must indicate what name you want to print the level data of."); + } - // Make an entry in the semNodesTable for this ModuleNode - semNodesTable.put(key,mte.getModuleNode()); + } else { + // unknown command + System.out.println("Unknown command: " + firstToken.toString()); + return; + } - // Print the concrete syntax tree for this ExternalModuleTableEntry - System.out.println("\n*** Concrete Syntax Tree for Module " + key); + } // end else - tla2sany.st.TreeNode stn = mte.getModuleNode().getTreeNode(); - stn.printST(0); // Zero indentation level + } - System.out.println("\n*** End of concrete syntax tree for Module " - + key); - } else { - System.out.println("\n*** Null ExternalModuleTableEntry. " + - "\n*** Next module did not parse, and cannot be printed."); - } - } else { - System.out.println("*** Null SemanticNode in ExternalModuleTableEntry. " + - "/n*** Next module did not parse, and cannot be printed."); - } + private void parseAndExecuteCommand() throws ExplorerQuitException { + + icmd = null; + icmd2 = null; + ntokens = 0; + + // Do nothing if cmd line contains no tokens + if (!inputTokens.hasMoreElements()) + return; + + // Process first token + ntokens++; + firstToken = (String) (inputTokens.nextElement()); + + // Try parsing first token as an Integer + try { + icmd = Integer.valueOf(firstToken); + } catch (Exception e) { + } + + // Process second token (if present) + if (inputTokens.hasMoreElements()) { + ntokens++; + secondToken = (String) (inputTokens.nextElement()); + + // Try parsing second token as an Integer + try { + icmd2 = Integer.valueOf(secondToken); + } catch (Exception e) { + } + } + + // A single token command defaults the depth to 20, except for + // "mt" command, which defaults to 2 + if (ntokens < 2 || (icmd2 != null && icmd2.intValue() < 0)) { + if (firstToken.toLowerCase().startsWith("mt")) { + icmd2 = new Integer(2); + } else { + icmd2 = new Integer(4); + } + } + + if (inputTokens.hasMoreElements()) { + System.out.println("Command has too many tokens"); + return; + } + + executeCommand(); + + } // end method + + public void dotSemanticGraph() { + final DotExplorerVisitor visitor = new DotExplorerVisitor(mt.getRootModule()); + mt.getRootModule().walkGraph(visitor.getTable(), visitor); + visitor.done(); + } - } - } + public void printSyntaxTree() { + int key; - public void main() throws ExplorerQuitException { + // Prepare to iterate over ExternalModuleTable entries + Iterator<ExternalModuleTable.ExternalModuleTableEntry> modules = mt.moduleHashTable.values().iterator(); + ExternalModuleTable.ExternalModuleTableEntry mte; - if (mt==null) { - System.out.println("*** module table == null in Explorer.main() ***"); - return; - } + // For each entry ExternalModuleTableEntry mte in the ExternalModuleTable mt ... + while (modules.hasNext()) { + key = -1; - // Get all semNodes in semNodeTable - mt.walkGraph(semNodesTable); + mte = modules.next(); - // Print initial user input prompt - System.out.println("\n\n*** TLA+ semantic graph exploration tool v 1.0 (DRJ)"); - System.out.print("\n>>"); + // Did the module parse correctly? + if (mte != null) { + if (mte.getModuleNode() != null) { + key = mte.getModuleNode().getUid(); - // Main command interpreter loop - while (getLine()) { + // Make an entry in the semNodesTable for this ModuleNode + semNodesTable.put(key, mte.getModuleNode()); - inputTokens = new StringTokenizer(input.toString()); + // Print the concrete syntax tree for this ExternalModuleTableEntry + System.out.println("\n*** Concrete Syntax Tree for Module " + key); - parseAndExecuteCommand(); + tla2sany.st.TreeNode stn = mte.getModuleNode().getTreeNode(); + stn.printST(0); // Zero indentation level - // Print next user prompt - System.out.print("\n>>"); + System.out.println("\n*** End of concrete syntax tree for Module " + key); + } else { + System.out.println("\n*** Null ExternalModuleTableEntry. " + + "\n*** Next module did not parse, and cannot be printed."); + } + } else { + System.out.println("*** Null SemanticNode in ExternalModuleTableEntry. " + + "/n*** Next module did not parse, and cannot be printed."); + } - } // end while + } + } - } // end main() method + public void main(final String[] args) throws ExplorerQuitException { + + if (mt == null) { + System.out.println("*** module table == null in Explorer.main() ***"); + return; + } + + final List<String> asList = Arrays.asList(args); + // Passing single token commands as command line parameter skips Explorer's + // interpreter mode. + if (asList.contains("cst")) { + inputTokens = new StringTokenizer("cst"); + parseAndExecuteCommand(); + System.exit(0); + } else if (asList.contains("dot")) { + inputTokens = new StringTokenizer("dot"); + parseAndExecuteCommand(); + System.exit(0); + } else if (asList.contains("mt")) { + inputTokens = new StringTokenizer("mt"); + parseAndExecuteCommand(); + System.exit(0); + } else if (asList.contains("mt*")) { + inputTokens = new StringTokenizer("mt*"); + parseAndExecuteCommand(); + System.exit(0); + } else { + // Get all semNodes in semNodeTable + mt.walkGraph(semNodesTable); + + // Print initial user input prompt + System.out.println("\n\n*** TLA+ semantic graph exploration tool v 1.0 (DRJ)"); + System.out.print("\n>>"); + // Main command interpreter loop + while (getLine()) { + + inputTokens = new StringTokenizer(input.toString()); + + parseAndExecuteCommand(); + + // Print next user prompt + System.out.print("\n>>"); + + } // end while + } + + } // end main() method } // end class diff --git a/tlatools/src/tla2sany/explorer/ExplorerVisitor.java b/tlatools/src/tla2sany/explorer/ExplorerVisitor.java new file mode 100644 index 0000000000000000000000000000000000000000..bc714a92f944fe791136e405db4a4470d0c44e00 --- /dev/null +++ b/tlatools/src/tla2sany/explorer/ExplorerVisitor.java @@ -0,0 +1,37 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tla2sany.explorer; + +public class ExplorerVisitor { + + public static final ExplorerVisitor NoopVisitor = new ExplorerVisitor(); + + public void preVisit(final ExploreNode exploreNode) { + } + + public void postVisit(final ExploreNode exploreNode) { + } +} diff --git a/tlatools/src/tla2sany/modanalyzer/ModuleRelationships.java b/tlatools/src/tla2sany/modanalyzer/ModuleRelationships.java index 5dea4c75f4de4d7c2739d3a40f17111496bdd2fb..b96fac62d33823fc170219f1df8f3b2760ae151c 100644 --- a/tlatools/src/tla2sany/modanalyzer/ModuleRelationships.java +++ b/tlatools/src/tla2sany/modanalyzer/ModuleRelationships.java @@ -33,7 +33,7 @@ class ModuleRelationships { } // end getContext() - Enumeration getKeys() { return modRelHashtable.keys(); } + Enumeration<ModulePointer> getKeys() { return modRelHashtable.keys(); } // Add the entries from otherMR into THIS; they are assumed not to overlap. diff --git a/tlatools/src/tla2sany/modanalyzer/ParseUnit.java b/tlatools/src/tla2sany/modanalyzer/ParseUnit.java index 994692db3d331d98bbd6915ec5d87e04c79d9b36..9d8fa747a2979d11cb7eaff3929961068ae418b2 100644 --- a/tlatools/src/tla2sany/modanalyzer/ParseUnit.java +++ b/tlatools/src/tla2sany/modanalyzer/ParseUnit.java @@ -24,6 +24,7 @@ package tla2sany.modanalyzer; import java.io.File; import java.io.IOException; import java.io.PrintWriter; +import java.nio.file.Path; import tla2sany.semantic.AbortException; import tla2sany.semantic.Errors; @@ -32,7 +33,10 @@ import tla2sany.st.ParseTree; import tla2sany.st.TreeNode; import tla2sany.utilities.Vector; import util.FileUtil; +import util.FilenameToStream.TLAFile; import util.NamedInputStream; +import util.TLAConstants; +import util.TLAFlightRecorder; import util.ToolIO; /** @@ -88,6 +92,13 @@ public class ParseUnit { parseStamp > nis.sourceFile().lastModified()); } + public boolean isLibraryModule() { + if (nis == null || !(nis.sourceFile() instanceof TLAFile)) { + return false; + } + return ((TLAFile) nis.sourceFile()).isLibraryModule(); + } + // Get-methods public final String getName() { return rootModuleName; } @@ -234,20 +245,30 @@ public class ParseUnit { } // Print user feedback + // MAK 01/2018: Always print the absolute path. Useful if e.g. users symlink + // common TLA files and want to verify that symlinking worked. + Path absoluteResolvedPath; + try { + absoluteResolvedPath = nis.getAbsoluteResolvedPath(); + } catch (IOException e1) { + // Fall back to to relative path if resolving the path to an absolute one fails. + // Chance of failure should be fairly low because existence is checked above. + absoluteResolvedPath = nis.sourceFile().toPath(); + } /*********************************************************************** * This is a bug. The higher-level parsing methods have a PrintStream * * argument to which such output gets written. That argument should * * have been passed down to this method. (LL: 11 Mar 08) * * * - * The following statement modified by LL on 13 Mary 08 to produce * + * The following statement modified by LL on 13 Mar 08 to produce * * more useful output for the GUI. * ***********************************************************************/ if (ToolIO.getMode() == ToolIO.SYSTEM) { - ToolIO.out.println("Parsing file " + nis.sourceFile()); + ToolIO.out.println(TLAFlightRecorder.message(String.format("%s %s", TLAConstants.LoggingAtoms.PARSING_FILE, absoluteResolvedPath))); } else { - ToolIO.out.println("Parsing module " + nis.getModuleName() + " in file " + nis.sourceFile()); + ToolIO.out.println(TLAFlightRecorder.message(String.format("Parsing module %s in file %s", nis.getModuleName(), absoluteResolvedPath))); } boolean parseSuccess; diff --git a/tlatools/src/tla2sany/modanalyzer/SpecObj.java b/tlatools/src/tla2sany/modanalyzer/SpecObj.java index fb9a0da5c5c924164f86e370d042efdd13a0b5cb..307699145876438d0d922b141336d115b3f7208a 100644 --- a/tlatools/src/tla2sany/modanalyzer/SpecObj.java +++ b/tlatools/src/tla2sany/modanalyzer/SpecObj.java @@ -2,13 +2,20 @@ // Portions Copyright (c) 2003 Microsoft Corporation. All rights reserved. package tla2sany.modanalyzer; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; import java.util.Enumeration; import java.util.HashSet; import java.util.Hashtable; +import java.util.Set; +import pcal.Validator; +import pcal.Validator.ValidationResult; import tla2sany.semantic.AbortException; import tla2sany.semantic.Errors; import tla2sany.semantic.ExternalModuleTable; +import tla2sany.semantic.ModuleNode; import tla2sany.st.TreeNode; import tla2sany.utilities.Vector; import util.FileUtil; @@ -28,7 +35,7 @@ public class SpecObj // The raw file name for the root (top) module, unprocessed by adding // ".tla" or by prepending the full file system path to it) - ExternalModuleTable externalModuleTable = new ExternalModuleTable(); + final ExternalModuleTable externalModuleTable = new ExternalModuleTable(); // This is the one ExternalModuleTable for the entire specification; // it includes ModuleNode's for the root module, and for all modules // that it depends on directly or indirectly by EXTENDS or INSTANCE @@ -38,7 +45,7 @@ public class SpecObj // must be done, i.e. if MODULE A references B, A is lower // on the stack. The same module name can occur multiple times. - public Hashtable parseUnitContext = new Hashtable(); + public Hashtable<String, ParseUnit> parseUnitContext = new Hashtable<String, ParseUnit>(); // Holds all known ParseUnit objects, i.e external, top-level // modules that have so far been encountered, keyed by module // (parseUnit) string name @@ -159,6 +166,10 @@ public class SpecObj { return primaryFileName; } + + public final ModuleNode getRootModule() { + return getExternalModuleTable().getRootModule(); + } /** * Returns the ExternalModuleTable object that contains all of the @@ -215,10 +226,19 @@ public class SpecObj return semanticErrors; } + public Set<String> getModuleNames() { + final Set<String> s = new HashSet<>(); + final Enumeration<ModulePointer> modules = getModules(); + while (modules.hasMoreElements()) { + s.add(modules.nextElement().getName()); + } + return s; + } + // Returns enumeration of the modules so far included in the spec. // As whoever wrote this documentation didn't think was worth mentioning, // it appears that the "modules" being returned are ModulePointer objects. - public final Enumeration getModules() + public final Enumeration<ModulePointer> getModules() { return moduleRelationshipsSpec.getKeys(); } @@ -232,12 +252,12 @@ public class SpecObj // Prints the context of one ParseUnit public final void printParseUnitContext() { - Enumeration enumerate = parseUnitContext.keys(); + Enumeration<String> enumerate = parseUnitContext.keys(); ToolIO.out.println("parseUnitContext ="); while (enumerate.hasMoreElements()) { - String key = (String) enumerate.nextElement(); + String key = enumerate.nextElement(); ToolIO.out.println(" " + key + "-->" + ((ParseUnit) parseUnitContext.get(key)).getName()); } } @@ -348,7 +368,7 @@ public class SpecObj // parseUnitName. If there is, cause an abort; otherwise return. private void nonCircularityTest(ParseUnit parseUnit, Errors errors) throws AbortException { - HashSet alreadyVisited = new HashSet(); + Set<ParseUnit> alreadyVisited = new HashSet<ParseUnit>(); Vector circularPath = new Vector(); circularPath.addElement(parseUnit); @@ -361,7 +381,7 @@ public class SpecObj // errors, and the method aborts. The set alreadyVisited is // used to prevent searching paths through the same candidate // multiple times. - private void nonCircularityBody(ParseUnit parseUnit, ParseUnit candidate, Errors errors, HashSet alreadyVisited, + private void nonCircularityBody(ParseUnit parseUnit, ParseUnit candidate, Errors errors, Set<ParseUnit> alreadyVisited, Vector circularPath) throws AbortException { // If we have already checked for circularities through this @@ -874,18 +894,33 @@ public class SpecObj } /** - * This method "loads" an entire specification, starting with the - * top-level rootExternalModule and followed by all of the external - * modules it references via EXTENDS and INSTANCE statements. + * This invokes {@code loadSpec(rootExternalModuleName, errors, false);} */ - public boolean loadSpec(String rootExternalModuleName, Errors errors) throws AbortException - { + public boolean loadSpec(final String rootExternalModuleName, final Errors errors) throws AbortException { + return loadSpec(rootExternalModuleName, errors, false); + } + + /** + * This method "loads" an entire specification, starting with the top-level + * rootExternalModule and followed by all of the external modules it references + * via EXTENDS and INSTANCE statements. + * + * @param validateParseUnits if true, each parseable unit will be checked for + * PlusCal and its accompanied TLA translation block, + * followed by a validation of them if they exist + * @see Validator + */ + public boolean loadSpec(final String rootExternalModuleName, final Errors errors, final boolean validateParseUnits) + throws AbortException { // If rootExternalModuleName" has *not* already been parsed, then // go to the file system and find the file containing it, create a // ParseUnit for it, and parse it. Parsing includes determining // module relationships. Aborts if not found in file system rootParseUnit = findOrCreateParsedUnit(rootExternalModuleName, errors, true /* first call */); rootModule = rootParseUnit.getRootModule(); + if (validateParseUnits) { + validateParseUnit(rootParseUnit); + } // Retrieve and parse all module extentions: As long as there is // another unresolved module name... @@ -924,6 +959,10 @@ public class SpecObj if (extentionFound) { + if (validateParseUnits) { + validateParseUnit(nextExtentionOrInstantiationParseUnit); + } + extenderOrInstancerParseUnit.addExtendee(nextExtentionOrInstantiationParseUnit); nextExtentionOrInstantiationParseUnit.addExtendedBy(extenderOrInstancerParseUnit); } @@ -963,6 +1002,54 @@ public class SpecObj return true; // loadUnresolvedRelatives(moduleRelationshipsSpec, rootModule, errors); } + + private void validateParseUnit(final ParseUnit parseUnit) { + final File f = parseUnit.getNis().sourceFile(); + + try (final FileInputStream fis = new FileInputStream(f)) { + final Set<ValidationResult> results = Validator.validate(parseUnit, fis); + + if (results.contains(ValidationResult.TLA_DIVERGENCE_EXISTS) && results.contains(ValidationResult.PCAL_DIVERGENCE_EXISTS)) { + /* Both hashes are invalid. + This is probably a sequel to Case 3, in which the user has decided either + (1) she has fixed the bug or (2) wants to change the spec and will later + modify the translation. + TLC called: By default, a warning should be raised. It should be considered + the same as Case 2. */ + ToolIO.out.println(String.format( + "!! WARNING: The PlusCal algorithm and its TLA+ translation in " + + "module %s filename since the last translation.", + parseUnit.getName())); + } else if (results.contains(ValidationResult.TLA_DIVERGENCE_EXISTS)) { + /* The algorithm hash is valid and the translation hash is invalid. + There are two reasons: (1) The user is debugging the spec, or + (2) She needed to modify the translation because she wants a spec that can't + be produced by PlusCal. + TLC called: In both cases, no warning should be needed. However, + in (1), she might have finished debugging and forgotten to run the + translator. To handle case (1), I suggest the default should be to run + TLC but raise a transient window with a warning that is easily ignored. + For case (2), it should be possible to put something in a translation + comment to disable the warning. */ + ToolIO.out.println(String.format("!! WARNING: The TLA+ translation in " + + "module %s has changed since its last translation.", parseUnit.getName())); + } else if (results.contains(ValidationResult.PCAL_DIVERGENCE_EXISTS)) { + /* The algorithm hash is invalid and the translation hash is valid. + TLC called: By default, a warning should be generated. I see little reason + for not generating the warning. So, it doesn't matter if its inconvenient + to turn off the warning, but turning it off should affect only the current + spec; and it should be easy to turn back on. */ + ToolIO.out.println(String.format("!! WARNING: The PlusCal algorithm in " + + "module %s has changed since its last translation.", parseUnit.getName())); + } else if (results.contains(ValidationResult.ERROR_ENCOUNTERED)) { + ToolIO.err.println("A unexpected problem was encountered attempting to validate the specification for " + + parseUnit.getName()); + } + } catch (final IOException e) { + ToolIO.err.println("Encountered an exception while attempt to validate " + f.getAbsolutePath() + " - " + + e.getMessage()); + } + } /** * Getter for the name resolver used in the Spec @@ -981,4 +1068,8 @@ public class SpecObj this.globalContextErrors = globalContextErrors; } + public ParseUnit getRootParseUnit() { + return this.parseUnitContext.get(this.getName()); + } + } \ No newline at end of file diff --git a/tlatools/src/tla2sany/parser/BracketStack.java b/tlatools/src/tla2sany/parser/BracketStack.java index 74a8713cc6e1204476b208e60b96640b00ade945..8ace9df9febc740f5fc32094e3c7f20c0a93a73c 100644 --- a/tlatools/src/tla2sany/parser/BracketStack.java +++ b/tlatools/src/tla2sany/parser/BracketStack.java @@ -132,8 +132,8 @@ public class BracketStack implements //LogCategories, Offset is the end column of the token ...\/ ou .../\ on utilise - 1 pour comparer au de'but de la partie significative du symbole. - De cette mani€re, le comportement ne change pas si - on utilise uniquement la forme non pr€fix€e des + De cette manire, le comportement ne change pas si + on utilise uniquement la forme non prafixe des symboles */ } } diff --git a/tlatools/src/tla2sany/parser/ParseException.java b/tlatools/src/tla2sany/parser/ParseException.java index 614bc91e5ea1008f25b66bab19a6a9bbd02bfe03..9eb892e8598c6300e5ba6e0ab6087c4f669e676f 100644 --- a/tlatools/src/tla2sany/parser/ParseException.java +++ b/tlatools/src/tla2sany/parser/ParseException.java @@ -111,8 +111,9 @@ public class ParseException extends Exception { // retval += add_escapes(tok.image); tok = tok.next; } - retval += "\" at line " + currentToken.next.beginLine + ", column " + currentToken.next.beginColumn; - return retval; + retval += "\" at line " + currentToken.next.beginLine + ", column " + currentToken.next.beginColumn + + " and token \"" + add_escapes(currentToken.image) + "\" "; + return retval; } /** diff --git a/tlatools/src/tla2sany/parser/TLAplusParser.java b/tlatools/src/tla2sany/parser/TLAplusParser.java index eea79546f5945da3ca22bf3847c2d39db4587ef6..e5e0b1c895a901dd4bfc5b58f221e39b5639390c 100644 --- a/tlatools/src/tla2sany/parser/TLAplusParser.java +++ b/tlatools/src/tla2sany/parser/TLAplusParser.java @@ -3,13 +3,13 @@ package tla2sany.parser; import tla2sany.st.ParseTree; import tla2sany.st.TreeNode; -import tlc2.output.EC; - -import tla2sany.utilities.Vector; import tla2sany.utilities.Stack; +import tla2sany.utilities.Vector; +import tlc2.output.EC; import util.Assert; -import util.UniqueString; +import util.TLAConstants; import util.ToolIO; +import util.UniqueString; public class TLAplusParser implements tla2sany.st.SyntaxTreeConstants, ParseTree, TLAplusParserConstants { @@ -1656,7 +1656,7 @@ Token t; SyntaxTreeNode tn, sn[]; Token t; bpa("Parameter declaration"); - expecting = "CONSTANT"; + expecting = TLAConstants.KeyWords.CONSTANT; tn = ParamSubDecl(); addHeir(tn); expecting = "Identifier, operator or _"; diff --git a/tlatools/src/tla2sany/parser/TLAplusParserTokenManager.java b/tlatools/src/tla2sany/parser/TLAplusParserTokenManager.java index 2e9e7763c596838edbaa4b65b4c16fada72aad62..fadad65d8564ceaea2f5c189115325a3d2a433ac 100644 --- a/tlatools/src/tla2sany/parser/TLAplusParserTokenManager.java +++ b/tlatools/src/tla2sany/parser/TLAplusParserTokenManager.java @@ -1,13 +1,5 @@ /* Generated By:23&JavaCC: Do not edit this line. TLAplusParserTokenManager.java */ package tla2sany.parser; -import tla2sany.st.ParseTree; -import tla2sany.st.TreeNode; -import tlc2.output.EC; -import tla2sany.utilities.Vector; -import tla2sany.utilities.Stack; -import util.Assert; -import util.UniqueString; -import util.ToolIO; public class TLAplusParserTokenManager implements TLAplusParserConstants { diff --git a/tlatools/src/tla2sany/semantic/APSubstInNode.java b/tlatools/src/tla2sany/semantic/APSubstInNode.java index e14ac1582babfe825512c878dea78f77e369bd71..4b804c6f8ff96b671e44e19cd90a29793c11589a 100644 --- a/tlatools/src/tla2sany/semantic/APSubstInNode.java +++ b/tlatools/src/tla2sany/semantic/APSubstInNode.java @@ -21,14 +21,17 @@ import java.util.HashSet; import java.util.Hashtable; import java.util.Iterator; +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +import tla2sany.explorer.ExploreNode; +import tla2sany.explorer.ExplorerVisitor; import tla2sany.st.TreeNode; import tla2sany.utilities.Strings; import tla2sany.utilities.Vector; +import tla2sany.xml.SymbolContext; import util.UniqueString; -import org.w3c.dom.Document; -import org.w3c.dom.Element; - public class APSubstInNode extends LevelNode { /** * For a APSubstInNode object s that has the WITH clause @@ -193,7 +196,8 @@ public class APSubstInNode extends LevelNode { * to this method can contain a mixture of explicit and implicit * substitutions */ - final void addExplicitSubstitute(Context instanceeCtxt, UniqueString lhs, + @SuppressWarnings("unused") // TODO final else block is dead code + final void addExplicitSubstitute(Context instanceCtxt, UniqueString lhs, TreeNode stn, ExprOrOpArgNode sub) { int index; for (index = 0; index < this.substs.length; index++) { @@ -221,7 +225,7 @@ public class APSubstInNode extends LevelNode { // the instancee context // look up the lhs symbol in the instancee context - SymbolNode lhsSymbol = instanceeCtxt.getSymbol(lhs); + SymbolNode lhsSymbol = instanceCtxt.getSymbol(lhs); // lhs must be an OpDeclNode; if not just return, as this error // will have been earlier, though semantic analysis was allowed @@ -288,6 +292,7 @@ public class APSubstInNode extends LevelNode { // private SetOfArgLevelConstraints argLevelConstraints; // private HashSet argLevelParams; + @Override public final boolean levelCheck(int itr) { if (this.levelChecked >= itr) return this.levelCorrect; this.levelChecked = itr ; @@ -308,7 +313,7 @@ public class APSubstInNode extends LevelNode { // Calculate the level information this.level = this.body.getLevel(); - HashSet lpSet = this.body.getLevelParams(); + HashSet<SymbolNode> lpSet = this.body.getLevelParams(); for (int i = 0; i < this.substs.length; i++) { if (lpSet.contains(this.getSubFor(i))) { this.level = Math.max(level, this.getSubWith(i).getLevel()); @@ -316,10 +321,9 @@ public class APSubstInNode extends LevelNode { } // this.levelParams = new HashSet(); - Iterator iter = lpSet.iterator(); + Iterator<SymbolNode> iter = lpSet.iterator(); while (iter.hasNext()) { - Object param = iter.next(); - this.levelParams.addAll(Subst.paramSet(param, this.substs)); + this.levelParams.addAll(Subst.paramSet(iter.next(), this.substs)); /******************************************************************* * At this point, levelCheck(itr) has been invoked on * * this.substs[i].getExpr() (which equals this.getSubWith(i)). * @@ -331,11 +335,10 @@ public class APSubstInNode extends LevelNode { * this.levelParams to compute this.allParams, except using * * Subst.allParamSet instead of Subst.paramSet. * ***********************************************************************/ - HashSet apSet = this.body.getAllParams(); + HashSet<SymbolNode> apSet = this.body.getAllParams(); iter = apSet.iterator(); while (iter.hasNext()) { - Object param = iter.next(); - this.allParams.addAll(Subst.allParamSet(param, this.substs)); + this.allParams.addAll(Subst.allParamSet(iter.next(), this.substs)); /******************************************************************* * At this point, levelCheck(itr) has been invoked on * * this.substs[i].getExpr() (which equals this.getSubWith(i)). * @@ -392,6 +395,7 @@ public class APSubstInNode extends LevelNode { // "ArgLevelParams: " + this.argLevelParams + "\n" ; // } + @Override public final String toString(int depth) { if (depth <= 0) return ""; @@ -421,6 +425,7 @@ public class APSubstInNode extends LevelNode { * The children of this node are the body and the expressions * being substituted for symbols. */ + @Override public SemanticNode[] getChildren() { SemanticNode[] res = new SemanticNode[this.substs.length + 1]; res[0] = this.body; @@ -430,31 +435,47 @@ public class APSubstInNode extends LevelNode { return res; } - public final void walkGraph(Hashtable semNodesTable) { - Integer uid = new Integer(myUID); + @Override + public final void walkGraph(Hashtable<Integer, ExploreNode> semNodesTable, ExplorerVisitor visitor) { + Integer uid = Integer.valueOf(myUID); if (semNodesTable.get(uid) != null) return; - semNodesTable.put(new Integer(myUID), this); + semNodesTable.put(uid, this); + visitor.preVisit(this); if (this.substs != null) { for (int i = 0; i < this.substs.length; i++) { - if (this.substs[i] != null) this.substs[i].walkGraph(semNodesTable); + if (this.substs[i] != null) this.substs[i].walkGraph(semNodesTable, visitor); } } - if (this.body != null) this.body.walkGraph(semNodesTable); + if (this.body != null) this.body.walkGraph(semNodesTable, visitor); + visitor.postVisit(this); return; } - protected Element getLevelElement(Document doc, tla2sany.xml.SymbolContext context) { + @Override + protected Element getLevelElement(Document doc, SymbolContext context) { Element sbts = doc.createElement("substs"); for (int i=0; i<substs.length; i++) { sbts.appendChild(substs[i].export(doc, context)); } Element bdy = doc.createElement("body"); bdy.appendChild(body.export(doc,context)); + + Element from = doc.createElement("instFrom"); + Element fromchild = this.instantiatingModule.export(doc, context); + from.appendChild(fromchild); + + Element to = doc.createElement("instTo"); + Element tochild = instantiatedModule.export(doc,context); + to.appendChild(tochild); + + Element ret = doc.createElement("APSubstInNode"); ret.appendChild(sbts); ret.appendChild(bdy); + ret.appendChild(from); + ret.appendChild(to); // at the end, we append the context of the symbols used in this node //ret.appendChild(context.export(doc)); @@ -462,8 +483,3 @@ public class APSubstInNode extends LevelNode { return ret; } } - - - - - diff --git a/tlatools/src/tla2sany/semantic/AnyDefNode.java b/tlatools/src/tla2sany/semantic/AnyDefNode.java index 64b159da6ad1e4730e668221220e7f3de135c465..629e4e0b332d36d7cfa1523ae4ebe86320b16e5a 100644 --- a/tlatools/src/tla2sany/semantic/AnyDefNode.java +++ b/tlatools/src/tla2sany/semantic/AnyDefNode.java @@ -1,52 +1,53 @@ -/** - * The AnyDefNode interface is implemented by OpDefNode and ThmOrAssumpDefNode. - * It was added by LL on 24 Oct 2012 to fix a bug in the level-checking of - * ThmOrAssumpDefNode objects. When a module containing a theorem or assumption - * such as - * - * THEOREM Fab == ... - * - * is instantiated with parameters in a statement like - * - * I(x, y) == INSTANCE M WITH ... - * - * then level checking of the expression I(e1, e2)!Fab did not check that - * the levels of e1 and e2 were appropriate for the instantiation. This was - * because the levelCheck method of OpApplNode treated an operator like Fab - * defined with a ThmOrAssumpDefNode differently from one defined with an - * OpDefNode. To solve this problem, level checking of ThmOrAssumpDefNode was - * changed to be essentially the same as for OpDefNode. The levelCheck method - * of OpApplNode was changed to handle ThmOrAssumpDefNode and OpDefNode operators - * the same by changing OpDefNode to AnyDefNode in a couple of places, and by - * replacing one access to an OpDefNode field by a call of a new method that - * returns the value of that field. - */ -package tla2sany.semantic; - -import java.util.HashSet; - -import util.UniqueString; - -/** - * @author lamport - * - */ -public interface AnyDefNode { - public boolean levelCheck(int itr) ; - public int getMaxLevel(int i) ; - public UniqueString getName() ; - public int getMinMaxLevel(int i, int j) ; - public boolean getOpLevelCond(int i, int j, int k) ; - public int getLevel() ; - public int getWeight(int i) ; - public HashSet getLevelParams() ; - public HashSet getAllParams() ; - public HashSet getNonLeibnizParams() ; - public int getArity() ; - public boolean[] getIsLeibnizArg() ; - public boolean getIsLeibniz() ; - public SetOfLevelConstraints getLevelConstraints() ; - public HashSet getArgLevelParams() ; - public SetOfArgLevelConstraints getArgLevelConstraints() ; - public FormalParamNode[] getParams() ; -} +/** + * The AnyDefNode interface is implemented by OpDefNode and ThmOrAssumpDefNode. + * It was added by LL on 24 Oct 2012 to fix a bug in the level-checking of + * ThmOrAssumpDefNode objects. When a module containing a theorem or assumption + * such as + * + * THEOREM Fab == ... + * + * is instantiated with parameters in a statement like + * + * I(x, y) == INSTANCE M WITH ... + * + * then level checking of the expression I(e1, e2)!Fab did not check that + * the levels of e1 and e2 were appropriate for the instantiation. This was + * because the levelCheck method of OpApplNode treated an operator like Fab + * defined with a ThmOrAssumpDefNode differently from one defined with an + * OpDefNode. To solve this problem, level checking of ThmOrAssumpDefNode was + * changed to be essentially the same as for OpDefNode. The levelCheck method + * of OpApplNode was changed to handle ThmOrAssumpDefNode and OpDefNode operators + * the same by changing OpDefNode to AnyDefNode in a couple of places, and by + * replacing one access to an OpDefNode field by a call of a new method that + * returns the value of that field. + */ +package tla2sany.semantic; + +import java.util.HashSet; + +import util.UniqueString; + +/** + * @author lamport + * + */ +public interface AnyDefNode { + public boolean levelCheck(int itr) ; + public int getMaxLevel(int i) ; + public UniqueString getName() ; + public int getMinMaxLevel(int i, int j) ; + public boolean getOpLevelCond(int i, int j, int k) ; + public int getLevel() ; + public int getWeight(int i) ; + public HashSet<SymbolNode> getLevelParams() ; + public HashSet<SymbolNode> getAllParams() ; + public HashSet<SymbolNode> getNonLeibnizParams() ; + public int getArity() ; + public boolean[] getIsLeibnizArg() ; + public boolean getIsLeibniz() ; + public SetOfLevelConstraints getLevelConstraints() ; + public HashSet<ArgLevelParam> getArgLevelParams() ; + public SetOfArgLevelConstraints getArgLevelConstraints() ; + public FormalParamNode[] getParams() ; + public AnyDefNode getSource(); +} diff --git a/tlatools/src/tla2sany/semantic/AssumeNode.java b/tlatools/src/tla2sany/semantic/AssumeNode.java index 961dacea486bbe3b894dc8ef743074a7a4170b4b..66aa37736f0c48c7b971dfd345beb60cd8f70cf0 100644 --- a/tlatools/src/tla2sany/semantic/AssumeNode.java +++ b/tlatools/src/tla2sany/semantic/AssumeNode.java @@ -5,13 +5,15 @@ package tla2sany.semantic; import java.util.HashSet; import java.util.Hashtable; -import tla2sany.st.TreeNode; -import tla2sany.utilities.Strings; - -import tla2sany.xml.XMLExportable; -import org.w3c.dom.Attr; import org.w3c.dom.Document; import org.w3c.dom.Element; +import org.w3c.dom.Node; + +import tla2sany.explorer.ExploreNode; +import tla2sany.explorer.ExplorerVisitor; +import tla2sany.st.TreeNode; +import tla2sany.utilities.Strings; +import tla2sany.xml.SymbolContext; /** * This class represents an assumption about the constants in a module. @@ -80,6 +82,7 @@ public AssumeNode(TreeNode stn, ExprNode expr, ModuleNode mn, /* Level checking */ int levelChecked = 0 ; + @Override public final boolean levelCheck(int iter) { if (levelChecked >= iter) {return true ;} ; levelChecked = iter; @@ -102,33 +105,40 @@ public AssumeNode(TreeNode stn, ExprNode expr, ModuleNode mn, return res; } + @Override public final int getLevel() { return this.assumeExpr.getLevel(); } - public final HashSet getLevelParams() { + @Override + public final HashSet<SymbolNode> getLevelParams() { return this.assumeExpr.getLevelParams(); } - public final HashSet getAllParams() { + @Override + public final HashSet<SymbolNode> getAllParams() { return this.assumeExpr.getAllParams(); } + @Override public final SetOfLevelConstraints getLevelConstraints() { return this.assumeExpr.getLevelConstraints(); } + @Override public final SetOfArgLevelConstraints getArgLevelConstraints() { return this.assumeExpr.getArgLevelConstraints(); } - public final HashSet getArgLevelParams() { + @Override + public final HashSet<ArgLevelParam> getArgLevelParams() { return this.assumeExpr.getArgLevelParams(); } /** * toString(), levelDataToString(), and walkGraph() methods */ + @Override public final String levelDataToString() { return "Level: " + getLevel() + "\n" + "LevelParameters: " + getLevelParams() + "\n" + @@ -142,6 +152,7 @@ public AssumeNode(TreeNode stn, ExprNode expr, ModuleNode mn, * interface; depth parameter is a bound on the depth of the portion * of the tree that is displayed. */ + @Override public final String toString (int depth) { if (depth <= 0) return ""; String res = @@ -164,6 +175,7 @@ public AssumeNode(TreeNode stn, ExprNode expr, ModuleNode mn, * The assume expression is the node's only child. */ + @Override public SemanticNode[] getChildren() { return new SemanticNode[] {this.assumeExpr}; } @@ -173,26 +185,84 @@ public AssumeNode(TreeNode stn, ExprNode expr, ModuleNode mn, * inserts them in the Hashtable semNodesTable for use by the * Explorer tool. */ - public final void walkGraph (Hashtable semNodesTable) { - Integer uid = new Integer(myUID); + @Override + public final void walkGraph (Hashtable<Integer, ExploreNode> semNodesTable, ExplorerVisitor visitor) { + Integer uid = Integer.valueOf(myUID); if (semNodesTable.get(uid) != null) return; semNodesTable.put(uid, this); - if (assumeExpr != null) {assumeExpr.walkGraph(semNodesTable);} ; + visitor.preVisit(this); + if (assumeExpr != null) {assumeExpr.walkGraph(semNodesTable, visitor);} ; + visitor.postVisit(this); } - public Element export(Document doc, tla2sany.xml.SymbolContext context) { - if (getDef() == null) - // we export the definition of the assumption - return super.export(doc,context); - else - // we export its name only, named assumptions will be exported through the ThmOrAss.. - return getDef().export(doc,context); + /* MR: This is the same as SymbolNode.exportDefinition. Exports the actual theorem content, not only a reference. + */ + public Element exportDefinition(Document doc, SymbolContext context) { + if (!context.isTop_level_entry()) + throw new IllegalArgumentException("Exporting theorem ref "+getNodeRef()+" twice!"); + context.resetTop_level_entry(); + try { + Element e = getLevelElement(doc, context); + // level + try { + Element l = appendText(doc,"level",Integer.toString(getLevel())); + e.insertBefore(l,e.getFirstChild()); + } catch (RuntimeException ee) { + // not sure it is legal for a LevelNode not to have level, debug it! + } + //location + try { + Element loc = getLocationElement(doc); + e.insertBefore(loc,e.getFirstChild()); + } catch (RuntimeException ee) { + // do nothing if no location + } + return e; + } catch (RuntimeException ee) { + System.err.println("failed for node.toString(): " + toString() + "\n with error "); + ee.printStackTrace(); + throw ee; + } } - protected Element getLevelElement(Document doc, tla2sany.xml.SymbolContext context) { + + protected String getNodeRef() { + return "AssumeNodeRef"; + } + +// public Element export(Document doc, tla2sany.xml.SymbolContext context) { +// if (getDef() == null) +// // we export the definition of the assumption +// return super.export(doc,context); +// else +// // we export its name only, named assumptions will be exported through the ThmOrAss.. +// return getDef().export(doc,context); +// } + + @Override + protected Element getLevelElement(Document doc, SymbolContext context) { Element e = doc.createElement("AssumeNode"); - e.appendChild(getAssume().export(doc,context)); + if (def != null) { + //if there is a definition, export it too + Node d = doc.createElement("definition"); + d.appendChild(def.export(doc, context)); + e.appendChild(d); + assert( def.getBody() == this.assumeExpr ); //make sure theorem and definition body agree before export + } + Node n = doc.createElement("body"); + n.appendChild(getAssume().export(doc,context)); + e.appendChild(n); + return e; + } + + /* overrides LevelNode.export and exports a UID reference instad of the full version*/ + @Override + public Element export(Document doc, SymbolContext context) { + // first add symbol to context + context.put(this, doc); + Element e = doc.createElement(getNodeRef()); + e.appendChild(appendText(doc,"UID",Integer.toString(myUID))); return e; } } diff --git a/tlatools/src/tla2sany/semantic/AssumeProveNode.java b/tlatools/src/tla2sany/semantic/AssumeProveNode.java index d2fd8dda8d60b187a9cdc605566843412d6abec3..28d0f6541b6641fab7bcf54c6beddeda7c34f1b7 100644 --- a/tlatools/src/tla2sany/semantic/AssumeProveNode.java +++ b/tlatools/src/tla2sany/semantic/AssumeProveNode.java @@ -6,12 +6,15 @@ package tla2sany.semantic; import java.util.Hashtable; -import tla2sany.st.TreeNode; -import tla2sany.utilities.Strings; - import org.w3c.dom.Document; import org.w3c.dom.Element; +import tla2sany.explorer.ExploreNode; +import tla2sany.explorer.ExplorerVisitor; +import tla2sany.st.TreeNode; +import tla2sany.utilities.Strings; +import tla2sany.xml.SymbolContext; + /*************************************************************************** * An assume prove node represents something like * * * @@ -191,6 +194,7 @@ public class AssumeProveNode extends LevelNode { * compute the level-related fields for an OpApplNode. This seems * * reasonable, but I don't know if it's really correct. - LL * *************************************************************************/ + @Override public boolean levelCheck(int iter) { /*********************************************************************** * Return immediately if this this.levelCheck(i) has already been * @@ -337,6 +341,7 @@ public class AssumeProveNode extends LevelNode { /** * The children of this node are the assumes and prove expressions. */ + @Override public SemanticNode[] getChildren() { SemanticNode[] res = new SemanticNode[this.assumes.length + 1]; res[assumes.length] = this.prove; @@ -346,16 +351,19 @@ public class AssumeProveNode extends LevelNode { return res; } - public final void walkGraph(Hashtable h) { - Integer uid = new Integer(myUID); + @Override + public final void walkGraph(Hashtable<Integer, ExploreNode> h, ExplorerVisitor visitor) { + Integer uid = Integer.valueOf(myUID); if (h.get(uid) != null) return; h.put(uid, this); + visitor.preVisit(this); int i = 0 ; while (i < assumes.length) { - assumes[i].walkGraph(h) ; + assumes[i].walkGraph(h, visitor) ; i = i+1; } ; - prove.walkGraph(h) ; + prove.walkGraph(h, visitor) ; + visitor.postVisit(this); } // end walkGraph() @@ -363,6 +371,7 @@ public class AssumeProveNode extends LevelNode { * Displays this node as a String, implementing ExploreNode interface; depth * parameter is a bound on the depth of the portion of the tree that is displayed. */ + @Override public final String toString(int depth) { if (depth <= 0) return ""; String assumeStr = "" ; @@ -385,7 +394,8 @@ public class AssumeProveNode extends LevelNode { * End fields and methods implementing the ExplorerNode interface: * *************************************************************************/ - public Element getLevelElement(Document doc, tla2sany.xml.SymbolContext context) { + @Override + public Element getLevelElement(Document doc, SymbolContext context) { Element e = doc.createElement("AssumeProveNode"); Element antecedent = doc.createElement("assumes"); Element succedent = doc.createElement("prove"); diff --git a/tlatools/src/tla2sany/semantic/AtNode.java b/tlatools/src/tla2sany/semantic/AtNode.java index 8c9d253caa9c63ec715a356ba0e78584b9365821..1d7f522ee9954272d71e926b0e7fa85b58efbc14 100644 --- a/tlatools/src/tla2sany/semantic/AtNode.java +++ b/tlatools/src/tla2sany/semantic/AtNode.java @@ -4,11 +4,14 @@ package tla2sany.semantic; import java.util.Hashtable; -import tla2sany.utilities.Strings; - import org.w3c.dom.Document; import org.w3c.dom.Element; +import tla2sany.explorer.ExploreNode; +import tla2sany.explorer.ExplorerVisitor; +import tla2sany.utilities.Strings; +import tla2sany.xml.SymbolContext; + public class AtNode extends ExprNode { private OpApplNode exceptRef; // reference to OpApplNode for the innermost @@ -72,6 +75,7 @@ public class AtNode extends ExprNode { // private SetOfArgLevelConstraints argLevelConstraints; // private HashSet argLevelParams; + @Override public final boolean levelCheck(int iter) { if (this.levelChecked >= iter) return true; this.levelChecked = iter; @@ -147,9 +151,12 @@ public class AtNode extends ExprNode { * walkGraph finds all reachable nodes in the semantic graph * and inserts them in the Hashtable semNodesTable for use by the Explorer tool. */ - public final void walkGraph(Hashtable h) { + @Override + public final void walkGraph(Hashtable<Integer, ExploreNode> h, ExplorerVisitor visitor) { + visitor.preVisit(this); // Empty because there are no nodes reachable through an AtNode that are not // reachable by other paths through the semantic graph. + visitor.postVisit(this); } // end walkGraph() @@ -157,6 +164,7 @@ public class AtNode extends ExprNode { * Displays this node as a String, implementing ExploreNode interface; depth * parameter is a bound on the depth of the portion of the tree that is displayed. */ + @Override public final String toString(int depth) { if (depth <= 0) return ""; return "\n*AtNode: " + super.toString(depth) + @@ -164,7 +172,8 @@ public class AtNode extends ExprNode { "\nExceptComponent: " + exceptComponentRef.getUid()); } - protected Element getLevelElement(Document doc, tla2sany.xml.SymbolContext context) { + @Override + protected Element getLevelElement(Document doc, SymbolContext context) { Element e = doc.createElement("AtNode"); SemanticNode exceptObj = exceptRef.getArgs()[0]; SemanticNode exceptComponents = exceptComponentRef.getArgs()[0]; diff --git a/tlatools/src/tla2sany/semantic/Context.java b/tlatools/src/tla2sany/semantic/Context.java index c0a321ad7f5c5cb721add5ec47e89bcd43fb0f73..2181c5ce6096a1d550e4b748e33f8ecd87af5df7 100644 --- a/tlatools/src/tla2sany/semantic/Context.java +++ b/tlatools/src/tla2sany/semantic/Context.java @@ -8,18 +8,17 @@ package tla2sany.semantic; +import java.util.Collection; import java.util.Enumeration; import java.util.Hashtable; import tla2sany.explorer.ExploreNode; +import tla2sany.explorer.ExplorerVisitor; import tla2sany.st.Location; import tla2sany.utilities.Strings; import tla2sany.utilities.Vector; import util.UniqueString; -import org.w3c.dom.Document; -import org.w3c.dom.Element; - // A context contains def/declNodes only. // Implements a simple context for symbol decls and defs. Also // supports the intial (or global) context of built-in operators. @@ -90,23 +89,23 @@ public class Context implements ExploreNode { public class InitialSymbolEnumeration { - Enumeration e = initialContext.content(); + Enumeration<Pair> e = initialContext.content(); public boolean hasMoreElements() { return e.hasMoreElements(); } public SymbolNode nextElement() { - return (SymbolNode)(((Pair)(e.nextElement())).getSymbol()); + return e.nextElement().getSymbol(); } } public class ContextSymbolEnumeration { - Enumeration e = Context.this.content(); + Enumeration<Pair> e = Context.this.content(); public boolean hasMoreElements() { return e.hasMoreElements(); } public SymbolNode nextElement() { - return ((Pair)(e.nextElement())).getSymbol(); + return e.nextElement().getSymbol(); } /* public Element export(Document doc) { @@ -132,7 +131,7 @@ public class Context implements ExploreNode { // belongs to is null for global contex shared by all modules. private Errors errors; // Object in which to register errors - private Hashtable table; // Mapping from symbol name to Pair's that include SymbolNode's + private Hashtable<Object, Pair> table; // Mapping from symbol name to Pair's that include SymbolNode's private Pair lastPair; // Pair added last to the this.table /** @@ -140,7 +139,7 @@ public class Context implements ExploreNode { * SymbolTable this Context is part of (or null). */ public Context(ExternalModuleTable mt, Errors errs) { - table = new Hashtable(); + table = new Hashtable<>(); this.exMT = mt; this.errors = errs; this.lastPair = null; @@ -163,6 +162,16 @@ public class Context implements ExploreNode { public static Context getGlobalContext() { return initialContext; } + + public static boolean isBuiltIn(final ExploreNode exploreNode) { + final Collection<Pair> pairs = initialContext.table.values(); + for (Pair p : pairs) { + if (exploreNode == p.info) { + return true; + } + } + return false; + } public Errors getErrors() { return errors; } @@ -216,7 +225,7 @@ public class Context implements ExploreNode { * Returns Enumeration of the elements of the Hashtable "Table", * which are pair of the form (Pair link, SymbolNode sn) */ - public Enumeration content() { + public Enumeration<Pair> content() { return table.elements(); } @@ -232,11 +241,11 @@ public class Context implements ExploreNode { * Returns a Vector of those SymbolNodes in this Context that are * instances of class "template" (or one of its subclasses) */ - public Vector getByClass( Class template ) { - Vector result = new Vector(); - Enumeration list = table.elements(); + public Vector<SymbolNode> getByClass( Class<?> template ) { + Vector<SymbolNode> result = new Vector<>(); + Enumeration<Pair> list = table.elements(); while (list.hasMoreElements()) { - Pair elt = (Pair)list.nextElement(); + Pair elt = list.nextElement(); if (template.isInstance(elt.info)) { result.addElement( elt.info ); } @@ -249,12 +258,12 @@ public class Context implements ExploreNode { * instances of class OpDefNode and that are NOT of kind BuiltInKind * or ModuleInstanceKind */ - public Vector getOpDefs() { + public Vector<OpDefNode> getOpDefs() { // SZ Apr 21, 2009: not used instance // Class template = OpDefNode.class; Pair nextPair = lastPair; - Vector result = new Vector(); + Vector<OpDefNode> result = new Vector<>(); while (nextPair != null) { if ( nextPair.info instanceof OpDefNode && // true for superclasses too. ((OpDefNode)nextPair.info).getKind() != ASTConstants.ModuleInstanceKind && @@ -270,12 +279,12 @@ public class Context implements ExploreNode { * instances of class ThmOrAssumpDefNode or ModuleInstanceKind * * Code copied from getOpDefs(). * *************************************************************************/ - public Vector getThmOrAssDefs() { + public Vector<ThmOrAssumpDefNode> getThmOrAssDefs() { // SZ Apr 21, 2009: not used instance // Class template = ThmOrAssumpDefNode.class; Pair nextPair = lastPair; - Vector result = new Vector(); + Vector<ThmOrAssumpDefNode> result = new Vector<>(); while (nextPair != null) { if ( nextPair.info instanceof ThmOrAssumpDefNode) { result.addElement( (ThmOrAssumpDefNode)(nextPair.info) );} ; @@ -287,13 +296,13 @@ public class Context implements ExploreNode { /** * Returns vector of OpDeclNodes that represent CONSTANT declarations */ - public Vector getConstantDecls() { - Class templateClass = OpDeclNode.class; - Enumeration list = table.elements(); + public Vector<SemanticNode> getConstantDecls() { + Class<? extends SemanticNode> templateClass = OpDeclNode.class; + Enumeration<Pair> list = table.elements(); - Vector result = new Vector(); + Vector<SemanticNode> result = new Vector<>(); while (list.hasMoreElements()) { - Pair elt = (Pair)list.nextElement(); + Pair elt = list.nextElement(); if (templateClass.isInstance(elt.info) && // true for superclasses too. ((OpDeclNode)elt.info).getKind() == ASTConstants.ConstantDeclKind ) result.addElement( (SemanticNode)(elt.info) ); @@ -303,13 +312,13 @@ public class Context implements ExploreNode { } /* Returns vector of OpDeclNodes that represent CONSTANT declarations */ - public Vector getVariableDecls() { - Class templateClass = OpDeclNode.class; - Enumeration list = table.elements(); + public Vector<SemanticNode> getVariableDecls() { + Class<? extends SemanticNode> templateClass = OpDeclNode.class; + Enumeration<Pair> list = table.elements(); - Vector result = new Vector(); + Vector<SemanticNode> result = new Vector<>(); while (list.hasMoreElements()) { - Pair elt = (Pair)list.nextElement(); + Pair elt = list.nextElement(); if (templateClass.isInstance(elt.info) && // true for superclasses too. ((OpDeclNode)elt.info).getKind() == ASTConstants.VariableDeclKind ) result.addElement( (SemanticNode)(elt.info) ); @@ -321,13 +330,13 @@ public class Context implements ExploreNode { * Returns a Vector of those SymbolNodes in this Context that are * instances of class ModuleNode */ - public Vector getModDefs() { - Class template = ModuleNode.class; - Enumeration list = table.elements(); + public Vector<SemanticNode> getModDefs() { + Class<? extends SemanticNode> template = ModuleNode.class; + Enumeration<Pair> list = table.elements(); - Vector result = new Vector(); + Vector<SemanticNode> result = new Vector<>(); while (list.hasMoreElements()) { - Pair elt = (Pair)list.nextElement(); + Pair elt = list.nextElement(); if (template.isInstance(elt.info)) // true for superclasses too. result.addElement( (SemanticNode)(elt.info) ); } @@ -351,72 +360,72 @@ public class Context implements ExploreNode { * The original implementation added the elements of Context ct to * this context in the inverse order as they appear in ct. It was * changed on 12 Mar 2013 by LL to add them in the same order. + * + * Note that the return value is never used in our code base. (2020.03.06) */ - public boolean mergeExtendContext(Context ct) { - boolean erc = true; - - // check locality, and multiplicity - // the .reversePairList was added to the following statement - // by LL on 12 Mar 2013 - Pair p = ct.lastPair.reversePairList(); - while (p != null) { - // Walk back along the list of pairs added to Context "ct" - SymbolNode sn = p.info; - - // Ignore local symbols in Context "ct" - if (!sn.isLocal()) { - Object sName; - if (sn instanceof ModuleNode) { - sName = new SymbolTable.ModuleName(sn.getName()); - } - else { - sName = sn.getName(); - } - - if (!table.containsKey(sName)) { - // If this context DOES NOT contain this name, add it: - table.put(sName, new Pair(sn)); - } - else { - // If this Context DOES contain this name - SymbolNode symbol = ((Pair)table.get(sName)).info; - if (symbol != sn) { - // if the two SymbolNodes with the same name are distinct nodes, - // We issue a warning or do nothing if they are instances of the same Java - // class--i.e. FormalParamNode, OpDeclNode, OpDefNode, or ModuleNode--doing - // nothing if they are both definitions coming from the same module. - // otherwise, it is considered to be an error. - // Option of doing nothing if from same module added by LL on 31 Oct 2012 to - // fix problem caused by the same module being both EXTENDed and imported with - // a LOCAL INSTANCE. Previously, it always added the warning. - if (symbol.getClass() == sn.getClass()) { - if (! symbol.sameOriginallyDefinedInModule(sn)) { - errors.addWarning( sn.getTreeNode().getLocation(), - "Warning: the " + kindOfNode(symbol) + " of '" + - sName.toString() + - "' conflicts with \nits " + kindOfNode(symbol) + " at " + - symbol.getTreeNode().getLocation() + "."); - } - } - else { - errors.addError( sn.getTreeNode().getLocation(), - "The " + kindOfNode(symbol) + " of '" + - sName.toString() + - "' conflicts with \nits " + kindOfNode(symbol) + " at " + - symbol.getTreeNode().getLocation() + "."); + public boolean mergeExtendContext(final Context ct) { + if (ct.lastPair == null) { + // If the context represents an inner module that defines no EXTENDS, ct.lastPair will be null + return true; + } + + boolean erc = true; + // check locality, and multiplicity + // the .reversePairList was added to the following statement + // by LL on 12 Mar 2013 + Pair p = ct.lastPair.reversePairList(); + while (p != null) { + // Walk back along the list of pairs added to Context "ct" + SymbolNode sn = p.info; + + // Ignore local symbols in Context "ct" + if (!sn.isLocal()) { + Object sName; + if (sn instanceof ModuleNode) { + sName = new SymbolTable.ModuleName(sn.getName()); + } else { + sName = sn.getName(); + } + + if (!table.containsKey(sName)) { + // If this context DOES NOT contain this name, add it: + table.put(sName, new Pair(sn)); + } else { + // If this Context DOES contain this name + SymbolNode symbol = ((Pair) table.get(sName)).info; + if (symbol != sn) { + // if the two SymbolNodes with the same name are distinct nodes, + // We issue a warning or do nothing if they are instances of the same Java + // class--i.e. FormalParamNode, OpDeclNode, OpDefNode, or ModuleNode--doing + // nothing if they are both definitions coming from the same module. + // otherwise, it is considered to be an error. + // Option of doing nothing if from same module added by LL on 31 Oct 2012 to + // fix problem caused by the same module being both EXTENDed and imported with + // a LOCAL INSTANCE. Previously, it always added the warning. + if (symbol.getClass() == sn.getClass()) { + if (!symbol.sameOriginallyDefinedInModule(sn)) { + errors.addWarning(sn.getTreeNode().getLocation(), + "Warning: the " + kindOfNode(symbol) + " of '" + sName.toString() + + "' conflicts with \nits " + kindOfNode(symbol) + " at " + + symbol.getTreeNode().getLocation() + "."); + } + } else { + errors.addError(sn.getTreeNode().getLocation(), + "The " + kindOfNode(symbol) + " of '" + sName.toString() + "' conflicts with \nits " + + kindOfNode(symbol) + " at " + symbol.getTreeNode().getLocation() + "."); // "Incompatible multiple definitions of symbol '" + // sName.toString() + // "'; \nthe conflicting declaration is at " + // symbol.getTreeNode().getLocation()+ "."); - erc = false; - } //end else - } // end if - } // end else - } - p = p.link; - } - return erc; + erc = false; + } // end else + } // end if + } // end else + } + p = p.link; + } + return erc; } private static String kindOfNode(SymbolNode symbol) { @@ -471,8 +480,8 @@ public class Context implements ExploreNode { * comment in the walkGraph method of this file for a bit more * * information. * *************************************************************************/ - public Vector getContextEntryStringVector(int depth, boolean b) { - Vector ctxtEntries = new Vector(100); // vector of Strings + public Vector<String> getContextEntryStringVector(int depth, boolean b) { + Vector<String> ctxtEntries = new Vector<>(100); // vector of Strings Context naturalsContext = exMT.getContext(UniqueString.uniqueStringOf("Naturals")); @@ -496,7 +505,7 @@ public class Context implements ExploreNode { } // Reverse the order of elements in the vector so they print properly - Object obj; + String obj; int n = ctxtEntries.size(); for (int i = 0; i < n/2; i++) { obj = ctxtEntries.elementAt(i); @@ -506,11 +515,12 @@ public class Context implements ExploreNode { return ctxtEntries; } - public void walkGraph(Hashtable semNodesTable) { + public void walkGraph(Hashtable<Integer, ExploreNode> semNodesTable, ExplorerVisitor visitor) { + visitor.preVisit(this); UniqueString key; - Enumeration Enum = table.keys(); + Enumeration<?> e = table.keys(); - while (Enum.hasMoreElements()) { + while (e.hasMoreElements()) { /********************************************************************* * Bug fix attempted by LL on 19 Apr 2007. * * * @@ -524,7 +534,7 @@ public class Context implements ExploreNode { * getContextEntryStringVector later on. I decided to stop wasting * * time on this. * *********************************************************************/ - Object next = Enum.nextElement(); + Object next = e.nextElement(); if (next instanceof SymbolTable.ModuleName) { key = ((SymbolTable.ModuleName) next).name ; System.out.println("Bug in debugging caused by inner module " + @@ -533,8 +543,9 @@ public class Context implements ExploreNode { } else { key = (UniqueString) next; - ((Pair)table.get(key)).info.walkGraph(semNodesTable); + ((Pair)table.get(key)).info.walkGraph(semNodesTable, visitor); } ; + visitor.postVisit(this); } } diff --git a/tlatools/src/tla2sany/semantic/DecimalNode.java b/tlatools/src/tla2sany/semantic/DecimalNode.java index 61995d1146a709ba0e0ae972a5ef84c906465120..22db04ecd4b140c17acb9ff17d0c2c9a67046eec 100644 --- a/tlatools/src/tla2sany/semantic/DecimalNode.java +++ b/tlatools/src/tla2sany/semantic/DecimalNode.java @@ -5,11 +5,14 @@ package tla2sany.semantic; import java.math.BigDecimal; import java.util.Hashtable; -import tla2sany.st.TreeNode; - import org.w3c.dom.Document; import org.w3c.dom.Element; +import tla2sany.explorer.ExploreNode; +import tla2sany.explorer.ExplorerVisitor; +import tla2sany.st.TreeNode; +import tla2sany.xml.SymbolContext; + /** * Describes a decimal like 1347.052. This number is represented by the * values @@ -67,9 +70,11 @@ public class DecimalNode extends ExprNode { * Returns the value as a string, exactly the way the user typed it--e.g., * without any normalization, removal of leading or trailing zero's, etc. */ + @Override public final String toString() { return this.image; } /* Level checking */ + @Override public final boolean levelCheck(int iter) { levelChecked = iter; /********************************************************************* @@ -109,11 +114,14 @@ public class DecimalNode extends ExprNode { * inserts them in the Hashtable semNodesTable for use by the * Explorer tool. */ - public final void walkGraph(Hashtable semNodesTable) { - Integer uid = new Integer(myUID); + @Override + public final void walkGraph(Hashtable<Integer, ExploreNode> semNodesTable, ExplorerVisitor visitor) { + Integer uid = Integer.valueOf(myUID); if (semNodesTable.get(uid) != null) return; - semNodesTable.put(new Integer(myUID), this); + semNodesTable.put(uid, this); + visitor.preVisit(this); + visitor.postVisit(this); } /** @@ -121,6 +129,7 @@ public class DecimalNode extends ExprNode { * interface; depth parameter is a bound on the depth of the portion * of the tree that is displayed. */ + @Override public final String toString(int depth) { if (depth <= 0) return ""; return( "\n*DecimalNode" + super.toString(depth) + "Mantissa: " @@ -130,7 +139,8 @@ public class DecimalNode extends ExprNode { ); } - protected Element getLevelElement(Document doc, tla2sany.xml.SymbolContext context) { + @Override + protected Element getLevelElement(Document doc, SymbolContext context) { Element e = doc.createElement("DecimalNode"); if (bigVal != null) { e.appendChild(appendText(doc,"mantissa",bigVal.unscaledValue().toString())); diff --git a/tlatools/src/tla2sany/semantic/DefStepNode.java b/tlatools/src/tla2sany/semantic/DefStepNode.java index 20f4de3b6ca80d1e73bf7b3db92a76f098c15cd2..2bba6d019525992375be96767c3a6d688c1ea385 100644 --- a/tlatools/src/tla2sany/semantic/DefStepNode.java +++ b/tlatools/src/tla2sany/semantic/DefStepNode.java @@ -1,92 +1,103 @@ -// Portions Copyright (c) 2007 Microsoft Corporation. All rights reserved. -package tla2sany.semantic; - -import java.util.Hashtable; - -import tla2sany.st.TreeNode; -import tla2sany.utilities.Strings; -import util.UniqueString; - -import org.w3c.dom.Document; -import org.w3c.dom.Element; - -/*************************************************************************** -* This class represents definition step of a proof, which consists of a * -* sequence of operator-, function-, or module-definition steps. (A * -* module definition is something like "Id == INSTANCE...".) * -***************************************************************************/ - -public class DefStepNode extends LevelNode { - - /************************************************************************* - * The fields. * - *************************************************************************/ - private UniqueString stepNumber ; - /*********************************************************************** - * The step number of the step if it has one, otherwise null if it's * - * not a numbered step. * - ***********************************************************************/ - - private OpDefNode[] defs ; - /*********************************************************************** - * The sequence of definitions. * - ***********************************************************************/ - - /************************************************************************* - * The constructor. * - *************************************************************************/ - public DefStepNode(TreeNode stn, UniqueString stepNum, OpDefNode[] theDefs){ - super(DefStepKind, stn); - this.stepNumber = stepNum; - this.defs = theDefs; - } - - /************************************************************************* - * The methods just return the field values. * - *************************************************************************/ - public UniqueString getStepNumber() {return stepNumber ;} - public OpDefNode[] getDefs() {return defs;} - - public boolean levelCheck(int iter) { - /*********************************************************************** - * Level check the steps and the instantiated modules coming from * - * module definitions. * - ***********************************************************************/ - return this.levelCheckSubnodes(iter, defs) ; - } - - public void walkGraph(Hashtable semNodesTable) { - Integer uid = new Integer(myUID); - if (semNodesTable.get(uid) != null) return; - semNodesTable.put(new Integer(myUID), this); - for (int i = 0; i < defs.length; i++) { - defs[i].walkGraph(semNodesTable); - } ; - } - - public SemanticNode[] getChildren() { - SemanticNode[] res = new SemanticNode[defs.length]; - for (int i = 0; i < defs.length; i++) { - res[i] = defs[i]; - } - return res; - } - public String toString(int depth) { - if (depth <= 0) return ""; - String ret = "\n*DefStepNode:\n" - + super.toString(depth) - + Strings.indent(2, "\ndefs:") ; - for (int i = 0 ; i < this.defs.length; i++) { - ret += Strings.indent(4, this.defs[i].toString(depth-1)) ; - } ; - return ret; - } - - protected Element getLevelElement(Document doc, tla2sany.xml.SymbolContext context) { - Element e = doc.createElement("DefStepNode"); - for (int i=0; i<defs.length;i++) { - e.appendChild(defs[i].export(doc,context)); - } - return e; - } -} +// Portions Copyright (c) 2007 Microsoft Corporation. All rights reserved. +package tla2sany.semantic; + +import java.util.Hashtable; + +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +import tla2sany.explorer.ExploreNode; +import tla2sany.explorer.ExplorerVisitor; +import tla2sany.st.TreeNode; +import tla2sany.utilities.Strings; +import tla2sany.xml.SymbolContext; +import util.UniqueString; + +/*************************************************************************** +* This class represents definition step of a proof, which consists of a * +* sequence of operator-, function-, or module-definition steps. (A * +* module definition is something like "Id == INSTANCE...".) * +***************************************************************************/ + +public class DefStepNode extends LevelNode { + + /************************************************************************* + * The fields. * + *************************************************************************/ + private UniqueString stepNumber ; + /*********************************************************************** + * The step number of the step if it has one, otherwise null if it's * + * not a numbered step. * + ***********************************************************************/ + + private OpDefNode[] defs ; + /*********************************************************************** + * The sequence of definitions. * + ***********************************************************************/ + + /************************************************************************* + * The constructor. * + *************************************************************************/ + public DefStepNode(TreeNode stn, UniqueString stepNum, OpDefNode[] theDefs){ + super(DefStepKind, stn); + this.stepNumber = stepNum; + this.defs = theDefs; + } + + /************************************************************************* + * The methods just return the field values. * + *************************************************************************/ + public UniqueString getStepNumber() {return stepNumber ;} + public OpDefNode[] getDefs() {return defs;} + + @Override + public boolean levelCheck(int iter) { + /*********************************************************************** + * Level check the steps and the instantiated modules coming from * + * module definitions. * + ***********************************************************************/ + return this.levelCheckSubnodes(iter, defs) ; + } + + @Override + public void walkGraph(Hashtable<Integer, ExploreNode> semNodesTable, ExplorerVisitor visitor) { + Integer uid = Integer.valueOf(myUID); + if (semNodesTable.get(uid) != null) return; + semNodesTable.put(uid, this); + visitor.preVisit(this); + for (int i = 0; i < defs.length; i++) { + defs[i].walkGraph(semNodesTable, visitor); + } ; + visitor.postVisit(this); + } + + @Override + public SemanticNode[] getChildren() { + SemanticNode[] res = new SemanticNode[defs.length]; + for (int i = 0; i < defs.length; i++) { + res[i] = defs[i]; + } + return res; + } + + @Override + public String toString(int depth) { + if (depth <= 0) return ""; + String ret = "\n*DefStepNode:\n" + + super.toString(depth) + + Strings.indent(2, "\ndefs:") ; + for (int i = 0 ; i < this.defs.length; i++) { + ret += Strings.indent(4, this.defs[i].toString(depth-1)) ; + } ; + return ret; + } + + @Override + protected Element getLevelElement(Document doc, SymbolContext context) { + Element e = doc.createElement("DefStepNode"); + for (int i=0; i<defs.length;i++) { + e.appendChild(defs[i].export(doc,context)); + } + return e; + } +} diff --git a/tlatools/src/tla2sany/semantic/ExternalModuleTable.java b/tlatools/src/tla2sany/semantic/ExternalModuleTable.java index 4a208f93b33d48482cf21d56aee097a5bfdbd56b..10835c4c439706e2ff39d34f57acffb205e95021 100644 --- a/tlatools/src/tla2sany/semantic/ExternalModuleTable.java +++ b/tlatools/src/tla2sany/semantic/ExternalModuleTable.java @@ -12,6 +12,7 @@ import java.util.Enumeration; import java.util.Hashtable; import tla2sany.explorer.ExploreNode; +import tla2sany.explorer.ExplorerVisitor; import tla2sany.utilities.Strings; import tla2sany.utilities.Vector; import util.UniqueString; @@ -49,9 +50,9 @@ public class ExternalModuleTable implements ExploreNode { */ public String levelDataToString() { return "Dummy level string"; } - public void walkGraph(Hashtable moduleNodesTable) { - if (moduleNode != null) moduleNode.walkGraph(moduleNodesTable); - if (ctxt != null) ctxt.walkGraph(moduleNodesTable); + public void walkGraph(Hashtable<Integer, ExploreNode> moduleNodesTable, ExplorerVisitor visitor) { + if (moduleNode != null) moduleNode.walkGraph(moduleNodesTable, visitor); + if (ctxt != null) ctxt.walkGraph(moduleNodesTable, visitor); } // end walkGraph() public String toString(int depth) { @@ -80,21 +81,21 @@ public class ExternalModuleTable implements ExploreNode { * moduleHashTable, and that each of its entries has a moduleName as the * * key and a value that's an ExternalModuleTableEntry object. * *************************************************************************/ - public Hashtable moduleHashTable; + public Hashtable<UniqueString, ExternalModuleTableEntry> moduleHashTable; // Vector moduleVector contains ModuleNodes (the same ones as // moduleHashTable), but preserves the order in which they were // inserted. If module A depends on module B, then A has a HIGHER // index than B. - public Vector moduleNodeVector; + public Vector<ModuleNode> moduleNodeVector; // The nodule node of the root module public ModuleNode rootModule; // Constructor public ExternalModuleTable() { - moduleHashTable = new Hashtable(); - moduleNodeVector = new Vector(); + moduleHashTable = new Hashtable<>(); + moduleNodeVector = new Vector<>(); } // Set and get the rootModule field @@ -102,11 +103,15 @@ public class ExternalModuleTable implements ExploreNode { public void setRootModule(ModuleNode mn) { rootModule = mn; } public final Context getContext( UniqueString key ) { - ExternalModuleTableEntry p = (ExternalModuleTableEntry)moduleHashTable.get(key); + ExternalModuleTableEntry p = moduleHashTable.get(key); if (p == null) return null; return p.ctxt; } + public final Context getContextForRootModule() { + return getContext(getRootModule().getName()); + } + /** * Returns a vector of ModuleNodes, one for each outer module (i.e. not * inner modules) in the specification. InnerModules can be obtained @@ -124,25 +129,26 @@ public class ExternalModuleTable implements ExploreNode { } public final ModuleNode getModuleNode( UniqueString key ) { - ExternalModuleTableEntry p = (ExternalModuleTableEntry)moduleHashTable.get(key); + ExternalModuleTableEntry p = moduleHashTable.get(key); if (p == null) return null; return p.moduleNode; } public final void put( UniqueString key, Context ctxt, ModuleNode moduleNode ) { - ExternalModuleTableEntry c = (ExternalModuleTableEntry)moduleHashTable.get( key ); + ExternalModuleTableEntry c = moduleHashTable.get( key ); if (c == null) { moduleHashTable.put( key, new ExternalModuleTableEntry(ctxt, moduleNode) ); moduleNodeVector.addElement(moduleNode); } } + @Override public String toString() { - Enumeration Enum = moduleHashTable.elements(); + Enumeration<ExternalModuleTableEntry> Enum = moduleHashTable.elements(); String ret = ""; - for (int i=1; Enum.hasMoreElements(); i++) { - ExternalModuleTableEntry mte = (ExternalModuleTableEntry)Enum.nextElement(); + while (Enum.hasMoreElements()) { + ExternalModuleTableEntry mte = Enum.nextElement(); ret = ret + mte.toString(); } return "\nModule Table:" + Strings.indent(2,ret); @@ -152,7 +158,7 @@ public class ExternalModuleTable implements ExploreNode { System.out.print("\nExternal Module Table:"); for (int i = 0; i < moduleNodeVector.size(); i++) { - ModuleNode mn = (ModuleNode)moduleNodeVector.elementAt(i); + ModuleNode mn = moduleNodeVector.elementAt(i); if (mn != null) { System.out.print(Strings.indent(2, "\nModule: ")); @@ -186,12 +192,16 @@ public class ExternalModuleTable implements ExploreNode { return ret; } - public void walkGraph(Hashtable moduleNodesTable) { - Enumeration Enum = moduleHashTable.elements(); + public void walkGraph(Hashtable<Integer, ExploreNode> moduleNodesTable) { + walkGraph(moduleNodesTable, ExplorerVisitor.NoopVisitor); + } + + public void walkGraph(Hashtable<Integer, ExploreNode> moduleNodesTable, ExplorerVisitor visitor) { + Enumeration<ExternalModuleTableEntry> Enum = moduleHashTable.elements(); while ( Enum.hasMoreElements() ) { - ExternalModuleTableEntry mte = (ExternalModuleTableEntry)Enum.nextElement(); - mte.walkGraph(moduleNodesTable); + ExternalModuleTableEntry mte = Enum.nextElement(); + mte.walkGraph(moduleNodesTable, visitor); } } diff --git a/tlatools/src/tla2sany/semantic/FormalParamNode.java b/tlatools/src/tla2sany/semantic/FormalParamNode.java index 262db8715291eb8d6af6fda8daf56c58e00112ba..46f0756e9073d9fe90172350d7fc37c8028de0a7 100644 --- a/tlatools/src/tla2sany/semantic/FormalParamNode.java +++ b/tlatools/src/tla2sany/semantic/FormalParamNode.java @@ -4,12 +4,15 @@ package tla2sany.semantic; import java.util.Hashtable; -import tla2sany.st.TreeNode; -import util.UniqueString; - import org.w3c.dom.Document; import org.w3c.dom.Element; +import tla2sany.explorer.ExploreNode; +import tla2sany.explorer.ExplorerVisitor; +import tla2sany.st.TreeNode; +import tla2sany.xml.SymbolContext; +import util.UniqueString; + /** * A FormalParamNode represents a formal parameter in a user * definition--for example, p and q in @@ -67,6 +70,7 @@ public class FormalParamNode extends SymbolNode { /* Level checking */ // private HashSet levelParams; + @Override public final boolean levelCheck(int iter) { if (levelChecked == 0) { /********************************************************************* @@ -111,13 +115,17 @@ public class FormalParamNode extends SymbolNode { // "ArgLevelParams: " + this.getArgLevelParams() + "\n" ; // } - public final void walkGraph(Hashtable semNodesTable) { - Integer uid = new Integer(myUID); + @Override + public final void walkGraph(Hashtable<Integer, ExploreNode> semNodesTable, ExplorerVisitor visitor) { + Integer uid = Integer.valueOf(myUID); if (semNodesTable.get(uid) != null) return; - semNodesTable.put(new Integer(myUID), this); + semNodesTable.put(uid, this); + visitor.preVisit(this); + visitor.postVisit(this); } + @Override public final String toString(int depth) { if (depth <= 0) return ""; return ("\n*FormalParamNode: " + this.getName().toString() + @@ -128,7 +136,7 @@ public class FormalParamNode extends SymbolNode { return "FormalParamNodeRef"; } - protected Element getSymbolElement(Document doc, tla2sany.xml.SymbolContext context) { + protected Element getSymbolElement(Document doc, SymbolContext context) { Element e = doc.createElement("FormalParamNode"); e.appendChild(appendText(doc,"uniquename",getName().toString())); e.appendChild(appendText(doc,"arity",Integer.toString(getArity()))); diff --git a/tlatools/src/tla2sany/semantic/Generator.java b/tlatools/src/tla2sany/semantic/Generator.java index d5917ac6bc0282566037012cb4221f6a14b3a6e8..9561f00c6b68eb2c329b7b3313d5ced9d66fcde2 100644 --- a/tlatools/src/tla2sany/semantic/Generator.java +++ b/tlatools/src/tla2sany/semantic/Generator.java @@ -33,8 +33,6 @@ import tlc2.output.MP; import util.UniqueString; import util.WrongInvocationException; - - // This class generates a semantic graph from a parse tree. It also uses // the list of modules to access contexts to instantiate or extend. @@ -49,469 +47,435 @@ import util.WrongInvocationException; // are working from the inside. /*************************************************************************** -* Only one object of class Generator is ever created--namely, the object * -* gen in drivers/SANY.java. So it seems like all fields and methods of * -* this class could have been made static. * -***************************************************************************/ - -public class Generator implements ASTConstants, SyntaxTreeConstants, - LevelConstants, TLAplusParserConstants { - - private Context context; // current context, not very useful. - private SymbolTable symbolTable; // symbol table used throughout the spec. - // except for embedded modules - /*********************************************************************** - * A SymbolTable is actually a stack of contexts, where a context is a * - * mapping from symbols to their meanings. Elements are looked up in * - * a SymbolTable by going through the stack of contexts from the top * - * down. * - * * - * symbolTable appears to contain the definitions and declarations of * - * all symbols that are currently defined or declared. When a * - * construct containing local definitions or declarations is * - * entered--for example, when constructing the ExprNode for a LET/IN, * - * a new context is pushed onto symbolTable to hold those local defs * - * or decls. It appears that after the complete semantic ModuleNode * - * has been created, symbolTable should contain only a single context, * - * which becomes the ModuleNode's context. * - * * - * symbolTable entries are made for SymbolNode objects (objects * - * belonging to a subclass of SymbolNode). It appears that all of * - * those objects are added (to the context at the top of the stack) * - * symbolTable by the SymbolNode object's constructor. * - ***********************************************************************/ - - private ExternalModuleTable moduleTable; - public Errors errors; - private Stack excStack; // Holds stack of OpApplNodes for $Except - // operators; used for @ - private Stack excSpecStack; // Holds stack of OpApplNode for @Pair - // operators representing ExceptSpecs; - // also used for @ - - // dummy definitions; used during the creation of the "-- TLA+ BUILTINS --" phony module, - // before real modules are processed; also used somewhat inconsistently to avoid returning - // null values, and to allow semantic analysis to proceed when an error is detected - private SubstInNode nullSubstIn; - private FormalParamNode[] nullParam; - private OpDefNode nullODN; - protected OpApplNode nullOAN; - protected LabelNode nullLabelNode; - /*********************************************************************** - * This is a special OpApplNode that gets returned in various places * - * when an error is detected. This allows semantic analysis to * - * continue, presumably by preventing a NullPointerException. * - ***********************************************************************/ - protected OpArgNode nullOpArg; - - private final static UniqueString S_e = UniqueString.uniqueStringOf("\\E"); - private final static UniqueString S_ex = UniqueString.uniqueStringOf("\\exists"); - private final static UniqueString S_f = UniqueString.uniqueStringOf("\\A"); - private final static UniqueString S_fx = UniqueString.uniqueStringOf("\\always"); - private final static UniqueString S_te = UniqueString.uniqueStringOf("\\EE"); - private final static UniqueString S_tf = UniqueString.uniqueStringOf("\\AA"); - private final static UniqueString S_a = UniqueString.uniqueStringOf("<<"); - private final static UniqueString S_brack = UniqueString.uniqueStringOf("["); - private final static UniqueString S_sf = UniqueString.uniqueStringOf("SF_"); - private final static UniqueString S_wf = UniqueString.uniqueStringOf("WF_"); - private final static UniqueString S_at = UniqueString.uniqueStringOf("@"); - private final static UniqueString S_lambda = - UniqueString.uniqueStringOf("LAMBDA"); - private final static UniqueString S_subexpression = - UniqueString.uniqueStringOf("$Subexpression"); - - class Function { - /*********************************************************************** - * The only object of this class ever created is `functions', declared * - * right after the class definition. So, I don't know why this * - * class's methods weren't just made ordinary methods of the Generator * - * class. Perhaps this was done so the subclass `pair' could be * - * defined within it without interfering with any other subclass by * - * that name. However, there is no other subclass by that name in the * - * Generator class. * - * * - * See the comments attached to the `functions' object for the class's * - * explanation. * - ***********************************************************************/ - class pair{ - UniqueString a; - OpApplNode b; - - pair(UniqueString uniqueString, OpApplNode oan) { a = uniqueString; b = oan; } - UniqueString uniqueString() { return a; } - OpApplNode oan() { return b; } - } - - Stack funcStack = new Stack(); - - void push(UniqueString uniqueString, OpApplNode oan) { - funcStack.push( new pair(uniqueString, oan) ); - } - - void pop() { funcStack.pop(); } - - // If same function found farther down on stack, then this is a recursive - // function definition--change the operator to indicate so. - boolean recursionCheck(UniqueString uniqueString) { - for (int lvi = funcStack.size()-1; lvi>=0; lvi-- ) { - if (uniqueString.equals( ((pair)funcStack.elementAt( lvi )).uniqueString())) { - // OA-rfs = recursive func spec - ((pair)funcStack.elementAt(lvi)).oan().resetOperator(OP_rfs); - return true; - } - } - return false; - } - - } // end class Function - - // This is the only instance of class Function - - Function functions = new Function(); - /*********************************************************************** - * This object is used to detect that a function definition is * - * recursive. It is a stack of <<UniqueString, OpApplNode>> pairs. * - * An element <<f, opAp>> in the stack indicates that the current node * - * being processed is inside the body of a function definition of the * - * form f[...] == body. The current context assigns to f an OpDefNode * - * whose body is the OpApplNode opAp whose operator is either * - * $RecursiveFcnSpec or $NonRecursiveFcnSpec having (an OpDeclNode * - * for) the bound identifier f and having the body of the definition * - * as its operand. The following methods can be applied: * - * * - * functions.push(UniqueString us, OpApplNode oan) * - * Pushes <<us, oan>> onto the stack. * - * * - * functions.pop() * - * Pops the last element off the stack. * - * * - * recursionCheck(UniqueString us) * - * If there is a pair <<us, oan>> on the stack, then the * - * operator of oan is set to $RecursiveFcnSpec. * - * * - * An entry for f is pushed on the stack before processing the body * - * f's definition, and popped upon return. * - * * - * When processing a FcnAppl syntax nodefor an expression f[...], * - * functions.recursionCheck is called to see the node occurs in the * - * body of the definition of f, in which case the definition body's * - * OpApplNode's operator is changed to indicate that f's definition is * - * recursive. * - ***********************************************************************/ - - - - // This class represents a generalized identifier, e.g. a syntactic - // phrase such as A(2)!B(x,y)!C!D(u,v,w) In this case the compoundID - // would be A!B!C!D and the args array would contain [2,x,y] - // (i.e. not including u,v,and w, because they are - // args to the main operator, and not part of the GenID - private class GenID { - - private TreeNode treeNode; - // The syntax tree node holding this GenID - private StringBuffer compoundID; - // The string name of the compound op, with "!"'s, if any - private Vector argsVector = new Vector(); - // Vector of arguments (ExprNodes and OpArgNodes) - // that are embedded in the generalized identifier - - // The next three fields are null until the finalAppend - // method has been called - private UniqueString compoundIDUS; - // UniqueString version of compoundID - private SymbolNode fullyQualifiedOp; - // SymbolNode for compoundID - private ExprOrOpArgNode[] args; - // Array with same contents as argsVector - - // Constructor - public GenID(TreeNode node) { - treeNode = node; - compoundID = new StringBuffer(""); - compoundIDUS = null; - fullyQualifiedOp = null; - args = null; - } - - public final UniqueString getCompoundIDUS() { return compoundIDUS;} - - public final SymbolNode getFullyQualifiedOp() { return fullyQualifiedOp; } - - public final ExprOrOpArgNode[] getArgs() { return args; } - - public final Vector getArgsVector() { return argsVector; } - - /** Append a new segment to the compound name of the operator */ - public final void append(String s) { - compoundID.append(s); - } - - /** Add a new argument to the vector of arguments being constructed */ - public final void addArg(ExprOrOpArgNode arg) { - argsVector.addElement(arg); - } - - /** - * Appends the final element of the fully-qualified name to the - * GenID that has been being built using addArg() and - * append(). Since it signals the completion of the construction - * of the name, this method converts the name from StringBuffer to - * UniqueString, resolves it to a SymbolNode, and converts the - * argument list from vector to array form. - */ - public final void finalAppend(String s, boolean unaryNegKludge) { - // append the final segment of the compound name - if (unaryNegKludge && s.equals("-")) { - compoundID.append("-."); - } - else { - compoundID.append(s); - } - - // convert the full name to a UniqueString - compoundIDUS = UniqueString.uniqueStringOf(compoundID.toString()); - - // look up the full name in the SymbolTable (may return null) - fullyQualifiedOp = symbolTable.resolveSymbol(Operators.resolveSynonym(compoundIDUS)); - - if (fullyQualifiedOp == null && compoundIDUS != S_at) { - // if not in the symbol table and not "@", then it is an unresolved symbol - errors.addError(treeNode.getLocation(), - "Could not find declaration or definition of symbol '" + - UniqueString.uniqueStringOf(compoundID.toString()) + "'."); - } - } - - public final void finalizeID() { - // copy argsVector contents into args array - args = new ExprOrOpArgNode[argsVector.size()]; - - for (int i = 0; i < args.length; i++) { - args[i] = (ExprOrOpArgNode)argsVector.elementAt(i); - } - } - - /** - * Special kluge to append a "." to the name of this ID; - * should be used ONLY to change unary "-" to "-." - */ - public final void appendDot() { - compoundIDUS = UniqueString.uniqueStringOf(compoundIDUS.toString() + "."); - } - - public final String toString(int n) { - String ret = "compound ID: " + compoundID.toString() + "\nargs: " + args.length + "\n"; - for (int i = 0; i < args.length; i++) { - ret += Strings.indent(2,args[i].toString(n)); - } - return ret; - } - - } // end GenID - - - final UniqueString AtUS = UniqueString.uniqueStringOf("@") ; - /*********************************************************************** - * This is needed in a couple of places. (I didn't notice that this * - * is already defined to equal S_at.) * - ***********************************************************************/ - - /************************************************************************* - * An object of subclass Selector is used to represent a the translation * - * of a GeneralId or an OpApplication whose operator is a GeneralId. It * - * contains the data represented in the Subexpression +cal algorithm by * - * the arrays ops and args. For example, the user expression * - * * - * Foo(a,b)!:!(c) * - * * - * produces the following in Subexpression algorithm: * - * * - * ops = << "Foo", ":", "null" >> * - * args = << <<a, b>>, << >>, << <<c>> >>, * - * * - * This is represented by a Selector object with four arrays of * - * length 3. The first is: * - * * - * int[] ops with ops[0] = NameSel * - * ops[1] = ColonSel * - * ops[2] = NullSel * - * * - * The set of all possible values of ops[i] are: * - * * - * An integer i >= 0 // Represents the i of "!i". * - * * - * One of the following integers < 0. * - *************************************************************************/ - - final int NameSel = -1 ; // A name like "Foo" ; - final int NullSel = -2 ; // A "(...)" selector ; - final int GGSel = -3 ; // ">>" ; - final int LLSel = -4 ; // "<<" ; - final int ColonSel = -5 ; // ":" ; - final int AtSel = -6 ; // "@" ; - - /************************************************************************* - * The remaining three arrays are: * - * * - * SyntaxTreeNode[] opsSTN with values equal to the syntax tree * - * nodes for the corresponding element * - * of ops. * - * * - * UniqueString[] opNames with opNames[0] = "Foo" * - * opNames[1] = ":" * - * opNames[2] = null * - * * - * SyntaxTreeNode[] args with args[0] = "(a, b)" * - * args[1] = null * - * args[2] = "(c)" * - * * - * Note: we can't turn the arguments into semantic nodes yet because we * - * want to use generateExprOrOpArg to do that, which requires that we * - * know the operator of which they're the arguments. Hence, that * - * computation has to be folded into the Subexpression algorithm. * - * * - * These arrays are initialized by the finish method, which is called * - * after all calls of addSelector have been issued. * - *************************************************************************/ - private class Selector { - /********************************************************************** - * A single field for the syntax tree node of the entire selector. * - **********************************************************************/ - SyntaxTreeNode selSTN ; - - /********************************************************************** - * The fields modified by addSelector: * - **********************************************************************/ - private Vector opVec = new Vector(); // of SyntaxTreeNode ; - private Vector argVec = new Vector(); // of SyntaxTreeNode ; - - /********************************************************************** - * The fields set from opVec and argVec by finalize. * - **********************************************************************/ - int[] ops = null ; - UniqueString[] opNames = null ; - SyntaxTreeNode[] opsSTN = null ; - SyntaxTreeNode[] args = null ; - - /********************************************************************** - * Constants not needed elsewhere. * - **********************************************************************/ - final UniqueString GGUS = UniqueString.uniqueStringOf(">>") ; - final UniqueString LLUS = UniqueString.uniqueStringOf("<<") ; - final UniqueString ColonUS = UniqueString.uniqueStringOf(":") ; - - /********************************************************************** - * The constructor. * - **********************************************************************/ - - Selector(SyntaxTreeNode tn) { - selSTN = tn ; - } - void addSelector(SyntaxTreeNode stn, SyntaxTreeNode opargs) { - /******************************************************************** - * opargs is the args entry. * - * * - * stn is the syntax tree node representing the ops entry. If * - * there are no arguments, it should be null. For a NullSel entry * - * it should be the OpArgs node (the "(arg1, ... , argN)") that is * - * used as the second argument. * - ********************************************************************/ - opVec.addElement(stn) ; - argVec.addElement(opargs) ; - } // addSelector - - void finish() throws AbortException { - int arrayLen = opVec.size() ; - ops = new int[arrayLen] ; - opNames = new UniqueString[arrayLen] ; - opsSTN = new SyntaxTreeNode[arrayLen] ; - args = new SyntaxTreeNode[arrayLen] ; - for (int i = 0; i < arrayLen; i++) { - args[i] = (SyntaxTreeNode) argVec.elementAt(i) ; - SyntaxTreeNode stn = (SyntaxTreeNode) opVec.elementAt(i) ; - opsSTN[i] = stn ; - opNames[i] = stn.getUS() ; - switch (stn.getKind()) { - case IDENTIFIER: - case N_InfixOp: - case N_NonExpPrefixOp: - case N_PostfixOp: - case N_PrefixOp: - case ProofStepLexeme: + * Only one object of class Generator is ever created--namely, the object * gen + * in drivers/SANY.java. So it seems like all fields and methods of * this class + * could have been made static. * + ***************************************************************************/ + +public class Generator implements ASTConstants, SyntaxTreeConstants, LevelConstants, TLAplusParserConstants { + + private Context context; // current context, not very useful. + private SymbolTable symbolTable; // symbol table used throughout the spec. + // except for embedded modules + /*********************************************************************** + * A SymbolTable is actually a stack of contexts, where a context is a * mapping + * from symbols to their meanings. Elements are looked up in * a SymbolTable by + * going through the stack of contexts from the top * down. * * symbolTable + * appears to contain the definitions and declarations of * all symbols that are + * currently defined or declared. When a * construct containing local + * definitions or declarations is * entered--for example, when constructing the + * ExprNode for a LET/IN, * a new context is pushed onto symbolTable to hold + * those local defs * or decls. It appears that after the complete semantic + * ModuleNode * has been created, symbolTable should contain only a single + * context, * which becomes the ModuleNode's context. * * symbolTable entries + * are made for SymbolNode objects (objects * belonging to a subclass of + * SymbolNode). It appears that all of * those objects are added (to the context + * at the top of the stack) * symbolTable by the SymbolNode object's + * constructor. * + ***********************************************************************/ + + private ExternalModuleTable moduleTable; + public Errors errors; + private Stack excStack; // Holds stack of OpApplNodes for $Except + // operators; used for @ + private Stack excSpecStack; // Holds stack of OpApplNode for @Pair + // operators representing ExceptSpecs; + // also used for @ + + // dummy definitions; used during the creation of the "-- TLA+ BUILTINS --" + // phony module, + // before real modules are processed; also used somewhat inconsistently to avoid + // returning + // null values, and to allow semantic analysis to proceed when an error is + // detected + private SubstInNode nullSubstIn; + private FormalParamNode[] nullParam; + private OpDefNode nullODN; + protected OpApplNode nullOAN; + protected LabelNode nullLabelNode; + /*********************************************************************** + * This is a special OpApplNode that gets returned in various places * when an + * error is detected. This allows semantic analysis to * continue, presumably by + * preventing a NullPointerException. * + ***********************************************************************/ + protected OpArgNode nullOpArg; + + private final static UniqueString S_e = UniqueString.uniqueStringOf("\\E"); + private final static UniqueString S_ex = UniqueString.uniqueStringOf("\\exists"); + private final static UniqueString S_f = UniqueString.uniqueStringOf("\\A"); + private final static UniqueString S_fx = UniqueString.uniqueStringOf("\\always"); + private final static UniqueString S_te = UniqueString.uniqueStringOf("\\EE"); + private final static UniqueString S_tf = UniqueString.uniqueStringOf("\\AA"); + private final static UniqueString S_a = UniqueString.uniqueStringOf("<<"); + private final static UniqueString S_brack = UniqueString.uniqueStringOf("["); + private final static UniqueString S_sf = UniqueString.uniqueStringOf("SF_"); + private final static UniqueString S_wf = UniqueString.uniqueStringOf("WF_"); + private final static UniqueString S_at = UniqueString.uniqueStringOf("@"); + private final static UniqueString S_lambda = UniqueString.uniqueStringOf("LAMBDA"); + private final static UniqueString S_subexpression = UniqueString.uniqueStringOf("$Subexpression"); + + class Function { + /*********************************************************************** + * The only object of this class ever created is `functions', declared * right + * after the class definition. So, I don't know why this * class's methods + * weren't just made ordinary methods of the Generator * class. Perhaps this was + * done so the subclass `pair' could be * defined within it without interfering + * with any other subclass by * that name. However, there is no other subclass + * by that name in the * Generator class. * * See the comments attached to the + * `functions' object for the class's * explanation. * + ***********************************************************************/ + class pair { + UniqueString a; + OpApplNode b; + + pair(UniqueString uniqueString, OpApplNode oan) { + a = uniqueString; + b = oan; + } + + UniqueString uniqueString() { + return a; + } + + OpApplNode oan() { + return b; + } + } + + Stack funcStack = new Stack(); + + void push(UniqueString uniqueString, OpApplNode oan) { + funcStack.push(new pair(uniqueString, oan)); + } + + void pop() { + funcStack.pop(); + } + + // If same function found farther down on stack, then this is a recursive + // function definition--change the operator to indicate so. + boolean recursionCheck(UniqueString uniqueString) { + for (int lvi = funcStack.size() - 1; lvi >= 0; lvi--) { + if (uniqueString.equals(((pair) funcStack.elementAt(lvi)).uniqueString())) { + // OA-rfs = recursive func spec + ((pair) funcStack.elementAt(lvi)).oan().resetOperator(OP_rfs); + return true; + } + } + return false; + } + + } // end class Function + + // This is the only instance of class Function + + Function functions = new Function(); + + /*********************************************************************** + * This object is used to detect that a function definition is * recursive. It + * is a stack of <<UniqueString, OpApplNode>> pairs. * An element <<f, opAp>> in + * the stack indicates that the current node * being processed is inside the + * body of a function definition of the * form f[...] == body. The current + * context assigns to f an OpDefNode * whose body is the OpApplNode opAp whose + * operator is either * $RecursiveFcnSpec or $NonRecursiveFcnSpec having (an + * OpDeclNode * for) the bound identifier f and having the body of the + * definition * as its operand. The following methods can be applied: * * + * functions.push(UniqueString us, OpApplNode oan) * Pushes <<us, oan>> onto the + * stack. * * functions.pop() * Pops the last element off the stack. * * + * recursionCheck(UniqueString us) * If there is a pair <<us, oan>> on the + * stack, then the * operator of oan is set to $RecursiveFcnSpec. * * An entry + * for f is pushed on the stack before processing the body * f's definition, and + * popped upon return. * * When processing a FcnAppl syntax nodefor an + * expression f[...], * functions.recursionCheck is called to see the node + * occurs in the * body of the definition of f, in which case the definition + * body's * OpApplNode's operator is changed to indicate that f's definition is + * * recursive. * + ***********************************************************************/ + + // This class represents a generalized identifier, e.g. a syntactic + // phrase such as A(2)!B(x,y)!C!D(u,v,w) In this case the compoundID + // would be A!B!C!D and the args array would contain [2,x,y] + // (i.e. not including u,v,and w, because they are + // args to the main operator, and not part of the GenID + private class GenID { + + private TreeNode treeNode; + // The syntax tree node holding this GenID + private StringBuffer compoundID; + // The string name of the compound op, with "!"'s, if any + private Vector argsVector = new Vector(); + // Vector of arguments (ExprNodes and OpArgNodes) + // that are embedded in the generalized identifier + + // The next three fields are null until the finalAppend + // method has been called + private UniqueString compoundIDUS; + // UniqueString version of compoundID + private SymbolNode fullyQualifiedOp; + // SymbolNode for compoundID + private ExprOrOpArgNode[] args; + // Array with same contents as argsVector + + // Constructor + public GenID(TreeNode node) { + treeNode = node; + compoundID = new StringBuffer(""); + compoundIDUS = null; + fullyQualifiedOp = null; + args = null; + } + + public final UniqueString getCompoundIDUS() { + return compoundIDUS; + } + + public final SymbolNode getFullyQualifiedOp() { + return fullyQualifiedOp; + } + + public final ExprOrOpArgNode[] getArgs() { + return args; + } + + public final Vector getArgsVector() { + return argsVector; + } + + /** Append a new segment to the compound name of the operator */ + public final void append(String s) { + compoundID.append(s); + } + + /** Add a new argument to the vector of arguments being constructed */ + public final void addArg(ExprOrOpArgNode arg) { + argsVector.addElement(arg); + } + + /** + * Appends the final element of the fully-qualified name to the GenID that has + * been being built using addArg() and append(). Since it signals the completion + * of the construction of the name, this method converts the name from + * StringBuffer to UniqueString, resolves it to a SymbolNode, and converts the + * argument list from vector to array form. + */ + public final void finalAppend(String s, boolean unaryNegKludge) { + // append the final segment of the compound name + if (unaryNegKludge && s.equals("-")) { + compoundID.append("-."); + } else { + compoundID.append(s); + } + + // convert the full name to a UniqueString + compoundIDUS = UniqueString.uniqueStringOf(compoundID.toString()); + + // look up the full name in the SymbolTable (may return null) + fullyQualifiedOp = symbolTable.resolveSymbol(Operators.resolveSynonym(compoundIDUS)); + + if (fullyQualifiedOp == null && compoundIDUS != S_at) { + // if not in the symbol table and not "@", then it is an unresolved symbol + errors.addError(treeNode.getLocation(), "Could not find declaration or definition of symbol '" + + UniqueString.uniqueStringOf(compoundID.toString()) + "'."); + } + } + + public final void finalizeID() { + // copy argsVector contents into args array + args = new ExprOrOpArgNode[argsVector.size()]; + + for (int i = 0; i < args.length; i++) { + args[i] = (ExprOrOpArgNode) argsVector.elementAt(i); + } + } + + /** + * Special kluge to append a "." to the name of this ID; should be used ONLY to + * change unary "-" to "-." + */ + public final void appendDot() { + compoundIDUS = UniqueString.uniqueStringOf(compoundIDUS.toString() + "."); + } + + public final String toString(int n) { + String ret = "compound ID: " + compoundID.toString() + "\nargs: " + args.length + "\n"; + for (int i = 0; i < args.length; i++) { + ret += Strings.indent(2, args[i].toString(n)); + } + return ret; + } + + } // end GenID + + final UniqueString AtUS = UniqueString.uniqueStringOf("@"); + /*********************************************************************** + * This is needed in a couple of places. (I didn't notice that this * is already + * defined to equal S_at.) * + ***********************************************************************/ + + /************************************************************************* + * An object of subclass Selector is used to represent a the translation * of a + * GeneralId or an OpApplication whose operator is a GeneralId. It * contains + * the data represented in the Subexpression +cal algorithm by * the arrays ops + * and args. For example, the user expression * * Foo(a,b)!:!(c) * * produces + * the following in Subexpression algorithm: * * ops = << "Foo", ":", "null" >> + * * args = << <<a, b>>, << >>, << <<c>> >>, * * This is represented by a + * Selector object with four arrays of * length 3. The first is: * * int[] ops + * with ops[0] = NameSel * ops[1] = ColonSel * ops[2] = NullSel * * The set of + * all possible values of ops[i] are: * * An integer i >= 0 // Represents the i + * of "!i". * * One of the following integers < 0. * + *************************************************************************/ + + final int NameSel = -1; // A name like "Foo" ; + final int NullSel = -2; // A "(...)" selector ; + final int GGSel = -3; // ">>" ; + final int LLSel = -4; // "<<" ; + final int ColonSel = -5; // ":" ; + final int AtSel = -6; // "@" ; + + /************************************************************************* + * The remaining three arrays are: * * SyntaxTreeNode[] opsSTN with values equal + * to the syntax tree * nodes for the corresponding element * of ops. * * + * UniqueString[] opNames with opNames[0] = "Foo" * opNames[1] = ":" * + * opNames[2] = null * * SyntaxTreeNode[] args with args[0] = "(a, b)" * args[1] + * = null * args[2] = "(c)" * * Note: we can't turn the arguments into semantic + * nodes yet because we * want to use generateExprOrOpArg to do that, which + * requires that we * know the operator of which they're the arguments. Hence, + * that * computation has to be folded into the Subexpression algorithm. * * + * These arrays are initialized by the finish method, which is called * after + * all calls of addSelector have been issued. * + *************************************************************************/ + private class Selector { + /********************************************************************** + * A single field for the syntax tree node of the entire selector. * + **********************************************************************/ + SyntaxTreeNode selSTN; + + /********************************************************************** + * The fields modified by addSelector: * + **********************************************************************/ + private Vector opVec = new Vector(); // of SyntaxTreeNode ; + private Vector argVec = new Vector(); // of SyntaxTreeNode ; + + /********************************************************************** + * The fields set from opVec and argVec by finalize. * + **********************************************************************/ + int[] ops = null; + UniqueString[] opNames = null; + SyntaxTreeNode[] opsSTN = null; + SyntaxTreeNode[] args = null; + + /********************************************************************** + * Constants not needed elsewhere. * + **********************************************************************/ + final UniqueString GGUS = UniqueString.uniqueStringOf(">>"); + final UniqueString LLUS = UniqueString.uniqueStringOf("<<"); + final UniqueString ColonUS = UniqueString.uniqueStringOf(":"); + + /********************************************************************** + * The constructor. * + **********************************************************************/ + + Selector(SyntaxTreeNode tn) { + selSTN = tn; + } + + void addSelector(SyntaxTreeNode stn, SyntaxTreeNode opargs) { + /******************************************************************** + * opargs is the args entry. * * stn is the syntax tree node representing the + * ops entry. If * there are no arguments, it should be null. For a NullSel + * entry * it should be the OpArgs node (the "(arg1, ... , argN)") that is * + * used as the second argument. * + ********************************************************************/ + opVec.addElement(stn); + argVec.addElement(opargs); + } // addSelector + + void finish() throws AbortException { + int arrayLen = opVec.size(); + ops = new int[arrayLen]; + opNames = new UniqueString[arrayLen]; + opsSTN = new SyntaxTreeNode[arrayLen]; + args = new SyntaxTreeNode[arrayLen]; + for (int i = 0; i < arrayLen; i++) { + args[i] = (SyntaxTreeNode) argVec.elementAt(i); + SyntaxTreeNode stn = (SyntaxTreeNode) opVec.elementAt(i); + opsSTN[i] = stn; + opNames[i] = stn.getUS(); + switch (stn.getKind()) { + case IDENTIFIER: + case N_InfixOp: + case N_NonExpPrefixOp: + case N_PostfixOp: + case N_PrefixOp: + case ProofStepLexeme: // xyz: following case added by LL on 13 Oct 2007 // - case ProofImplicitStepLexeme: - ops[i] = NameSel ; - break ; - - - - case N_StructOp: - if (stn.heirs().length > 0) { - /************************************************************ - * This is a number. * - ************************************************************/ - TreeNode numNode = stn.heirs()[0].heirs()[0] ; - ops[i] = Integer.parseInt(numNode.getImage()) ; - } - else { - /************************************************************ - * This is not a number, so it is ">>", "<<", "@", or ":". * - ************************************************************/ - UniqueString us = stn.getUS() ; - if (us == GGUS) {ops[i] = GGSel ;} - else if (us == LLUS) {ops[i] = LLSel ;} - else if (us == ColonUS) {ops[i] = ColonSel ;} - else if (us == AtUS) {ops[i] = AtSel ; } - else { errors.addAbort( - stn.getLocation(), - "Internal error: Unexpected selector `" + - stn.getImage() + "'.") ;} - } // if stn.heirs().length > 0 - break ; - - case N_OpArgs: - ops[i] = NullSel ; - break ; - - default: - /*************************************************************** - * This error occurs on silly input like * - * * - * USE DEF <<1, 2>> * - * * - * It therefore seems better to report a mysterious error and * - * let processing continue in the hopes that it will generate * - * a later, more useful error. * - ***************************************************************/ + case ProofImplicitStepLexeme: + ops[i] = NameSel; + break; + + case N_StructOp: + if (stn.heirs().length > 0) { + /************************************************************ + * This is a number. * + ************************************************************/ + TreeNode numNode = stn.heirs()[0].heirs()[0]; + ops[i] = Integer.parseInt(numNode.getImage()); + } else { + /************************************************************ + * This is not a number, so it is ">>", "<<", "@", or ":". * + ************************************************************/ + UniqueString us = stn.getUS(); + if (us == GGUS) { + ops[i] = GGSel; + } else if (us == LLUS) { + ops[i] = LLSel; + } else if (us == ColonUS) { + ops[i] = ColonSel; + } else if (us == AtUS) { + ops[i] = AtSel; + } else { + errors.addAbort(stn.getLocation(), + "Internal error: Unexpected selector `" + stn.getImage() + "'."); + } + } // if stn.heirs().length > 0 + break; + + case N_OpArgs: + ops[i] = NullSel; + break; + + default: + /*************************************************************** + * This error occurs on silly input like * * USE DEF <<1, 2>> * * It therefore + * seems better to report a mysterious error and * let processing continue in + * the hopes that it will generate * a later, more useful error. * + ***************************************************************/ // errors.addAbort( - errors.addError( - stn.getLocation(), + errors.addError(stn.getLocation(), // "Internal error: Selector had unexpected node kind " + // stn.getKind()) ; - "Unexpected token found." ) ; - break ; - } ; - } // for - } - - public String toString() { - /******************************************************************** - * For debugging. * - ********************************************************************/ - String retval = "Selector object:\n" ; - for (int i = 0; i < ops.length; i++) { - retval = retval + " elt " + i + " : ops = " + ops[i] + - ", opNames = " + opNames[i].toString() + - ", opsSTN.kind = " + opsSTN[i].getKind() + - ", args.kind = " + - ((args[i] == null)?"null":(args[i].getKind()+" ")) + "\n" ; - } - return retval ; - } - - } // class Selector + "Unexpected token found."); + break; + } + ; + } // for + } + + public String toString() { + /******************************************************************** + * For debugging. * + ********************************************************************/ + String retval = "Selector object:\n"; + for (int i = 0; i < ops.length; i++) { + retval = retval + " elt " + i + " : ops = " + ops[i] + ", opNames = " + opNames[i].toString() + + ", opsSTN.kind = " + opsSTN[i].getKind() + ", args.kind = " + + ((args[i] == null) ? "null" : (args[i].getKind() + " ")) + "\n"; + } + return retval; + } + + } // class Selector // private ExprOrOpArgNode[] opArgsToArray(SyntaxTreeNode opArgNode) // /*********************************************************************** @@ -530,3993 +494,3743 @@ public class Generator implements ASTConstants, SyntaxTreeConstants, // } // for // } // opArgsToArray - private Selector genIdToSelector(SyntaxTreeNode genId) - throws AbortException { - /*********************************************************************** - * Constructs a selector in which all the addSelector calls to * - * translate the GeneralId node genId have been made and finish has * - * been called. * - * * - * See the commments in tla+.jj before the OpOrExpr() production to * - * see what the tree structure of a GeneralId node looks like. * - ***********************************************************************/ - Selector retval = new Selector(genId) ; - TreeNode prefix = genId.heirs()[0] ; - TreeNode[] prefixElts = prefix.heirs() ; - SyntaxTreeNode lastOp = (SyntaxTreeNode) genId.heirs()[1] ; - for (int i = 0 ; i < prefixElts.length; i++) { - TreeNode[] pe = prefixElts[i].heirs(); - if (pe.length == 0) { - /******************************************************************* - * We reach this point when processing the nonsensical input * - * HIDE DEF X' * - * So we report a not very helpful error in the hopes that further * - * processing will produce a more useful error message. * - *******************************************************************/ - errors.addError(genId.getLocation(), - "Was expecting a GeneralId."); - break ;} ; - SyntaxTreeNode thisPrefix = (SyntaxTreeNode) pe[0] ; - switch (thisPrefix.getKind()) { - case N_OpArgs: - retval.addSelector(thisPrefix, thisPrefix) ; - break; - case N_StructOp: - retval.addSelector(thisPrefix, null) ; - break; - default: - /***************************************************************** - * This must be an identifier or operator. It may or may not * - * have arguments. * - *****************************************************************/ - if (prefixElts[i].heirs().length == 2) { - /*************************************************************** - * There are no arguments (the 2nd heir is the "!"). * - ***************************************************************/ - retval.addSelector(thisPrefix, null) ; - } - else { - /*************************************************************** - * There are arguments, which are heirs()[1]. * - ***************************************************************/ - if (prefixElts[i].heirs().length != 3) { - // Note added 13 April 2015 by LL: - // This error is caused by the spurious "(x)" in the leaf proof - // BY ... DEF A!foo(x) - // It would be nice if this produced a more helpful error - // message, but I have no idea if there are other bad inputs - // that can cause it. - errors.addAbort( - prefixElts[i].getLocation(), - "Internal error: " + - "IdPrefixElement has other than 2 or 3 heirs.") ; - } ; - retval.addSelector(thisPrefix, - (SyntaxTreeNode) prefixElts[i].heirs()[1]) ; - } // if} - break ; - } // switch - }; // for i - - if (lastOp.getKind() == N_OpArgs) {retval.addSelector(lastOp, lastOp) ;} - else {retval.addSelector(lastOp, null) ;} ; - - retval.finish() ; - return retval ; - } // genIdToSelector - - - /*********************************************************************** - * Constants for use in selectorToNode. * - ***********************************************************************/ - private final int FindingOpName = 11 ; - private final int FollowingLabels = 22 ; - private final int FindingSubExpr = 33 ; - - private final int ArgNum(int op, int arity) { - /*********************************************************************** - * This is the implementation of ArgNum in Subexpression.tla. Beware * - * that this is the human argument number (where the first argument is * - * argument 1), not the Java argument number. * - ***********************************************************************/ - if (op > 0) {if (op <= arity) {return op;} ; - return -1 ; - } ; - if (op == LLSel) {return (arity > 0) ? 1 : -1 ;} ; - if (op == GGSel) {// if (arity == 1) {return 1;} ; - /************************************************** - * Commented out on 9 Mar 2010 by LL. Apparently, * - * I once thought it was a good idea to let !>> * - * refer to the argument of a unary operator. I * - * no longer think so. * - **************************************************/ - if (arity == 2) {return 2;} ; - } ; - return -1; - } // ArgNum - - LevelNode selectorToNode(Selector sel, // The selector. - int expectedArity, - boolean isFact, // true if looking for a fact - boolean isDef, // true if looking for a DEF - // clause item. - ModuleNode cm ) // The current module. - throws AbortException { - /*********************************************************************** - * This is an implementation of the +cal algorithm in module * - * Subexpression.tla, which is attached as a comment to the end of * - * this file. If expectedArity >= 0, then it returns an * - * ExprOrOpArgNode, otherwise it returns an OpDefNode or a * - * ThmOrAssumpDefNode. On an error, it returns nullOAN which will * - * allow semantic analysis to continue. * - * * - * The +cal algorithm describes only the case isFact = false and isDef * - * = false. It should be updated to include the other case as well. * - * It also doesn't mention the case of a NumberedProofStepKind OpDef * - * node. * - * * - * This code was modified on 15 Oct 2007 by LL to allow the use of a * - * ModuleInstanceKind node (the M in "M == INSTANCE ...") as a fact or * - * a DEF in a BY, USE, or HIDE. Parameterized instances can be used in * - * a DEF (without the parameters), but only a non-parameterized module * - * instance can be used as a fact. * - * * - * It appears that, if an error has occurred, a call with * - * expectedArity>0 can return something other than an OpArgNode * - * (probably an OpApplNode). This caused a problem in one * - * place--namely, in generateOpArg. I didn't try figuring out whether * - * this is actually a bug in selectorToNode. Instead, I just kludged * - * something to handle that case. * - ***********************************************************************/ - Vector substInPrefix = new Vector() ; // of SubstInNode - Vector params = new Vector() ; // of FormalParamNode - Vector allArgs = new Vector() ; // of ExprOrOpArgNode - - /*********************************************************************** - * Local algorithm variables. * - ***********************************************************************/ - UniqueString curName = null ; - UniqueString newName = null ; // Initial value set to make Eclipse happy. - SemanticNode curNode = null ; - SemanticNode newNode ; - - int idx = 0 ; - /********************************************************************* - * We use the Java convention of counting from 0 instead of the * - * human convention of couunting from 1. * - *********************************************************************/ - int mode = FindingOpName ; - int prevMode = -999 ; - /********************************************************************* - * The compiler is too dumb to figure out that it always gets set * - * before it's used. * - *********************************************************************/ - Context letInContext = null ; - /********************************************************************* - * This implements the algorithm's curContext variable when that * - * context is a LET/IN node's context. When mode = FindingOpName, * - * letInContext is null iff this is the first time that mode is * - * entered--i.e., for the name that is the initial part of the * - * selector. In this case, the algorithm's curContext is * - * represented by the method's "global variable" symbolTable, since * - * any name legal in that context can be referred to. When mode is * - * subsequently set to FindingOpName, it means that the name being * - * sought is from a LET/IN node. In that case, letInContext will * - * contain the Context that implements the algorithm's curContext * - * variable. * - *********************************************************************/ - int opDefArityFound = 0 ; - Vector opDefArgs = new Vector() ; // of ExprOrOpArgNode objects - boolean firstFindingOpName = true; - SymbolNode subExprOf = null; - - boolean inAPsuffices = false ; - /********************************************************************* - * Added 16 Feb 2009. An AssumeProveNode with suffices field true * - * represents SUFFICE ASSUME / PROVE, and the SUFFICES is treated as * - * if it were a 1-argument operator. This flag is set true when the * - * node is first encountered. * - *********************************************************************/ - - while (idx < sel.args.length) { - /********************************************************************* - * Check the one part of the algorithm's assert that should not * - * automatically hold -- the first conjunct of the last conjunct -- * - * for i = idx: * - *********************************************************************/ - if ( ( ( (sel.ops[idx] != NameSel) - && (sel.ops[idx] != NullSel)) - || (expectedArity != 0) ) - && (sel.args[idx] != null)) { - errors.addError( - sel.opsSTN[idx].getLocation(), - "Selector `" + selectorItemToString(sel, idx) + - "' should not have argument(s).") ; - return nullOAN ; - } // if (sel.ops[idx] == NameSel) ... ; - - /********************************************************************* - * Check that, if sel.args[idx] != null, then it is an OpArgs node. * - *********************************************************************/ - if ( (sel.args[idx] != null) - && (sel.args[idx].getKind() != N_OpArgs) ) { - errors.addAbort( - sel.args[idx].getLocation(), - "Internal error: Unexpected syntax node kind."); - } ; - - switch (mode) { - case FindingOpName : - // Following code changed on 23 Sep 2009 to fix bug, and corrected - // on 9 Nov 2009. See description in Subexpression.tla. - SymbolNode newSymbolNode = null; - Vector tempArgs = new Vector() ; - // a vector of SyntaxTreeNode objects, one for each argument - // found for an undefined operator name in the following loop - - while (newSymbolNode == null && idx < sel.args.length) { - /****************************************************************** - * +cal: newName := IF IsName(ops[idx]) ... ELSE null ; * - ******************************************************************/ - if (sel.ops[idx] == NameSel) { - if (curName == null) { - newName = Operators.resolveSynonym(sel.opNames[idx]) ; - /************************************************************ - * Need to call resolveSynonym so things like (+), aka * - * \oplus, are handled properly. * - ************************************************************/ - } - else { newName = - UniqueString.uniqueStringOf( - curName.toString() + "!" + - Operators.resolveSynonym(sel.opNames[idx]).toString()) ; - } ; - } // if (sel.ops[idx] = NameSel) - else { newName = null ; } ; - - if ( (curName == null) - && (sel.ops[idx] != NameSel)) { - if (idx == 0) { errors.addError( - sel.opsSTN[idx].getLocation(), - "Need name or step number here, not `" + - sel.opNames[idx] + "'.") ; - return nullOAN ; } - else {errors.addAbort(sel.opsSTN[idx].getLocation(), - "Internal error: should have name here.") ; - } ; - } ; // if (curName == null) ... ; - if (newName != null) { - if (letInContext == null) { - /*************************************************************** - * See the comments for the declaration of letInContext. * - ***************************************************************/ - newSymbolNode = symbolTable.resolveSymbol(newName) ; - } - else { - newSymbolNode = letInContext.getSymbol(newName) ; - } ; - } ; // if (newName != null) - if (newSymbolNode == null) { - curName = newName; - if (sel.args[idx] != null) { - // sel.args[idx].heirs() is the array of SyntaxTreeNode objects - // representing: - // "(" "arg1" "," ... "," "arg_n" ")" - // We add these SyntaxTreeNode objects to tempArgs. - int numOfOpArgs = (sel.args[idx].heirs().length - 1) / 2 ; - for (int i = 0 ; i < numOfOpArgs; i++) { - tempArgs.addElement(sel.args[idx].heirs()[2*i+1]); - } - } - idx++; - } - } // while (newNode == null) - - if (newSymbolNode == null) { - int eidx = (idx < sel.args.length) ? idx : (sel.args.length-1); - errors.addError( - sel.opsSTN[eidx].getLocation(), - "Unknown operator: `" + - selectorItemToString(sel, eidx) + "'.") ; - return nullOAN ; - } ; - - if (newSymbolNode.getKind() == ModuleKind) { - errors.addError( - sel.opsSTN[idx].getLocation(), - "Module name (" + sel.opNames[idx].toString() + - ") not allowed here.") ; - return nullOAN ; - } ; // if - - curNode = newSymbolNode ; - SymbolNode curSymbolNode = newSymbolNode ; - /**************************************************************** - * A version of curNode "cast" as a SymbolNode. * - ****************************************************************/ - curName = newName ; - switch (curSymbolNode.getKind()) { - case ConstantDeclKind: - case VariableDeclKind: - case FormalParamKind: - case BuiltInKind: - case NewConstantKind: - case NewVariableKind: - case NewStateKind: - case NewActionKind: - case NewTemporalKind: - case NumberedProofStepKind: - /************************************************************** - * This last case added by LL on 17 Aug 2007. I think it * - * fits in here nicely, but I wouldn't swear to it. * - **************************************************************/ - if (idx != 0) { - errors.addAbort( - sel.selSTN.getLocation(), - "Internal error: impossible naming of declaration.") ; - } - else if (sel.ops.length != 1) { - errors.addError( - sel.opsSTN[idx].getLocation(), - "Cannot take subexpression of `" + curName.toString() - + "'.") ; - return nullOAN ; - } // ; - - /**************************************************************** - * Warning: horrible Java programming hack. There is * - * deliberately no break here, so this case "joins" the * - * following case. * - ****************************************************************/ - //$FALL-THROUGH$ - case UserDefinedOpKind: - case ThmOrAssumpDefKind: - case ModuleInstanceKind: - /************************************************************** - * +cal: nodeArity := Arity(curNode) ; * - **************************************************************/ - int nodeArity = curSymbolNode.getArity() ; - /************************************************************** - * +cal: if expectedArity = 0 * - **************************************************************/ - if (expectedArity == 0) { - SyntaxTreeNode opArgs = sel.args[idx] ; - int numOfOpArgs = 0 ; - if (opArgs != null) { - // if there are arguments, add them to tempArgs. - /********************************************************** - * The heirs of an oparg node are * - * * - * "(" "arg1" "," ... "," "arg_n" ")" * - **********************************************************/ - numOfOpArgs = (opArgs.heirs().length - 1) / 2 ; - for (int i = 0 ; i < numOfOpArgs; i++) { - tempArgs.addElement(sel.args[idx].heirs()[2*i+1]); - } ; - - }; - /************************************************************** - * +cal then if opDefArityFound + Len(tempArgs) # nodeArity * - **************************************************************/ - if (opDefArityFound + tempArgs.size() != nodeArity) { - errors.addError( - (opArgs == null)? sel.selSTN.getLocation() : - sel.args[idx].getLocation(), - "The operator " + curName.toString() + " requires " + - (nodeArity - opDefArityFound) + " arguments.") ; - return nullOAN ; - } ; // if - ExprOrOpArgNode[] opArgNodes = - new ExprOrOpArgNode[tempArgs.size()] ; - /********************************************************** - * The array of semantic nodes generated by the arguments. * - **********************************************************/ - for (int i = 0 ; i < tempArgs.size(); i++) { - /********************************************************** - * Call generateExprOrOpArg to generate the semantic * - * nodes for the arguments and add them to the vector * - * opDefArgs. Note that curSymbolNode is used as the * - * argument to generateExprOrOpArg that specifies the * - * operator to which the arguments are being given. (The * - * comments for that method assumes that the operator * - * arguments appear in an OpApplication node, but the * - * method doesn't really care; it just uses that operator * - * to check the arity of the arguments.) Note that * - * argument number i of sel.args[idx] represents argument * - * number i + opDefArityFound of the operator described * - * by curSymbolNode. * - **********************************************************/ - opDefArgs.addElement( - generateExprOrOpArg( - curSymbolNode, - sel.opsSTN[idx], - i + opDefArityFound, - (TreeNode) tempArgs.elementAt(i), - cm)); - } ; // end for - - } // if expectedArity = 0 - /************************************************************** - * +cal: else if expectedArity > 0) * - **************************************************************/ - else { - if (expectedArity > 0) { - /********************************************************** - * Subexpression.tla has a test to report an error if a * - * higher-order operator is specified, since such an * - * operator can't be used as an operator argument. That * - * test is eliminated here because this error should be * - * caught by the caller of the selectorToNode method. * - **********************************************************/ - } - } ; // else [expectedArity != 0] - opDefArityFound = nodeArity ; - - if (curNode.getKind() == ModuleInstanceKind) { - if ((idx == sel.ops.length - 1) && ! (isDef || isFact)) { - errors.addError( - sel.opsSTN[idx].getLocation(), - "Operator name " + curName.toString() + " is incomplete."); - return nullOAN ; - }; - } - else { - /************************************************************** - * curNode.getKind() \in * - * {UserDefinedOpKind, ThmOrAssumpDefKind, * - * ConstantDeclKind, VariableDeclKind, FormalParamKind, * - * BuiltInKind, BoundSymbolKind, NumberedProofStepKind} * - **************************************************************/ - /************************************************************** - * +cal: then if /\ curNode.kind = UserDefinedOpKind ... * - **************************************************************/ - if ( (curNode.getKind() == UserDefinedOpKind) - && ( ! ((OpDefNode) curNode).isDefined ) - && (sel.ops.length != 1)) { - errors.addError( - sel.opsSTN[idx].getLocation(), - "Subexpression of `" + curName.toString() + - "' used inside the operator's definition."); - return nullOAN ; - } ; - - /************************************************************** - * +cal: if /\ firstFindingOpName ... * - **************************************************************/ - if ( firstFindingOpName - && ( (curNode.getKind() == UserDefinedOpKind) - || (curNode.getKind() == ThmOrAssumpDefKind))) { - subExprOf = (SymbolNode) curNode; - } - /************************************************************** - * +cal: idx # Len(ops) * - **************************************************************/ - if (idx != sel.ops.length - 1) { - FormalParamNode[] opParams ; - if (curNode.getKind() == UserDefinedOpKind) { - opParams = ((OpDefNode) curNode).getParams() ; - newNode = ((OpDefNode) curNode).getBody() ; - } // if - else { // curNode.getKind() == ThmOrAssumpDefKind - opParams = ((ThmOrAssumpDefNode) curNode).getParams() ; - newNode = ((ThmOrAssumpDefNode) curNode).getBody() ; - } ; - for (int i = 0 ; i < opParams.length ; i++) { - params.addElement(opParams[i]) ; - } ; // for - curName = null ; - if (sel.ops[idx+1] == NameSel) {mode = FollowingLabels; } - else {mode = FindingSubExpr ;} ; - for (int i = 0 ; i < opDefArgs.size() ; i++) { - allArgs.addElement(opDefArgs.elementAt(i)) ; - } ; // for - opDefArityFound = 0; - opDefArgs = new Vector() ; - - /************************************************************ - * If newNode = null, then I think there's an error. It * - * should be caught later. * - ************************************************************/ - if (newNode != null) { - while (newNode.getKind() == SubstInKind) { - substInPrefix.addElement(newNode) ; - newNode = ((SubstInNode) newNode).getBody() ; - }; // while - while (newNode.getKind() == APSubstInKind) { - substInPrefix.addElement(newNode) ; - newNode = ((APSubstInNode) newNode).getBody() ; - }; // while - } ; - if (mode == FindingSubExpr) { - curNode = newNode ; - } ; - } // if (idx != sel.ops.length - 1) - } // else curNode.getKind() \in {UserDefinedOpKind, ...} - prevMode = FindingOpName ; - break ; - - default: - errors.addAbort(sel.opsSTN[idx].getLocation(), - "Internal error: unexpected node kind.") ; - break ; - } ; // switch (curSymbolNode.getKind()) - - break ; // case FindingOpName - - - case FollowingLabels : - /****************************************************************** - * Invariant: sel.ops[idx] = NameSel * - ******************************************************************/ - if ( ( (prevMode == FindingOpName) - && (curNode.getKind() != UserDefinedOpKind) - && (curNode.getKind() != ThmOrAssumpDefKind)) - || ( (prevMode != FindingOpName) - && (curNode.getKind() != LabelKind))) { - errors.addAbort(sel.selSTN.getLocation(), - "Unexpected node kind in FollowingLabels mode.") ; - } ; - - LabelNode newLabelNode = - ((OpDefOrLabelNode) curNode).getLabel(sel.opNames[idx]) ; - - if (newLabelNode == null) { - errors.addError( - sel.opsSTN[idx].getLocation(), - "Cannot find label `" + sel.opNames[idx].toString() + "'."); - return nullOAN ; - } ; - - curNode = newLabelNode ; - - if (illegalLabelRef(newLabelNode, sel.opsSTN[idx])) { - errors.addError( - sel.opsSTN[idx].getLocation(), - "Accessing subexpression labeled `" + - sel.opNames[idx].toString() + - "' of ASSUME/PROVE clause within the scope of " + - "a declaration\n from outside that declaration's scope."); - return nullOAN ; - } ; - - if (expectedArity == 0) { - - /**************************************************************** - * Check that label has right number of arguments. * - ****************************************************************/ - if (newLabelNode.getArity() != - ((sel.args[idx] == null) - ? 0 - : (sel.args[idx].heirs().length-1)/2)) { - errors.addError( - sel.opsSTN[idx].getLocation(), - "Label `" + sel.opNames[idx].toString() - + "' used with wrong number of arguments."); - return nullOAN ; - } ; - for (int i = 0; i < newLabelNode.getArity(); i++) { - allArgs.addElement(generateExpression( - sel.args[idx].heirs()[2 * i + 1] , cm)) ; - } ; - }; // if (expectedArity == 0) - - for (int i = 0; i < newLabelNode.getArity(); i++) { - FormalParamNode pdecl = newLabelNode.params[i] ; - params.addElement(pdecl); - } ; - - if ((idx < sel.ops.length - 1) && (sel.ops[idx+1] != NameSel)) { - mode = FindingSubExpr ; - } ; - - if ((mode == FindingSubExpr) || (idx == sel.ops.length)) { - curNode = newLabelNode.getBody() ; - } ; - - prevMode = FollowingLabels ; - break ; // case FollowingLabels - - case FindingSubExpr: - if (sel.ops[idx] == ColonSel) { - if ( (prevMode == FindingSubExpr) - || ! ( ( (idx == sel.ops.length - 1) - && (prevMode == FindingOpName)) - || ( (idx < sel.ops.length - 1) - && (sel.ops[idx+1] == NameSel)))) { - errors.addError( - sel.opsSTN[idx].getLocation(), - "`!:' can be used only after a name and either at the " + - "end after an\noperator name or before an operator name."); - return nullOAN ; - } // if ( (prevMode == FindingSubExpr) ...) - } // if (sel.ops[idx] == ColonSel) - else if (curNode == null) { - errors.addError(sel.opsSTN[idx].getLocation(), - "Subexpression selection failed, probably due to " + - "an error in the selected expression."); - return nullOAN ; - } - else if (curNode.getKind() == LetInKind) { - if (ArgNum(sel.ops[idx], 1) == 1) { - curNode = ((LetInNode) curNode).getBody() ; - } else { - errors.addError(sel.opsSTN[idx].getLocation(), - "A LET/IN expression has only one operand."); - return nullOAN ; - } ; - } // else if (curNode.getKind() == LetInKind) - - else if (curNode.getKind() == OpApplKind) { - OpApplNode curOpApplNode = (OpApplNode) curNode ; - ExprOrOpArgNode[] curArgs = curOpApplNode.getArgs() ; - SymbolNode opNode = curOpApplNode.getOperator(); - if ( (opNode.getKind() == FormalParamKind) - || (opNode.getKind() == ConstantDeclKind) - || (opNode.getKind() == UserDefinedOpKind)) { - int temp = ArgNum(sel.ops[idx], opNode.getArity()) ; - if (temp == -1) { - reportSelectorError(sel, idx) ; + private Selector genIdToSelector(SyntaxTreeNode genId) throws AbortException { + /*********************************************************************** + * Constructs a selector in which all the addSelector calls to * translate the + * GeneralId node genId have been made and finish has * been called. * * See the + * commments in tla+.jj before the OpOrExpr() production to * see what the tree + * structure of a GeneralId node looks like. * + ***********************************************************************/ + Selector retval = new Selector(genId); + TreeNode prefix = genId.heirs()[0]; + TreeNode[] prefixElts = prefix.heirs(); + SyntaxTreeNode lastOp = (SyntaxTreeNode) genId.heirs()[1]; + for (int i = 0; i < prefixElts.length; i++) { + TreeNode[] pe = prefixElts[i].heirs(); + if (pe.length == 0) { + /******************************************************************* + * We reach this point when processing the nonsensical input * HIDE DEF X' * So + * we report a not very helpful error in the hopes that further * processing + * will produce a more useful error message. * + *******************************************************************/ + errors.addError(genId.getLocation(), "Was expecting a GeneralId."); + break; + } + ; + SyntaxTreeNode thisPrefix = (SyntaxTreeNode) pe[0]; + switch (thisPrefix.getKind()) { + case N_OpArgs: + retval.addSelector(thisPrefix, thisPrefix); + break; + case N_StructOp: + retval.addSelector(thisPrefix, null); + break; + default: + /***************************************************************** + * This must be an identifier or operator. It may or may not * have arguments. * + *****************************************************************/ + if (prefixElts[i].heirs().length == 2) { + /*************************************************************** + * There are no arguments (the 2nd heir is the "!"). * + ***************************************************************/ + retval.addSelector(thisPrefix, null); + } else { + /*************************************************************** + * There are arguments, which are heirs()[1]. * + ***************************************************************/ + if (prefixElts[i].heirs().length != 3) { + // Note added 13 April 2015 by LL: + // This error is caused by the spurious "(x)" in the leaf proof + // BY ... DEF A!foo(x) + // It would be nice if this produced a more helpful error + // message, but I have no idea if there are other bad inputs + // that can cause it. + errors.addAbort(prefixElts[i].getLocation(), + "Internal error: " + "IdPrefixElement has other than 2 or 3 heirs."); + } + ; + retval.addSelector(thisPrefix, (SyntaxTreeNode) prefixElts[i].heirs()[1]); + } // if} + break; + } // switch + } + ; // for i + + if (lastOp.getKind() == N_OpArgs) { + retval.addSelector(lastOp, lastOp); + } else { + retval.addSelector(lastOp, null); + } + ; + + retval.finish(); + return retval; + } // genIdToSelector + + /*********************************************************************** + * Constants for use in selectorToNode. * + ***********************************************************************/ + private final int FindingOpName = 11; + private final int FollowingLabels = 22; + private final int FindingSubExpr = 33; + + private final int ArgNum(int op, int arity) { + /*********************************************************************** + * This is the implementation of ArgNum in Subexpression.tla. Beware * that this + * is the human argument number (where the first argument is * argument 1), not + * the Java argument number. * + ***********************************************************************/ + if (op > 0) { + if (op <= arity) { + return op; + } + ; + return -1; + } + ; + if (op == LLSel) { + return (arity > 0) ? 1 : -1; + } + ; + if (op == GGSel) {// if (arity == 1) {return 1;} ; + /************************************************** + * Commented out on 9 Mar 2010 by LL. Apparently, * I once thought it was a good + * idea to let !>> * refer to the argument of a unary operator. I * no longer + * think so. * + **************************************************/ + if (arity == 2) { + return 2; + } + ; + } + ; + return -1; + } // ArgNum + + LevelNode selectorToNode(Selector sel, // The selector. + int expectedArity, boolean isFact, // true if looking for a fact + boolean isDef, // true if looking for a DEF + // clause item. + ModuleNode cm) // The current module. + throws AbortException { + /*********************************************************************** + * This is an implementation of the +cal algorithm in module * + * Subexpression.tla, which is attached as a comment to the end of * this file. + * If expectedArity >= 0, then it returns an * ExprOrOpArgNode, otherwise it + * returns an OpDefNode or a * ThmOrAssumpDefNode. On an error, it returns + * nullOAN which will * allow semantic analysis to continue. * * The +cal + * algorithm describes only the case isFact = false and isDef * = false. It + * should be updated to include the other case as well. * It also doesn't + * mention the case of a NumberedProofStepKind OpDef * node. * * This code was + * modified on 15 Oct 2007 by LL to allow the use of a * ModuleInstanceKind node + * (the M in "M == INSTANCE ...") as a fact or * a DEF in a BY, USE, or HIDE. + * Parameterized instances can be used in * a DEF (without the parameters), but + * only a non-parameterized module * instance can be used as a fact. * * It + * appears that, if an error has occurred, a call with * expectedArity>0 can + * return something other than an OpArgNode * (probably an OpApplNode). This + * caused a problem in one * place--namely, in generateOpArg. I didn't try + * figuring out whether * this is actually a bug in selectorToNode. Instead, I + * just kludged * something to handle that case. * + ***********************************************************************/ + Vector substInPrefix = new Vector(); // of SubstInNode + Vector params = new Vector(); // of FormalParamNode + Vector allArgs = new Vector(); // of ExprOrOpArgNode + + /*********************************************************************** + * Local algorithm variables. * + ***********************************************************************/ + UniqueString curName = null; + UniqueString newName = null; // Initial value set to make Eclipse happy. + SemanticNode curNode = null; + SemanticNode newNode; + + int idx = 0; + /********************************************************************* + * We use the Java convention of counting from 0 instead of the * human + * convention of couunting from 1. * + *********************************************************************/ + int mode = FindingOpName; + int prevMode = -999; + /********************************************************************* + * The compiler is too dumb to figure out that it always gets set * before it's + * used. * + *********************************************************************/ + Context letInContext = null; + /********************************************************************* + * This implements the algorithm's curContext variable when that * context is a + * LET/IN node's context. When mode = FindingOpName, * letInContext is null iff + * this is the first time that mode is * entered--i.e., for the name that is the + * initial part of the * selector. In this case, the algorithm's curContext is * + * represented by the method's "global variable" symbolTable, since * any name + * legal in that context can be referred to. When mode is * subsequently set to + * FindingOpName, it means that the name being * sought is from a LET/IN node. + * In that case, letInContext will * contain the Context that implements the + * algorithm's curContext * variable. * + *********************************************************************/ + int opDefArityFound = 0; + Vector opDefArgs = new Vector(); // of ExprOrOpArgNode objects + boolean firstFindingOpName = true; + SymbolNode subExprOf = null; + + boolean inAPsuffices = false; + /********************************************************************* + * Added 16 Feb 2009. An AssumeProveNode with suffices field true * represents + * SUFFICE ASSUME / PROVE, and the SUFFICES is treated as * if it were a + * 1-argument operator. This flag is set true when the * node is first + * encountered. * + *********************************************************************/ + + while (idx < sel.args.length) { + /********************************************************************* + * Check the one part of the algorithm's assert that should not * automatically + * hold -- the first conjunct of the last conjunct -- * for i = idx: * + *********************************************************************/ + if ((((sel.ops[idx] != NameSel) && (sel.ops[idx] != NullSel)) || (expectedArity != 0)) + && (sel.args[idx] != null)) { + errors.addError(sel.opsSTN[idx].getLocation(), + "Selector `" + selectorItemToString(sel, idx) + "' should not have argument(s)."); + return nullOAN; + } // if (sel.ops[idx] == NameSel) ... ; + + /********************************************************************* + * Check that, if sel.args[idx] != null, then it is an OpArgs node. * + *********************************************************************/ + if ((sel.args[idx] != null) && (sel.args[idx].getKind() != N_OpArgs)) { + errors.addAbort(sel.args[idx].getLocation(), "Internal error: Unexpected syntax node kind."); + } + ; + + switch (mode) { + case FindingOpName: + // Following code changed on 23 Sep 2009 to fix bug, and corrected + // on 9 Nov 2009. See description in Subexpression.tla. + SymbolNode newSymbolNode = null; + Vector tempArgs = new Vector(); + // a vector of SyntaxTreeNode objects, one for each argument + // found for an undefined operator name in the following loop + + while (newSymbolNode == null && idx < sel.args.length) { + /****************************************************************** + * +cal: newName := IF IsName(ops[idx]) ... ELSE null ; * + ******************************************************************/ + if (sel.ops[idx] == NameSel) { + if (curName == null) { + newName = Operators.resolveSynonym(sel.opNames[idx]); + /************************************************************ + * Need to call resolveSynonym so things like (+), aka * \oplus, are handled + * properly. * + ************************************************************/ + } else { + newName = UniqueString.uniqueStringOf( + curName.toString() + "!" + Operators.resolveSynonym(sel.opNames[idx]).toString()); + } + ; + } // if (sel.ops[idx] = NameSel) + else { + newName = null; + } + ; + + if ((curName == null) && (sel.ops[idx] != NameSel)) { + if (idx == 0) { + errors.addError(sel.opsSTN[idx].getLocation(), + "Need name or step number here, not `" + sel.opNames[idx] + "'."); + return nullOAN; + } else { + errors.addAbort(sel.opsSTN[idx].getLocation(), "Internal error: should have name here."); + } + ; + } + ; // if (curName == null) ... ; + if (newName != null) { + if (letInContext == null) { + /*************************************************************** + * See the comments for the declaration of letInContext. * + ***************************************************************/ + newSymbolNode = symbolTable.resolveSymbol(newName); + } else { + newSymbolNode = letInContext.getSymbol(newName); + } + ; + } + ; // if (newName != null) + if (newSymbolNode == null) { + curName = newName; + if (sel.args[idx] != null) { + // sel.args[idx].heirs() is the array of SyntaxTreeNode objects + // representing: + // "(" "arg1" "," ... "," "arg_n" ")" + // We add these SyntaxTreeNode objects to tempArgs. + int numOfOpArgs = (sel.args[idx].heirs().length - 1) / 2; + for (int i = 0; i < numOfOpArgs; i++) { + tempArgs.addElement(sel.args[idx].heirs()[2 * i + 1]); + } + } + idx++; + } + } // while (newNode == null) + + if (newSymbolNode == null) { + int eidx = (idx < sel.args.length) ? idx : (sel.args.length - 1); + errors.addError(sel.opsSTN[eidx].getLocation(), + "Unknown operator: `" + selectorItemToString(sel, eidx) + "'."); + return nullOAN; + } + ; + + if (newSymbolNode.getKind() == ModuleKind) { + errors.addError(sel.opsSTN[idx].getLocation(), + "Module name (" + sel.opNames[idx].toString() + ") not allowed here."); + return nullOAN; + } + ; // if + + curNode = newSymbolNode; + SymbolNode curSymbolNode = newSymbolNode; + /**************************************************************** + * A version of curNode "cast" as a SymbolNode. * + ****************************************************************/ + curName = newName; + switch (curSymbolNode.getKind()) { + case ConstantDeclKind: + case VariableDeclKind: + case FormalParamKind: + case BuiltInKind: + case NewConstantKind: + case NewVariableKind: + case NewStateKind: + case NewActionKind: + case NewTemporalKind: + case NumberedProofStepKind: + /************************************************************** + * This last case added by LL on 17 Aug 2007. I think it * fits in here nicely, + * but I wouldn't swear to it. * + **************************************************************/ + if (idx != 0) { + errors.addAbort(sel.selSTN.getLocation(), "Internal error: impossible naming of declaration."); + } else if (sel.ops.length != 1) { + errors.addError(sel.opsSTN[idx].getLocation(), + "Cannot take subexpression of `" + curName.toString() + "'."); + return nullOAN; + } // ; + + /**************************************************************** + * Warning: horrible Java programming hack. There is * deliberately no break + * here, so this case "joins" the * following case. * + ****************************************************************/ + //$FALL-THROUGH$ + case UserDefinedOpKind: + case ThmOrAssumpDefKind: + case ModuleInstanceKind: + /************************************************************** + * +cal: nodeArity := Arity(curNode) ; * + **************************************************************/ + int nodeArity = curSymbolNode.getArity(); + /************************************************************** + * +cal: if expectedArity = 0 * + **************************************************************/ + if (expectedArity == 0) { + SyntaxTreeNode opArgs = sel.args[idx]; + int numOfOpArgs = 0; + if (opArgs != null) { + // if there are arguments, add them to tempArgs. + /********************************************************** + * The heirs of an oparg node are * * "(" "arg1" "," ... "," "arg_n" ")" * + **********************************************************/ + numOfOpArgs = (opArgs.heirs().length - 1) / 2; + for (int i = 0; i < numOfOpArgs; i++) { + tempArgs.addElement(sel.args[idx].heirs()[2 * i + 1]); + } + ; + + } + ; + /************************************************************** + * +cal then if opDefArityFound + Len(tempArgs) # nodeArity * + **************************************************************/ + if (opDefArityFound + tempArgs.size() != nodeArity) { + errors.addError((opArgs == null) ? sel.selSTN.getLocation() : sel.args[idx].getLocation(), + "The operator " + curName.toString() + " requires " + (nodeArity - opDefArityFound) + + " arguments."); + return nullOAN; + } + ; // if + ExprOrOpArgNode[] opArgNodes = new ExprOrOpArgNode[tempArgs.size()]; + /********************************************************** + * The array of semantic nodes generated by the arguments. * + **********************************************************/ + for (int i = 0; i < tempArgs.size(); i++) { + /********************************************************** + * Call generateExprOrOpArg to generate the semantic * nodes for the arguments + * and add them to the vector * opDefArgs. Note that curSymbolNode is used as + * the * argument to generateExprOrOpArg that specifies the * operator to which + * the arguments are being given. (The * comments for that method assumes that + * the operator * arguments appear in an OpApplication node, but the * method + * doesn't really care; it just uses that operator * to check the arity of the + * arguments.) Note that * argument number i of sel.args[idx] represents + * argument * number i + opDefArityFound of the operator described * by + * curSymbolNode. * + **********************************************************/ + opDefArgs.addElement(generateExprOrOpArg(curSymbolNode, sel.opsSTN[idx], + i + opDefArityFound, (TreeNode) tempArgs.elementAt(i), cm)); + } + ; // end for + + } // if expectedArity = 0 + /************************************************************** + * +cal: else if expectedArity > 0) * + **************************************************************/ + else { + if (expectedArity > 0) { + /********************************************************** + * Subexpression.tla has a test to report an error if a * higher-order operator + * is specified, since such an * operator can't be used as an operator argument. + * That * test is eliminated here because this error should be * caught by the + * caller of the selectorToNode method. * + **********************************************************/ + } + } + ; // else [expectedArity != 0] + opDefArityFound = nodeArity; + + if (curNode.getKind() == ModuleInstanceKind) { + if ((idx == sel.ops.length - 1) && !(isDef || isFact)) { + errors.addError(sel.opsSTN[idx].getLocation(), + "Operator name " + curName.toString() + " is incomplete."); + return nullOAN; + } + ; + } else { + /************************************************************** + * curNode.getKind() \in * {UserDefinedOpKind, ThmOrAssumpDefKind, * + * ConstantDeclKind, VariableDeclKind, FormalParamKind, * BuiltInKind, + * BoundSymbolKind, NumberedProofStepKind} * + **************************************************************/ + /************************************************************** + * +cal: then if /\ curNode.kind = UserDefinedOpKind ... * + **************************************************************/ + if ((curNode.getKind() == UserDefinedOpKind) && (!((OpDefNode) curNode).isDefined) + && (sel.ops.length != 1)) { + errors.addError(sel.opsSTN[idx].getLocation(), "Subexpression of `" + curName.toString() + + "' used inside the operator's definition."); + return nullOAN; + } + ; + + /************************************************************** + * +cal: if /\ firstFindingOpName ... * + **************************************************************/ + if (firstFindingOpName && ((curNode.getKind() == UserDefinedOpKind) + || (curNode.getKind() == ThmOrAssumpDefKind))) { + subExprOf = (SymbolNode) curNode; + } + /************************************************************** + * +cal: idx # Len(ops) * + **************************************************************/ + if (idx != sel.ops.length - 1) { + FormalParamNode[] opParams; + if (curNode.getKind() == UserDefinedOpKind) { + opParams = ((OpDefNode) curNode).getParams(); + newNode = ((OpDefNode) curNode).getBody(); + } // if + else { // curNode.getKind() == ThmOrAssumpDefKind + opParams = ((ThmOrAssumpDefNode) curNode).getParams(); + newNode = ((ThmOrAssumpDefNode) curNode).getBody(); + } + ; + for (int i = 0; i < opParams.length; i++) { + params.addElement(opParams[i]); + } + ; // for + curName = null; + if (sel.ops[idx + 1] == NameSel) { + mode = FollowingLabels; + } else { + mode = FindingSubExpr; + } + ; + for (int i = 0; i < opDefArgs.size(); i++) { + allArgs.addElement(opDefArgs.elementAt(i)); + } + ; // for + opDefArityFound = 0; + opDefArgs = new Vector(); + + /************************************************************ + * If newNode = null, then I think there's an error. It * should be caught + * later. * + ************************************************************/ + if (newNode != null) { + while (newNode.getKind() == SubstInKind) { + substInPrefix.addElement(newNode); + newNode = ((SubstInNode) newNode).getBody(); + } + ; // while + while (newNode.getKind() == APSubstInKind) { + substInPrefix.addElement(newNode); + newNode = ((APSubstInNode) newNode).getBody(); + } + ; // while + } + ; + if (mode == FindingSubExpr) { + curNode = newNode; + } + ; + } // if (idx != sel.ops.length - 1) + } // else curNode.getKind() \in {UserDefinedOpKind, ...} + prevMode = FindingOpName; + break; + + default: + errors.addAbort(sel.opsSTN[idx].getLocation(), "Internal error: unexpected node kind."); + break; + } + ; // switch (curSymbolNode.getKind()) + + break; // case FindingOpName + + case FollowingLabels: + /****************************************************************** + * Invariant: sel.ops[idx] = NameSel * + ******************************************************************/ + if (((prevMode == FindingOpName) && (curNode.getKind() != UserDefinedOpKind) + && (curNode.getKind() != ThmOrAssumpDefKind)) + || ((prevMode != FindingOpName) && (curNode.getKind() != LabelKind))) { + errors.addAbort(sel.selSTN.getLocation(), "Unexpected node kind in FollowingLabels mode."); + } + ; + + LabelNode newLabelNode = ((OpDefOrLabelNode) curNode).getLabel(sel.opNames[idx]); + + if (newLabelNode == null) { + errors.addError(sel.opsSTN[idx].getLocation(), + "Cannot find label `" + sel.opNames[idx].toString() + "'."); + return nullOAN; + } + ; + + curNode = newLabelNode; + + if (illegalLabelRef(newLabelNode, sel.opsSTN[idx])) { + errors.addError(sel.opsSTN[idx].getLocation(), + "Accessing subexpression labeled `" + sel.opNames[idx].toString() + + "' of ASSUME/PROVE clause within the scope of " + + "a declaration\n from outside that declaration's scope."); + return nullOAN; + } + ; + + if (expectedArity == 0) { + + /**************************************************************** + * Check that label has right number of arguments. * + ****************************************************************/ + if (newLabelNode + .getArity() != ((sel.args[idx] == null) ? 0 : (sel.args[idx].heirs().length - 1) / 2)) { + errors.addError(sel.opsSTN[idx].getLocation(), + "Label `" + sel.opNames[idx].toString() + "' used with wrong number of arguments."); + return nullOAN; + } + ; + for (int i = 0; i < newLabelNode.getArity(); i++) { + allArgs.addElement(generateExpression(sel.args[idx].heirs()[2 * i + 1], cm)); + } + ; + } + ; // if (expectedArity == 0) + + for (int i = 0; i < newLabelNode.getArity(); i++) { + FormalParamNode pdecl = newLabelNode.params[i]; + params.addElement(pdecl); + } + ; + + if ((idx < sel.ops.length - 1) && (sel.ops[idx + 1] != NameSel)) { + mode = FindingSubExpr; + } + ; + + if ((mode == FindingSubExpr) || (idx == sel.ops.length)) { + curNode = newLabelNode.getBody(); + } + ; + + prevMode = FollowingLabels; + break; // case FollowingLabels + + case FindingSubExpr: + if (sel.ops[idx] == ColonSel) { + if ((prevMode == FindingSubExpr) || !(((idx == sel.ops.length - 1) && (prevMode == FindingOpName)) + || ((idx < sel.ops.length - 1) && (sel.ops[idx + 1] == NameSel)))) { + errors.addError(sel.opsSTN[idx].getLocation(), + "`!:' can be used only after a name and either at the " + + "end after an\noperator name or before an operator name."); + return nullOAN; + } // if ( (prevMode == FindingSubExpr) ...) + } // if (sel.ops[idx] == ColonSel) + else if (curNode == null) { + errors.addError(sel.opsSTN[idx].getLocation(), "Subexpression selection failed, probably due to " + + "an error in the selected expression."); + return nullOAN; + } else if (curNode.getKind() == LetInKind) { + if (ArgNum(sel.ops[idx], 1) == 1) { + curNode = ((LetInNode) curNode).getBody(); + } else { + errors.addError(sel.opsSTN[idx].getLocation(), "A LET/IN expression has only one operand."); + return nullOAN; + } + ; + } // else if (curNode.getKind() == LetInKind) + + else if (curNode.getKind() == OpApplKind) { + OpApplNode curOpApplNode = (OpApplNode) curNode; + ExprOrOpArgNode[] curArgs = curOpApplNode.getArgs(); + SymbolNode opNode = curOpApplNode.getOperator(); + if ((opNode.getKind() == FormalParamKind) || (opNode.getKind() == ConstantDeclKind) + || (opNode.getKind() == UserDefinedOpKind)) { + int temp = ArgNum(sel.ops[idx], opNode.getArity()); + if (temp == -1) { + reportSelectorError(sel, idx); // errors.addError(sel.opsSTN[idx].getLocation(), // "NoNexistent operand specified by `" // + sel.opNames[idx].toString() + "'."); - return nullOAN ; - } ; - curNode = curArgs[temp-1] ; - } // if (opNode.getKind() == FormalParamKind) || ... - - else if (opNode.getKind() == BuiltInKind) { - if ( (opNode.getName() == OP_rc) // $RcdConstructor - || (opNode.getName() == OP_sor)){ // $SetOfRcds - - int temp = ArgNum(sel.ops[idx], curArgs.length) ; - if (temp == -1) { + return nullOAN; + } + ; + curNode = curArgs[temp - 1]; + } // if (opNode.getKind() == FormalParamKind) || ... + + else if (opNode.getKind() == BuiltInKind) { + if ((opNode.getName() == OP_rc) // $RcdConstructor + || (opNode.getName() == OP_sor)) { // $SetOfRcds + + int temp = ArgNum(sel.ops[idx], curArgs.length); + if (temp == -1) { // errors.addError(sel.opsSTN[idx].getLocation(), // "NonExistent operand specified by `" // + sel.opNames[idx].toString() + "'."); - reportSelectorError(sel, idx) ; - return nullOAN ; - } ; - curOpApplNode = (OpApplNode) curArgs[temp-1] ; - if (curOpApplNode.getOperator().getName() != OP_pair) { - errors.addAbort( - sel.opsSTN[idx].getLocation(), - "Internal error: Expecting $Pair and didn't find it.") ; - } ; - curNode = curOpApplNode.getArgs()[1] ; - } // if opNode.name == $RcdConstructor or $SetOfRcds - - else if (opNode.getName() == OP_case) { //$Case - if (idx == sel.ops.length - 1) { - errors.addError( - sel.opsSTN[idx].getLocation(), - "Subexpression of CASE must have form !i!j."); - return nullOAN ; - } ; - int temp = ArgNum(sel.ops[idx], curArgs.length) ; - if (temp == -1) { - reportSelectorError(sel, idx) ; - return nullOAN ; - } ; - curOpApplNode = (OpApplNode) curArgs[temp-1] ; - if (curOpApplNode.getOperator().getName() != OP_pair) { - errors.addAbort( - sel.opsSTN[idx].getLocation(), - "Internal error: Expecting $Pair and didn't find it.") ; - } ; - idx = idx + 1; - temp = ArgNum(sel.ops[idx], 2) ; - if (temp == -1) { - errors.addError( - sel.opsSTN[idx].getLocation(), - "Second selector for CASE subexpression must specify " - + " one of two operands."); - return nullOAN ; - } ; - curNode = curOpApplNode.getArgs()[temp-1] ; - if (curNode == null) { - errors.addError( - sel.opsSTN[idx].getLocation(), - "Selecting OTHER in a CASE statement."); - return nullOAN ; - } ; - } // if opNode.name = $Case - - else if (opNode.getName() == OP_exc) { //$Except - /************************************************************ - * In an $Except node representing * - * * - * [exp_1 ELSE !... = exp_2, ..., !... = exp_n] * - * * - * operand i names exp_i. * - ************************************************************/ - int temp = ArgNum(sel.ops[idx], curArgs.length) ; - if (temp == -1) { - reportSelectorError(sel, idx) ; - return nullOAN ; - } ; - /************************************************************ - * Selection of subexpressions of EXCEPT have been outlawed * - * because they may contain a dangling "@". This doesn't * - * seem to be worth fixing because it's unlikely that * - * anyone will actually want to refer to such a * - * subexpression. * - * Change made 25 Oct 2007. * - ************************************************************/ - if (temp > 1) { - errors.addError( - sel.opsSTN[idx].getLocation(), - "Selecting subexpression of an " + - "EXCEPT not yet implemented."); - return nullOAN ; - } ; - curNode = curArgs[temp-1]; - if (isNullSelection(curNode, sel, idx)) - { return nullOAN ; } ; - if (temp > 1) { - curOpApplNode = (OpApplNode) curNode ; - if (curOpApplNode.getOperator().getName() != OP_pair) { - errors.addAbort( - sel.opsSTN[idx].getLocation(), - "Internal error: Expecting $Pair and didn't find it.") ; - } ; - curNode = curOpApplNode.getArgs()[1] ; - }; // if (temp > 1) - } // if opNode.name = $Except - - else { // Handled by standard procedure - if ( (curOpApplNode.getNumberOfBoundedBoundSymbols() == 0) - && ( (curOpApplNode.getUnbdedQuantSymbols() == null) - || (curOpApplNode.getUnbdedQuantSymbols().length - == 0) ) - /***************************************************** - * I'm not sure getUnbdedQuantSymbols always returns * - * null if there are no such symbols. * - *****************************************************/ - ) { - /********************************************************** - * Current subexpression has no bound variables. * - **********************************************************/ - int temp = ArgNum(sel.ops[idx], - curOpApplNode.getArgs().length) ; - if (temp == -1) { - reportSelectorError(sel, idx) ; - return nullOAN ; - } ; - curNode = curOpApplNode.getArgs()[temp-1] ; - } // if current subexpression has no bound variables - else { - /********************************************************** - * Current subexpression has bound variables. * - **********************************************************/ - if ( (sel.ops[idx] == NullSel) - || (sel.ops[idx] == AtSel)) { - /******************************************************** - * Set temp to the array of FormalParamNodes for the * - * parameters. * - ********************************************************/ - FormalParamNode[] temp ; - if (curOpApplNode.getNumberOfBoundedBoundSymbols() > 0) { - FormalParamNode[][] symbs = - curOpApplNode.getBdedQuantSymbolLists() ; - int numSymbs = 0 ; - for (int i = 0 ; i < symbs.length ; i++) { - numSymbs = numSymbs + symbs[i].length ; - }; // for - temp = new FormalParamNode[numSymbs] ; - int k = 0 ; - for (int i = 0 ; i < symbs.length ; i++) { - for (int j = 0 ; j < symbs[i].length ; j++) { - temp[k] = symbs[i][j] ; - k++; - }; // for j - }; // for i - } // if (curOpApplNode.getNumberOf... > 0) - else { - temp = curOpApplNode.getUnbdedQuantSymbols() ; - } ; // else not (curOpApplNode.getNumberOf... > 0) - - /******************************************************* - * Add the elements of temp to the params vector. * - *******************************************************/ - for (int i = 0 ; i < temp.length ; i++) { - params.addElement(temp[i]) ; - } ; // for i - - if (sel.ops[idx] == NullSel) { - /***************************************************** - * Add the arguments to allArgs, checking if there * - * are the right number. * - *****************************************************/ - int numOfArgs = (sel.args[idx].heirs().length-1)/2 ; - if ( temp.length != numOfArgs) { - errors.addError( - sel.opsSTN[idx].getLocation(), - "Selector with " + numOfArgs + - " argument(s) used for quantifier with " + - temp.length + " bound identifier(s)."); - return nullOAN ; - } ; - for (int i = 0; i < numOfArgs; i++) { - allArgs.addElement( - generateExpression( - sel.args[idx].heirs()[2 * i + 1] , cm)) ; - } ; // for i - } ; // if (sel.ops[idx] == NullSel) - curNode = curOpApplNode.getArgs()[0] ; - } // if (sel.ops[idx] == NullSel) || ... - else { - int temp = ArgNum( - sel.ops[idx], - curOpApplNode.getBdedQuantBounds().length); - if (temp == -1) { - reportSelectorError(sel, idx) ; - return nullOAN ; - } ; - curNode = curOpApplNode.getBdedQuantBounds()[temp-1] ; - }; // else - }; // else Current subexpression has bound variables - }; // Handled by standard procedure - } // else if (opNode.getKind() == BuiltInKind) - - else { - errors.addError( - sel.opsSTN[idx].getLocation(), - "Choosing operand `" + selectorItemToString(sel, idx) + - "' of subexpression with no operands." ); - return nullOAN ; - } ; - - } // else if ((curNode.getKind() == OpApplKind) - - else if (curNode.getKind() == AssumeProveKind) { - AssumeProveNode curAPNode = (AssumeProveNode) curNode; - - /**************************************************************** - * 16 Feb 2009: Case of curAPNode.suffices true added. * - ****************************************************************/ - if ( (curAPNode.isSuffices()) - && (! inAPsuffices) ) { - /************************************************************** - * In this case, the selector must be 1, and the curNode is * - * left equal to the AssumeProve, but with inAPsuffices set * - * to true. * - **************************************************************/ - if (ArgNum(sel.ops[idx], 1) != 1) { - errors.addError(sel.opsSTN[idx].getLocation(), - "Accessing non-existent subexpression of " + - "a SUFFICES"); - return nullOAN ; - }; - inAPsuffices = true ; - } // if (curAPNode.isSuffices()) - else { - inAPsuffices = false; - /************************************************************ - * Added 16 Feb 2009 by LL. * - ************************************************************/ - int temp = ArgNum(sel.ops[idx], - 1 + curAPNode.getAssumes().length); - if (temp == -1) { - reportSelectorError(sel, idx) ; - return nullOAN ; - } ; - - if (illegalAPPosRef(curAPNode, temp)){ - errors.addError(sel.opsSTN[idx].getLocation(), - "Accessing ASSUME/PROVE clause within the scope of " + - "a declaration\n from outside that declaration's scope."); - return nullOAN ; - } ; - if (temp <= curAPNode.getAssumes().length) { - curNode = curAPNode.getAssumes()[temp-1] ; - if (isNullSelection(curNode, sel, idx)) { return nullOAN; } ; - if ( (curNode.getKind() == NewSymbKind) - && (idx != sel.args.length-1)) { - /************************************************************ - * Extra conjunct added to if test to allow selection of a * - * NEW clause as a fact. * - ************************************************************/ - errors.addError( - sel.opsSTN[idx].getLocation(), - "Selected a subexpression of a NEW clause of an ASSUME."); - return nullOAN ; - } - } - else { - curNode = curAPNode.getProve() ; - }; - } // else if (curAPNode.isSuffices()) - } // else if (curNode.getKind() == AssumeProveKind) - - else if (curNode.getKind() == OpArgKind) { - SymbolNode opNode = ((OpArgNode) curNode).getOp() ; - if ( (opNode.getKind() != UserDefinedOpKind) - || (opNode.getName() !=S_lambda)) { - errors.addError( - sel.opsSTN[idx].getLocation(), - "Trying to select subexpression of an operator argument."); - return nullOAN ; - } ; - OpDefNode opDefOpNode = (OpDefNode) opNode ; - if ( (sel.ops[idx] != NullSel) - && (sel.ops[idx] != AtSel)) { - errors.addError( - sel.opsSTN[idx].getLocation(), - "Cannot use !" + sel.opNames[idx].toString() + - " to select subexpression of a LAMBDA."); - return nullOAN ; - } ; - if (sel.ops[idx] == NullSel) { - int numOfArgs = (sel.args[idx].heirs().length-1)/2 ; - if (opDefOpNode.getArity() != numOfArgs) { - errors.addError( - sel.opsSTN[idx].getLocation(), - "Selector with " + numOfArgs + - "arguments used for LAMBDA expression taking " + - opDefOpNode.getArity() + " arguments."); - return nullOAN ; - } ; - for (int i = 0; i < numOfArgs; i++) { - allArgs.addElement(generateExpression( - sel.args[idx].heirs()[2 * i + 1] , cm)) ; - } ; - } ; // if (sel.ops[idx] == NullSel) - for (int i = 0; i < opDefOpNode.getArity(); i++) { - params.addElement(opDefOpNode.getParams()[i]) ; - } ; - curNode = opDefOpNode.getBody() ; - } // else if (curNode.getKind() == OpArgKind) - - else if ( (curNode.getKind() == UserDefinedOpKind) - || (curNode.getKind() == BuiltInKind) - || (curNode.getKind() == NumberedProofStepKind)) { - errors.addAbort( - sel.opsSTN[idx].getLocation(), - "Internal error: " + - " Should not have been able to select this node.") ; - } // else if (curNode.getKind() == UserDefinedOpKind) || ... - - else if ( (curNode.getKind() == AtNodeKind) - || (curNode.getKind() == DecimalKind) - || (curNode.getKind() == NumeralKind) - || (curNode.getKind() == StringKind) - || (curNode.getKind() == FormalParamKind) - || (curNode.getKind() == ConstantDeclKind) - || (curNode.getKind() == VariableDeclKind) - || (curNode.getKind() == BoundSymbolKind) ) { - errors.addError( - sel.opsSTN[idx].getLocation(), - "Selecting subexpression of expression that has none.") ; - return nullOAN ; - } // else if (curNode.getKind() == AtNodeKind) || ... - - else if (curNode.getKind() == LabelKind) { - curNode = ((LabelNode) curNode).getBody() ; - idx = idx - 1; - } // else if (curNode.getKind() == LabelKind) - - else { - errors.addAbort( - sel.opsSTN[idx].getLocation(), - "Internal error: " + - " Unknown node kind.") ; - } ; // end last else of if sel.ops[idx] != ColonSel - - if (isNullSelection(curNode, sel, idx)) { return nullOAN; } ; - - if (idx != sel.ops.length - 1) { - if (sel.ops[idx+1] == NameSel) { - while (curNode.getKind() == LabelKind) { - curNode = ((LabelNode) curNode).getBody(); - if (isNullSelection(curNode, sel, idx)) { return nullOAN; } ; - }; // while - if (curNode.getKind() == LetInKind) { - letInContext = ((LetInNode) curNode).context ; - mode = FindingOpName ; - firstFindingOpName = false; - } - else { - errors.addError( - sel.opsSTN[idx].getLocation(), - "A name selector here must be from a LET clause.") ; - return nullOAN ; - } - } // if (sel.ops[idx+1] == NameSel) - else if (sel.ops[idx+1] == ColonSel) { - errors.addError( - sel.opsSTN[idx].getLocation(), - "!: should not follow an operand selector.") ; - return nullOAN ; - } - } ; // if (idx != sel.ops.length - 1) - - prevMode = FindingSubExpr ; - break ; // case FindingSubExpr - - default: - errors.addAbort(sel.selSTN.getLocation(), - "Internal error: Unexpected mode") ; - } // switch (mode) - idx++ ; - } // while idx < - /********************************************************************** - * +cal: end while ; * - **********************************************************************/ - - /*********************************************************************** - * +cal: if curNode.kind = AssumeProveKind ... end if * - * * - * Modified 17 Feb 2009 by LL to allow ASSUME/PROVE to be used as a * - * fact. This seems to have been a bug in the original. * - * * - * Not corrected in the PlusCal code because that code doesn't talk * - * about the isFact case. * - ***********************************************************************/ - if (curNode.getKind() == AssumeProveKind) { - if (isFact) { return (AssumeProveNode) curNode ; }; - errors.addError( - sel.selSTN.getLocation(), - "Selected ASSUME/PROVE instead of expression.") ; - return nullOAN ; - } ; - - /*********************************************************************** - * The following added to allow naming of NEW constructs of an * - * ASSUME/PROVE in facts. * - ***********************************************************************/ - if (curNode.getKind() == NewSymbKind) { - if (isFact) { return (LevelNode) curNode ; } ; - errors.addError( - sel.selSTN.getLocation(), - "Selected a NEW declaration as an expression or operator.") ; - return nullOAN ; - } ; - - /*********************************************************************** - * +cal: if expectedArity < 0 ... end if; * - ***********************************************************************/ - if (expectedArity < 0) { - if ( (prevMode != FindingOpName) - || !( (curNode.getKind() == UserDefinedOpKind) - || (curNode.getKind() == ThmOrAssumpDefKind) - || (curNode.getKind() == NumberedProofStepKind) - || // This clause added by LL 15 Oct 2007, not in +cal code - ( (curNode.getKind() == ModuleInstanceKind) - && isDef))) { - errors.addError( - sel.selSTN.getLocation(), - "DEF clause entry should describe a defined operator.") ; - return nullOAN ; - } ; - if ( (curNode.getKind() == NumberedProofStepKind) - && (((OpDefNode) curNode).getStepNode().getKind() != DefStepKind)) { - errors.addError( - sel.selSTN.getLocation(), - "DEF clause entry refers to a non-definition step.") ; - return nullOAN ; - } - return (LevelNode) curNode; - }; // if (expectedArity < 0) - - if (curNode.getKind() == NumberedProofStepKind) { - errors.addError( - sel.selSTN.getLocation(), - isFact ? "Step number of non-fact used as a fact" - : "Step number of non-expression step used as an expression.") ; - return nullOAN ; - } - - /*********************************************************************** - * +cal: if expectedArity > 0 ... end if * - ***********************************************************************/ - if (expectedArity > 0) { - int temp = params.size() ; - if (curNode.getKind() == OpArgKind) { - temp = temp + ((OpArgNode) curNode).getArity(); - } - else if (prevMode == FindingOpName) { - temp = temp + ((SymbolNode) curNode).getArity(); - } ; - - if (expectedArity != temp) { - errors.addError( - sel.selSTN.getLocation(), - "Expected arity " + expectedArity + - " but found operator of arity " + temp + ".") ; - return nullOAN ; - } ; - }; // if (expectedArity > 0) - - /*********************************************************************** - * If opDefArgs is non-empty, we will need to convert it to an array, * - * so we might as well do it here. * - ***********************************************************************/ - ExprOrOpArgNode[] opDefArgArray = - new ExprOrOpArgNode[opDefArgs.size()] ; - for (int i = 0 ; i < opDefArgs.size() ; i++) { - opDefArgArray[i] = (ExprOrOpArgNode) opDefArgs.elementAt(i) ; - } ; // for - - /*********************************************************************** - * +cal: if /\ prevMode = "FindingOpName" ... end if; * - ***********************************************************************/ - if ( (prevMode == FindingOpName) - && (params.size() + substInPrefix.size() > 0)) { - - /********************************************************************* - * Set nodeParams to the +cal program's curNode.params value. * - *********************************************************************/ - FormalParamNode[] nodeParams = null ; - if (curNode.getKind() == UserDefinedOpKind) { - nodeParams = ((OpDefNode) curNode).getParams(); - } - else { - if (curNode.getKind() != ThmOrAssumpDefKind) { - errors.addAbort( - sel.opsSTN[sel.opsSTN.length-1].getLocation(), - "Internal Error: " + - " Found unexpected node kind after FindingOpName") ; - } ; - nodeParams = ((ThmOrAssumpDefNode) curNode).getParams(); - } ; - - for (int i = 0 ; i < opDefArgs.size() ; i++) { - allArgs.addElement(opDefArgArray[i]) ; - } ; // for - - ExprOrOpArgNode[] temp = new ExprOrOpArgNode[nodeParams.length] ; - for (int i = 0; i < nodeParams.length; i++) { - FormalParamNode pm = nodeParams[i] ; - /******************************************************************* - * Set newpm to a "clone" of pm. * - *******************************************************************/ - FormalParamNode newpm = new FormalParamNode(pm.getName(), - pm.getArity(), - pm.stn, - null, - cm) ; - - /******************************************************************* - * Set eoag to the ExprOrOpArgNode constructed from newpm. * - *******************************************************************/ - ExprOrOpArgNode eoag = null ; - if (pm.getArity() == 0) { - /***************************************************************** - * Formal parameter eoag is an ordinary (non-operator) parameter. * - *****************************************************************/ - eoag = new OpApplNode(newpm, - new ExprNode[0], - sel.selSTN, - cm) ; - } - else { - /***************************************************************** - * Formal parameter eoag is an operator parameter. * - *****************************************************************/ - eoag = new OpArgNode(newpm, sel.selSTN, cm) ; - } ; - temp[i] = eoag ; - params.addElement(newpm) ; - } ; // for - SymbolNode curSymNode = (SymbolNode) curNode ; - curNode = new OpApplNode(curSymNode, - temp, // opDefArgArray, - sel.selSTN, // TreeNode - cm ); - - } ;// if (prevMode == FindingOpName) ... - - - /*********************************************************************** - * +cal: if curNode.kind = OpArgKind * - ***********************************************************************/ - if (curNode.getKind() == OpArgKind) { - OpArgNode curOpArgNode = (OpArgNode) curNode ; - - /********************************************************************* - * +cal: if expectedArity = 0 then ... elsif * - *********************************************************************/ - if (expectedArity == 0) { - errors.addError( - sel.selSTN.getLocation(), - "Selected operator argument when expression expected.") ; - return nullOAN ; - } ; - - /********************************************************************* - * +cal: elsif expectedArity # Len(params) + ... else * - *********************************************************************/ - int temp = params.size() + curOpArgNode.getArity() ; - if (expectedArity != temp) { - errors.addError( - sel.selSTN.getLocation(), - "Expected operator of arity " + expectedArity + - " but selected operator has arity " + temp + ".") ; - return nullOAN ; - }; - - /********************************************************************* - * +cal: else ... end if * - *********************************************************************/ - if (params.size() + substInPrefix.size() > 0) { - FormalParamNode[] temp2 = - new FormalParamNode[curOpArgNode.getArity()]; - for (int i = 0; i < temp2.length; i++) { - UniqueString temp3 = UniqueString.uniqueStringOf( - "NewParam" + i) ; - temp2[i] = new FormalParamNode( - temp3, // name - 0, // arity - new SyntaxTreeNode(temp3), // syntax tree node - null, // symbol table - cm) ; // module - params.addElement(temp2[i]) ; - } ; // for - curNode = new OpApplNode(curOpArgNode.getOp(), - opDefArgArray, - sel.selSTN, // TreeNode - cm ); - } ; // if (params.size() + ...) - - }; // if (curNode.getKind() == OpArgKind) - - /*********************************************************************** - * This code doesn't seem to be present in the +cal code. I forget * - * why not; perhaps I added it without correcting the +cal. * - * * - * Modified 19 May 2008 by LL because of addition of labeled * - * ASSUME/PROVE nodes. * - ***********************************************************************/ - if ( ! isFact - && ( ( (curNode.getKind() == ThmOrAssumpDefKind) - && ( ((ThmOrAssumpDefNode) curNode).getBody().getKind() - == AssumeProveKind)) - /*********************************************************** - * curNode is an ASSUME/PROVE theorem. * - ***********************************************************/ - || ( (curNode.getKind() == LabelKind) - && (((LabelNode) curNode).isAssumeProve))) - /*********************************************************** - * curNode is a LabelNode naming an ASSUME/PROVE. * - ***********************************************************/ - ) { - errors.addError( - sel.selSTN.getLocation(), - "ASSUME/PROVE used where an expression is required.") ; - return nullOAN ; - }; - - /*********************************************************************** - * The following code added 18 Oct 2007 to check if a * - * pseudo-expression like "HAVE P" or "PICK ..." was used as an * - * expression. * - ***********************************************************************/ - if ( (! isFact) - && (curNode.getKind() == ThmOrAssumpDefKind) - && (((ThmOrAssumpDefNode) curNode).getBody().getKind() - == OpApplKind)) { - UniqueString opName = - ((OpApplNode) ((ThmOrAssumpDefNode) - curNode).getBody()).getOperator().getName() ; - String exprType = null ; - if (opName == OP_qed) { exprType = "QED step" ;} - else if (opName == OP_pfcase) { exprType = "CASE" ;} - else if (opName == OP_have) { exprType = "HAVE" ;} - else if (opName == OP_take) { exprType = "TAKE" ;} - else if (opName == OP_pick) { exprType = "PICK" ;} - else if (opName == OP_witness) { exprType = "WITNESS" ;} - else if (opName == OP_suffices) { exprType = "SUFFICES" ;}; - /******************************************************************* - * OP_suffices added by LL 16 Feb 2009. * - *******************************************************************/ - if (exprType != null) { - errors.addError( - sel.selSTN.getLocation(), - exprType + " proof step selected instead of expression."); - return nullOAN ; - } ; - } // if - - - /*********************************************************************** - * +cal: if curNode.kind \in {UserDefinedOpKind, ConstantDeclKind... * - ***********************************************************************/ - if ( (curNode.getKind() == UserDefinedOpKind) - || (curNode.getKind() == ConstantDeclKind) - || (curNode.getKind() == VariableDeclKind) - || (curNode.getKind() == FormalParamKind) - || (curNode.getKind() == BuiltInKind) - || (curNode.getKind() == BoundSymbolKind) - || (curNode.getKind() == ThmOrAssumpDefKind) - || (curNode.getKind() == NewConstantKind) - || (curNode.getKind() == NewVariableKind) - || (curNode.getKind() == NewStateKind) - || (curNode.getKind() == NewActionKind) - || (curNode.getKind() == NewTemporalKind)) { - SymbolNode curSymbolNode = (SymbolNode) curNode ; - if (expectedArity > 0) { - return new OpArgNode(curSymbolNode, sel.selSTN, cm) ; - } - - /********************************************************************* - * +cal: elsif expectedArity = 0 then * - *********************************************************************/ - else { - /******************************************************************* - * expectedArity = 0 * - *******************************************************************/ - OpApplNode oan = new OpApplNode(curSymbolNode, - opDefArgArray, - sel.selSTN, - cm ) ; - oan.subExpressionOf = subExprOf; - return oan; - } // if (expectedArity > 0) - } ; // if (curNode.getKind() == UserDefinedOpKind) - - /*********************************************************************** - * +cal: elsif curNode.kind = OpArgKind then * - ***********************************************************************/ - if (curNode.getKind() == OpArgKind) { return (OpArgNode) curNode; } ; - - /*********************************************************************** - * +cal: else * - ***********************************************************************/ - - /*********************************************************************** - * The following test is not in Subexpression.tla and should not be, * - * having been added to allow the use of a ModuleInstanceKind as a * - * Fact or Def. The else case should not occur because if it would be * - * the case, then the error should have been caught earlier (I think). * - ***********************************************************************/ - if (curNode.getKind() == ModuleInstanceKind) { - if (isFact || isDef) { - return (OpDefNode) curNode; - } - else { - errors.addError( - sel.selSTN.getLocation(), - "Module instantiation selected instead of expression."); - return nullOAN ; - } - } ; - /*********************************************************************** - * curNode should be an expression node. * - ***********************************************************************/ - if (!(curNode instanceof ExprNode)) { - errors.addAbort(sel.selSTN.getLocation(), - "Internal error: Expected expression node.") ; - } ; - ExprNode curExprNode = (ExprNode) curNode ; - int temp = substInPrefix.size() ; - - /*********************************************************************** - * +cal: while temp > 0 do ... end while * - ***********************************************************************/ - while (temp > 0) { - // Modified on 13 Nov 2009 by LL to handle the case of a subexpression - // of a Theorem or Assumption. - Object substOb = substInPrefix.elementAt(temp-1) ; - if (substOb instanceof SubstInNode) { - SubstInNode subst = (SubstInNode) substOb; - curExprNode = new SubstInNode(subst.stn, - subst.getSubsts(), - curExprNode, - subst.getInstantiatingModule(), - subst.getInstantiatedModule() - ); - } else { - APSubstInNode subst = (APSubstInNode) substOb; - curExprNode = new SubstInNode(subst.stn, - subst.getSubsts(), - curExprNode, - subst.getInstantiatingModule(), - subst.getInstantiatedModule() - ); - - } - temp = temp - 1; - }; // while - - /*********************************************************************** - * Will need params as an array. * - ***********************************************************************/ - FormalParamNode[] paramsArray = new FormalParamNode[params.size()] ; - for (int i = 0; i < params.size(); i++) { - paramsArray[i] = (FormalParamNode) params.elementAt(i) ; - } ; - - /*********************************************************************** - * If there are params, will need a Lambda operator. * - ***********************************************************************/ - OpDefNode newLambda = null ; - if (paramsArray.length > 0) { - newLambda = - new OpDefNode(S_lambda, // The operator name is "LAMBDA" - UserDefinedOpKind, // The node kind - paramsArray, // the array of formal parameters - false , // localness, which is meaningless - curExprNode, // the body - cm, // the module - null, // the symbol table - sel.selSTN, // syntax-tree node - true, // Is defined. - null ) ; // Source - } ; - - /*********************************************************************** - * +cal: if expectedArity > 0 * - ***********************************************************************/ - if (expectedArity > 0) { - if (paramsArray.length != expectedArity) { - errors.addError( - sel.selSTN.getLocation(), - "Expected operator argument with arity " + expectedArity + - " but found one of arity " + paramsArray.length + ".") ; - return nullOAN ; - } ; - return new OpArgNode(newLambda, sel.selSTN, cm) ; - } // if (expectedArity > 0) - - /*********************************************************************** - * expectedArity = 0 * - * +cal: else if Len(params) # Len(allArgs) * - ***********************************************************************/ - if (paramsArray.length != allArgs.size()) { - errors.addError( - sel.selSTN.getLocation(), - "Expected " + paramsArray.length + " arguments but found " + - allArgs.size() + ".") ; - return nullOAN ; - } ; - /*********************************************************************** - * +cal: if Len(params) = 0 then * - ***********************************************************************/ - if (paramsArray.length == 0) { - /********************************************************************* - * In this case, we don't have to construct a new LAMBDA or OpAppl * - * node. However, we want to have a new node to which to attach the * - * SyntaxTree node. So, we create an OpApplNode with the dummy * - * builtin operator $Nop and return it. * - *********************************************************************/ - ExprOrOpArgNode[] args = new ExprOrOpArgNode[1] ; - args[0] = curExprNode ; - OpApplNode ln = new OpApplNode(OP_nop, args, sel.selSTN, cm) ; - ln.subExpressionOf = subExprOf ; - return ln;} - - /*********************************************************************** - * +cal: else * - ***********************************************************************/ - /*********************************************************************** - * Will need allArgs as an array. * - ***********************************************************************/ - ExprOrOpArgNode[] allArgsArray = new ExprOrOpArgNode[allArgs.size()] ; - for (int i = 0; i < allArgs.size(); i++) { - allArgsArray[i] = (ExprOrOpArgNode) allArgs.elementAt(i) ; - } ; - - OpApplNode oan = - new OpApplNode (newLambda, allArgsArray, sel.selSTN, cm) ; - oan.subExpressionOf = subExprOf ; - return oan ; - - } // selectorToNode - - private static String selectorItemToString(Selector sel, int idx) { - String selStr = sel.opNames[idx].toString() ; - if (selStr.equals("N_StructOp")) { - selStr = "argument selector " + - sel.opsSTN[idx].heirs()[0].heirs()[0].getImage() ; - } - else if (selStr.equals("N_OpArgs")) { - selStr = "!(...)" ; - }; - return selStr ; - } - - private void reportSelectorError(Selector sel, int idx) { - /*********************************************************************** - * Just a method to perform error reporting when an error is detected * - * when executing selectorToNode. * - ***********************************************************************/ - errors.addError(sel.opsSTN[idx].getLocation(), - "Nonexistent operand specified by `" - + selectorItemToString(sel, idx) + "'."); - } - - private boolean isNullSelection(SemanticNode node, Selector sel, int idx) { - /*********************************************************************** - * A method for use in selectorToNode to test if a a null node has * - * been selected. This should happen only if the null node was * - * created as a result of a previous error. If the node is null, then * - * an error is reported and true is returned. * - ***********************************************************************/ - boolean val = (node == null) ; - if (val) { - errors.addError(sel.opsSTN[idx].getLocation(), - "An unexpected null node specified by " - + selectorItemToString(sel, idx) + "'." - + "\nThis is probably due to a previous error."); - }; - return val ; - } - - // Constructor - public Generator(ExternalModuleTable moduleTable, Errors errs) { - nullParam = new FormalParamNode[0]; - nullODN = new OpDefNode(UniqueString.uniqueStringOf("nullODN")); - nullOAN = new OpApplNode(nullODN); - nullOpArg = new OpArgNode(UniqueString.uniqueStringOf("nullOpArg")); - nullLabelNode = new LabelNode(nullOAN) ; - this.errors = errs; - this.moduleTable = moduleTable; - this.symbolTable = new SymbolTable(moduleTable, errors); - this.excStack = new Stack(); - this.excSpecStack = new Stack(); - } - - public final SymbolTable getSymbolTable() { return symbolTable; } - - public final ModuleNode generate(TreeNode treeNode) throws AbortException { - /************************************************************************* - * This is the method called to generate the semantic graph for a module. * - *************************************************************************/ + reportSelectorError(sel, idx); + return nullOAN; + } + ; + curOpApplNode = (OpApplNode) curArgs[temp - 1]; + if (curOpApplNode.getOperator().getName() != OP_pair) { + errors.addAbort(sel.opsSTN[idx].getLocation(), + "Internal error: Expecting $Pair and didn't find it."); + } + ; + curNode = curOpApplNode.getArgs()[1]; + } // if opNode.name == $RcdConstructor or $SetOfRcds + + else if (opNode.getName() == OP_case) { // $Case + if (idx == sel.ops.length - 1) { + errors.addError(sel.opsSTN[idx].getLocation(), + "Subexpression of CASE must have form !i!j."); + return nullOAN; + } + ; + int temp = ArgNum(sel.ops[idx], curArgs.length); + if (temp == -1) { + reportSelectorError(sel, idx); + return nullOAN; + } + ; + curOpApplNode = (OpApplNode) curArgs[temp - 1]; + if (curOpApplNode.getOperator().getName() != OP_pair) { + errors.addAbort(sel.opsSTN[idx].getLocation(), + "Internal error: Expecting $Pair and didn't find it."); + } + ; + idx = idx + 1; + temp = ArgNum(sel.ops[idx], 2); + if (temp == -1) { + errors.addError(sel.opsSTN[idx].getLocation(), + "Second selector for CASE subexpression must specify " + + " one of two operands."); + return nullOAN; + } + ; + curNode = curOpApplNode.getArgs()[temp - 1]; + if (curNode == null) { + errors.addError(sel.opsSTN[idx].getLocation(), "Selecting OTHER in a CASE statement."); + return nullOAN; + } + ; + } // if opNode.name = $Case + + else if (opNode.getName() == OP_exc) { // $Except + /************************************************************ + * In an $Except node representing * * [exp_1 ELSE !... = exp_2, ..., !... = + * exp_n] * * operand i names exp_i. * + ************************************************************/ + int temp = ArgNum(sel.ops[idx], curArgs.length); + if (temp == -1) { + reportSelectorError(sel, idx); + return nullOAN; + } + ; + /************************************************************ + * Selection of subexpressions of EXCEPT have been outlawed * because they may + * contain a dangling "@". This doesn't * seem to be worth fixing because it's + * unlikely that * anyone will actually want to refer to such a * subexpression. + * * Change made 25 Oct 2007. * + ************************************************************/ + if (temp > 1) { + errors.addError(sel.opsSTN[idx].getLocation(), + "Selecting subexpression of an " + "EXCEPT not yet implemented."); + return nullOAN; + } + ; + curNode = curArgs[temp - 1]; + if (isNullSelection(curNode, sel, idx)) { + return nullOAN; + } + ; + if (temp > 1) { + curOpApplNode = (OpApplNode) curNode; + if (curOpApplNode.getOperator().getName() != OP_pair) { + errors.addAbort(sel.opsSTN[idx].getLocation(), + "Internal error: Expecting $Pair and didn't find it."); + } + ; + curNode = curOpApplNode.getArgs()[1]; + } + ; // if (temp > 1) + } // if opNode.name = $Except + + else { // Handled by standard procedure + if ((curOpApplNode.getNumberOfBoundedBoundSymbols() == 0) + && ((curOpApplNode.getUnbdedQuantSymbols() == null) + || (curOpApplNode.getUnbdedQuantSymbols().length == 0)) + /***************************************************** + * I'm not sure getUnbdedQuantSymbols always returns * null if there are no such + * symbols. * + *****************************************************/ + ) { + /********************************************************** + * Current subexpression has no bound variables. * + **********************************************************/ + int temp = ArgNum(sel.ops[idx], curOpApplNode.getArgs().length); + if (temp == -1) { + reportSelectorError(sel, idx); + return nullOAN; + } + ; + curNode = curOpApplNode.getArgs()[temp - 1]; + } // if current subexpression has no bound variables + else { + /********************************************************** + * Current subexpression has bound variables. * + **********************************************************/ + if ((sel.ops[idx] == NullSel) || (sel.ops[idx] == AtSel)) { + /******************************************************** + * Set temp to the array of FormalParamNodes for the * parameters. * + ********************************************************/ + FormalParamNode[] temp; + if (curOpApplNode.getNumberOfBoundedBoundSymbols() > 0) { + FormalParamNode[][] symbs = curOpApplNode.getBdedQuantSymbolLists(); + int numSymbs = 0; + for (int i = 0; i < symbs.length; i++) { + numSymbs = numSymbs + symbs[i].length; + } + ; // for + temp = new FormalParamNode[numSymbs]; + int k = 0; + for (int i = 0; i < symbs.length; i++) { + for (int j = 0; j < symbs[i].length; j++) { + temp[k] = symbs[i][j]; + k++; + } + ; // for j + } + ; // for i + } // if (curOpApplNode.getNumberOf... > 0) + else { + temp = curOpApplNode.getUnbdedQuantSymbols(); + } + ; // else not (curOpApplNode.getNumberOf... > 0) + + /******************************************************* + * Add the elements of temp to the params vector. * + *******************************************************/ + for (int i = 0; i < temp.length; i++) { + params.addElement(temp[i]); + } + ; // for i + + if (sel.ops[idx] == NullSel) { + /***************************************************** + * Add the arguments to allArgs, checking if there * are the right number. * + *****************************************************/ + int numOfArgs = (sel.args[idx].heirs().length - 1) / 2; + if (temp.length != numOfArgs) { + errors.addError(sel.opsSTN[idx].getLocation(), + "Selector with " + numOfArgs + + " argument(s) used for quantifier with " + temp.length + + " bound identifier(s)."); + return nullOAN; + } + ; + for (int i = 0; i < numOfArgs; i++) { + allArgs.addElement( + generateExpression(sel.args[idx].heirs()[2 * i + 1], cm)); + } + ; // for i + } + ; // if (sel.ops[idx] == NullSel) + curNode = curOpApplNode.getArgs()[0]; + } // if (sel.ops[idx] == NullSel) || ... + else { + int temp = ArgNum(sel.ops[idx], curOpApplNode.getBdedQuantBounds().length); + if (temp == -1) { + reportSelectorError(sel, idx); + return nullOAN; + } + ; + curNode = curOpApplNode.getBdedQuantBounds()[temp - 1]; + } + ; // else + } + ; // else Current subexpression has bound variables + } + ; // Handled by standard procedure + } // else if (opNode.getKind() == BuiltInKind) + + else { + errors.addError(sel.opsSTN[idx].getLocation(), "Choosing operand `" + + selectorItemToString(sel, idx) + "' of subexpression with no operands."); + return nullOAN; + } + ; + + } // else if ((curNode.getKind() == OpApplKind) + + else if (curNode.getKind() == AssumeProveKind) { + AssumeProveNode curAPNode = (AssumeProveNode) curNode; + + /**************************************************************** + * 16 Feb 2009: Case of curAPNode.suffices true added. * + ****************************************************************/ + if ((curAPNode.isSuffices()) && (!inAPsuffices)) { + /************************************************************** + * In this case, the selector must be 1, and the curNode is * left equal to the + * AssumeProve, but with inAPsuffices set * to true. * + **************************************************************/ + if (ArgNum(sel.ops[idx], 1) != 1) { + errors.addError(sel.opsSTN[idx].getLocation(), + "Accessing non-existent subexpression of " + "a SUFFICES"); + return nullOAN; + } + ; + inAPsuffices = true; + } // if (curAPNode.isSuffices()) + else { + inAPsuffices = false; + /************************************************************ + * Added 16 Feb 2009 by LL. * + ************************************************************/ + int temp = ArgNum(sel.ops[idx], 1 + curAPNode.getAssumes().length); + if (temp == -1) { + reportSelectorError(sel, idx); + return nullOAN; + } + ; + + if (illegalAPPosRef(curAPNode, temp)) { + errors.addError(sel.opsSTN[idx].getLocation(), + "Accessing ASSUME/PROVE clause within the scope of " + + "a declaration\n from outside that declaration's scope."); + return nullOAN; + } + ; + if (temp <= curAPNode.getAssumes().length) { + curNode = curAPNode.getAssumes()[temp - 1]; + if (isNullSelection(curNode, sel, idx)) { + return nullOAN; + } + ; + if ((curNode.getKind() == NewSymbKind) && (idx != sel.args.length - 1)) { + /************************************************************ + * Extra conjunct added to if test to allow selection of a * NEW clause as a + * fact. * + ************************************************************/ + errors.addError(sel.opsSTN[idx].getLocation(), + "Selected a subexpression of a NEW clause of an ASSUME."); + return nullOAN; + } + } else { + curNode = curAPNode.getProve(); + } + ; + } // else if (curAPNode.isSuffices()) + } // else if (curNode.getKind() == AssumeProveKind) + + else if (curNode.getKind() == OpArgKind) { + SymbolNode opNode = ((OpArgNode) curNode).getOp(); + if ((opNode.getKind() != UserDefinedOpKind) || (opNode.getName() != S_lambda)) { + errors.addError(sel.opsSTN[idx].getLocation(), + "Trying to select subexpression of an operator argument."); + return nullOAN; + } + ; + OpDefNode opDefOpNode = (OpDefNode) opNode; + if ((sel.ops[idx] != NullSel) && (sel.ops[idx] != AtSel)) { + errors.addError(sel.opsSTN[idx].getLocation(), + "Cannot use !" + sel.opNames[idx].toString() + " to select subexpression of a LAMBDA."); + return nullOAN; + } + ; + if (sel.ops[idx] == NullSel) { + int numOfArgs = (sel.args[idx].heirs().length - 1) / 2; + if (opDefOpNode.getArity() != numOfArgs) { + errors.addError(sel.opsSTN[idx].getLocation(), + "Selector with " + numOfArgs + "arguments used for LAMBDA expression taking " + + opDefOpNode.getArity() + " arguments."); + return nullOAN; + } + ; + for (int i = 0; i < numOfArgs; i++) { + allArgs.addElement(generateExpression(sel.args[idx].heirs()[2 * i + 1], cm)); + } + ; + } + ; // if (sel.ops[idx] == NullSel) + for (int i = 0; i < opDefOpNode.getArity(); i++) { + params.addElement(opDefOpNode.getParams()[i]); + } + ; + curNode = opDefOpNode.getBody(); + } // else if (curNode.getKind() == OpArgKind) + + else if ((curNode.getKind() == UserDefinedOpKind) || (curNode.getKind() == BuiltInKind) + || (curNode.getKind() == NumberedProofStepKind)) { + errors.addAbort(sel.opsSTN[idx].getLocation(), + "Internal error: " + " Should not have been able to select this node."); + } // else if (curNode.getKind() == UserDefinedOpKind) || ... + + else if ((curNode.getKind() == AtNodeKind) || (curNode.getKind() == DecimalKind) + || (curNode.getKind() == NumeralKind) || (curNode.getKind() == StringKind) + || (curNode.getKind() == FormalParamKind) || (curNode.getKind() == ConstantDeclKind) + || (curNode.getKind() == VariableDeclKind) || (curNode.getKind() == BoundSymbolKind)) { + errors.addError(sel.opsSTN[idx].getLocation(), + "Selecting subexpression of expression that has none."); + return nullOAN; + } // else if (curNode.getKind() == AtNodeKind) || ... + + else if (curNode.getKind() == LabelKind) { + curNode = ((LabelNode) curNode).getBody(); + idx = idx - 1; + } // else if (curNode.getKind() == LabelKind) + + else { + errors.addAbort(sel.opsSTN[idx].getLocation(), "Internal error: " + " Unknown node kind."); + } + ; // end last else of if sel.ops[idx] != ColonSel + + if (isNullSelection(curNode, sel, idx)) { + return nullOAN; + } + ; + + if (idx != sel.ops.length - 1) { + if (sel.ops[idx + 1] == NameSel) { + while (curNode.getKind() == LabelKind) { + curNode = ((LabelNode) curNode).getBody(); + if (isNullSelection(curNode, sel, idx)) { + return nullOAN; + } + ; + } + ; // while + if (curNode.getKind() == LetInKind) { + letInContext = ((LetInNode) curNode).context; + mode = FindingOpName; + firstFindingOpName = false; + } else { + errors.addError(sel.opsSTN[idx].getLocation(), + "A name selector here must be from a LET clause."); + return nullOAN; + } + } // if (sel.ops[idx+1] == NameSel) + else if (sel.ops[idx + 1] == ColonSel) { + errors.addError(sel.opsSTN[idx].getLocation(), "!: should not follow an operand selector."); + return nullOAN; + } + } + ; // if (idx != sel.ops.length - 1) + + prevMode = FindingSubExpr; + break; // case FindingSubExpr + + default: + errors.addAbort(sel.selSTN.getLocation(), "Internal error: Unexpected mode"); + } // switch (mode) + idx++; + } // while idx < + /********************************************************************** + * +cal: end while ; * + **********************************************************************/ + + /*********************************************************************** + * +cal: if curNode.kind = AssumeProveKind ... end if * * Modified 17 Feb 2009 + * by LL to allow ASSUME/PROVE to be used as a * fact. This seems to have been a + * bug in the original. * * Not corrected in the PlusCal code because that code + * doesn't talk * about the isFact case. * + ***********************************************************************/ + if (curNode.getKind() == AssumeProveKind) { + if (isFact) { + return (AssumeProveNode) curNode; + } + ; + errors.addError(sel.selSTN.getLocation(), "Selected ASSUME/PROVE instead of expression."); + return nullOAN; + } + ; + + /*********************************************************************** + * The following added to allow naming of NEW constructs of an * ASSUME/PROVE in + * facts. * + ***********************************************************************/ + if (curNode.getKind() == NewSymbKind) { + if (isFact) { + return (LevelNode) curNode; + } + ; + errors.addError(sel.selSTN.getLocation(), "Selected a NEW declaration as an expression or operator."); + return nullOAN; + } + ; + + /*********************************************************************** + * +cal: if expectedArity < 0 ... end if; * + ***********************************************************************/ + if (expectedArity < 0) { + if ((prevMode != FindingOpName) || !((curNode.getKind() == UserDefinedOpKind) + || (curNode.getKind() == ThmOrAssumpDefKind) || (curNode.getKind() == NumberedProofStepKind) || // This + // clause + // added + // by + // LL + // 15 + // Oct + // 2007, + // not + // in + // +cal + // code + ((curNode.getKind() == ModuleInstanceKind) && isDef))) { + errors.addError(sel.selSTN.getLocation(), "DEF clause entry should describe a defined operator."); + return nullOAN; + } + ; + if ((curNode.getKind() == NumberedProofStepKind) + && (((OpDefNode) curNode).getStepNode().getKind() != DefStepKind)) { + errors.addError(sel.selSTN.getLocation(), "DEF clause entry refers to a non-definition step."); + return nullOAN; + } + return (LevelNode) curNode; + } + ; // if (expectedArity < 0) + + if (curNode.getKind() == NumberedProofStepKind) { + errors.addError(sel.selSTN.getLocation(), isFact ? "Step number of non-fact used as a fact" + : "Step number of non-expression step used as an expression."); + return nullOAN; + } + + /*********************************************************************** + * +cal: if expectedArity > 0 ... end if * + ***********************************************************************/ + if (expectedArity > 0) { + int temp = params.size(); + if (curNode.getKind() == OpArgKind) { + temp = temp + ((OpArgNode) curNode).getArity(); + } else if (prevMode == FindingOpName) { + temp = temp + ((SymbolNode) curNode).getArity(); + } + ; + + if (expectedArity != temp) { + errors.addError(sel.selSTN.getLocation(), + "Expected arity " + expectedArity + " but found operator of arity " + temp + "."); + return nullOAN; + } + ; + } + ; // if (expectedArity > 0) + + /*********************************************************************** + * If opDefArgs is non-empty, we will need to convert it to an array, * so we + * might as well do it here. * + ***********************************************************************/ + ExprOrOpArgNode[] opDefArgArray = new ExprOrOpArgNode[opDefArgs.size()]; + for (int i = 0; i < opDefArgs.size(); i++) { + opDefArgArray[i] = (ExprOrOpArgNode) opDefArgs.elementAt(i); + } + ; // for + + /*********************************************************************** + * +cal: if /\ prevMode = "FindingOpName" ... end if; * + ***********************************************************************/ + if ((prevMode == FindingOpName) && (params.size() + substInPrefix.size() > 0)) { + + /********************************************************************* + * Set nodeParams to the +cal program's curNode.params value. * + *********************************************************************/ + FormalParamNode[] nodeParams = null; + if (curNode.getKind() == UserDefinedOpKind) { + nodeParams = ((OpDefNode) curNode).getParams(); + } else { + if (curNode.getKind() != ThmOrAssumpDefKind) { + errors.addAbort(sel.opsSTN[sel.opsSTN.length - 1].getLocation(), + "Internal Error: " + " Found unexpected node kind after FindingOpName"); + } + ; + nodeParams = ((ThmOrAssumpDefNode) curNode).getParams(); + } + ; + + for (int i = 0; i < opDefArgs.size(); i++) { + allArgs.addElement(opDefArgArray[i]); + } + ; // for + + ExprOrOpArgNode[] temp = new ExprOrOpArgNode[nodeParams.length]; + for (int i = 0; i < nodeParams.length; i++) { + FormalParamNode pm = nodeParams[i]; + /******************************************************************* + * Set newpm to a "clone" of pm. * + *******************************************************************/ + FormalParamNode newpm = new FormalParamNode(pm.getName(), pm.getArity(), pm.stn, null, cm); + + /******************************************************************* + * Set eoag to the ExprOrOpArgNode constructed from newpm. * + *******************************************************************/ + ExprOrOpArgNode eoag = null; + if (pm.getArity() == 0) { + /***************************************************************** + * Formal parameter eoag is an ordinary (non-operator) parameter. * + *****************************************************************/ + eoag = new OpApplNode(newpm, new ExprNode[0], sel.selSTN, cm); + } else { + /***************************************************************** + * Formal parameter eoag is an operator parameter. * + *****************************************************************/ + eoag = new OpArgNode(newpm, sel.selSTN, cm); + } + ; + temp[i] = eoag; + params.addElement(newpm); + } + ; // for + SymbolNode curSymNode = (SymbolNode) curNode; + curNode = new OpApplNode(curSymNode, temp, // opDefArgArray, + sel.selSTN, // TreeNode + cm); + + } + ;// if (prevMode == FindingOpName) ... + + /*********************************************************************** + * +cal: if curNode.kind = OpArgKind * + ***********************************************************************/ + if (curNode.getKind() == OpArgKind) { + OpArgNode curOpArgNode = (OpArgNode) curNode; + + /********************************************************************* + * +cal: if expectedArity = 0 then ... elsif * + *********************************************************************/ + if (expectedArity == 0) { + errors.addError(sel.selSTN.getLocation(), "Selected operator argument when expression expected."); + return nullOAN; + } + ; + + /********************************************************************* + * +cal: elsif expectedArity # Len(params) + ... else * + *********************************************************************/ + int temp = params.size() + curOpArgNode.getArity(); + if (expectedArity != temp) { + errors.addError(sel.selSTN.getLocation(), "Expected operator of arity " + expectedArity + + " but selected operator has arity " + temp + "."); + return nullOAN; + } + ; + + /********************************************************************* + * +cal: else ... end if * + *********************************************************************/ + if (params.size() + substInPrefix.size() > 0) { + FormalParamNode[] temp2 = new FormalParamNode[curOpArgNode.getArity()]; + for (int i = 0; i < temp2.length; i++) { + UniqueString temp3 = UniqueString.uniqueStringOf("NewParam" + i); + temp2[i] = new FormalParamNode(temp3, // name + 0, // arity + new SyntaxTreeNode(temp3), // syntax tree node + null, // symbol table + cm); // module + params.addElement(temp2[i]); + } + ; // for + curNode = new OpApplNode(curOpArgNode.getOp(), opDefArgArray, sel.selSTN, // TreeNode + cm); + } + ; // if (params.size() + ...) + + } + ; // if (curNode.getKind() == OpArgKind) + + /*********************************************************************** + * This code doesn't seem to be present in the +cal code. I forget * why not; + * perhaps I added it without correcting the +cal. * * Modified 19 May 2008 by + * LL because of addition of labeled * ASSUME/PROVE nodes. * + ***********************************************************************/ + if (!isFact && (((curNode.getKind() == ThmOrAssumpDefKind) + && (((ThmOrAssumpDefNode) curNode).getBody().getKind() == AssumeProveKind)) + /*********************************************************** + * curNode is an ASSUME/PROVE theorem. * + ***********************************************************/ + || ((curNode.getKind() == LabelKind) && (((LabelNode) curNode).isAssumeProve))) + /*********************************************************** + * curNode is a LabelNode naming an ASSUME/PROVE. * + ***********************************************************/ + ) { + errors.addError(sel.selSTN.getLocation(), "ASSUME/PROVE used where an expression is required."); + return nullOAN; + } + ; + + /*********************************************************************** + * The following code added 18 Oct 2007 to check if a * pseudo-expression like + * "HAVE P" or "PICK ..." was used as an * expression. * + ***********************************************************************/ + if ((!isFact) && (curNode.getKind() == ThmOrAssumpDefKind) + && (((ThmOrAssumpDefNode) curNode).getBody().getKind() == OpApplKind)) { + UniqueString opName = ((OpApplNode) ((ThmOrAssumpDefNode) curNode).getBody()).getOperator().getName(); + String exprType = null; + if (opName == OP_qed) { + exprType = "QED step"; + } else if (opName == OP_pfcase) { + exprType = "CASE"; + } else if (opName == OP_have) { + exprType = "HAVE"; + } else if (opName == OP_take) { + exprType = "TAKE"; + } else if (opName == OP_pick) { + exprType = "PICK"; + } else if (opName == OP_witness) { + exprType = "WITNESS"; + } else if (opName == OP_suffices) { + exprType = "SUFFICES"; + } + ; + /******************************************************************* + * OP_suffices added by LL 16 Feb 2009. * + *******************************************************************/ + if (exprType != null) { + errors.addError(sel.selSTN.getLocation(), exprType + " proof step selected instead of expression."); + return nullOAN; + } + ; + } // if + + /*********************************************************************** + * +cal: if curNode.kind \in {UserDefinedOpKind, ConstantDeclKind... * + ***********************************************************************/ + if ((curNode.getKind() == UserDefinedOpKind) || (curNode.getKind() == ConstantDeclKind) + || (curNode.getKind() == VariableDeclKind) || (curNode.getKind() == FormalParamKind) + || (curNode.getKind() == BuiltInKind) || (curNode.getKind() == BoundSymbolKind) + || (curNode.getKind() == ThmOrAssumpDefKind) || (curNode.getKind() == NewConstantKind) + || (curNode.getKind() == NewVariableKind) || (curNode.getKind() == NewStateKind) + || (curNode.getKind() == NewActionKind) || (curNode.getKind() == NewTemporalKind)) { + SymbolNode curSymbolNode = (SymbolNode) curNode; + if (expectedArity > 0) { + return new OpArgNode(curSymbolNode, sel.selSTN, cm); + } + + /********************************************************************* + * +cal: elsif expectedArity = 0 then * + *********************************************************************/ + else { + /******************************************************************* + * expectedArity = 0 * + *******************************************************************/ + OpApplNode oan = new OpApplNode(curSymbolNode, opDefArgArray, sel.selSTN, cm); + oan.subExpressionOf = subExprOf; + return oan; + } // if (expectedArity > 0) + } + ; // if (curNode.getKind() == UserDefinedOpKind) + + /*********************************************************************** + * +cal: elsif curNode.kind = OpArgKind then * + ***********************************************************************/ + if (curNode.getKind() == OpArgKind) { + return (OpArgNode) curNode; + } + ; + + /*********************************************************************** + * +cal: else * + ***********************************************************************/ + + /*********************************************************************** + * The following test is not in Subexpression.tla and should not be, * having + * been added to allow the use of a ModuleInstanceKind as a * Fact or Def. The + * else case should not occur because if it would be * the case, then the error + * should have been caught earlier (I think). * + ***********************************************************************/ + if (curNode.getKind() == ModuleInstanceKind) { + if (isFact || isDef) { + return (OpDefNode) curNode; + } else { + errors.addError(sel.selSTN.getLocation(), "Module instantiation selected instead of expression."); + return nullOAN; + } + } + ; + /*********************************************************************** + * curNode should be an expression node. * + ***********************************************************************/ + if (!(curNode instanceof ExprNode)) { + errors.addAbort(sel.selSTN.getLocation(), "Internal error: Expected expression node."); + } + ; + ExprNode curExprNode = (ExprNode) curNode; + int temp = substInPrefix.size(); + + /*********************************************************************** + * +cal: while temp > 0 do ... end while * + ***********************************************************************/ + while (temp > 0) { + // Modified on 13 Nov 2009 by LL to handle the case of a subexpression + // of a Theorem or Assumption. + Object substOb = substInPrefix.elementAt(temp - 1); + if (substOb instanceof SubstInNode) { + SubstInNode subst = (SubstInNode) substOb; + curExprNode = new SubstInNode(subst.stn, subst.getSubsts(), curExprNode, subst.getInstantiatingModule(), + subst.getInstantiatedModule()); + } else { + APSubstInNode subst = (APSubstInNode) substOb; + curExprNode = new SubstInNode(subst.stn, subst.getSubsts(), curExprNode, subst.getInstantiatingModule(), + subst.getInstantiatedModule()); + + } + temp = temp - 1; + } + ; // while + + /*********************************************************************** + * Will need params as an array. * + ***********************************************************************/ + FormalParamNode[] paramsArray = new FormalParamNode[params.size()]; + for (int i = 0; i < params.size(); i++) { + paramsArray[i] = (FormalParamNode) params.elementAt(i); + } + ; + + /*********************************************************************** + * If there are params, will need a Lambda operator. * + ***********************************************************************/ + OpDefNode newLambda = null; + if (paramsArray.length > 0) { + newLambda = new OpDefNode(S_lambda, // The operator name is "LAMBDA" + UserDefinedOpKind, // The node kind + paramsArray, // the array of formal parameters + false, // localness, which is meaningless + curExprNode, // the body + cm, // the module + null, // the symbol table + sel.selSTN, // syntax-tree node + true, // Is defined. + null); // Source + } + ; + + /*********************************************************************** + * +cal: if expectedArity > 0 * + ***********************************************************************/ + if (expectedArity > 0) { + if (paramsArray.length != expectedArity) { + errors.addError(sel.selSTN.getLocation(), "Expected operator argument with arity " + expectedArity + + " but found one of arity " + paramsArray.length + "."); + return nullOAN; + } + ; + return new OpArgNode(newLambda, sel.selSTN, cm); + } // if (expectedArity > 0) + + /*********************************************************************** + * expectedArity = 0 * +cal: else if Len(params) # Len(allArgs) * + ***********************************************************************/ + if (paramsArray.length != allArgs.size()) { + errors.addError(sel.selSTN.getLocation(), + "Expected " + paramsArray.length + " arguments but found " + allArgs.size() + "."); + return nullOAN; + } + ; + /*********************************************************************** + * +cal: if Len(params) = 0 then * + ***********************************************************************/ + if (paramsArray.length == 0) { + /********************************************************************* + * In this case, we don't have to construct a new LAMBDA or OpAppl * node. + * However, we want to have a new node to which to attach the * SyntaxTree node. + * So, we create an OpApplNode with the dummy * builtin operator $Nop and return + * it. * + *********************************************************************/ + ExprOrOpArgNode[] args = new ExprOrOpArgNode[1]; + args[0] = curExprNode; + OpApplNode ln = new OpApplNode(OP_nop, args, sel.selSTN, cm); + ln.subExpressionOf = subExprOf; + return ln; + } + + /*********************************************************************** + * +cal: else * + ***********************************************************************/ + /*********************************************************************** + * Will need allArgs as an array. * + ***********************************************************************/ + ExprOrOpArgNode[] allArgsArray = new ExprOrOpArgNode[allArgs.size()]; + for (int i = 0; i < allArgs.size(); i++) { + allArgsArray[i] = (ExprOrOpArgNode) allArgs.elementAt(i); + } + ; + + OpApplNode oan = new OpApplNode(newLambda, allArgsArray, sel.selSTN, cm); + oan.subExpressionOf = subExprOf; + return oan; + + } // selectorToNode + + private static String selectorItemToString(Selector sel, int idx) { + String selStr = sel.opNames[idx].toString(); + if (selStr.equals("N_StructOp")) { + selStr = "argument selector " + sel.opsSTN[idx].heirs()[0].heirs()[0].getImage(); + } else if (selStr.equals("N_OpArgs")) { + selStr = "!(...)"; + } + ; + return selStr; + } + + private void reportSelectorError(Selector sel, int idx) { + /*********************************************************************** + * Just a method to perform error reporting when an error is detected * when + * executing selectorToNode. * + ***********************************************************************/ + errors.addError(sel.opsSTN[idx].getLocation(), + "Nonexistent operand specified by `" + selectorItemToString(sel, idx) + "'."); + } + + private boolean isNullSelection(SemanticNode node, Selector sel, int idx) { + /*********************************************************************** + * A method for use in selectorToNode to test if a a null node has * been + * selected. This should happen only if the null node was * created as a result + * of a previous error. If the node is null, then * an error is reported and + * true is returned. * + ***********************************************************************/ + boolean val = (node == null); + if (val) { + errors.addError(sel.opsSTN[idx].getLocation(), "An unexpected null node specified by " + + selectorItemToString(sel, idx) + "'." + "\nThis is probably due to a previous error."); + } + ; + return val; + } + + // Constructor + public Generator(ExternalModuleTable moduleTable, Errors errs) { + nullParam = new FormalParamNode[0]; + nullODN = new OpDefNode(UniqueString.uniqueStringOf("nullODN")); + nullOAN = new OpApplNode(nullODN); + nullOpArg = new OpArgNode(UniqueString.uniqueStringOf("nullOpArg")); + nullLabelNode = new LabelNode(nullOAN); + this.errors = errs; + this.moduleTable = moduleTable; + this.symbolTable = new SymbolTable(moduleTable, errors); + this.excStack = new Stack(); + this.excSpecStack = new Stack(); + } + + public final SymbolTable getSymbolTable() { + return symbolTable; + } + + public final ModuleNode generate(TreeNode treeNode) throws AbortException { + /************************************************************************* + * This is the method called to generate the semantic graph for a module. * + *************************************************************************/ //System.out.println("TRUE = " + symbolTable.resolveSymbol( // UniqueString.uniqueStringOf("TRUE"))) ; //System.out.println("$SetEnumerate = " + symbolTable.resolveSymbol( // UniqueString.uniqueStringOf("$SetEnumerate"))) ; - - if (treeNode.isKind( N_Module )) { - this.context = symbolTable.getContext(); - return this.generateModule(treeNode, null); - } - return null; - } - - private final Context getContext(UniqueString us) { - ModuleNode symbolNode = symbolTable.resolveModule(us); - - if (symbolNode == null) { - return moduleTable.getContext(us); - } - return symbolNode.getContext(); - } - - private final ModuleNode generateModule(TreeNode treeNode, ModuleNode parent) - throws AbortException { - moduleNestingLevel++ ; - TreeNode[] children = treeNode.heirs(); // Array of heirs of the module root - TreeNode[] definitions = null; - // Array of definitions in the module - /********************************************************************* - * Actually, the array of all syntactic nodes that are heirs of the * - * module's body node--that is, definitions, theorems, parameter * - * declarations, etc. * - *********************************************************************/ - TreeNode[] ss = children[0].heirs(); // Array of heirs of the module header - // ss[1] is always the module name - String moduleName = ss[1].getImage(); // the module name - ModuleNode currentModule = new ModuleNode(ss[1].getUS(), context, treeNode); - currentModule.nestingLevel = moduleNestingLevel ; - // if this is an internal module, add its ModuleNode to the end of - // the list of definitions for the parent - if (parent != null) { - parent.appendDef(currentModule); - } - - symbolTable.setModuleNode( currentModule ); - // children[1] == extends - // children[1] is always the Extend's list (even if none) - processExtendsList(children[1].heirs(), currentModule); - - // children[2] == body - // children[2] is always the module body note that this line - // redefines "children" to be the array of definitions in the - // module - definitions = children[2].heirs(); - - // for each declaration, op definition, etc. in the body... - for (int lvi = 0; lvi < definitions.length; lvi++) { - switch (definitions[lvi].getKind()) { - case N_VariableDeclaration : - checkIfInRecursiveSection(definitions[lvi], "A VARIABLE declaration") ; - processVariables(definitions[lvi].heirs(), currentModule); - break; - - case N_ParamDeclaration : - checkIfInRecursiveSection(definitions[lvi], "A declaration") ; - processParameters(definitions[lvi].heirs(), currentModule); - break; - - case N_OperatorDefinition : - processOperator(definitions[lvi], null, currentModule); - break; - - case N_FunctionDefinition : - processFunction(definitions[lvi], null, currentModule); - break; - - case N_ModuleDefinition : + if (treeNode.isKind(N_Module)) { + this.context = symbolTable.getContext(); + return this.generateModule(treeNode, null); + } + return null; + } + + private final Context getContext(UniqueString us) { + ModuleNode symbolNode = symbolTable.resolveModule(us); + + if (symbolNode == null) { + return moduleTable.getContext(us); + } + return symbolNode.getContext(); + } + + private final ModuleNode generateModule(TreeNode treeNode, ModuleNode parent) throws AbortException { + moduleNestingLevel++; + TreeNode[] children = treeNode.heirs(); // Array of heirs of the module root + TreeNode[] definitions = null; + // Array of definitions in the module + /********************************************************************* + * Actually, the array of all syntactic nodes that are heirs of the * module's + * body node--that is, definitions, theorems, parameter * declarations, etc. * + *********************************************************************/ + TreeNode[] ss = children[0].heirs(); // Array of heirs of the module header + // ss[1] is always the module name + String moduleName = ss[1].getImage(); // the module name + ModuleNode currentModule = new ModuleNode(ss[1].getUS(), context, treeNode); + currentModule.nestingLevel = moduleNestingLevel; + // if this is an internal module, add its ModuleNode to the end of + // the list of definitions for the parent + if (parent != null) { + parent.appendDef(currentModule); + } + + symbolTable.setModuleNode(currentModule); + // children[1] == extends + // children[1] is always the Extend's list (even if none) + processExtendsList(children[1].heirs(), currentModule); + + // children[2] == body + // children[2] is always the module body note that this line + // redefines "children" to be the array of definitions in the + // module + definitions = children[2].heirs(); + + // for each declaration, op definition, etc. in the body... + for (int lvi = 0; lvi < definitions.length; lvi++) { + switch (definitions[lvi].getKind()) { + case N_VariableDeclaration: + checkIfInRecursiveSection(definitions[lvi], "A VARIABLE declaration"); + processVariables(definitions[lvi].heirs(), currentModule); + break; + + case N_ParamDeclaration: + checkIfInRecursiveSection(definitions[lvi], "A declaration"); + processParameters(definitions[lvi].heirs(), currentModule); + break; + + case N_OperatorDefinition: + processOperator(definitions[lvi], null, currentModule); + break; + + case N_FunctionDefinition: + processFunction(definitions[lvi], null, currentModule); + break; + + case N_ModuleDefinition: // We now allow INSTANCEs in recursive sections. // checkIfInRecursiveSection(definitions[lvi], "An INSTANCE") ; - processModuleDefinition(definitions[lvi], null, null, currentModule); - break; - - case N_Module : - // Modules can be nested, but inner ones need to keep a - // separate SymbolTable of their own. - checkIfInRecursiveSection(definitions[lvi], "A MODULE ") ; - SymbolTable oldSt = symbolTable; - symbolTable = new SymbolTable(moduleTable, errors, oldSt); - context = new Context(moduleTable, errors); - symbolTable.pushContext(context); - ModuleNode mn = generateModule(definitions[lvi], currentModule); - symbolTable.popContext(); - symbolTable = oldSt; - - // Add the inner module's name to the context of the outer module - symbolTable.addModule(mn.getName(), mn); - - /******************************************************************* - * Append the opDefsInRecursiveSection field of the inner module * - * to that of the current module. * - *******************************************************************/ - for (int i = 0; i < mn.opDefsInRecursiveSection.size(); i++) { - currentModule.opDefsInRecursiveSection.addElement( - mn.opDefsInRecursiveSection.elementAt(i)); - } ; - // System.err.println(mn.getName() + " added to SymbolTable for " + currentModule.getName()); - break; - - case N_Instance : + processModuleDefinition(definitions[lvi], null, null, currentModule); + break; + + case N_Module: + // Modules can be nested, but inner ones need to keep a + // separate SymbolTable of their own. + checkIfInRecursiveSection(definitions[lvi], "A MODULE "); + SymbolTable oldSt = symbolTable; + symbolTable = new SymbolTable(moduleTable, errors, oldSt); + context = new Context(moduleTable, errors); + symbolTable.pushContext(context); + ModuleNode mn = generateModule(definitions[lvi], currentModule); + symbolTable.popContext(); + symbolTable = oldSt; + + // Add the inner module's name to the context of the outer module + symbolTable.addModule(mn.getName(), mn); + + /******************************************************************* + * Append the opDefsInRecursiveSection field of the inner module * to that of + * the current module. * + *******************************************************************/ + for (int i = 0; i < mn.opDefsInRecursiveSection.size(); i++) { + currentModule.opDefsInRecursiveSection.addElement(mn.opDefsInRecursiveSection.elementAt(i)); + } + ; + // System.err.println(mn.getName() + " added to SymbolTable for " + + // currentModule.getName()); + break; + + case N_Instance: // We now allow INSTANCEs in recursive sections. // checkIfInRecursiveSection(definitions[lvi], "An INSTANCE") ; - generateInstance(definitions[lvi], currentModule, true); - break; - - case N_Proof : - checkIfInRecursiveSection(definitions[lvi], "A proof") ; - break; - - case N_Theorem : - checkIfInRecursiveSection(definitions[lvi], "A THEOREM") ; - processTheorem(definitions[lvi], currentModule); - break; - - case N_Assumption : - checkIfInRecursiveSection(definitions[lvi], "An ASSUME"); - processAssumption(definitions[lvi], currentModule); - break; - - case N_UseOrHide : - checkIfInRecursiveSection(definitions[lvi], "A USE or HIDE"); - UseOrHideNode uohn = generateUseOrHide(definitions[lvi], - currentModule) ; - uohn.factCheck(); - // Added 4 Mar 2009. - if (uohn.facts.length + uohn.defs.length == 0) { - errors.addError(definitions[lvi].getLocation(), - "Empty USE or HIDE statement."); - }; - currentModule.addTopLevel(uohn) ; - break; - - case SEPARATOR : - /******************************************************************* - * This code was originally * - * * - * case 35 * - * // Intended to handle "---------". Kludge for a parser bug. * - * * - * I have no idea why 35 was used instead of SEPARATOR, and what * - * kind of parser bug mentioned was encountered. The original * - * code did not import parser.TLAplusParserConstants where * - * SEPARATOR was used, so perhaps there was some reason why this * - * class could not be imported. * - *******************************************************************/ - break; - - case N_Recursive : - processRecursive(definitions[lvi], currentModule); - break; - default : - errors.addAbort( - definitions[ lvi ].getLocation(), - "Internal error: Syntax node of kind " + - definitions[ lvi ].getKind() + " unsupported " + - definitions[ lvi ].getImage(), true); - break; - } - } - - checkForUndefinedRecursiveOps(currentModule) ; - -Vector vec = currentModule.recursiveOpDefNodes ; -for (int i = 0 ; i < vec.size() ; i++) { -OpDefNode node = (OpDefNode) vec.elementAt(i); + generateInstance(definitions[lvi], currentModule, true); + break; + + case N_Proof: + checkIfInRecursiveSection(definitions[lvi], "A proof"); + break; + + case N_Theorem: + checkIfInRecursiveSection(definitions[lvi], "A THEOREM"); + processTheorem(definitions[lvi], currentModule); + break; + + case N_Assumption: + checkIfInRecursiveSection(definitions[lvi], "An ASSUME"); + processAssumption(definitions[lvi], currentModule); + break; + + case N_UseOrHide: + checkIfInRecursiveSection(definitions[lvi], "A USE or HIDE"); + UseOrHideNode uohn = generateUseOrHide(definitions[lvi], currentModule); + uohn.factCheck(); + // Added 4 Mar 2009. + if (uohn.facts.length + uohn.defs.length == 0) { + errors.addError(definitions[lvi].getLocation(), "Empty USE or HIDE statement."); + } + ; + currentModule.addTopLevel(uohn); + break; + + case SEPARATOR: + /******************************************************************* + * This code was originally * * case 35 * // Intended to handle "---------". + * Kludge for a parser bug. * * I have no idea why 35 was used instead of + * SEPARATOR, and what * kind of parser bug mentioned was encountered. The + * original * code did not import parser.TLAplusParserConstants where * + * SEPARATOR was used, so perhaps there was some reason why this * class could + * not be imported. * + *******************************************************************/ + break; + + case N_Recursive: + processRecursive(definitions[lvi], currentModule); + break; + default: + errors.addAbort(definitions[lvi].getLocation(), "Internal error: Syntax node of kind " + + definitions[lvi].getKind() + " unsupported " + definitions[lvi].getImage(), true); + break; + } + } + + checkForUndefinedRecursiveOps(currentModule); + + Vector vec = currentModule.recursiveOpDefNodes; + for (int i = 0; i < vec.size(); i++) { + OpDefNode node = (OpDefNode) vec.elementAt(i); // System.out.println("symbol " + node.getName() + ": recSect = " // + node.recursiveSection + ", inRecSect = " // + node.inRecursiveSection + ", letInLevel = " // + node.letInLevel); - } ; - - moduleNestingLevel -- ; - return currentModule; - - } // generateModule - - // This method must be extended so that the Extends list hangs off of - // the ModuleNode to support a getExtends() method that might reasonably - // be in the API. - private final void processExtendsList(TreeNode treeNodes[], ModuleNode cm) - throws AbortException{ - Vector extendeeVector = new Vector(2); - - if (treeNodes != null) { - // module names in the EXTENDS list are separated by commas; hence incr by 2 - for (int lvi = 1; lvi < treeNodes.length; lvi += 2) { - // Try to find the ModuleNode for the module being EXTENDED in the symbolTable - UniqueString extendeeID = treeNodes[lvi].getUS(); - ModuleNode extendee = symbolTable.resolveModule(extendeeID); - - // It must be an external module if it isn't in the symbolTable; - // try to find it in moduleTable (it cannot be in both places) - if (extendee == null) { - extendee = moduleTable.getModuleNode(extendeeID); - if (extendee == null) { - errors.addAbort(treeNodes[lvi].getLocation(), - "Could not find module " + extendeeID, - false); - } - } - - extendeeVector.addElement(extendee); - - Context context = this.getContext(extendeeID); - if (context != null) { - symbolTable.getContext().mergeExtendContext(context); - } - else { - errors.addError(treeNodes[lvi].getLocation(), - "Couldn't find context for module `" + extendeeID - + "'."); - } - - // copy nonlocal Assumes and Theorems - cm.copyAssumes(extendee); - cm.copyTheorems(extendee); - cm.copyTopLevel(extendee); - } - } - cm.createExtendeeArray(extendeeVector); - } // processExtendsList - - // This method must be extended so that the variable declarations list - // hangs off of the module node to support the getVariableDecls method - // of the API. - private final void processVariables(TreeNode treeNodes[], ModuleNode cm) { - for (int lvi = 1; lvi < treeNodes.length; lvi += 2) { - UniqueString us = treeNodes[ lvi ].getUS(); - - if (us == S_at) { - errors.addError(treeNodes[lvi].getLocation(), - "Attempted to declare '@' as a variable."); - } - - // The next line has its side-effects in the constructor; in particular, - // the new OpDeclNode is placed in the symbolTble there. - new OpDeclNode(us, VariableDeclKind, 1, 0, cm, symbolTable, treeNodes[lvi]); - } - } - - - - private final OpDeclNode buildParameter(TreeNode treeNode, - int declKind, - int declLevel, - ModuleNode cm, - boolean declare) { - /*********************************************************************** - * This method was originally called only by the processParameters * - * method to build an OpDeclNode for a module-level CONSTANT * - * declaration. For a declaration "CONSTANT foo(_), _+_" it would be * - * called twice, once with treeNode the node for "foo(_)", and once * - * with it equal to the node for "_+_". The OpDeclNode is not returned * - * because its constructor adds the node to symbolTable. * - * * - * It was modified by LL on 22 Mar 2007 for use in the generateNewSymb * - * method as well. The modifications consisted of returning the * - * OpDeclNode and adding arguments to specify the kind and level of * - * the OpDeclNode. * - * * - * Further modified by LL on 6 Apr 2007 for use in the * - * processRecursive method as well. The modification consisted of * - * adding the declare argument. If true, it calls the OpDeclNode * - * constructor with symbol table symbolTable, so the symbol is added * - * to the current symbol table. If false, it calls the constructor * - * with a null symbol table, so nothing is declared. The * - * processRecursive method just uses the name and arity from the * - * returned OpDeclNode. * - ***********************************************************************/ - UniqueString us = null; - int arity = 0; - TreeNode[] ss = treeNode.heirs(); - - if ( treeNode.isKind( N_IdentDecl ) ) { - us = ss[0].getUS(); - arity = (ss.length - 1) / 2; - } - else if ( treeNode.isKind( N_PrefixDecl ) ) { - us = ss[0].getUS(); - arity = 1; - } - else if ( treeNode.isKind( N_InfixDecl ) ) { - us = ss[1].getUS(); - arity = 2; - } - else if ( treeNode.isKind( N_PostfixDecl ) ) { - us = ss[1].getUS(); - arity = 1; - } - else { - errors.addError(treeNode.getLocation(), - "Unknown parameter declaration `" + treeNode.getUS() - + "'."); - } + } + ; + + moduleNestingLevel--; + return currentModule; + + } // generateModule + + // This method must be extended so that the Extends list hangs off of + // the ModuleNode to support a getExtends() method that might reasonably + // be in the API. + private final void processExtendsList(TreeNode treeNodes[], ModuleNode cm) throws AbortException { + Vector extendeeVector = new Vector(2); + + if (treeNodes != null) { + // module names in the EXTENDS list are separated by commas; hence incr by 2 + for (int lvi = 1; lvi < treeNodes.length; lvi += 2) { + // Try to find the ModuleNode for the module being EXTENDED in the symbolTable + UniqueString extendeeID = treeNodes[lvi].getUS(); + ModuleNode extendee = symbolTable.resolveModule(extendeeID); + + // It must be an external module if it isn't in the symbolTable; + // try to find it in moduleTable (it cannot be in both places) + if (extendee == null) { + extendee = moduleTable.getModuleNode(extendeeID); + if (extendee == null) { + errors.addAbort(treeNodes[lvi].getLocation(), "Could not find module " + extendeeID, false); + } + } + + extendeeVector.addElement(extendee); + + Context context = this.getContext(extendeeID); + if (context != null) { + symbolTable.getContext().mergeExtendContext(context); + } else { + errors.addError(treeNodes[lvi].getLocation(), + "Couldn't find context for module `" + extendeeID + "'."); + } + + // copy nonlocal Assumes and Theorems + cm.copyAssumes(extendee); + cm.copyTheorems(extendee); + cm.copyTopLevel(extendee); + } + } + cm.createExtendeeArray(extendeeVector); + } // processExtendsList + + // This method must be extended so that the variable declarations list + // hangs off of the module node to support the getVariableDecls method + // of the API. + private final void processVariables(TreeNode treeNodes[], ModuleNode cm) { + for (int lvi = 1; lvi < treeNodes.length; lvi += 2) { + UniqueString us = treeNodes[lvi].getUS(); + + if (us == S_at) { + errors.addError(treeNodes[lvi].getLocation(), "Attempted to declare '@' as a variable."); + } + + // The next line has its side-effects in the constructor; in particular, + // the new OpDeclNode is placed in the symbolTble there. + new OpDeclNode(us, VariableDeclKind, 1, 0, cm, symbolTable, treeNodes[lvi]); + } + } + + private final OpDeclNode buildParameter(TreeNode treeNode, int declKind, int declLevel, ModuleNode cm, + boolean declare) { + /*********************************************************************** + * This method was originally called only by the processParameters * method to + * build an OpDeclNode for a module-level CONSTANT * declaration. For a + * declaration "CONSTANT foo(_), _+_" it would be * called twice, once with + * treeNode the node for "foo(_)", and once * with it equal to the node for + * "_+_". The OpDeclNode is not returned * because its constructor adds the node + * to symbolTable. * * It was modified by LL on 22 Mar 2007 for use in the + * generateNewSymb * method as well. The modifications consisted of returning + * the * OpDeclNode and adding arguments to specify the kind and level of * the + * OpDeclNode. * * Further modified by LL on 6 Apr 2007 for use in the * + * processRecursive method as well. The modification consisted of * adding the + * declare argument. If true, it calls the OpDeclNode * constructor with symbol + * table symbolTable, so the symbol is added * to the current symbol table. If + * false, it calls the constructor * with a null symbol table, so nothing is + * declared. The * processRecursive method just uses the name and arity from the + * * returned OpDeclNode. * + ***********************************************************************/ + UniqueString us = null; + int arity = 0; + TreeNode[] ss = treeNode.heirs(); + + if (treeNode.isKind(N_IdentDecl)) { + us = ss[0].getUS(); + arity = (ss.length - 1) / 2; + } else if (treeNode.isKind(N_PrefixDecl)) { + us = ss[0].getUS(); + arity = 1; + } else if (treeNode.isKind(N_InfixDecl)) { + us = ss[1].getUS(); + arity = 2; + } else if (treeNode.isKind(N_PostfixDecl)) { + us = ss[1].getUS(); + arity = 1; + } else { + errors.addError(treeNode.getLocation(), "Unknown parameter declaration `" + treeNode.getUS() + "'."); + } // SymbolNode symbolNode = - SymbolTable st = null ; - if (declare) {st = symbolTable;}; - - /********************************************************************** - * Changed us to Operators.resolveSynonym(us) in the following so * - * constant declarations work with any synonym for an operator--e.g., * - * with both (+) and \oplus. * - **********************************************************************/ - return new OpDeclNode(Operators.resolveSynonym(us), declKind, declLevel, arity, cm, - st, treeNode); - } - - private final void processParameters(TreeNode treeNodes[], ModuleNode cm) { - for (int lvi = 1; lvi < treeNodes.length; lvi +=2 ) { - if (treeNodes[lvi].getUS() == S_at) { - errors.addError(treeNodes[lvi].getLocation(), - "Attempted to declare '@' as a constant."); - } ; - OpDeclNode odn = - buildParameter(treeNodes[lvi], ConstantDeclKind, ConstantLevel, - cm, true); - /******************************************************************* - * We throw away the OpDeclNode because, when it was constructed, * - * it was added to symbolTable, whose top context should be the * - * top-level context of the module. * - *******************************************************************/ - } - } - - /** - * Processes the LHS of an operator definition, in several ways, - * depending on whether it is a prefix, infix, postfix, or a - * parameter-free or parameter-ful function-notation operator. Also - * creates context entries for the operator and its parameters. - */ - /************************************************************************ - * Processes a syntactic node of type N_OperatorDefinition. It is * - * called by generateModule (with defs = null) when processing an * - * outer-level definition and by processLetIn (with defs /= null) when * - * processing a LET definition. It creates an OpDef node and puts it * - * in ModuleNode cm's set of definitions. * - ************************************************************************/ - private final void processOperator(TreeNode treeNode, Vector defs, - ModuleNode cm) throws AbortException { - TreeNode syntaxTreeNode = treeNode; - UniqueString name = null; - int arity = 0; - boolean local = syntaxTreeNode.zero() != null; - TreeNode [] children = syntaxTreeNode.one(); - TreeNode [] ss = children[0].heirs(); - FormalParamNode [] params = null; - Context ctxt = new Context(moduleTable, errors); - boolean isRecursive = false ; - /********************************************************************* - * Will be set to true if this operator was declared in a RECURSIVE * - * statement. * - *********************************************************************/ - // New context needed because parameter symbols may have to be - // added if the operator being defined takes params - symbolTable.pushContext( ctxt ); - - // If the operator is an identifier (possibly with params), as - // opposed to a prefix, infix, or postfix symbol - if ( children[ 0 ].isKind( N_IdentLHS ) ) { - if ( ss.length > 2 ) { - // If the operator has arguments - params = new FormalParamNode[ (ss.length-2) / 2 ]; - for ( int lvi = 2; lvi < ss.length; lvi += 2 ) { - TreeNode sss[] = ss[ lvi ].heirs(); - if ( ss[ lvi ].isKind( N_IdentDecl ) ) { - // parameter is simple identifier - name = sss[0].getUS(); - arity = (sss.length - 1 )/ 2; - } - else if ( ss[ lvi ].isKind( N_InfixDecl ) ) { - // parameter is infix operator - // Call of Operators.resolveSynonym added by LL on 27 Mar 2013 - name = sss[1].getUS(); - - // Following added by LL on 27 Mar 2013 - // to handle parameters like _(+)_ - name = Operators.resolveSynonym(name); - - arity = 2; - } - else if ( ss[ lvi ].isKind( N_PrefixDecl ) ) { - // parameter is prefix operator - name = sss[0].getUS(); - arity = 1; - } - else { - // parameter must be postfix operator - // SZ Jul 13, 2009: added the message to the assert - if(! ss[ lvi ].isKind( N_PostfixDecl )) - { - throw new WrongInvocationException(MP.getMessage(EC.TLC_PARAMETER_MUST_BE_POSTFIX)); - } - name = sss[1].getUS(); - arity = 1; - } - params[ (lvi-2)/2 ] = - new FormalParamNode(name, arity, ss[lvi], symbolTable, cm); - } - } - else { - // The operator has no arguments - params = new FormalParamNode[0]; - } - name = ss[0].getUS(); - } - else if (children[ 0 ].isKind(N_PrefixLHS)) { // operator is a prefix operator - // Process the parameter - params = new FormalParamNode[1]; - params[0] = new FormalParamNode(ss[1].getUS(), 0, ss[1], symbolTable, cm); - - // Process the operator - name = Operators.resolveSynonym(ss[0].getUS()); - } - else if (children[ 0 ].isKind(N_InfixLHS)) { // operator is an infix operator - params = new FormalParamNode[2]; - // Process the first param - params[0] = new FormalParamNode(ss[0].getUS(), 0, ss[0], symbolTable, cm); - - // Process the second param - params[1] = new FormalParamNode(ss[2].getUS(), 0, ss[2], symbolTable, cm); - - // Process the operator - name = Operators.resolveSynonym(ss[1].getUS()); - } - else if (children[ 0 ].isKind(N_PostfixLHS)) { // operator is a postfix operator - // Process the parameter - params = new FormalParamNode[1]; - params[0] = new FormalParamNode(ss[0].getUS(), 0, ss[0], symbolTable, cm); - - // Process the operator - name = Operators.resolveSynonym(ss[1].getUS()); - } - else { - errors.addError(children[0].getLocation(), - "Unknown parameter declaration `" + - children[0].getUS() + "'."); - } - - /*********************************************************************** - * The following code added by LL on 1 April 2007 to handle * - * recursively defined operators. * - ***********************************************************************/ - OpDefNode odn = null ; - /*********************************************************************** - * Check if this operator has already been declared or defined. If * - * so, if it was declared in a RECURSIVE statement at this let/in * - * level and not yet defined, then set the OpDefNode's fields * - * appropriately, otherwise report an error. * - ***********************************************************************/ - SymbolNode symbolNode = symbolTable.resolveSymbol(name) ; - - if (symbolNode != null) { - /********************************************************************* - * The symbol has already been defined or declared. Check if it was * - * declared in a RECURSIVE statement. * - *********************************************************************/ - if (symbolNode instanceof OpDefNode) {odn = (OpDefNode) symbolNode;}; - if ( (odn != null) - && odn.inRecursive - && (! odn.isDefined) ) { - if (odn.letInLevel == curLevel) { - isRecursive = true; - - /***************************************************************** - * Check that the parameters of the definition match the ones in * - * the RECURSIVE declaration. * - *****************************************************************/ - boolean paramsMatch = (odn.getParams().length == params.length) ; - if (paramsMatch) { - for (int i = 0 ; i < params.length ; i++) { - paramsMatch = (params[i].getArity() == 0) ; - }; - }; // if - if (!paramsMatch) { - errors.addError(treeNode.getLocation(), - "Definition of " + odn.getName() + - " has different arity than " + - "its RECURSIVE declaration."); - }; - - odn.setParams(params) ; - /*************************************************************** - * Here's where the params field of the OpDefNode is set for an * - * operator declared in a RECURSIVE statement. * - ***************************************************************/ - } // if (odn.letInLevel == curLevel) - else {errors.addError(treeNode.getLocation(), - "Recursive operator " + name.toString() + - " defined at wrong LET/IN level.") ; - odn = null ; - } // else - } // if (odn != null) ... - else { errors.addError(treeNode.getLocation(), - "Operator " + name.toString() + - " already defined or declared.") ; - } - } // if (symbolNode != null) - - pushLS() ; - /********************************************************************* - * Start fresh processing of labels. * - *********************************************************************/ - // Generate expression that is the body of the operator - ExprNode exp = generateExpression( children[2], cm ); - - // Restore old context, popping off the context containing the parameters - symbolTable.popContext(); - - - if (isRecursive) {endOpDefNode(odn, exp, syntaxTreeNode) ; } - else { - // Create OpDefNode using symbolTable whose top context contains the - // parameter symbols - /******************************************************************** - * This comment appears to be incorrect, because it looks like the * - * parameter symbols were just popped off symbolTable. * - ********************************************************************/ - odn = new OpDefNode(name, UserDefinedOpKind, params, local, - exp, cm, symbolTable, syntaxTreeNode, true, null); - symbolNode = odn ; - /******************************************************************* - * Keeping symbolNode for historical reasons--I'm too lazy to * - * rearrange the code to get rid of the redundant identifier. * - *******************************************************************/ - - setOpDefNodeRecursionFields(odn, cm) ; - - } // else - - Hashtable ht = popLabelNodeSet() ; - /********************************************************************* - * Succeed or fail, we must execute popLabelNodeSet to match the * - * previous pushLS. * - *********************************************************************/ - if (odn != null) {odn.setLabels(ht);} ; - /********************************************************************* - * If there was no error, then odn is an OpDefNode and we must set * - * it labels field. * - *********************************************************************/ - cm.appendDef(symbolNode) ; - /******************************************************************* - * Add the new OpDefNode to the current module's set of * - * definitions. * - *******************************************************************/ - // defs is non-null iff this definition is in the Let part of a - // Let-In expression - if (defs != null) defs.addElement(symbolNode); - } // processOperator - - private final void processQuantBoundArgs( - TreeNode[]treeNodeA, // node whose children include - // the bounded quants - int offset, // nodes to skip to get to first - // "quantifier" - FormalParamNode[][] odna, - boolean []bt, // set to true if arg is a tuple; - // otherwise false - ExprNode[] ena, - ModuleNode cm) - /*********************************************************************** - * Despite the incoherent and/or incorrect comments, here's what I * - * think is going on: * - * * - * - odna, ena, and bt have the same length N. * - * * - * - treeNodeA is an array of nodes containing a subsequence of * - * N_QuantBound nodes of length N that starts at * - * treeNodeA[offset]. * - * * - * - odna, ena, and bt are set to the arguments for the OpApplNode * - * constructor that produces an OpApplNode for the operator * - * having this sequence of N_QuantBound nodes. * - ***********************************************************************/ - - throws AbortException { - // For each quantifier, evaluate the bound in the context of the - // current symbol table, i.e. before the quantified variables are - // added to the new context, since the quantified vars may not - // appear in the bounds. - for (int lvi = 0; lvi < bt.length; lvi++ ) { - // Make ss point to each N_QuantBound node in turn. - TreeNode[] ss = treeNodeA[offset + 2 * lvi].heirs(); - // the last element in ss is expression for the quantifier bound - ena[lvi] = generateExpression( ss[ ss.length - 1 ], cm ); - } - - // Now for each quantifier, process the variable names - for (int lvi = 0; lvi < bt.length; lvi++ ) { - TreeNode treeNode = treeNodeA[offset + 2 * lvi]; - - // The variable bound to the "quantifier" - TreeNode[] ss = treeNode.heirs(); - - if (ss[0].isKind(N_IdentifierTuple)) { // three elements only, go into node - bt[lvi] = true; - TreeNode[] sss = ss[0].heirs(); - odna[lvi] = new FormalParamNode[ sss.length / 2 ]; - - for (int lvj = 0; lvj < sss.length / 2; lvj++ ) { - odna[lvi][lvj] = new FormalParamNode( - sss[ 2*lvj+1 ].getUS(), 0, sss[ 2*lvj+1 ], - symbolTable, cm) ; - } - } - else { // gotta be N_Identifier - bt[lvi] = false; - odna[lvi] = new FormalParamNode[ (ss.length - 1)/2 ]; - - for ( int lvj = 0; lvj < (ss.length - 1)/2 ; lvj++ ) { - odna[lvi][lvj] = new FormalParamNode( - ss[ 2*lvj ].getUS(), 0, ss[ 2*lvj ], - symbolTable, cm); - } - } - } - } - - // Process a function definition - private final void processFunction(TreeNode treeNode, Vector defs, ModuleNode cm) - throws AbortException { - TreeNode syntaxTreeNode = treeNode; - boolean local = syntaxTreeNode.zero() != null; - TreeNode[] ss = syntaxTreeNode.one(); - // Heirs to N_FunctionDefinition node - int ql = (ss.length-4)/2; - // number of QuantBound's - OpApplNode oan; - OpDefNode odn = null; - FormalParamNode[][] quants = new FormalParamNode[ql][0]; - FormalParamNode[] fcnDeclForRecursion = new FormalParamNode[1]; - /********************************************************************* - * This is an array of length 1 instead of just an OpDeclNode * - * because it is needed in such an array to pass as an argument to * - * new OpApplNode. * - *********************************************************************/ - boolean [] tuples = new boolean[ql]; - ExprNode [] domains = new ExprNode[ql]; - ExprNode [] lhs = new ExprNode[1]; - Context newContext = new Context(moduleTable, errors); - boolean isRecursive = false ; - /********************************************************************* - * Will be set to true if this operator was declared in a RECURSIVE * - * statement. * - *********************************************************************/ - - // Fill arrays with quantifier-related information; must be called - // in scope of *new* context, since it adds parameter symbols to - // the context. - symbolTable.pushContext(newContext); - processQuantBoundArgs( ss, 2, quants, tuples, domains, cm ); - - UniqueString name = ss[0].getUS() ; - SymbolNode symbolNode = symbolTable.resolveSymbol(name) ; - - - // This is in anticipation of the possibility that the function is - // recursive. We are creating a new bound symbol of the same name - // as the function to stand in the body for the function. The - // arity of the bound symbol is 0 because a function formally has - // arity 0 as an operator, even if it has several arguments as a - // function. - /*********************************************************************** - * If the function name is already defined (which should happen only * - * if it was declared in a RECURSIVE statement, then there is no need * - * to add this OpDeclNode to the symbol table. As near as I can tell * - * (which might not be near enough), this OpDeclNode's entry in the * - * symbol table is needed only so the name will be declared in case * - * of a recursive call. The check for whether a function call in the * - * body actually is recursive is made by calling the recursionCheck * - * method of the Function subclass to see if the OpApplNode is in the * - * Function object's funcStack. * - ***********************************************************************/ - SymbolTable st = null ; - if (symbolNode == null) {st = symbolTable; } ; - fcnDeclForRecursion[0] = - new FormalParamNode(name, 0, treeNode, st, cm); - symbolTable.popContext(); - - // Create OpApplNode to hold the function body; type is assumed to - // be non-recursive function (OP_nrfs); if the body is recursive, - // this will be discovered during generateExpression() for the - // body - oan = new OpApplNode(OP_nrfs, fcnDeclForRecursion, new ExprNode[0], - quants, tuples, domains, syntaxTreeNode, cm); - // constructor 5 - - /*********************************************************************** - * The following code added by LL on 19 April 2007 to handle * - * recursively defined operators. * - ***********************************************************************/ - if (symbolNode == null) { - odn = new OpDefNode(ss[0].getUS(), UserDefinedOpKind, nullParam, local, - oan, cm, symbolTable, syntaxTreeNode, true, null); - setOpDefNodeRecursionFields(odn, cm) ; - } - else - { - /********************************************************************* - * The symbol has already been defined or declared. Check if it was * - * declared in a RECURSIVE statement. * - *********************************************************************/ - if (symbolNode instanceof OpDefNode) {odn = (OpDefNode) symbolNode;}; - if ( (odn != null) - && odn.inRecursive - && (! odn.isDefined) ) { - if (odn.letInLevel == curLevel) { - isRecursive = true; - /***************************************************************** - * Check that the RECURSIVE declaration had no paramters. * - *****************************************************************/ - if (odn.getArity() == 0) { - endOpDefNode(odn, oan, syntaxTreeNode) ; - } - else { - /************************************************************* - * RECURSIVE declaration had parameters. * - *************************************************************/ - errors.addError(treeNode.getLocation(), - "Function " + odn.getName() + - " has operator arguments in " + - "its RECURSIVE declaration."); - } ; - } // if (odn.letInLevel == curLevel) - else {errors.addError(treeNode.getLocation(), - "Recursive function " + name.toString() + - " defined at wrong LET/IN level.") ; - odn = null ; - } // else - } // if (odn != null) ... - else { errors.addError(treeNode.getLocation(), - "Function name `" + name.toString() + - "' already defined or declared.") ; - } - } // else (symbolNode != null) - - // Create OpDefNode to hold the function definition, including - // reference to the function body ("oan") - if (odn != null) { - /********************************************************************* - * There was no error, so the OpDefNode was created. * - *********************************************************************/ - cm.appendDef(odn); - - // defs is non-null iff this function definition is in the Let - // part of a Let-In expression. If so, then we have to accumulate - // these defs in a vector. - if (defs != null) defs.addElement(odn); - - // Function body must be processed in the scope of the new - // context, including the parms - symbolTable.pushContext( newContext ); - - }; // if (odn != null) - - // Keep stack of nested function defs to enable detection of recursion - functions.push( ss[0].getUS(), oan ); - - - // Create semantic graph function body in the inner context including parameters - pushLS() ; - pushFormalParams(flattenParams(quants)) ; - /********************************************************************* - * Push a new empty label set onto LS, and push the function * - * definition's formal parameters onto Last(LS).paramSeq for * - * processing the function body. * - *********************************************************************/ - lhs[0] = generateExpression( ss[ss.length - 1], cm ); - popFormalParams() ; - /********************************************************************* - * Pop the formal params from Last(LS).paramSeq, which should now be * - * empty. This isn't really necessary, since we're going to pop the * - * LS stack below, but I hate to have a push without a pop. * - *********************************************************************/ - Hashtable ht = popLabelNodeSet() ; - /********************************************************************* - * The matching pop for the pushLS above. * - *********************************************************************/ - if (odn != null) { odn.setLabels(ht); } ; - /********************************************************************* - * If there was no error and we created an OpDefNode, then set its * - * label set. * - *********************************************************************/ - functions.pop(); - - oan.setArgs( lhs ); - /*********************************************************************** - * Test for odn != null added 2 Jul 2009 to avoid bug that caused * - * popping of empty symbol table when the function name was * - * already declared to be a variable. * - ***********************************************************************/ - if (odn != null){ - // Restore old context - symbolTable.popContext(); - } - - // if the function body turned out to be non-recursive, then we - // should null-out the fcnDefForRecursion ref put in place above, - // since it is unnecessary for a nonrecursive func - if (oan.getOperator().getName() == OP_nrfs) { - oan.makeNonRecursive(); - } - } // end processFunction() - - private final ExprNode processLetIn(TreeNode treeNode, TreeNode[] children, - ModuleNode cm) - throws AbortException { - TreeNode[] syntaxTreeNode = children[1].heirs(); // extract LetDefinitions - Vector defVec = new Vector(4); - Vector instVec = new Vector(1); - - Context letCtxt = new Context(moduleTable, errors) ; - symbolTable.pushContext(letCtxt); - /********************************************************************* - * Create a new sub-Context for the IN expression, containing the * - * LET definitions * - *********************************************************************/ - - /*********************************************************************** - * Increment curLevel. * - ***********************************************************************/ - if (curLevel < MaxLetInLevel) { curLevel++ ; } - else { errors.addAbort(treeNode.getLocation(), - "LETs nested more than " + MaxLetInLevel + - " deep.") ; - } ; - unresolvedCnt[curLevel] = 0 ; - /********************************************************************* - * Will not have been initialized if we haven't yet reached this * - * LET/IN nesting depth. * - *********************************************************************/ - - for (int lvi = 0; lvi < syntaxTreeNode.length; lvi++) { - /********************************************************************* - * Note: LL changed from an "if" and a sequence of "elseif"s to a * - * switch statement on 7 Apr 2007 when adding the N_Recursive case. * - *********************************************************************/ - switch (syntaxTreeNode[lvi].getKind()) { - case N_OperatorDefinition : - processOperator(syntaxTreeNode[ lvi ], defVec, cm); - break ; - - case N_FunctionDefinition : - processFunction(syntaxTreeNode[ lvi ], defVec, cm ); - break ; - - case N_ModuleDefinition : - processModuleDefinition(syntaxTreeNode[lvi], defVec, instVec, cm); - break ; - - case N_Recursive : - processRecursive(syntaxTreeNode[ lvi ], cm) ; - break ; - - default : - errors.addAbort(syntaxTreeNode[ lvi ].getLocation(), - "Internal error: found unexpected syntax " + - "tree node in LET.") ; - } // switch - } // for - - checkForUndefinedRecursiveOps(cm) ; - - /*********************************************************************** - * Decrement curLevel. * - ***********************************************************************/ - curLevel -- ; - if (curLevel < 0) { curLevel = 0;} ; - - - ExprNode body = generateExpression(children[3], cm); - - /*********************************************************************** - * Convert from Vector to array of SymbolNode, whose elements may be * - * OpDefNode or ThmOrAssumpDefNode objects. * - ***********************************************************************/ - SymbolNode[] opDefs = new SymbolNode[defVec.size()]; - for (int i = 0; i < opDefs.length; i++) { - opDefs[i] = (SymbolNode)defVec.elementAt(i); - } - - InstanceNode[] insts = new InstanceNode[instVec.size()]; - for (int i = 0; i < insts.length; i++) { - insts[i] = (InstanceNode)instVec.elementAt(i); - } - LetInNode letIn = new LetInNode(treeNode, opDefs, insts, body, letCtxt); - symbolTable.popContext(); - return letIn; - } // end processLetIn - - private final ExprNode generateExpression(TreeNode treeNode, ModuleNode cm) - throws AbortException { - /*********************************************************************** - * Must return an ExprNode that represents an expression, and not a * - * labeled ASSUME/PROVE * - ***********************************************************************/ - return generateExpressionOrLAP(treeNode, cm, false) ; - } - - private final ExprNode generateExpressionOrLAP( - TreeNode treeNode, ModuleNode cm, boolean allowLabeledAP) - /*********************************************************************** - * Can return a LabelNode labeling an ASSUME/PROVE iff allowLabeledAP * - * = true. Otherwise, it can return only an ExprNode that represents * - * an expression. * - ***********************************************************************/ - throws AbortException { - TreeNode[] children = treeNode.heirs(); - TreeNode[] ss = null; // grandchildren - SymbolNode opn = null; - TreeNode op = null; - GenID genID; - ExprOrOpArgNode[] sns; // a ExprNode list used for arguments - - switch (treeNode.getKind()) { - - case N_Real : - return new DecimalNode(children[0].getImage(),children[2].getImage(), treeNode); - - case N_Number : - return new NumeralNode( children[0].getImage(), treeNode); - - case N_String : - return new StringNode( treeNode, true); - - case N_ParenExpr : - return generateExpression( children[1], cm ); - - case N_InfixExpr : - genID = generateGenID(children[1], cm); - - sns = new ExprOrOpArgNode[2]; - opn = symbolTable.resolveSymbol(Operators.resolveSynonym(genID.getCompoundIDUS())); - if ( opn == null ) { - errors.addError(treeNode.getLocation(), - "Couldn't resolve infix operator symbol `" + - genID.getCompoundIDUS() + "'." ); - return null; - } - - sns[0] = generateExpression( children[0], cm ); - sns[1] = generateExpression( children[2], cm ); - return new OpApplNode(opn, sns, treeNode, cm); - - case N_PrefixExpr : - // 1 get gen operator node - ss = children[0].heirs(); - - // 2 get rightmost part of the possibly compound Op itself; - op = ss[1]; - genID = generateGenID(children[0], cm, true); - sns = new ExprOrOpArgNode[1]; - opn = symbolTable.resolveSymbol(Operators.resolveSynonym(genID.getCompoundIDUS())); - - if ( opn == null ) { - errors.addError(treeNode.getLocation(), - "Couldn't resolve prefix operator symbol `" + - genID.getCompoundIDUS() + "'." ); - return null; - } - - sns[0] = generateExpression( children[1], cm ); - return new OpApplNode(opn, sns, treeNode, cm); // constructor 2 - - case N_PostfixExpr : - genID = generateGenID(children[1], cm); - - sns = new ExprNode[1]; - opn = symbolTable.resolveSymbol(Operators.resolveSynonym(genID.getCompoundIDUS())); - if ( opn == null ) { - errors.addError(treeNode.getLocation(), "Couldn't resolve postfix " + - "operator symbol `" + genID.getCompoundIDUS() + "'."); - return null; - } - - sns[0] = generateExpression( children[0], cm ); - return new OpApplNode(opn, sns, treeNode, cm); // constructor 2 - - case N_Times : // or cartesian product - sns = new ExprNode[ (children.length+1)/2 ]; - - for (int lvi = 0; lvi < sns.length; lvi ++ ) { - sns[ lvi ] = generateExpression( children[ 2 * lvi ], cm ); - } - return new OpApplNode(OP_cp, sns, treeNode, cm); // constructor 3 - - case N_SetEnumerate : - int size = (children.length-1) / 2; - sns = new ExprNode [size]; - for ( int lvi = 0; lvi < size; lvi++ ) { - sns[lvi] = generateExpression( children[ 2 * lvi + 1 ], cm ); - } - return new OpApplNode(OP_se, sns, treeNode, cm); // constructor 3 - - case N_GeneralId : - // This is a zero-ary operator; it should show in the syntax - // tree as an OpApp, but it does not. Hence, an OpApplication - // node with zero arguments must be constructed for it - // if we get here, the GeneralID really is an OpApplication with 0 - // primary arguments, but with any number of prefix arguments - - // process the generalized identifier, complete with its - // embedded argument lists (if any) - - /********************************************************************* - * If the N_GeneralId represents the identifier "@", then check for * - * errors and return an AtNode if none. * - *********************************************************************/ - SyntaxTreeNode sTreeNode = (SyntaxTreeNode) treeNode ; - if ( (sTreeNode.heirs()[1].getKind() == IDENTIFIER) - && (sTreeNode.heirs()[1].getUS() == AtUS) - && (((SyntaxTreeNode) sTreeNode.heirs()[0]).heirs().length == 0) ) { - if (excStack.empty() || excSpecStack.empty()) { - // if either stack is empty, then @ used in improper EXCEPT context - errors.addError(sTreeNode.getLocation(), - "@ used where its meaning is not defined."); - return nullOAN ; - } - else { - // So, the context for @ is proper, then construct the - // AtNode and return it - return new AtNode((OpApplNode)excStack.peek(), - (OpApplNode)excSpecStack.peek()); - } - } ; - - ExprNode retVal = - (ExprNode) selectorToNode(genIdToSelector(sTreeNode), - 0, false, false, cm) ; - - /********************************************************************* - * A function definition generates an OpDefNode whose body is an * - * OpApplNode whose operator is either $RecursiveFcnSpec or * - * $NonRecursiveFcnSpec, the latter when the definition is * - * recursive. However, in SANY1 the definition * - * * - * f[x \in S] == ... * - * * - * was found to be recursive only if a subexpression f[...] occurs * - * in the body. It was not marked as recursive if another instance * - * of f occurs, such as Foo(f). To fix this, we need to check if * - * this genID is actually the Identifier of a function currently * - * being defined. The following call to functions.recursionCheck * - * does that. * - *********************************************************************/ - if (retVal.getKind() == OpApplKind) { - functions.recursionCheck( - ((OpApplNode) retVal).getOperator().getName()) ; - } ; - - return retVal ; - /************************** old version - genID = generateGenID(treeNode, cm); - - // if the symbol is "@" then check for errors and - // return an AtNode if none. - if (genID.getCompoundIDUS() == S_at) { - if (excStack.empty() || excSpecStack.empty()) { - // if either stack is empty, then @ used in improper EXCEPT context - errors.addError(treeNode.getLocation(), - "@ used where its meaning is not defined."); - } - else { - // So, the context for @ is proper, then construct the - // AtNode and return it - return new AtNode((OpApplNode)excStack.peek(), - (OpApplNode)excSpecStack.peek()); - } - } - else if (genID.getFullyQualifiedOp() == null - || genID.getArgs() == null) { - // If it is not an "@" symbol, it may still be an unresolved symbol - return nullOAN; - } - else if (genID.getFullyQualifiedOp().getKind() == ModuleKind) { - errors.addError( - treeNode.getLocation(), - "Module name '" + genID.getFullyQualifiedOp().getName() + - "' used as operator."); - return nullOAN; - } - else { - // but if there are no problems then we are in a situation in - // which return the appropriate OpApplNode an N_GenID node in - // the syntax tree really stands for an OpApplication - -// ********************************************************************* -// * Modified on 20 Apr 2007 by LL to correct the following bug. * -// * * -// * A function definition generates an OpDefNode whose body is an * -// * OpApplNode whose operator is either $RecursiveFcnSpec or * -// * $NonRecursiveFcnSpec, the latter when the definition is * -// * recursive. However, the definition * -// * * -// * f[x \in S] == ... * -// * * -// * was found to be recursive only if a subexpression f[...] occurs * -// * in the body. It was not marked as recursive if another instance * -// * of f occurs, such as Foo(f). To fix this, we need to check if * -// * this genID is actually the Identifier of a function currently * -// * being defined. The call to functions.recursionCheck was added * -// * here to do that. * -// ********************************************************************* - SymbolNode symNode = genID.getFullyQualifiedOp() ; - - if (symNode.getKind() == ThmOrAssumpDefKind) { - errors.addError(treeNode.getLocation(), - "Theorem or Assumption name used as expression.") ; - }; - OpApplNode retVal = - // return - new OpApplNode(symNode, genID.getArgs(), - treeNode, cm); - functions.recursionCheck(symNode.getName()); - return retVal ; - } - - ******************************* old version ******************/ - case N_OpApplication: - // for an operator with arguments - // Note: in neither case can this be an operator passed as an argument; - // the opAppl argument forces the return of an Operator application - // operators passed as arguments generate OpArg nodes, and that - // can happen only in certain contexts, not in every context where an - // expression can occur, which is the context we are in here - - SyntaxTreeNode genIdNode = (SyntaxTreeNode) treeNode.heirs()[0] ; - SyntaxTreeNode opApplNode = (SyntaxTreeNode) treeNode.heirs()[1] ; - - /********************************************************************* - * First a check. It appears that the children of an OpApplication * - * node must be a GeneralId node and an OpArgs node, but let's be * - * sure. * - *********************************************************************/ - if ( (genIdNode.getKind() != N_GeneralId) - || (opApplNode.getKind() != N_OpArgs)) { - errors.addAbort( - treeNode.getLocation(), - "Internal error: OpAppl node with unexpected children.", - true); - } ; - - Selector sel = genIdToSelector(genIdNode) ; - sel.args[sel.args.length - 1] = opApplNode ; - sel.selSTN = (SyntaxTreeNode) treeNode ; - /******************************************************************* - * For error reporting, make the syntax tree node of the selector * - * include both the general ID node and its argument. * - *******************************************************************/ - return (ExprNode) selectorToNode(sel, 0, false, false, cm) ; - -/***************** old version ********************* - return generateOpAppl(treeNode, cm); -************************/ - - case N_Tuple : - size = (children.length - 1) / 2; - sns = new ExprNode [size]; - for ( int lvi = 0; lvi < size; lvi++ ) { - sns[lvi] = generateExpression( children[ 2 * lvi + 1 ], cm ); - } - return new OpApplNode(OP_tup, sns, treeNode, cm); // Constructor 3 - - case N_FcnAppl : // Apparent function application - // Number of arguments to the apparent func app - int numArgs = (children.length - 2) / 2; - - // Function appl involves two semantic nodes: 1 for function, - // and 1 for arg or args tuple. - sns = new ExprNode[2]; - - // Generate expression tree for the function itself - sns[0] = generateExpression( children[0], cm ); - - if (sns[0] == null) {return null;} ; - /******************************************************************* - * sns[0] can be null if the parsing the function generates an * - * error (which is the case if the function is missing). * - * Added by LL on 29 Feb 2008 * - *******************************************************************/ - - // If the function is an OpApplNode (and could it be otherwise?) - if (sns[0].getKind() == OpApplKind) { - // Note if this is a recursive function, and change the top level - functions.recursionCheck(((OpApplNode)sns[0]).getOperator().getName()); - } - - // We next check that the number of arguments to a user-defined - // function is correct, if possible. - - // Retrieve the expression that represents the function being - // applied, i.e. the 1st arg to $FcnApply - ExprOrOpArgNode fcn = sns[0]; - - // The entire next conditional is for one purpose: to make sure - // that a function symbol is applied to the right number of - // arguments, when it is possible to do that during semantic - // analysis. This means that if a function is declared with, - // say, 3 parameters, e.g. "f[a,b,c] == {a,b,c}", then it is - // never used with 2 or 4 arguments. However, it can appear - // with zero arguments as the expression "f" (not as "f[]"), and - // it can appear with one argument, as in "f[e]", because e - // might be a 3-tuple value. (Whether it always is or not - // cannot be determined at the time of semantic analysis.) - // Furthermore a function declared with exactly one parameter - // can appear with any number of argument expressions because, - // e.g. f[1,2,3,4] is considered just an alternate way of - // writing f[<<1,2,3,4>>], so there is really just one argument - // value. - - // If it is an OpApplNode (as opposed to, say, an OpDeclNode) - if ( fcn instanceof OpApplNode ) { - // Retrieve the function being applied - SymbolNode funcOperator = ((OpApplNode)fcn).getOperator(); - - // If the function being applied is a user-defined function - // (as opposed to OpDeclNode, FormalParamNode, builtin - // operator, or expression) - if (funcOperator instanceof OpDefNode && - funcOperator.getKind() == UserDefinedOpKind) { - // Retrieve the function body expression - ExprOrOpArgNode funcBody = ((OpDefNode)funcOperator).getBody(); - - // if the function body is an OpApplNode (as opposed to, - // say, NumeralNode, DecimalNode, etc.) - if (funcBody instanceof OpApplNode && - (((OpApplNode)funcBody).getOperator().getName()==OP_nrfs || - ((OpApplNode)funcBody).getOperator().getName()==OP_rfs )) { - - // find out how many arguments it is SUPPOSED to have - int numParms = ((OpApplNode)funcBody ).getNumberOfBoundedBoundSymbols(); - - // If the function appears with numArgs >= 2 argument - // expressions, it must be declared with exactly numArgs - // parameters; and a function with numParms parameters in - // its definition should be applied to exactly numParms - // expressions, or 1 expression (representing arguments in - // tuple form), or 0 expressions (representing the - // function itself). Note: one cannot define a function - // with 0 arguments in TLA+. - if ( numArgs >= 2 && numParms != numArgs ) { - errors.addError(treeNode.getLocation(), - "Function '" + - ((OpApplNode)sns[0]).getOperator().getName() + - "' is defined with " + numParms + - " parameters, but is applied to " + - numArgs + " arguments."); - return nullOAN; - } // end if - } // end if - } // end if - } // end if - - // Assert.check(numArgs > 0); - if (numArgs == 1) { - sns[1] = generateExpression(children[2], cm); - } - else { - // If there is more than one arg we have to create a tuple for the arguments. - ExprOrOpArgNode[] exprs = new ExprNode[ numArgs ]; // One for each of the arguments - - // For each argument... - for (int lvi = 0; lvi < numArgs; lvi++) { - // Create the expression for that argument - exprs[lvi] = generateExpression( children[ 2+2*lvi ], cm ); - } - // Create an application of $Tuple - sns[1] = new OpApplNode(OP_tup, exprs, treeNode, cm); - } - // Create the function application node. - return new OpApplNode(OP_fa, sns, treeNode, cm); - - case N_UnboundOrBoundChoose : - return processChoose(treeNode, children, cm ); - - case N_BoundQuant : - return processBoundQuant(treeNode, children, cm); - - case N_UnboundQuant : - return processUnboundQuant( treeNode, children, cm ); - - case N_IfThenElse : - sns = new ExprNode[3]; - sns[0] = generateExpression( children[1], cm ); - sns[1] = generateExpression( children[3], cm ); - sns[2] = generateExpression( children[5], cm ); - return new OpApplNode(OP_ite, sns, treeNode, cm); - - case N_Case : - return processCase(treeNode, children, cm); - - case N_DisjList: - case N_ConjList: - sns = new ExprNode[ children.length ]; - for (int lvi = 0; lvi< sns.length; lvi++ ) { - sns[lvi] = generateExpression( children[lvi].heirs()[1], cm ); - } - if ( treeNode.isKind(N_DisjList ) ) - return new OpApplNode(OP_dl, sns, treeNode, cm); - else - return new OpApplNode(OP_cl, sns, treeNode, cm); - - case N_RecordComponent : // really RcdSelect in the API - sns = new ExprNode[2]; - sns[0] = generateExpression( children[0], cm ); - sns[1] = new StringNode(children[2], false); - return new OpApplNode(OP_rs, sns, treeNode, cm); - - case N_SetOfFcns: /* [S -> T] */ - sns = new ExprNode[2]; - sns[0] = generateExpression( children[1], cm ); - sns[1] = generateExpression( children[3], cm ); - - return new OpApplNode(OP_sof, sns, treeNode, cm); - - case N_SubsetOf : - return processSubsetOf( treeNode, children, cm ); - - case N_SetOfAll : - return processSetOfAll( treeNode, children, cm ); - - case N_RcdConstructor: - return processRcdForms( OP_rc, treeNode, children, cm ); - - case N_SetOfRcds: - return processRcdForms( OP_sor, treeNode, children, cm ); - - case N_FcnConst: - return processFcnConst( treeNode, children, cm ); - - case N_ActionExpr: - case N_FairnessExpr: - return processAction( treeNode, children, cm ); - - case N_Except: - return processExcept( treeNode, children, cm ); - - case N_LetIn: - return processLetIn( treeNode, children, cm ); - - case N_Lambda: - errors.addError( - treeNode.getLocation(), - "LAMBDA expression used where an expression is required." ); - return null; - - case N_Label: - LabelNode ln = generateLabel(treeNode, cm); - if (ln.isAssumeProve && !allowLabeledAP) { - errors.addError( - treeNode.getLocation(), - "Labeled ASSUME/PROVE used where an expression is required." ); - } - return ln ; - - default: - errors.addError(treeNode.getLocation(), - "Unsupported expression type `" + - treeNode.getImage() + "'."); - return null; - - } // end switch - - } // end generateExpression() - -/*************************************************************************** -* The following variables are used in the processing of ASSUME/PROVE * -* nodes, and the labels that lie within them. * -***************************************************************************/ - private int assumeProveDepth = 0 ; - /*********************************************************************** - * The nesting depth of ASSUME/PROVEs within which the node corrently * - * being processed lies. In * - * * - * THEOREM ASSUME P PROVE Q * - * * - * the nodes for P and Q are at depth 1 (not 0). * - ***********************************************************************/ - - private ThmOrAssumpDefNode currentGoal = null ; - private int currentGoalClause ; - /*********************************************************************** - * currentGoal is the named theorem or proof-step node within * - * which lies the current node that is being processed, or null if * - * we're not inside such a step. If currentGoal != null, and we're * - * within an ASSUME/PROVE step (which is true iff assumeProveDepth > * - * 0), then then currentGoalClause equals the clause of within which * - * the node being processed lies. (If we're processing Q in ASSUME P * - * PROVE Q, then Q = 1.) * - ***********************************************************************/ - - private static final int maxAPDepth = 100 ; - /*********************************************************************** - * Maximum allowed nesting depth of ASSUME/PROVES. 100 ought to * - * suffice. * - ***********************************************************************/ - - private boolean[] inScopeOfAPDecl = new boolean[maxAPDepth] ; - /*********************************************************************** - * For all i \leq assumeProveDepth, inScopeOfAPDecl[i] = true iff the * - * node currently being processed lies within a declaration made at * - * assumeProveDepth = i. Thus in * - * * - * THEOREM ASSUME NEW x \in S, * - * P, * - * ASSUME R , ACTION A PROVE T * - * PROVE Q * - * * - * inScopeOfAPDecl[1] equals false when S is being processed and * - * equals true when P, Q, R, and T are being processed. * - * * - * inScopeOfAPDecl[2] equals false when R is being processed and true * - * when T is being processed. * - ***********************************************************************/ - - - private boolean noLabelsAllowed() { - /*********************************************************************** - * This returns true if we are inside an ASSUME/PROVE node where a * - * label is not allowed because we are in the scope of a declaration * - * from an inner ASSUME/PROVE. * - ***********************************************************************/ - for (int i = 2; i <= assumeProveDepth; i++) { - if (inScopeOfAPDecl[i]) { return true ; } ; - } ; - return false ; - } - - - private final boolean illegalLabelRef(LabelNode ln, SyntaxTreeNode stn) - throws AbortException { - /*********************************************************************** - * True iff we are currently in a point where a reference to this * - * label is illegal because it lies inside an ASSUME/PROVE clause from * - * outside the scope of a symbol it may contain. See the comments in * - * AssumeProveNode.java for an explanation of this method. * - ***********************************************************************/ - ThmOrAssumpDefNode goal = ln.goal ; - if (goal == null) { return false ; } ; - if ( (goal.getBody() == null) - || (goal.getBody().getKind() != AssumeProveKind)) { - errors.addAbort( - stn.getLocation(), - "Internal error: Expecting label to be in AssumeProveNode, " + - "but it's not.") ; - } ; - AssumeProveNode ap = (AssumeProveNode) goal.getBody() ; - return ap.inScopeOfDecl[ln.goalClause] - && (goal.isSuffices() == ap.inProof) ; - } // illegalLabelRef - - private final boolean illegalAPPosRef(AssumeProveNode ap, int pos) { - /*********************************************************************** - * True iff it is illegal for the node currently being processed to * - * access the clause number pos of AssumeProve node ap because it is * - * not in the scope of symbols declared in previous ASSUME clauses of * - * ap. See the comments in AssumeProveNode.java for an explanation of * - * this method. * - ***********************************************************************/ - return ap.inScopeOfDecl[pos-1] - && ( (ap.getGoal() == null) - || (ap.getGoal().isSuffices() == ap.inProof)) ; - } - + SymbolTable st = null; + if (declare) { + st = symbolTable; + } + ; + + /********************************************************************** + * Changed us to Operators.resolveSynonym(us) in the following so * constant + * declarations work with any synonym for an operator--e.g., * with both (+) and + * \oplus. * + **********************************************************************/ + return new OpDeclNode(Operators.resolveSynonym(us), declKind, declLevel, arity, cm, st, treeNode); + } + + private final void processParameters(TreeNode treeNodes[], ModuleNode cm) { + for (int lvi = 1; lvi < treeNodes.length; lvi += 2) { + if (treeNodes[lvi].getUS() == S_at) { + errors.addError(treeNodes[lvi].getLocation(), "Attempted to declare '@' as a constant."); + } + ; + OpDeclNode odn = buildParameter(treeNodes[lvi], ConstantDeclKind, ConstantLevel, cm, true); + /******************************************************************* + * We throw away the OpDeclNode because, when it was constructed, * it was added + * to symbolTable, whose top context should be the * top-level context of the + * module. * + *******************************************************************/ + } + } + + /** + * Processes the LHS of an operator definition, in several ways, depending on + * whether it is a prefix, infix, postfix, or a parameter-free or parameter-ful + * function-notation operator. Also creates context entries for the operator and + * its parameters. + */ + /************************************************************************ + * Processes a syntactic node of type N_OperatorDefinition. It is * called by + * generateModule (with defs = null) when processing an * outer-level definition + * and by processLetIn (with defs /= null) when * processing a LET definition. + * It creates an OpDef node and puts it * in ModuleNode cm's set of definitions. + * * + ************************************************************************/ + private final void processOperator(TreeNode treeNode, Vector defs, ModuleNode cm) throws AbortException { + TreeNode syntaxTreeNode = treeNode; + UniqueString name = null; + int arity = 0; + boolean local = syntaxTreeNode.zero() != null; + TreeNode[] children = syntaxTreeNode.one(); + TreeNode[] ss = children[0].heirs(); + FormalParamNode[] params = null; + Context ctxt = new Context(moduleTable, errors); + boolean isRecursive = false; + /********************************************************************* + * Will be set to true if this operator was declared in a RECURSIVE * statement. + * * + *********************************************************************/ + // New context needed because parameter symbols may have to be + // added if the operator being defined takes params + symbolTable.pushContext(ctxt); + + // If the operator is an identifier (possibly with params), as + // opposed to a prefix, infix, or postfix symbol + if (children[0].isKind(N_IdentLHS)) { + if (ss.length > 2) { + // If the operator has arguments + params = new FormalParamNode[(ss.length - 2) / 2]; + for (int lvi = 2; lvi < ss.length; lvi += 2) { + TreeNode sss[] = ss[lvi].heirs(); + if (ss[lvi].isKind(N_IdentDecl)) { + // parameter is simple identifier + name = sss[0].getUS(); + arity = (sss.length - 1) / 2; + } else if (ss[lvi].isKind(N_InfixDecl)) { + // parameter is infix operator + // Call of Operators.resolveSynonym added by LL on 27 Mar 2013 + name = sss[1].getUS(); + + // Following added by LL on 27 Mar 2013 + // to handle parameters like _(+)_ + name = Operators.resolveSynonym(name); + + arity = 2; + } else if (ss[lvi].isKind(N_PrefixDecl)) { + // parameter is prefix operator + name = sss[0].getUS(); + arity = 1; + } else { + // parameter must be postfix operator + // SZ Jul 13, 2009: added the message to the assert + if (!ss[lvi].isKind(N_PostfixDecl)) { + throw new WrongInvocationException(MP.getMessage(EC.TLC_PARAMETER_MUST_BE_POSTFIX)); + } + name = sss[1].getUS(); + arity = 1; + } + params[(lvi - 2) / 2] = new FormalParamNode(name, arity, ss[lvi], symbolTable, cm); + } + } else { + // The operator has no arguments + params = new FormalParamNode[0]; + } + name = ss[0].getUS(); + } else if (children[0].isKind(N_PrefixLHS)) { // operator is a prefix operator + // Process the parameter + params = new FormalParamNode[1]; + params[0] = new FormalParamNode(ss[1].getUS(), 0, ss[1], symbolTable, cm); + + // Process the operator + name = Operators.resolveSynonym(ss[0].getUS()); + } else if (children[0].isKind(N_InfixLHS)) { // operator is an infix operator + params = new FormalParamNode[2]; + // Process the first param + params[0] = new FormalParamNode(ss[0].getUS(), 0, ss[0], symbolTable, cm); + + // Process the second param + params[1] = new FormalParamNode(ss[2].getUS(), 0, ss[2], symbolTable, cm); + + // Process the operator + name = Operators.resolveSynonym(ss[1].getUS()); + } else if (children[0].isKind(N_PostfixLHS)) { // operator is a postfix operator + // Process the parameter + params = new FormalParamNode[1]; + params[0] = new FormalParamNode(ss[0].getUS(), 0, ss[0], symbolTable, cm); + + // Process the operator + name = Operators.resolveSynonym(ss[1].getUS()); + } else { + errors.addError(children[0].getLocation(), "Unknown parameter declaration `" + children[0].getUS() + "'."); + } + + /*********************************************************************** + * The following code added by LL on 1 April 2007 to handle * recursively + * defined operators. * + ***********************************************************************/ + OpDefNode odn = null; + /*********************************************************************** + * Check if this operator has already been declared or defined. If * so, if it + * was declared in a RECURSIVE statement at this let/in * level and not yet + * defined, then set the OpDefNode's fields * appropriately, otherwise report an + * error. * + ***********************************************************************/ + SymbolNode symbolNode = symbolTable.resolveSymbol(name); + + if (symbolNode != null) { + /********************************************************************* + * The symbol has already been defined or declared. Check if it was * declared + * in a RECURSIVE statement. * + *********************************************************************/ + if (symbolNode instanceof OpDefNode) { + odn = (OpDefNode) symbolNode; + } + ; + if ((odn != null) && odn.inRecursive && (!odn.isDefined)) { + if (odn.letInLevel == curLevel) { + isRecursive = true; + + /***************************************************************** + * Check that the parameters of the definition match the ones in * the RECURSIVE + * declaration. * + *****************************************************************/ + boolean paramsMatch = (odn.getParams().length == params.length); + if (paramsMatch) { + for (int i = 0; i < params.length; i++) { + paramsMatch = (params[i].getArity() == 0); + } + ; + } + ; // if + if (!paramsMatch) { + errors.addError(treeNode.getLocation(), "Definition of " + odn.getName() + + " has different arity than " + "its RECURSIVE declaration."); + } + ; + + odn.setParams(params); + /*************************************************************** + * Here's where the params field of the OpDefNode is set for an * operator + * declared in a RECURSIVE statement. * + ***************************************************************/ + } // if (odn.letInLevel == curLevel) + else { + errors.addError(treeNode.getLocation(), + "Recursive operator " + name.toString() + " defined at wrong LET/IN level."); + odn = null; + } // else + } // if (odn != null) ... + else { + errors.addError(treeNode.getLocation(), + "Operator " + name.toString() + " already defined or declared."); + } + } // if (symbolNode != null) + + pushLS(); + /********************************************************************* + * Start fresh processing of labels. * + *********************************************************************/ + // Generate expression that is the body of the operator + ExprNode exp = generateExpression(children[2], cm); + + // Restore old context, popping off the context containing the parameters + symbolTable.popContext(); + + if (isRecursive) { + endOpDefNode(odn, exp, syntaxTreeNode); + } else { + // Create OpDefNode using symbolTable whose top context contains the + // parameter symbols + /******************************************************************** + * This comment appears to be incorrect, because it looks like the * parameter + * symbols were just popped off symbolTable. * + ********************************************************************/ + odn = new OpDefNode(name, UserDefinedOpKind, params, local, exp, cm, symbolTable, syntaxTreeNode, true, + null); + symbolNode = odn; + /******************************************************************* + * Keeping symbolNode for historical reasons--I'm too lazy to * rearrange the + * code to get rid of the redundant identifier. * + *******************************************************************/ + + setOpDefNodeRecursionFields(odn, cm); + + } // else + + Hashtable ht = popLabelNodeSet(); + /********************************************************************* + * Succeed or fail, we must execute popLabelNodeSet to match the * previous + * pushLS. * + *********************************************************************/ + if (odn != null) { + odn.setLabels(ht); + } + ; + /********************************************************************* + * If there was no error, then odn is an OpDefNode and we must set * it labels + * field. * + *********************************************************************/ + cm.appendDef(symbolNode); + /******************************************************************* + * Add the new OpDefNode to the current module's set of * definitions. * + *******************************************************************/ + // defs is non-null iff this definition is in the Let part of a + // Let-In expression + if (defs != null) + defs.addElement(symbolNode); + } // processOperator + + private final void processQuantBoundArgs(TreeNode[] treeNodeA, // node whose children include + // the bounded quants + int offset, // nodes to skip to get to first + // "quantifier" + FormalParamNode[][] odna, boolean[] bt, // set to true if arg is a tuple; + // otherwise false + ExprNode[] ena, ModuleNode cm) + /*********************************************************************** + * Despite the incoherent and/or incorrect comments, here's what I * think is + * going on: * * - odna, ena, and bt have the same length N. * * - treeNodeA is + * an array of nodes containing a subsequence of * N_QuantBound nodes of length + * N that starts at * treeNodeA[offset]. * * - odna, ena, and bt are set to the + * arguments for the OpApplNode * constructor that produces an OpApplNode for + * the operator * having this sequence of N_QuantBound nodes. * + ***********************************************************************/ + + throws AbortException { + // For each quantifier, evaluate the bound in the context of the + // current symbol table, i.e. before the quantified variables are + // added to the new context, since the quantified vars may not + // appear in the bounds. + for (int lvi = 0; lvi < bt.length; lvi++) { + // Make ss point to each N_QuantBound node in turn. + TreeNode[] ss = treeNodeA[offset + 2 * lvi].heirs(); + // the last element in ss is expression for the quantifier bound + ena[lvi] = generateExpression(ss[ss.length - 1], cm); + } + + // Now for each quantifier, process the variable names + for (int lvi = 0; lvi < bt.length; lvi++) { + TreeNode treeNode = treeNodeA[offset + 2 * lvi]; + + // The variable bound to the "quantifier" + TreeNode[] ss = treeNode.heirs(); + + if (ss[0].isKind(N_IdentifierTuple)) { // three elements only, go into node + bt[lvi] = true; + TreeNode[] sss = ss[0].heirs(); + odna[lvi] = new FormalParamNode[sss.length / 2]; + + for (int lvj = 0; lvj < sss.length / 2; lvj++) { + odna[lvi][lvj] = new FormalParamNode(sss[2 * lvj + 1].getUS(), 0, sss[2 * lvj + 1], symbolTable, + cm); + } + } else { // gotta be N_Identifier + bt[lvi] = false; + odna[lvi] = new FormalParamNode[(ss.length - 1) / 2]; + + for (int lvj = 0; lvj < (ss.length - 1) / 2; lvj++) { + odna[lvi][lvj] = new FormalParamNode(ss[2 * lvj].getUS(), 0, ss[2 * lvj], symbolTable, cm); + } + } + } + } + + // Process a function definition + private final void processFunction(TreeNode treeNode, Vector defs, ModuleNode cm) throws AbortException { + TreeNode syntaxTreeNode = treeNode; + boolean local = syntaxTreeNode.zero() != null; + TreeNode[] ss = syntaxTreeNode.one(); + // Heirs to N_FunctionDefinition node + int ql = (ss.length - 4) / 2; + // number of QuantBound's + OpApplNode oan; + OpDefNode odn = null; + FormalParamNode[][] quants = new FormalParamNode[ql][0]; + FormalParamNode[] fcnDeclForRecursion = new FormalParamNode[1]; + /********************************************************************* + * This is an array of length 1 instead of just an OpDeclNode * because it is + * needed in such an array to pass as an argument to * new OpApplNode. * + *********************************************************************/ + boolean[] tuples = new boolean[ql]; + ExprNode[] domains = new ExprNode[ql]; + ExprNode[] lhs = new ExprNode[1]; + Context newContext = new Context(moduleTable, errors); + boolean isRecursive = false; + /********************************************************************* + * Will be set to true if this operator was declared in a RECURSIVE * statement. + * * + *********************************************************************/ + + // Fill arrays with quantifier-related information; must be called + // in scope of *new* context, since it adds parameter symbols to + // the context. + symbolTable.pushContext(newContext); + processQuantBoundArgs(ss, 2, quants, tuples, domains, cm); + + UniqueString name = ss[0].getUS(); + SymbolNode symbolNode = symbolTable.resolveSymbol(name); + + // This is in anticipation of the possibility that the function is + // recursive. We are creating a new bound symbol of the same name + // as the function to stand in the body for the function. The + // arity of the bound symbol is 0 because a function formally has + // arity 0 as an operator, even if it has several arguments as a + // function. + /*********************************************************************** + * If the function name is already defined (which should happen only * if it was + * declared in a RECURSIVE statement, then there is no need * to add this + * OpDeclNode to the symbol table. As near as I can tell * (which might not be + * near enough), this OpDeclNode's entry in the * symbol table is needed only so + * the name will be declared in case * of a recursive call. The check for + * whether a function call in the * body actually is recursive is made by + * calling the recursionCheck * method of the Function subclass to see if the + * OpApplNode is in the * Function object's funcStack. * + ***********************************************************************/ + SymbolTable st = null; + if (symbolNode == null) { + st = symbolTable; + } + ; + fcnDeclForRecursion[0] = new FormalParamNode(name, 0, treeNode, st, cm); + symbolTable.popContext(); + + // Create OpApplNode to hold the function body; type is assumed to + // be non-recursive function (OP_nrfs); if the body is recursive, + // this will be discovered during generateExpression() for the + // body + oan = new OpApplNode(OP_nrfs, fcnDeclForRecursion, new ExprNode[0], quants, tuples, domains, syntaxTreeNode, + cm); + // constructor 5 + + /*********************************************************************** + * The following code added by LL on 19 April 2007 to handle * recursively + * defined operators. * + ***********************************************************************/ + if (symbolNode == null) { + odn = new OpDefNode(ss[0].getUS(), UserDefinedOpKind, nullParam, local, oan, cm, symbolTable, + syntaxTreeNode, true, null); + setOpDefNodeRecursionFields(odn, cm); + } else { + /********************************************************************* + * The symbol has already been defined or declared. Check if it was * declared + * in a RECURSIVE statement. * + *********************************************************************/ + if (symbolNode instanceof OpDefNode) { + odn = (OpDefNode) symbolNode; + } + ; + if ((odn != null) && odn.inRecursive && (!odn.isDefined)) { + if (odn.letInLevel == curLevel) { + isRecursive = true; + /***************************************************************** + * Check that the RECURSIVE declaration had no paramters. * + *****************************************************************/ + if (odn.getArity() == 0) { + endOpDefNode(odn, oan, syntaxTreeNode); + } else { + /************************************************************* + * RECURSIVE declaration had parameters. * + *************************************************************/ + errors.addError(treeNode.getLocation(), "Function " + odn.getName() + + " has operator arguments in " + "its RECURSIVE declaration."); + } + ; + } // if (odn.letInLevel == curLevel) + else { + errors.addError(treeNode.getLocation(), + "Recursive function " + name.toString() + " defined at wrong LET/IN level."); + odn = null; + } // else + } // if (odn != null) ... + else { + errors.addError(treeNode.getLocation(), + "Function name `" + name.toString() + "' already defined or declared."); + } + } // else (symbolNode != null) + + // Create OpDefNode to hold the function definition, including + // reference to the function body ("oan") + if (odn != null) { + /********************************************************************* + * There was no error, so the OpDefNode was created. * + *********************************************************************/ + cm.appendDef(odn); + + // defs is non-null iff this function definition is in the Let + // part of a Let-In expression. If so, then we have to accumulate + // these defs in a vector. + if (defs != null) + defs.addElement(odn); + + // Function body must be processed in the scope of the new + // context, including the parms + symbolTable.pushContext(newContext); + + } + ; // if (odn != null) + + // Keep stack of nested function defs to enable detection of recursion + functions.push(ss[0].getUS(), oan); + + // Create semantic graph function body in the inner context including parameters + pushLS(); + pushFormalParams(flattenParams(quants)); + /********************************************************************* + * Push a new empty label set onto LS, and push the function * definition's + * formal parameters onto Last(LS).paramSeq for * processing the function body. + * * + *********************************************************************/ + lhs[0] = generateExpression(ss[ss.length - 1], cm); + popFormalParams(); + /********************************************************************* + * Pop the formal params from Last(LS).paramSeq, which should now be * empty. + * This isn't really necessary, since we're going to pop the * LS stack below, + * but I hate to have a push without a pop. * + *********************************************************************/ + Hashtable ht = popLabelNodeSet(); + /********************************************************************* + * The matching pop for the pushLS above. * + *********************************************************************/ + if (odn != null) { + odn.setLabels(ht); + } + ; + /********************************************************************* + * If there was no error and we created an OpDefNode, then set its * label set. + * * + *********************************************************************/ + functions.pop(); + + oan.setArgs(lhs); + /*********************************************************************** + * Test for odn != null added 2 Jul 2009 to avoid bug that caused * popping of + * empty symbol table when the function name was * already declared to be a + * variable. * + ***********************************************************************/ + if (odn != null) { + // Restore old context + symbolTable.popContext(); + } + + // if the function body turned out to be non-recursive, then we + // should null-out the fcnDefForRecursion ref put in place above, + // since it is unnecessary for a nonrecursive func + if (oan.getOperator().getName() == OP_nrfs) { + oan.makeNonRecursive(); + } + } // end processFunction() + + private final ExprNode processLetIn(TreeNode treeNode, TreeNode[] children, ModuleNode cm) throws AbortException { + TreeNode[] syntaxTreeNode = children[1].heirs(); // extract LetDefinitions + Vector defVec = new Vector(4); + Vector instVec = new Vector(1); + + Context letCtxt = new Context(moduleTable, errors); + symbolTable.pushContext(letCtxt); + /********************************************************************* + * Create a new sub-Context for the IN expression, containing the * LET + * definitions * + *********************************************************************/ + + /*********************************************************************** + * Increment curLevel. * + ***********************************************************************/ + if (curLevel < MaxLetInLevel) { + curLevel++; + } else { + errors.addAbort(treeNode.getLocation(), "LETs nested more than " + MaxLetInLevel + " deep."); + } + ; + unresolvedCnt[curLevel] = 0; + /********************************************************************* + * Will not have been initialized if we haven't yet reached this * LET/IN + * nesting depth. * + *********************************************************************/ + + for (int lvi = 0; lvi < syntaxTreeNode.length; lvi++) { + /********************************************************************* + * Note: LL changed from an "if" and a sequence of "elseif"s to a * switch + * statement on 7 Apr 2007 when adding the N_Recursive case. * + *********************************************************************/ + switch (syntaxTreeNode[lvi].getKind()) { + case N_OperatorDefinition: + processOperator(syntaxTreeNode[lvi], defVec, cm); + break; + + case N_FunctionDefinition: + processFunction(syntaxTreeNode[lvi], defVec, cm); + break; + + case N_ModuleDefinition: + processModuleDefinition(syntaxTreeNode[lvi], defVec, instVec, cm); + break; + + case N_Recursive: + processRecursive(syntaxTreeNode[lvi], cm); + break; + + default: + errors.addAbort(syntaxTreeNode[lvi].getLocation(), + "Internal error: found unexpected syntax " + "tree node in LET."); + } // switch + } // for + + checkForUndefinedRecursiveOps(cm); + + /*********************************************************************** + * Decrement curLevel. * + ***********************************************************************/ + curLevel--; + if (curLevel < 0) { + curLevel = 0; + } + ; + + ExprNode body = generateExpression(children[3], cm); + + /*********************************************************************** + * Convert from Vector to array of SymbolNode, whose elements may be * OpDefNode + * or ThmOrAssumpDefNode objects. * + ***********************************************************************/ + SymbolNode[] opDefs = new SymbolNode[defVec.size()]; + for (int i = 0; i < opDefs.length; i++) { + opDefs[i] = (SymbolNode) defVec.elementAt(i); + } + + InstanceNode[] insts = new InstanceNode[instVec.size()]; + for (int i = 0; i < insts.length; i++) { + insts[i] = (InstanceNode) instVec.elementAt(i); + } + LetInNode letIn = new LetInNode(treeNode, opDefs, insts, body, letCtxt); + symbolTable.popContext(); + return letIn; + } // end processLetIn + + private final ExprNode generateExpression(TreeNode treeNode, ModuleNode cm) throws AbortException { + /*********************************************************************** + * Must return an ExprNode that represents an expression, and not a * labeled + * ASSUME/PROVE * + ***********************************************************************/ + return generateExpressionOrLAP(treeNode, cm, false); + } + + private final ExprNode generateExpressionOrLAP(TreeNode treeNode, ModuleNode cm, boolean allowLabeledAP) + /*********************************************************************** + * Can return a LabelNode labeling an ASSUME/PROVE iff allowLabeledAP * = true. + * Otherwise, it can return only an ExprNode that represents * an expression. * + ***********************************************************************/ + throws AbortException { + TreeNode[] children = treeNode.heirs(); + TreeNode[] ss = null; // grandchildren + SymbolNode opn = null; + TreeNode op = null; + GenID genID; + ExprOrOpArgNode[] sns; // a ExprNode list used for arguments + + switch (treeNode.getKind()) { + + case N_Real: + return new DecimalNode(children[0].getImage(), children[2].getImage(), treeNode); + + case N_Number: + return new NumeralNode(children[0].getImage(), treeNode); + + case N_String: + return new StringNode(treeNode, true); + + case N_ParenExpr: + return generateExpression(children[1], cm); + + case N_InfixExpr: + genID = generateGenID(children[1], cm); + + sns = new ExprOrOpArgNode[2]; + opn = symbolTable.resolveSymbol(Operators.resolveSynonym(genID.getCompoundIDUS())); + if (opn == null) { + errors.addError(treeNode.getLocation(), + "Couldn't resolve infix operator symbol `" + genID.getCompoundIDUS() + "'."); + return null; + } + + sns[0] = generateExpression(children[0], cm); + sns[1] = generateExpression(children[2], cm); + return new OpApplNode(opn, sns, treeNode, cm); + + case N_PrefixExpr: + // 1 get gen operator node + ss = children[0].heirs(); + + // 2 get rightmost part of the possibly compound Op itself; + op = ss[1]; + genID = generateGenID(children[0], cm, true); + sns = new ExprOrOpArgNode[1]; + opn = symbolTable.resolveSymbol(Operators.resolveSynonym(genID.getCompoundIDUS())); + + if (opn == null) { + errors.addError(treeNode.getLocation(), + "Couldn't resolve prefix operator symbol `" + genID.getCompoundIDUS() + "'."); + return null; + } + + sns[0] = generateExpression(children[1], cm); + return new OpApplNode(opn, sns, treeNode, cm); // constructor 2 + + case N_PostfixExpr: + genID = generateGenID(children[1], cm); + + sns = new ExprNode[1]; + opn = symbolTable.resolveSymbol(Operators.resolveSynonym(genID.getCompoundIDUS())); + if (opn == null) { + errors.addError(treeNode.getLocation(), + "Couldn't resolve postfix " + "operator symbol `" + genID.getCompoundIDUS() + "'."); + return null; + } + + sns[0] = generateExpression(children[0], cm); + return new OpApplNode(opn, sns, treeNode, cm); // constructor 2 + + case N_Times: // or cartesian product + sns = new ExprNode[(children.length + 1) / 2]; + + for (int lvi = 0; lvi < sns.length; lvi++) { + sns[lvi] = generateExpression(children[2 * lvi], cm); + } + return new OpApplNode(OP_cp, sns, treeNode, cm); // constructor 3 + + case N_SetEnumerate: + int size = (children.length - 1) / 2; + sns = new ExprNode[size]; + for (int lvi = 0; lvi < size; lvi++) { + sns[lvi] = generateExpression(children[2 * lvi + 1], cm); + } + return new OpApplNode(OP_se, sns, treeNode, cm); // constructor 3 + + case N_GeneralId: + // This is a zero-ary operator; it should show in the syntax + // tree as an OpApp, but it does not. Hence, an OpApplication + // node with zero arguments must be constructed for it + // if we get here, the GeneralID really is an OpApplication with 0 + // primary arguments, but with any number of prefix arguments + + // process the generalized identifier, complete with its + // embedded argument lists (if any) + + /********************************************************************* + * If the N_GeneralId represents the identifier "@", then check for * errors and + * return an AtNode if none. * + *********************************************************************/ + SyntaxTreeNode sTreeNode = (SyntaxTreeNode) treeNode; + if ((sTreeNode.heirs()[1].getKind() == IDENTIFIER) && (sTreeNode.heirs()[1].getUS() == AtUS) + && (((SyntaxTreeNode) sTreeNode.heirs()[0]).heirs().length == 0)) { + if (excStack.empty() || excSpecStack.empty()) { + // if either stack is empty, then @ used in improper EXCEPT context + errors.addError(sTreeNode.getLocation(), "@ used where its meaning is not defined."); + return nullOAN; + } else { + // So, the context for @ is proper, then construct the + // AtNode and return it + return new AtNode((OpApplNode) excStack.peek(), (OpApplNode) excSpecStack.peek()); + } + } + ; + + ExprNode retVal = (ExprNode) selectorToNode(genIdToSelector(sTreeNode), 0, false, false, cm); + + /********************************************************************* + * A function definition generates an OpDefNode whose body is an * OpApplNode + * whose operator is either $RecursiveFcnSpec or * $NonRecursiveFcnSpec, the + * latter when the definition is * recursive. However, in SANY1 the definition * + * * f[x \in S] == ... * * was found to be recursive only if a subexpression + * f[...] occurs * in the body. It was not marked as recursive if another + * instance * of f occurs, such as Foo(f). To fix this, we need to check if * + * this genID is actually the Identifier of a function currently * being + * defined. The following call to functions.recursionCheck * does that. * + *********************************************************************/ + if (retVal.getKind() == OpApplKind) { + functions.recursionCheck(((OpApplNode) retVal).getOperator().getName()); + } + ; + + return retVal; + /************************** + * old version genID = generateGenID(treeNode, cm); + * + * // if the symbol is "@" then check for errors and // return an AtNode if + * none. if (genID.getCompoundIDUS() == S_at) { if (excStack.empty() || + * excSpecStack.empty()) { // if either stack is empty, then @ used in improper + * EXCEPT context errors.addError(treeNode.getLocation(), "@ used where its + * meaning is not defined."); } else { // So, the context for @ is proper, then + * construct the // AtNode and return it return new + * AtNode((OpApplNode)excStack.peek(), (OpApplNode)excSpecStack.peek()); } } + * else if (genID.getFullyQualifiedOp() == null || genID.getArgs() == null) { // + * If it is not an "@" symbol, it may still be an unresolved symbol return + * nullOAN; } else if (genID.getFullyQualifiedOp().getKind() == ModuleKind) { + * errors.addError( treeNode.getLocation(), "Module name '" + + * genID.getFullyQualifiedOp().getName() + "' used as operator."); return + * nullOAN; } else { // but if there are no problems then we are in a situation + * in // which return the appropriate OpApplNode an N_GenID node in // the + * syntax tree really stands for an OpApplication + * + * // ********************************************************************* // * + * Modified on 20 Apr 2007 by LL to correct the following bug. * // * * // * A + * function definition generates an OpDefNode whose body is an * // * OpApplNode + * whose operator is either $RecursiveFcnSpec or * // * $NonRecursiveFcnSpec, + * the latter when the definition is * // * recursive. However, the definition * + * // * * // * f[x \in S] == ... * // * * // * was found to be recursive only if + * a subexpression f[...] occurs * // * in the body. It was not marked as + * recursive if another instance * // * of f occurs, such as Foo(f). To fix + * this, we need to check if * // * this genID is actually the Identifier of a + * function currently * // * being defined. The call to functions.recursionCheck + * was added * // * here to do that. * // + * ********************************************************************* + * SymbolNode symNode = genID.getFullyQualifiedOp() ; + * + * if (symNode.getKind() == ThmOrAssumpDefKind) { + * errors.addError(treeNode.getLocation(), "Theorem or Assumption name used as + * expression.") ; }; OpApplNode retVal = // return new OpApplNode(symNode, + * genID.getArgs(), treeNode, cm); functions.recursionCheck(symNode.getName()); + * return retVal ; } + ******************************* + * old version + ******************/ + case N_OpApplication: + // for an operator with arguments + // Note: in neither case can this be an operator passed as an argument; + // the opAppl argument forces the return of an Operator application + // operators passed as arguments generate OpArg nodes, and that + // can happen only in certain contexts, not in every context where an + // expression can occur, which is the context we are in here + + SyntaxTreeNode genIdNode = (SyntaxTreeNode) treeNode.heirs()[0]; + SyntaxTreeNode opApplNode = (SyntaxTreeNode) treeNode.heirs()[1]; + + /********************************************************************* + * First a check. It appears that the children of an OpApplication * node must + * be a GeneralId node and an OpArgs node, but let's be * sure. * + *********************************************************************/ + if ((genIdNode.getKind() != N_GeneralId) || (opApplNode.getKind() != N_OpArgs)) { + errors.addAbort(treeNode.getLocation(), "Internal error: OpAppl node with unexpected children.", true); + } + ; + + Selector sel = genIdToSelector(genIdNode); + sel.args[sel.args.length - 1] = opApplNode; + sel.selSTN = (SyntaxTreeNode) treeNode; + /******************************************************************* + * For error reporting, make the syntax tree node of the selector * include both + * the general ID node and its argument. * + *******************************************************************/ + return (ExprNode) selectorToNode(sel, 0, false, false, cm); + + /***************** + * old version ********************* return generateOpAppl(treeNode, cm); + ************************/ + + case N_Tuple: + size = (children.length - 1) / 2; + sns = new ExprNode[size]; + for (int lvi = 0; lvi < size; lvi++) { + sns[lvi] = generateExpression(children[2 * lvi + 1], cm); + } + return new OpApplNode(OP_tup, sns, treeNode, cm); // Constructor 3 + + case N_FcnAppl: // Apparent function application + // Number of arguments to the apparent func app + int numArgs = (children.length - 2) / 2; + + // Function appl involves two semantic nodes: 1 for function, + // and 1 for arg or args tuple. + sns = new ExprNode[2]; + + // Generate expression tree for the function itself + sns[0] = generateExpression(children[0], cm); + + if (sns[0] == null) { + return null; + } + ; + /******************************************************************* + * sns[0] can be null if the parsing the function generates an * error (which is + * the case if the function is missing). * Added by LL on 29 Feb 2008 * + *******************************************************************/ + + // If the function is an OpApplNode (and could it be otherwise?) + if (sns[0].getKind() == OpApplKind) { + // Note if this is a recursive function, and change the top level + functions.recursionCheck(((OpApplNode) sns[0]).getOperator().getName()); + } + + // We next check that the number of arguments to a user-defined + // function is correct, if possible. + + // Retrieve the expression that represents the function being + // applied, i.e. the 1st arg to $FcnApply + ExprOrOpArgNode fcn = sns[0]; + + // The entire next conditional is for one purpose: to make sure + // that a function symbol is applied to the right number of + // arguments, when it is possible to do that during semantic + // analysis. This means that if a function is declared with, + // say, 3 parameters, e.g. "f[a,b,c] == {a,b,c}", then it is + // never used with 2 or 4 arguments. However, it can appear + // with zero arguments as the expression "f" (not as "f[]"), and + // it can appear with one argument, as in "f[e]", because e + // might be a 3-tuple value. (Whether it always is or not + // cannot be determined at the time of semantic analysis.) + // Furthermore a function declared with exactly one parameter + // can appear with any number of argument expressions because, + // e.g. f[1,2,3,4] is considered just an alternate way of + // writing f[<<1,2,3,4>>], so there is really just one argument + // value. + + // If it is an OpApplNode (as opposed to, say, an OpDeclNode) + if (fcn instanceof OpApplNode) { + // Retrieve the function being applied + SymbolNode funcOperator = ((OpApplNode) fcn).getOperator(); + + // If the function being applied is a user-defined function + // (as opposed to OpDeclNode, FormalParamNode, builtin + // operator, or expression) + if (funcOperator instanceof OpDefNode && funcOperator.getKind() == UserDefinedOpKind) { + // Retrieve the function body expression + ExprOrOpArgNode funcBody = ((OpDefNode) funcOperator).getBody(); + + // if the function body is an OpApplNode (as opposed to, + // say, NumeralNode, DecimalNode, etc.) + if (funcBody instanceof OpApplNode && (((OpApplNode) funcBody).getOperator().getName() == OP_nrfs + || ((OpApplNode) funcBody).getOperator().getName() == OP_rfs)) { + + // find out how many arguments it is SUPPOSED to have + int numParms = ((OpApplNode) funcBody).getNumberOfBoundedBoundSymbols(); + + // If the function appears with numArgs >= 2 argument + // expressions, it must be declared with exactly numArgs + // parameters; and a function with numParms parameters in + // its definition should be applied to exactly numParms + // expressions, or 1 expression (representing arguments in + // tuple form), or 0 expressions (representing the + // function itself). Note: one cannot define a function + // with 0 arguments in TLA+. + if (numArgs >= 2 && numParms != numArgs) { + errors.addError(treeNode.getLocation(), + "Function '" + ((OpApplNode) sns[0]).getOperator().getName() + "' is defined with " + + numParms + " parameters, but is applied to " + numArgs + " arguments."); + return nullOAN; + } // end if + } // end if + } // end if + } // end if + + // Assert.check(numArgs > 0); + if (numArgs == 1) { + sns[1] = generateExpression(children[2], cm); + } else { + // If there is more than one arg we have to create a tuple for the arguments. + ExprOrOpArgNode[] exprs = new ExprNode[numArgs]; // One for each of the arguments + + // For each argument... + for (int lvi = 0; lvi < numArgs; lvi++) { + // Create the expression for that argument + exprs[lvi] = generateExpression(children[2 + 2 * lvi], cm); + } + // Create an application of $Tuple + sns[1] = new OpApplNode(OP_tup, exprs, treeNode, cm); + } + // Create the function application node. + return new OpApplNode(OP_fa, sns, treeNode, cm); + + case N_UnboundOrBoundChoose: + return processChoose(treeNode, children, cm); + + case N_BoundQuant: + return processBoundQuant(treeNode, children, cm); + + case N_UnboundQuant: + return processUnboundQuant(treeNode, children, cm); + + case N_IfThenElse: + sns = new ExprNode[3]; + sns[0] = generateExpression(children[1], cm); + sns[1] = generateExpression(children[3], cm); + sns[2] = generateExpression(children[5], cm); + return new OpApplNode(OP_ite, sns, treeNode, cm); + + case N_Case: + return processCase(treeNode, children, cm); + + case N_DisjList: + case N_ConjList: + sns = new ExprNode[children.length]; + for (int lvi = 0; lvi < sns.length; lvi++) { + sns[lvi] = generateExpression(children[lvi].heirs()[1], cm); + } + if (treeNode.isKind(N_DisjList)) + return new OpApplNode(OP_dl, sns, treeNode, cm); + else + return new OpApplNode(OP_cl, sns, treeNode, cm); + + case N_RecordComponent: // really RcdSelect in the API + sns = new ExprNode[2]; + sns[0] = generateExpression(children[0], cm); + sns[1] = new StringNode(children[2], false); + return new OpApplNode(OP_rs, sns, treeNode, cm); + + case N_SetOfFcns: /* [S -> T] */ + sns = new ExprNode[2]; + sns[0] = generateExpression(children[1], cm); + sns[1] = generateExpression(children[3], cm); + + return new OpApplNode(OP_sof, sns, treeNode, cm); + + case N_SubsetOf: + return processSubsetOf(treeNode, children, cm); + + case N_SetOfAll: + return processSetOfAll(treeNode, children, cm); + + case N_RcdConstructor: + return processRcdForms(OP_rc, treeNode, children, cm); + + case N_SetOfRcds: + return processRcdForms(OP_sor, treeNode, children, cm); + + case N_FcnConst: + return processFcnConst(treeNode, children, cm); + + case N_ActionExpr: + case N_FairnessExpr: + return processAction(treeNode, children, cm); + + case N_Except: + return processExcept(treeNode, children, cm); + + case N_LetIn: + return processLetIn(treeNode, children, cm); + + case N_Lambda: + errors.addError(treeNode.getLocation(), "LAMBDA expression used where an expression is required."); + return null; + + case N_Label: + LabelNode ln = generateLabel(treeNode, cm); + if (ln.isAssumeProve && !allowLabeledAP) { + errors.addError(treeNode.getLocation(), "Labeled ASSUME/PROVE used where an expression is required."); + } + return ln; + + default: + errors.addError(treeNode.getLocation(), "Unsupported expression type `" + treeNode.getImage() + "'."); + return null; + + } // end switch + + } // end generateExpression() + + /*************************************************************************** + * The following variables are used in the processing of ASSUME/PROVE * nodes, + * and the labels that lie within them. * + ***************************************************************************/ + private int assumeProveDepth = 0; + /*********************************************************************** + * The nesting depth of ASSUME/PROVEs within which the node corrently * being + * processed lies. In * * THEOREM ASSUME P PROVE Q * * the nodes for P and Q are + * at depth 1 (not 0). * + ***********************************************************************/ + + private ThmOrAssumpDefNode currentGoal = null; + private int currentGoalClause; + /*********************************************************************** + * currentGoal is the named theorem or proof-step node within * which lies the + * current node that is being processed, or null if * we're not inside such a + * step. If currentGoal != null, and we're * within an ASSUME/PROVE step (which + * is true iff assumeProveDepth > * 0), then then currentGoalClause equals the + * clause of within which * the node being processed lies. (If we're processing + * Q in ASSUME P * PROVE Q, then Q = 1.) * + ***********************************************************************/ + + private static final int maxAPDepth = 100; + /*********************************************************************** + * Maximum allowed nesting depth of ASSUME/PROVES. 100 ought to * suffice. * + ***********************************************************************/ + + private boolean[] inScopeOfAPDecl = new boolean[maxAPDepth]; + + /*********************************************************************** + * For all i \leq assumeProveDepth, inScopeOfAPDecl[i] = true iff the * node + * currently being processed lies within a declaration made at * + * assumeProveDepth = i. Thus in * * THEOREM ASSUME NEW x \in S, * P, * ASSUME R + * , ACTION A PROVE T * PROVE Q * * inScopeOfAPDecl[1] equals false when S is + * being processed and * equals true when P, Q, R, and T are being processed. * + * * inScopeOfAPDecl[2] equals false when R is being processed and true * when T + * is being processed. * + ***********************************************************************/ + + private boolean noLabelsAllowed() { + /*********************************************************************** + * This returns true if we are inside an ASSUME/PROVE node where a * label is + * not allowed because we are in the scope of a declaration * from an inner + * ASSUME/PROVE. * + ***********************************************************************/ + for (int i = 2; i <= assumeProveDepth; i++) { + if (inScopeOfAPDecl[i]) { + return true; + } + ; + } + ; + return false; + } + + private final boolean illegalLabelRef(LabelNode ln, SyntaxTreeNode stn) throws AbortException { + /*********************************************************************** + * True iff we are currently in a point where a reference to this * label is + * illegal because it lies inside an ASSUME/PROVE clause from * outside the + * scope of a symbol it may contain. See the comments in * AssumeProveNode.java + * for an explanation of this method. * + ***********************************************************************/ + ThmOrAssumpDefNode goal = ln.goal; + if (goal == null) { + return false; + } + ; + if ((goal.getBody() == null) || (goal.getBody().getKind() != AssumeProveKind)) { + errors.addAbort(stn.getLocation(), + "Internal error: Expecting label to be in AssumeProveNode, " + "but it's not."); + } + ; + AssumeProveNode ap = (AssumeProveNode) goal.getBody(); + return ap.inScopeOfDecl[ln.goalClause] && (goal.isSuffices() == ap.inProof); + } // illegalLabelRef + + private final boolean illegalAPPosRef(AssumeProveNode ap, int pos) { + /*********************************************************************** + * True iff it is illegal for the node currently being processed to * access the + * clause number pos of AssumeProve node ap because it is * not in the scope of + * symbols declared in previous ASSUME clauses of * ap. See the comments in + * AssumeProveNode.java for an explanation of * this method. * + ***********************************************************************/ + return ap.inScopeOfDecl[pos - 1] && ((ap.getGoal() == null) || (ap.getGoal().isSuffices() == ap.inProof)); + } // XXXX Currently, this handles both label and positional subexpression // specifiers. To handle positional ones, we need to know what // the position is. // private final boolean withinScopeOf(SemanticNode goal) { - /************************************************************************* - * Returns true iff the node currently being processed lies within the * - * scope of (the declarations made in the ASSUME clauses) of goal, which * - * will be a TheoremNode or a proof-step node. * - * * - * XXXXX Dummy implementation for now. * - *************************************************************************/ + /************************************************************************* + * Returns true iff the node currently being processed lies within the * scope + * of (the declarations made in the ASSUME clauses) of goal, which * will be a + * TheoremNode or a proof-step node. * * XXXXX Dummy implementation for now. * + *************************************************************************/ // return true; // if (goal == null) {return true;} ; // return false ; // } - // The following objects added 10 Feb 2011 by LL are used to check that - // a []ASSUME does not lie within the scope of (the assumptions of) - // an ordinary ASSUME. It does this by declaring the dummy OpDeclNode - // InAssumeDummyNode as if it were declared within the ASSUME and then - // checking if it's defined where the []ASSUME is used. - private final static UniqueString S_InAssume = UniqueString.uniqueStringOf("$$InAssume"); - private final static OpDeclNode InAssumeDummyNode = - new OpDeclNode(S_InAssume, 0, 0, 0, null, null, null) ; - - private final AssumeProveNode - generateAssumeProve(TreeNode treeNode, ModuleNode cm) - /*********************************************************************** - * Added by LL on 17 Mar 2007. * - ***********************************************************************/ - throws AbortException { - // The following flag is used to record if this is a - // Following code added on 9 Nov 2009 so that the goal - // field of the AssumeProveNode is null unless this is - // a top-level Assume/Prove. - ThmOrAssumpDefNode cg = null ; - if (assumeProveDepth == 0) { - cg = currentGoal; - } ; - assumeProveDepth++ ; - AssumeProveNode apn = new AssumeProveNode(treeNode, cg) ; - TreeNode[] children = treeNode.heirs(); - int numOfChildren = children.length; - if (numOfChildren % 2 != 0) { - throw new WrongInvocationException("AssumeProve has odd number of children"); } ; - int numOfAssumptions = (numOfChildren - 2) / 2 ; - // Check if this is a []ASSUME and that the PROVE matches - // the ASSUME. - boolean isBoxAssumeProve = false; - String proveString = children[children.length -2].getImage(); - if (children[0].getImage().equals("[]ASSUME")) { - isBoxAssumeProve = true; - if (!proveString.equals("[]PROVE")) { - errors.addError(children[0].getLocation(), - "[]ASSUME matched by PROVE instead of []PROVE"); - } - } else { - if (!proveString.equals("PROVE")) { - errors.addError(children[0].getLocation(), - "ASSUME matched by []PROVE instead of PROVE"); - } - } - apn.setIsBoxAssumeProve(isBoxAssumeProve); - - apn.assumes = new LevelNode[numOfAssumptions] ; - boolean inDeclScope = false ; - /****************************************************************** - * Set true when after we've processed a declaration. * - ******************************************************************/ - apn.inScopeOfDecl = new boolean[numOfAssumptions + 1] ; - apn.inScopeOfDecl[0] = false ; - inScopeOfAPDecl[assumeProveDepth] = false ; - if (assumeProveDepth != 1) { - /****************************************************************** - * The context is pushed by the top-level caller of * - * generateAssumeProve, which is processTheorem. * - ******************************************************************/ - symbolTable.pushContext(new Context(moduleTable, errors)) ; - /****************************************************************** - * I don't understand exactly what's going on here, but this * - * seems to be the magic incantation for starting a new context * - * into which declarations among the assumptions should be put * - * for interpreting expressions later in the assumption list and * - * in the "prove" expression. * - ******************************************************************/ - } // if - - if (isBoxAssumeProve) { - if (symbolTable.resolveSymbol(S_InAssume) != null) { - errors.addError(children[0].getLocation(), - "[]ASSUME used within the scope of an ordinary ASSUME's assumptions"); - } - } else { - if (symbolTable.resolveSymbol(S_InAssume) == null) { - symbolTable.addSymbol(S_InAssume, InAssumeDummyNode); - } - } - - if (assumeProveDepth == 1) {currentGoalClause = 0 ; } ; - for (int i = 0 ; i < numOfAssumptions ; i++) { - /****************************************************************** - * The current assumption is assumption number i+1 (in human * - * numbering). * - ******************************************************************/ - apn.inScopeOfDecl[i+1] = apn.inScopeOfDecl[i] ; - TreeNode tn = children[2*i + 1] ; - switch (tn.getKind()) { - case N_AssumeProve : - apn.assumes[i] = generateAssumeProve(tn, cm) ; - break ; - case N_NewSymb : - apn.assumes[i] = generateNewSymb(tn, cm) ; - apn.inScopeOfDecl[i+1] = true ; - inScopeOfAPDecl[assumeProveDepth] = true ; - OpDeclNode[] odn = new OpDeclNode[1] ; - odn[0] = ((NewSymbNode) apn.assumes[i]).getOpDeclNode() ; - break ; - default : - /************************************************************** - * Should be an expression node or a labeled ASSUME/PROVE. * - **************************************************************/ + // The following objects added 10 Feb 2011 by LL are used to check that + // a []ASSUME does not lie within the scope of (the assumptions of) + // an ordinary ASSUME. It does this by declaring the dummy OpDeclNode + // InAssumeDummyNode as if it were declared within the ASSUME and then + // checking if it's defined where the []ASSUME is used. + private final static UniqueString S_InAssume = UniqueString.uniqueStringOf("$$InAssume"); + private final static OpDeclNode InAssumeDummyNode = new OpDeclNode(S_InAssume, 0, 0, 0, null, null, null); + + private final AssumeProveNode generateAssumeProve(TreeNode treeNode, ModuleNode cm) + /*********************************************************************** + * Added by LL on 17 Mar 2007. * + ***********************************************************************/ + throws AbortException { + // The following flag is used to record if this is a + // Following code added on 9 Nov 2009 so that the goal + // field of the AssumeProveNode is null unless this is + // a top-level Assume/Prove. + ThmOrAssumpDefNode cg = null; + if (assumeProveDepth == 0) { + cg = currentGoal; + } + ; + assumeProveDepth++; + AssumeProveNode apn = new AssumeProveNode(treeNode, cg); + TreeNode[] children = treeNode.heirs(); + int numOfChildren = children.length; + if (numOfChildren % 2 != 0) { + throw new WrongInvocationException("AssumeProve has odd number of children"); + } + ; + int numOfAssumptions = (numOfChildren - 2) / 2; + // Check if this is a []ASSUME and that the PROVE matches + // the ASSUME. + boolean isBoxAssumeProve = false; + String proveString = children[children.length - 2].getImage(); + if (children[0].getImage().equals("[]ASSUME")) { + isBoxAssumeProve = true; + if (!proveString.equals("[]PROVE")) { + errors.addError(children[0].getLocation(), "[]ASSUME matched by PROVE instead of []PROVE"); + } + } else { + if (!proveString.equals("PROVE")) { + errors.addError(children[0].getLocation(), "ASSUME matched by []PROVE instead of PROVE"); + } + } + apn.setIsBoxAssumeProve(isBoxAssumeProve); + + apn.assumes = new LevelNode[numOfAssumptions]; + boolean inDeclScope = false; + /****************************************************************** + * Set true when after we've processed a declaration. * + ******************************************************************/ + apn.inScopeOfDecl = new boolean[numOfAssumptions + 1]; + apn.inScopeOfDecl[0] = false; + inScopeOfAPDecl[assumeProveDepth] = false; + if (assumeProveDepth != 1) { + /****************************************************************** + * The context is pushed by the top-level caller of * generateAssumeProve, which + * is processTheorem. * + ******************************************************************/ + symbolTable.pushContext(new Context(moduleTable, errors)); + /****************************************************************** + * I don't understand exactly what's going on here, but this * seems to be the + * magic incantation for starting a new context * into which declarations among + * the assumptions should be put * for interpreting expressions later in the + * assumption list and * in the "prove" expression. * + ******************************************************************/ + } // if + + if (isBoxAssumeProve) { + if (symbolTable.resolveSymbol(S_InAssume) != null) { + errors.addError(children[0].getLocation(), + "[]ASSUME used within the scope of an ordinary ASSUME's assumptions"); + } + } else { + if (symbolTable.resolveSymbol(S_InAssume) == null) { + symbolTable.addSymbol(S_InAssume, InAssumeDummyNode); + } + } + + if (assumeProveDepth == 1) { + currentGoalClause = 0; + } + ; + for (int i = 0; i < numOfAssumptions; i++) { + /****************************************************************** + * The current assumption is assumption number i+1 (in human * numbering). * + ******************************************************************/ + apn.inScopeOfDecl[i + 1] = apn.inScopeOfDecl[i]; + TreeNode tn = children[2 * i + 1]; + switch (tn.getKind()) { + case N_AssumeProve: + apn.assumes[i] = generateAssumeProve(tn, cm); + break; + case N_NewSymb: + apn.assumes[i] = generateNewSymb(tn, cm); + apn.inScopeOfDecl[i + 1] = true; + inScopeOfAPDecl[assumeProveDepth] = true; + OpDeclNode[] odn = new OpDeclNode[1]; + odn[0] = ((NewSymbNode) apn.assumes[i]).getOpDeclNode(); + break; + default: + /************************************************************** + * Should be an expression node or a labeled ASSUME/PROVE. * + **************************************************************/ // apn.assumes[i] = generateExpression(tn, cm) ; - apn.assumes[i] = generateExpressionOrLAP(tn, cm, true) ; - break ; - } ; // end switch - if (assumeProveDepth == 1) { - currentGoalClause = currentGoalClause + 1; - } ; - } ; // end for - - apn.prove = generateExpression(children[numOfChildren - 1], cm) ; - - if (assumeProveDepth != 1) { - symbolTable.popContext(); - /**************************************************************** - * Restore the current context, removing the declarations by * - * the assumptions. assumption list. * - ****************************************************************/ - } ; - assumeProveDepth-- ; - return apn ; } - - private final NewSymbNode generateNewSymb(TreeNode treeNode, ModuleNode cm) - /*********************************************************************** - * Added by LL on 21 Mar 2007. * - ***********************************************************************/ - throws AbortException { - TreeNode[] children = treeNode.heirs(); - int numOfChildren = children.length; - - /********************************************************************* - * Determine if this node should have a non-null "set" field, which * - * is the case iff the declaration ends with "\in S", in which case * - * set the set field to the ExprNode for S. * - *********************************************************************/ - ExprNode set = null ; - if (children[numOfChildren-2].getKind() == IN) { - set = generateExpression(children[numOfChildren-1], cm) ;} ; - - int declKind ; - int declLevel ; - /********************************************************************* - * Set declKind to the kind of the NewSymbNode object's OpDeclNode. * - * Set declLevel to its level. * - * * - * Start by setting i to 1 or 0 depending on whether the declaration * - * begins with a NEW token. * - *********************************************************************/ - int i = 0 ; - if (children[0].getKind() == NEW) {i = 1;}; - - /********************************************************************* - * Set declKind, and leave i so that children[i+1] is the syntax * - * tree node describing the declared symbol and its arity. * - * * - * We do this by seeing if the next token is "CONSTANT", "VARIABLE", * - * etc. If it isn't, then the declaration begins "NEW id", so it's * - * a CONSTANT declaration and i must be set to 0. * - *********************************************************************/ - switch (children[i].getKind()) { - case CONSTANT : - declKind = NewConstantKind ; - declLevel = ConstantLevel; - break ; - case VARIABLE : - declKind = NewVariableKind ; - declLevel = VariableLevel; - break ; - case STATE : - declKind = NewStateKind ; - declLevel = VariableLevel; - break ; - case ACTION : - declKind = NewActionKind ; - declLevel = ActionLevel; - break ; - case TEMPORAL : - declKind = NewTemporalKind ; - declLevel = TemporalLevel; - break ; - default : - declKind = NewConstantKind ; - declLevel = ConstantLevel; - i = 0 ; - }; // switch - return new NewSymbNode( - buildParameter(children[i+1], declKind, declLevel, cm, true), - set, - treeNode) ; - } - - private final ExprNode - processChoose(TreeNode treeNode, TreeNode[] children, ModuleNode cm) - throws AbortException { - ExprNode[] semanticNode = new ExprNode[1]; - TreeNode[] syntaxTreeNode = children[2].heirs(); - OpApplNode result; - - symbolTable.pushContext( new Context(moduleTable, errors) ); - - if (syntaxTreeNode == null || syntaxTreeNode.length == 0) { - // unbounded case - FormalParamNode[] odn; - boolean tuple; - - // either Tuple or single identifier - if (children[1].isKind( N_IdentifierTuple)) { - syntaxTreeNode = children[1].heirs(); - odn = new FormalParamNode[ syntaxTreeNode.length / 2 ]; - for (int lvj = 0; lvj < syntaxTreeNode.length / 2; lvj++ ) { - odn[lvj] = new FormalParamNode( - syntaxTreeNode[ 2*lvj+1 ].getUS(), 0, - syntaxTreeNode[ 2*lvj+1 ], symbolTable, cm); - } - tuple = true; - } - else { - odn = new FormalParamNode[1]; - odn[0] = new FormalParamNode(children[1].getUS(), 0, children[0], - symbolTable, cm); - tuple = false; - } - pushFormalParams(odn) ; - /******************************************************************* - * Push formal parameters on Last(LS).paramSeq for processing the * - * body. * - *******************************************************************/ - semanticNode[0] = generateExpression( children[4], cm ); - popFormalParams() ; - - result = new OpApplNode(OP_uc, semanticNode, odn, treeNode, cm); - } - else { - // bounded case - FormalParamNode[][] odna = new FormalParamNode[1][0]; - boolean[] tuples = new boolean[1]; - ExprNode[] exprs = new ExprNode[1]; - - // syntaxTreeNode can be reused further down. - exprs[0] = generateExpression(syntaxTreeNode[1], cm); - - if ( children[1].isKind( N_IdentifierTuple ) ) { - syntaxTreeNode = children[1].heirs(); - odna[0] = new FormalParamNode[ syntaxTreeNode.length / 2 ]; - for (int lvj = 0; lvj < syntaxTreeNode.length / 2; lvj++ ) { - odna[0][lvj] = - new FormalParamNode( - syntaxTreeNode[ 2*lvj+1 ].getUS(), 0, - syntaxTreeNode[2*lvj+1], symbolTable, cm); - } - tuples[0] = true; - } - else { - odna[0] = new FormalParamNode[1]; - odna[0][0] = new FormalParamNode(children[1].getUS(), 0, - children[1], symbolTable, cm); - tuples[0] = false; - } - pushFormalParams(flattenParams(odna)) ; - /******************************************************************* - * Push the bound variables on Last(LS).paramSeq and process the * - * body of the CHOOSE. * - *******************************************************************/ - semanticNode[0] = generateExpression( children[4], cm ); - popFormalParams() ; - - result = new OpApplNode(OP_bc, null, semanticNode, odna, - tuples, exprs, treeNode, cm); - } - symbolTable.popContext(); - return result; - } - - private final ExprNode processBoundQuant(TreeNode treeNode, - TreeNode[] children, - ModuleNode cm) - throws AbortException { - // Create data structures for all parameters - int length = (children.length - 2) / 2; - FormalParamNode[][] odna = new FormalParamNode[length][0]; - boolean[] bt = new boolean[length]; - ExprNode[] ea = new ExprNode[ length ]; - - // then process parameters - symbolTable.pushContext( new Context(moduleTable, errors) ); - processQuantBoundArgs( children, 1, odna, bt, ea, cm ); - - // process expression - ExprNode semanticNode[] = new ExprNode[1]; - pushFormalParams(flattenParams(odna)) ; - /******************************************************************* - * Push the bound variables on Last(LS).paramSeq and process the * - * body of the quantified expression. * - *******************************************************************/ - semanticNode[0] = generateExpression( children[ children.length - 1 ], cm ); - popFormalParams() ; - - symbolTable.popContext(); - - // then return new node. - // which variety? look under first child. - boolean isExists = children[0].getUS().equals( S_e ) - || children[0].getUS().equals( S_ex ); - if (isExists) { - return new OpApplNode(OP_be, null, semanticNode, odna, - bt, ea, treeNode, cm); - } - else { - return new OpApplNode(OP_bf, null, semanticNode, odna, - bt, ea, treeNode, cm); - } - } - - private final ExprNode processUnboundQuant(TreeNode treeNode, - TreeNode[] children, - ModuleNode cm) - throws AbortException { - // which variety? look under first child. - UniqueString us = children[0].getUS(); - UniqueString r_us; - int level; - - if ( us.equals (S_e ) ) { r_us = OP_ue; level = 0; } // \E - else if ( us.equals (S_ex) ) { r_us = OP_ue; level = 0; } // \exists - else if ( us.equals (S_f ) ) { r_us = OP_uf; level = 0; } // \A - else if ( us.equals (S_fx) ) { r_us = OP_uf; level = 0; } // \always - else if ( us.equals (S_te) ) { r_us = OP_te; level = 1; } // \EE - else { r_us = OP_tf; level = 1; } // \AA - - // Process all identifiers bound by thus quantifier - int length = ( children.length - 2 ) / 2; - FormalParamNode odn[] = new FormalParamNode[ length ]; - symbolTable.pushContext( new Context(moduleTable, errors) ); - - for ( int lvi = 0; lvi < length; lvi ++ ) { - odn[lvi] = new FormalParamNode( - children[2*lvi +1].getUS(), 0, - children[2*lvi +1], symbolTable, cm); - } - - // now the expression - ExprNode semanticNode[] = new ExprNode[1]; - pushFormalParams(odn) ; - /********************************************************************** - * Push formal parameters on Last(LS).paramSeq for processing the * - * body. * - **********************************************************************/ - semanticNode[0] = generateExpression(children[children.length-1], cm); - popFormalParams() ; - - // wrap up. - symbolTable.popContext(); - return new OpApplNode(r_us, semanticNode, odn, treeNode, cm); - } - - private final ExprNode processCase(TreeNode treeNode, TreeNode[] children, ModuleNode cm) - throws AbortException { - // number of arms to CASE-expr, not counting the CASE nodse itself - // or the []-separators - int armCount = children.length/2; - ExprNode[] casePairs = new ExprNode[armCount]; - - for (int lvi = 0; lvi < armCount; lvi++) { - TreeNode caseArm = children[2*lvi+1]; - TreeNode[] ss = caseArm.heirs(); - ExprNode[] sops = new ExprNode[2]; - if (!caseArm.isKind(N_OtherArm)) { - sops[0] = this.generateExpression(ss[0], cm); - } - sops[1] = this.generateExpression(ss[2], cm); - casePairs[lvi] = new OpApplNode(OP_pair, sops, caseArm, cm); - } - return new OpApplNode(OP_case, casePairs, treeNode, cm); - } - - private final ExprNode processSubsetOf(TreeNode treeNode, - TreeNode children[], - ModuleNode cm ) - throws AbortException { - // cfr. unbounded choose - ExprNode[] ops = new ExprNode[1]; - FormalParamNode[][] odna = new FormalParamNode[1][0]; - boolean[] tuples = new boolean[1]; - ExprNode[] exprs = new ExprNode[1]; - - exprs[0] = generateExpression( children[3], cm ); - - symbolTable.pushContext( new Context(moduleTable, errors) ); - - if ( children[1].isKind( N_IdentifierTuple ) ) { - TreeNode[] ss = children[1].heirs(); - odna[0] = new FormalParamNode[ ss.length / 2 ]; - for (int lvj = 0; lvj < ss.length / 2; lvj++ ) { - odna[0][lvj] = new FormalParamNode( - ss[ 2*lvj+1 ].getUS(), 0, ss[ 2*lvj+1 ], - symbolTable, cm); - } - tuples[0] = true; - } - else { - odna[0] = new FormalParamNode[1]; - odna[0][0] = new FormalParamNode( - children[1].getUS(), 0, children[1], - symbolTable, cm); - tuples[0] = false; - } - - pushFormalParams(flattenParams(odna)) ; - /********************************************************************* - * Push the bound variables on Last(LS).paramSeq and process the * - * body of the CHOOSE. * - *********************************************************************/ - ops[0] = generateExpression( children[5], cm ); - popFormalParams() ; - - symbolTable.popContext(); - return new OpApplNode(OP_sso, null, ops, odna, tuples, exprs, - treeNode, cm); - } - - private final ExprNode processSetOfAll(TreeNode treeNode, - TreeNode children[], - ModuleNode cm) - throws AbortException { - ExprNode[] ops = new ExprNode[1]; - int length = (children.length - 3) / 2; - FormalParamNode[][] odna = new FormalParamNode[length][0]; - boolean[] tuples = new boolean[length]; - ExprNode[] exprs = new ExprNode[length]; - - symbolTable.pushContext(new Context(moduleTable, errors)); - processQuantBoundArgs(children, 3, odna, tuples, exprs, cm); - - pushFormalParams(flattenParams(odna)) ; - /********************************************************************* - * Push the bound variables on Last(LS).paramSeq and process the * - * body of the CHOOSE. * - *********************************************************************/ - ops[0] = generateExpression( children[1], cm ); - popFormalParams() ; - - symbolTable.popContext(); - - return new OpApplNode(OP_soa, null, ops, odna, tuples, exprs, - treeNode, cm); - } - - private final ExprNode processFcnConst(TreeNode treeNode, - TreeNode children[], ModuleNode cm) - throws AbortException { - ExprNode[] ops = new ExprNode[1]; - int length = (children.length - 3) / 2; // number of args to function constant - FormalParamNode[][] odna = new FormalParamNode[length][0]; - boolean[] tuples = new boolean[length]; - ExprNode[] exprs = new ExprNode[length]; - - symbolTable.pushContext( new Context(moduleTable, errors) ); - processQuantBoundArgs( children, 1, odna, tuples, exprs, cm ); - - pushFormalParams(flattenParams(odna)) ; - /********************************************************************* - * Push the bound variables on Last(LS).paramSeq and process the * - * body of the CHOOSE. * - *********************************************************************/ - ops[0] = generateExpression( children[children.length-2], cm ); - popFormalParams() ; - - symbolTable.popContext(); - - return new OpApplNode(OP_fc, null, ops, odna, tuples, exprs, - treeNode, cm); - } - - /** - * This method processes both the RecordConstructor construct and the - * SetOfRecords operator. The two are essentially identical except for - * which builtin operator is used. - */ - private final ExprNode processRcdForms(UniqueString operator, - TreeNode treeNode, - TreeNode children[], - ModuleNode cm) - throws AbortException { - // handles RcdConstructor or SetOfRcds - int length = (children.length - 1) / 2; - - // Create an array of pairs to handle all of the fields mentioned in the form - ExprNode[] fieldPairs = new ExprNode[length]; - // Create an array of Unique Strings to check uniqueness of fields. Not very efficient - // but a HashTable may be overkill most of the time. - // Remember a label is a string. - UniqueString [] labels = new UniqueString[length]; - - // For each field in the RcdConstructor or SetOfRecords - for ( int lvi = 0; lvi < length; lvi++ ) { - TreeNode syntaxTreeNode[] = children[ 2*lvi + 1].heirs(); - - // Create a pair of SemanticNodes to represent one record component - ExprNode sops[] = new ExprNode[2]; - - // The first one gets a new StringNode indicating the field name - sops[0] = new StringNode(syntaxTreeNode[0], false); - labels[ lvi ] = ((StringNode) sops[0]).getRep(); - for ( int cmpIndex=0; cmpIndex < lvi; cmpIndex++) { - if ( labels[lvi].compareTo(labels[cmpIndex]) == 0) { - errors.addError(syntaxTreeNode[0].getLocation(), - "Non-unique fields in constructor."); - } - } - - // The second one gets the expression indicating the field value (or set of values) - sops[1] = generateExpression( syntaxTreeNode[2], cm ); - - // Put the $Pair OpApplNode into the fieldPairs array - fieldPairs[lvi] = new OpApplNode(OP_pair, sops, children[2*lvi+1], cm); - } - // Create the top-level OpApplNode, for either the SetOfRecords op - // or the RcdConstructor op. - return new OpApplNode(operator, fieldPairs, treeNode, cm); - } - - private final ExprNode processAction(TreeNode treeNode, TreeNode children[], ModuleNode cm) - throws AbortException { - UniqueString match = children[0].getUS(); - if ( match.equals( S_a ) ) - match = OP_aa; - else if ( match.equals( S_brack ) ) - match = OP_sa; - else if ( match.equals( S_sf) ) - match = OP_sf; - else if ( match.equals( S_wf) ) - match = OP_wf; - - ExprNode ops[] = new ExprNode[2]; - ops[0] = generateExpression( children[1], cm ); - ops[1] = generateExpression( children[3], cm ); - return new OpApplNode( match, ops, treeNode, cm); - } - - private final ExprNode processExcept(TreeNode treeNode, TreeNode[] children, ModuleNode cm) - throws AbortException { - int numExcepts = (children.length-3)/2 ; // number of ExceptSpec's; - ExprNode[] operands = new ExprNode[numExcepts+1]; // 1 for each ExceptionSpec + first expr - OpApplNode excNode; // Holds OpApplNode for $Except operator - OpApplNode excSpecNode; // Holds $Pair node for ExceptSpec - - // The first operand of the $Except operator is the expression to - // which the exceptions apply Note this first operand is generated - // BEFORE the $Except node is stacked in the next couple of lines, - // because an @ in the first expression does NOT refer to the - // $Except node currently being generated, but to the next outer - // $Except node. - operands[0] = generateExpression( children[ 1 ], cm ); - - // Create the $Except OpApplNode that will be returned by this - // method. We create it now, and fill out its contents later, - // because we need a reference to it in order to process @ properly. - excNode = new OpApplNode( OP_exc, operands, treeNode, cm); - - // for each of the ExceptSpecs produce another element of the operands array - for ( int excSpecIx = 0; excSpecIx < numExcepts; excSpecIx++ ) { - TreeNode[] syntaxTreeNode = children[3 + 2*excSpecIx].heirs(); // extract ExceptSpec - ExprNode[] sops = new ExprNode[2]; // Each ExceptionSpec is a $Pair - int slength = syntaxTreeNode.length - 3; // # of ExceptComponents in ExceptSpec - ExprNode[] ssops = new ExprNode[ slength ]; // to store ExceptComponents - - // Process the LHS of the ExceptSpec - - // for each ExceptComponent of the form .h or [i] or [i,j,k] add - // an arg to $SEQ node and add build up the syntax tree for - // exceptionTarget - for ( int excCompIx = 0; excCompIx < slength ; excCompIx++ ) { - // the heirs of an ExceptComponent - TreeNode subSyntaxTreeNode[] = syntaxTreeNode[ 1 + excCompIx ].heirs(); - - if (subSyntaxTreeNode[0].getUS().equals( S_brack ) ) { - // The first heir is "[" , indicates one or more fcn args; - // add expressions as function args - if ( subSyntaxTreeNode.length > 3 ) { - // if so, must generate a tuple for comma list of fcn args - int sslength = (subSyntaxTreeNode.length-1)/2; // number of func args in comma list - ExprNode[] sssops = new ExprNode[ sslength ]; // holds the comma list of fcn args - - // for each of multiple function args in the ExceptComponent - for (int fArgIx=0; fArgIx < sslength; fArgIx++ ) { - sssops[ fArgIx ] = generateExpression( subSyntaxTreeNode[1+ 2*fArgIx], cm); - } - - // add one ExceptComponent to vector of ExceptComponents - ssops[ excCompIx ] = - new OpApplNode(OP_tup, sssops, syntaxTreeNode[2*excCompIx+1], cm); - } - else { - // add one ExceptComponent to vector of ExceptComponents - ssops[ excCompIx ] = generateExpression( subSyntaxTreeNode[1], cm ); - } - } - else { - // otherwise a "." indicates record selection; add a - // StringNode operand as record selector add one - // ExceptComponent to vector of ExceptComponents - ssops[ excCompIx ] = new StringNode(subSyntaxTreeNode[1], false); - } - } // end for (each ExceptionComponent) - - // Create OpAppl for $SEQ applied to array of ExceptComponents - // following record or func expr or ! - // This is the LHS of an ExceptionSpec - sops[0] = new OpApplNode(OP_seq, ssops, children[3 + 2*excSpecIx], cm); - - // Process the RHS of the ExceptionSpec - - // Create exceptSpec node now, so that it can be available in - // case the RHS expression of this ExceptSpec contains an @ - excSpecNode = new OpApplNode(OP_pair, sops, children[3+2*excSpecIx], cm); - - // Push the except node and the except spec node on stacks so - // that the RHS of the ExceptSpec, which might contain an @, can - // be evaluated in their "context". - excSpecStack.push(excSpecNode); - excStack.push(excNode); - - // Generate the expression constituting the RHS of the - // ExceptionSpec allow @ in the context of this expression. - sops[1] = generateExpression(syntaxTreeNode[syntaxTreeNode.length-1], cm ); - - // Pop them back off - excSpecStack.pop(); - excStack.pop(); - - // Store excSpecNode as another operand of $Except - operands[ excSpecIx+1 ] = excSpecNode; - } // end for (each ExceptionSpec) - - return excNode; - } // end processExcept() - - /** - * This method generates an expression tree or an OpArgNode as an - * argument to an OpApplNode - * - * mainOp is the operator under that node - * mainSTN is the "parent" OpApplNode, used in case an error message - * must be generated - * argPosition is the argument position (counting from 0) that treeNode - * represents - * argRoot is the argument syntax tree that either becomes an - * ExprNode or an OpArgNode - * mn is the module these expressions are part of - */ - private ExprOrOpArgNode generateExprOrOpArg(SymbolNode mainOp, - TreeNode mainSTN, - int argPosition, - TreeNode argRoot, - ModuleNode mn) - throws AbortException { - SymbolNode argOp = null; - // the SymbolNode that heads the argRoot expression - int argArity; - // number of actual arguments under argRoot - int arityExpected; - // arity that mainOp expects of argument number <argPosition> - - if ( mainOp == null) { - errors.addError( - mainSTN.getLocation(), - "Unable to generate expression or operator argument; " + - "this is probably because of previously reported errors." ); - return nullOAN; - } - - // Are we sure the "operator" is not a ModuleNode? - if (mainOp instanceof ModuleNode) { - errors.addError(mainSTN.getLocation(), - "Module name '" + mainOp.getName() + - "' used as operator."); - return nullOAN; - } - - // Are there too many arguments to mainOp? - if (argPosition+1 > mainOp.getArity()) { - errors.addError(mainSTN.getLocation(), - "Too many arguments for operator '" + - mainOp.getName() + "'. There should be only " + - mainOp.getArity() + "." ); - - return nullOAN; - } - - // For user-defined mainOp check the FormalParamNodes array - // associated with the mainOp to find out whether it expects an - // operator argument or an ordinary expression argument in - // position number argPosition. (Only UserDefined ops can have - // other multi-arg ops passed to them as params, so we can just - // assign arityExpected = 0 in other cases.) - /*********************************************************************** - * In SANY2, mainOp can also be a ModuleInstanceKind, so the if test * - * was modified appropriately. * - ***********************************************************************/ - if ( (mainOp.getKind() == UserDefinedOpKind) - || (mainOp.getKind() == ModuleInstanceKind)) { - arityExpected = - ((OpDefNode)mainOp).getParams()[argPosition].getArity(); - } else { - arityExpected = 0; - }; - - // if mainOp expects zero arguments, then it expects an ordinary - // expression arg in this position, so generate an expression. - // Any errors will be found in the generateExpression method. - if ( arityExpected == 0 ) { - return generateExpression( argRoot, mn ); - } - else { - // otherwise, we are expecting an OpArg or Lambda - /********************************************************************* - * The following test was originally * - * * - * if (argRoot.getImage().equals("N_OpApplication")) * - * * - * However, that was not sufficient, because there are lots of other * - * nodes that represent expressions. At best, these caused weird * - * error messages. At worst, as in the case of a number or string, * - * they caused an array out of bounds exception when generateGenID * - * was called a few lines later. I hope that the following test * - * covers all the cases in which this should be called with argRoot * - * not an expression. * - * * - * Change made by LL on 9 May 2007. * - *********************************************************************/ - if ( !( argRoot.getImage().equals("N_GeneralId") - || argRoot.getImage().equals("N_GenInfixOp") - || argRoot.getImage().equals("N_GenNonExpPrefixOp") - || argRoot.getImage().equals("N_GenPostfixOp") - || argRoot.getImage().equals("N_GenPrefixOp") - || argRoot.getImage().equals("N_Lambda") )) { - errors.addError( - argRoot.getLocation(), - "An expression appears as argument number " + (argPosition+1) + - " (counting from 1) to operator '" + mainOp.getName() + - "', in a position an operator is required."); - return nullOAN; - } // end if - - if (argRoot.getKind() == N_Lambda) { - /******************************************************************* - * The argument is a lambda expression. * - *******************************************************************/ - argOp = generateLambda(argRoot, mn) ; - if (arityExpected == argOp.getArity()) - {return new OpArgNode(argOp, argRoot, mn);} // if - else { errors.addError( - mainSTN.getLocation(), - "Lambda expression with arity " + argOp.getArity() + - " used as argument " + (argPosition+1) + - " of operator `" + mainOp.getName() + - "', \nbut an operator of arity " + arityExpected - + " is required."); - return nullOpArg; - } // else - } // if (argRoot.kind == N_Lambda) - else { - /******************************************************************* - * The argument is not a lambda expression. * - *******************************************************************/ - - /******************************************************************* - * If the argument is a GeneralId node, then we use * - * genIdToSelector and selectorToNode to generate the OpArg node. * - *******************************************************************/ - if (argRoot.getKind() == N_GeneralId) { - return (ExprOrOpArgNode) - selectorToNode(genIdToSelector((SyntaxTreeNode) argRoot), - arityExpected, false, false, mn); - } ; - - /******************************************************************* - * If the argument is not a GeneralId node, we use the code from * - * SANY1 to generate the OpArg node. * - *******************************************************************/ - GenID genID = generateGenID(argRoot, mn); - argOp = genID.getFullyQualifiedOp(); ; - - // If the symbol has not been defined, then indicate an error and - // return a nullOAN, allowing semantic analysis to continue - if (argOp == null) - return nullOAN; - - argArity = argOp.getArity(); - if ( arityExpected == argArity && genID.getArgs().length == 0) { - return new OpArgNode(genID.getFullyQualifiedOp(), argRoot, mn); - } - else if (genID.getArgs().length > 0) { - // expression (with or without correct number of args) being - // used where operator should be - errors.addError( - mainSTN.getLocation(), - "Expression used in argument position " + (argPosition+1) + - " (counting from 1) of operator `" + mainOp.getName() + - "', whereas an operator of arity " + arityExpected + - " is required."); - return nullOpArg; - } - else { - // operator of the wrong arity is being passed as argument - errors.addError( - mainSTN.getLocation(), - "Operator with incorrect arity passed as argument. " + - "\nOperator '" + argOp.getName() + "' of arity " + argArity + - " is argument number " + (argPosition+1) + - " (counting from 1) to operator `" + mainOp + - "', \nbut an operator of arity " + arityExpected + - " was expected."); - return nullOpArg; - } - } // else of non-Lambda expression case - } // end else of if (arityExpected == 0) - } // end generateExprOrOpArgOrLambda - - /** - * Process the General ID syntax tree, i.e. the part that contains - * an expression of the form A(x,y)!B!C(u,v,w)!D (with no params to - * D) and represents an operator. A General ID occurs in the - * syntax in only a few places: as an operator in an operator - * application; as an operator used as an operand to another - * operator, and as an operator being substituted for a suitable - * constant in module instantiation. - * - * Returns a GenID object, which must be further processed. - */ - private GenID generateGenID(TreeNode syntaxTreeNode, ModuleNode mn) throws AbortException { - return generateGenID(syntaxTreeNode, mn, false); - } - - /************************************************************************* - * It appears that this is called with unaryNegKludge = true only when * - * processing a prefix operator. * - *************************************************************************/ - private GenID generateGenID(TreeNode syntaxTreeNode, ModuleNode mn, boolean unaryNegKludge) - throws AbortException { - GenID genID; // Holds components of the generalized ID for this operator - TreeNode[] children = syntaxTreeNode.heirs(); // To contain N_IdPrefix node and the main operator - TreeNode[] prefix = null; // Contains array of prefix elements - // This is the array of N_IdPrefixElements for the operator, - // i.e. A!B(3)!C has 2 N_IdPrefixElements, A and B(3). - TreeNode[] prefixElt; // a prefixElement; 2- or 3-elem array: [op, (args), "!"] - TreeNode[] allArgs = null; // to collect arg arrays from prefix - TreeNode[] argsList = null; // Will hold an arg list tree - - if (children == null || children.length <= 0) { - // almost certainly an @ used outside of EXCEPT, which is detected elsewhere - return null; - } - - prefix = children[0].heirs(); - - // Allocate object to hold the Generized ID that is part of this OpAppl - genID = new GenID(syntaxTreeNode); - - // Number of elements in the prefix - int len = prefix.length; - - // Allocate array of SyntaxTreeNodes, one for each prefix element - allArgs = new SyntaxTreeNode[ len ]; - - // Process all of the prefix elements; construct the compound - // identifier (with embedded !-characters), and also accumulate an - // array of argument arrays (allArgs) for each prefix element - for (int i = 0; i < len; i++ ) { - // prefixElt becomes a 2- or 3-element array containing - // [operator, args (optional), and "!"] - prefixElt = prefix[i].heirs(); - - // Append the next part of compound identifier name and a "!" - genID.append(prefixElt[0].getImage()); - genID.append("!"); - - // allArgs[i] = array of arg syntax trees for next prefix - // element (if any) or "!" (if not) - allArgs[i] = prefixElt[1]; // Note: whether this is args or a "!" is checked below - } - - // Append the primary (rightmost) operator name to compoundID; - // calling "finalAppend" signals that the appending is finished, - // i.e. that the string can be converted to a UniqueString and to - // a SymbolNode inside the genID object - genID.finalAppend(children[1].getImage(), unaryNegKludge); - - // for each argument in each potential argument list in the prefix, - // generate the expression or opArg corresponding to it - - // for each argument list in prefix - int iarg = 0; - for (int i = 0; i < allArgs.length ; i++ ) { - // if there is an actual arg list here (instead of a "!" or null) - if ( allArgs[i] != null && allArgs[i].isKind( N_OpArgs ) ) { - // pick up array of arg list syntax elements - argsList = allArgs[ i ].heirs(); - - // The odd numbered syntax elements are the args expressions; - // the even numbered ones are parens and commas. - for (int ia = 1; ia < argsList.length; ia += 2) { - // Each arg may be an ordinary expression, or it may be an OpArg; - // produce appropriate semantic tree or node for it. - // Note that operators can be used in place of expressions in only two contexts: - // as argument to suitable user-defined ops, and in the RHS of a substitution - // in module instantiation - genID.addArg(generateExprOrOpArg(genID.getFullyQualifiedOp(), syntaxTreeNode, - iarg, argsList[ia], mn)); - iarg++; - } - } - } - - // "finalize the GenID object (i.e. convert argument vector to array) - genID.finalizeID(); - return genID; - } - - /************************************************************************* - * Added by LL on 27 March 2007. * - * * - * A lambda expression is represented as an OpDefNode. Since it is * - * always used only as an operator argument, this OpDefNode will appear * - * in an OpArgNode. * - *************************************************************************/ - private final OpDefNode generateLambda(TreeNode syntaxTreeNode, - ModuleNode cm) - throws AbortException { - TreeNode [] children = syntaxTreeNode.heirs(); - int arity = (children.length - 2) / 2 ; - /********************************************************************* - * children = <<"LAMBDA", arg_1, ",", ... , "," , arg_arity, ":", * - * body>> * - * so children.length = 3 + arity + arity-1 * - * so arity = (children.length - 2) / 2. * - *********************************************************************/ - Context ctxt = new Context(moduleTable, errors); - symbolTable.pushContext( ctxt ); - /********************************************************************* - * The context ctxt will hold the parameters of the lambda * - * expression, which may appear in its body. * - *********************************************************************/ - FormalParamNode [] params = new FormalParamNode[arity] ; - int argPos = 1 ; - /********************************************************************* - * children[argPos] is the next parameter's identifier node. * - *********************************************************************/ - for (int i = 0 ; i < arity ; i++) { - /********************************************************************* - * Set params[i] to the FormalParamNode for the i-th formal * - * parameter. * - *********************************************************************/ - params[i] = new FormalParamNode(children[argPos].getUS(), - 0, // parameters of a lambda expression - // have arity 0. - children[argPos], - symbolTable, - cm) ; - argPos = argPos + 2 ; - } // for - + apn.assumes[i] = generateExpressionOrLAP(tn, cm, true); + break; + } + ; // end switch + if (assumeProveDepth == 1) { + currentGoalClause = currentGoalClause + 1; + } + ; + } + ; // end for + + apn.prove = generateExpression(children[numOfChildren - 1], cm); + + if (assumeProveDepth != 1) { + symbolTable.popContext(); + /**************************************************************** + * Restore the current context, removing the declarations by * the assumptions. + * assumption list. * + ****************************************************************/ + } + ; + assumeProveDepth--; + return apn; + } + + private final NewSymbNode generateNewSymb(TreeNode treeNode, ModuleNode cm) + /*********************************************************************** + * Added by LL on 21 Mar 2007. * + ***********************************************************************/ + throws AbortException { + TreeNode[] children = treeNode.heirs(); + int numOfChildren = children.length; + + /********************************************************************* + * Determine if this node should have a non-null "set" field, which * is the + * case iff the declaration ends with "\in S", in which case * set the set field + * to the ExprNode for S. * + *********************************************************************/ + ExprNode set = null; + if (children[numOfChildren - 2].getKind() == IN) { + set = generateExpression(children[numOfChildren - 1], cm); + } + ; + + int declKind; + int declLevel; + /********************************************************************* + * Set declKind to the kind of the NewSymbNode object's OpDeclNode. * Set + * declLevel to its level. * * Start by setting i to 1 or 0 depending on whether + * the declaration * begins with a NEW token. * + *********************************************************************/ + int i = 0; + if (children[0].getKind() == NEW) { + i = 1; + } + ; + + /********************************************************************* + * Set declKind, and leave i so that children[i+1] is the syntax * tree node + * describing the declared symbol and its arity. * * We do this by seeing if the + * next token is "CONSTANT", "VARIABLE", * etc. If it isn't, then the + * declaration begins "NEW id", so it's * a CONSTANT declaration and i must be + * set to 0. * + *********************************************************************/ + switch (children[i].getKind()) { + case CONSTANT: + declKind = NewConstantKind; + declLevel = ConstantLevel; + break; + case VARIABLE: + declKind = NewVariableKind; + declLevel = VariableLevel; + break; + case STATE: + declKind = NewStateKind; + declLevel = VariableLevel; + break; + case ACTION: + declKind = NewActionKind; + declLevel = ActionLevel; + break; + case TEMPORAL: + declKind = NewTemporalKind; + declLevel = TemporalLevel; + break; + default: + declKind = NewConstantKind; + declLevel = ConstantLevel; + i = 0; + } + ; // switch + return new NewSymbNode(buildParameter(children[i + 1], declKind, declLevel, cm, true), set, treeNode); + } + + private final ExprNode processChoose(TreeNode treeNode, TreeNode[] children, ModuleNode cm) throws AbortException { + ExprNode[] semanticNode = new ExprNode[1]; + TreeNode[] syntaxTreeNode = children[2].heirs(); + OpApplNode result; + + symbolTable.pushContext(new Context(moduleTable, errors)); + + if (syntaxTreeNode == null || syntaxTreeNode.length == 0) { + // unbounded case + FormalParamNode[] odn; + boolean tuple; + + // either Tuple or single identifier + if (children[1].isKind(N_IdentifierTuple)) { + syntaxTreeNode = children[1].heirs(); + odn = new FormalParamNode[syntaxTreeNode.length / 2]; + for (int lvj = 0; lvj < syntaxTreeNode.length / 2; lvj++) { + odn[lvj] = new FormalParamNode(syntaxTreeNode[2 * lvj + 1].getUS(), 0, syntaxTreeNode[2 * lvj + 1], + symbolTable, cm); + } + tuple = true; + } else { + odn = new FormalParamNode[1]; + odn[0] = new FormalParamNode(children[1].getUS(), 0, children[0], symbolTable, cm); + tuple = false; + } + pushFormalParams(odn); + /******************************************************************* + * Push formal parameters on Last(LS).paramSeq for processing the * body. * + *******************************************************************/ + semanticNode[0] = generateExpression(children[4], cm); + popFormalParams(); + + result = new OpApplNode(OP_uc, semanticNode, odn, treeNode, cm); + } else { + // bounded case + FormalParamNode[][] odna = new FormalParamNode[1][0]; + boolean[] tuples = new boolean[1]; + ExprNode[] exprs = new ExprNode[1]; + + // syntaxTreeNode can be reused further down. + exprs[0] = generateExpression(syntaxTreeNode[1], cm); + + if (children[1].isKind(N_IdentifierTuple)) { + syntaxTreeNode = children[1].heirs(); + odna[0] = new FormalParamNode[syntaxTreeNode.length / 2]; + for (int lvj = 0; lvj < syntaxTreeNode.length / 2; lvj++) { + odna[0][lvj] = new FormalParamNode(syntaxTreeNode[2 * lvj + 1].getUS(), 0, + syntaxTreeNode[2 * lvj + 1], symbolTable, cm); + } + tuples[0] = true; + } else { + odna[0] = new FormalParamNode[1]; + odna[0][0] = new FormalParamNode(children[1].getUS(), 0, children[1], symbolTable, cm); + tuples[0] = false; + } + pushFormalParams(flattenParams(odna)); + /******************************************************************* + * Push the bound variables on Last(LS).paramSeq and process the * body of the + * CHOOSE. * + *******************************************************************/ + semanticNode[0] = generateExpression(children[4], cm); + popFormalParams(); + + result = new OpApplNode(OP_bc, null, semanticNode, odna, tuples, exprs, treeNode, cm); + } + symbolTable.popContext(); + return result; + } + + private final ExprNode processBoundQuant(TreeNode treeNode, TreeNode[] children, ModuleNode cm) + throws AbortException { + // Create data structures for all parameters + int length = (children.length - 2) / 2; + FormalParamNode[][] odna = new FormalParamNode[length][0]; + boolean[] bt = new boolean[length]; + ExprNode[] ea = new ExprNode[length]; + + // then process parameters + symbolTable.pushContext(new Context(moduleTable, errors)); + processQuantBoundArgs(children, 1, odna, bt, ea, cm); + + // process expression + ExprNode semanticNode[] = new ExprNode[1]; + pushFormalParams(flattenParams(odna)); + /******************************************************************* + * Push the bound variables on Last(LS).paramSeq and process the * body of the + * quantified expression. * + *******************************************************************/ + semanticNode[0] = generateExpression(children[children.length - 1], cm); + popFormalParams(); + + symbolTable.popContext(); + + // then return new node. + // which variety? look under first child. + boolean isExists = children[0].getUS().equals(S_e) || children[0].getUS().equals(S_ex); + if (isExists) { + return new OpApplNode(OP_be, null, semanticNode, odna, bt, ea, treeNode, cm); + } else { + return new OpApplNode(OP_bf, null, semanticNode, odna, bt, ea, treeNode, cm); + } + } + + private final ExprNode processUnboundQuant(TreeNode treeNode, TreeNode[] children, ModuleNode cm) + throws AbortException { + // which variety? look under first child. + UniqueString us = children[0].getUS(); + UniqueString r_us; + int level; + + if (us.equals(S_e)) { + r_us = OP_ue; + level = 0; + } // \E + else if (us.equals(S_ex)) { + r_us = OP_ue; + level = 0; + } // \exists + else if (us.equals(S_f)) { + r_us = OP_uf; + level = 0; + } // \A + else if (us.equals(S_fx)) { + r_us = OP_uf; + level = 0; + } // \always + else if (us.equals(S_te)) { + r_us = OP_te; + level = 1; + } // \EE + else { + r_us = OP_tf; + level = 1; + } // \AA + + // Process all identifiers bound by thus quantifier + int length = (children.length - 2) / 2; + FormalParamNode odn[] = new FormalParamNode[length]; + symbolTable.pushContext(new Context(moduleTable, errors)); + + for (int lvi = 0; lvi < length; lvi++) { + odn[lvi] = new FormalParamNode(children[2 * lvi + 1].getUS(), 0, children[2 * lvi + 1], symbolTable, cm); + } + + // now the expression + ExprNode semanticNode[] = new ExprNode[1]; + pushFormalParams(odn); + /********************************************************************** + * Push formal parameters on Last(LS).paramSeq for processing the * body. * + **********************************************************************/ + semanticNode[0] = generateExpression(children[children.length - 1], cm); + popFormalParams(); + + // wrap up. + symbolTable.popContext(); + return new OpApplNode(r_us, semanticNode, odn, treeNode, cm); + } + + private final ExprNode processCase(TreeNode treeNode, TreeNode[] children, ModuleNode cm) throws AbortException { + // number of arms to CASE-expr, not counting the CASE nodse itself + // or the []-separators + int armCount = children.length / 2; + ExprNode[] casePairs = new ExprNode[armCount]; + + for (int lvi = 0; lvi < armCount; lvi++) { + TreeNode caseArm = children[2 * lvi + 1]; + TreeNode[] ss = caseArm.heirs(); + ExprNode[] sops = new ExprNode[2]; + if (!caseArm.isKind(N_OtherArm)) { + sops[0] = this.generateExpression(ss[0], cm); + } + sops[1] = this.generateExpression(ss[2], cm); + casePairs[lvi] = new OpApplNode(OP_pair, sops, caseArm, cm); + } + return new OpApplNode(OP_case, casePairs, treeNode, cm); + } + + private final ExprNode processSubsetOf(TreeNode treeNode, TreeNode children[], ModuleNode cm) + throws AbortException { + // cfr. unbounded choose + ExprNode[] ops = new ExprNode[1]; + FormalParamNode[][] odna = new FormalParamNode[1][0]; + boolean[] tuples = new boolean[1]; + ExprNode[] exprs = new ExprNode[1]; + + exprs[0] = generateExpression(children[3], cm); + + symbolTable.pushContext(new Context(moduleTable, errors)); + + if (children[1].isKind(N_IdentifierTuple)) { + TreeNode[] ss = children[1].heirs(); + odna[0] = new FormalParamNode[ss.length / 2]; + for (int lvj = 0; lvj < ss.length / 2; lvj++) { + odna[0][lvj] = new FormalParamNode(ss[2 * lvj + 1].getUS(), 0, ss[2 * lvj + 1], symbolTable, cm); + } + tuples[0] = true; + } else { + odna[0] = new FormalParamNode[1]; + odna[0][0] = new FormalParamNode(children[1].getUS(), 0, children[1], symbolTable, cm); + tuples[0] = false; + } + + pushFormalParams(flattenParams(odna)); + /********************************************************************* + * Push the bound variables on Last(LS).paramSeq and process the * body of the + * CHOOSE. * + *********************************************************************/ + ops[0] = generateExpression(children[5], cm); + popFormalParams(); + + symbolTable.popContext(); + return new OpApplNode(OP_sso, null, ops, odna, tuples, exprs, treeNode, cm); + } + + private final ExprNode processSetOfAll(TreeNode treeNode, TreeNode children[], ModuleNode cm) + throws AbortException { + ExprNode[] ops = new ExprNode[1]; + int length = (children.length - 3) / 2; + FormalParamNode[][] odna = new FormalParamNode[length][0]; + boolean[] tuples = new boolean[length]; + ExprNode[] exprs = new ExprNode[length]; + + symbolTable.pushContext(new Context(moduleTable, errors)); + processQuantBoundArgs(children, 3, odna, tuples, exprs, cm); + + pushFormalParams(flattenParams(odna)); + /********************************************************************* + * Push the bound variables on Last(LS).paramSeq and process the * body of the + * CHOOSE. * + *********************************************************************/ + ops[0] = generateExpression(children[1], cm); + popFormalParams(); + + symbolTable.popContext(); + + return new OpApplNode(OP_soa, null, ops, odna, tuples, exprs, treeNode, cm); + } + + private final ExprNode processFcnConst(TreeNode treeNode, TreeNode children[], ModuleNode cm) + throws AbortException { + ExprNode[] ops = new ExprNode[1]; + int length = (children.length - 3) / 2; // number of args to function constant + FormalParamNode[][] odna = new FormalParamNode[length][0]; + boolean[] tuples = new boolean[length]; + ExprNode[] exprs = new ExprNode[length]; + + symbolTable.pushContext(new Context(moduleTable, errors)); + processQuantBoundArgs(children, 1, odna, tuples, exprs, cm); + + pushFormalParams(flattenParams(odna)); + /********************************************************************* + * Push the bound variables on Last(LS).paramSeq and process the * body of the + * CHOOSE. * + *********************************************************************/ + ops[0] = generateExpression(children[children.length - 2], cm); + popFormalParams(); + + symbolTable.popContext(); + + return new OpApplNode(OP_fc, null, ops, odna, tuples, exprs, treeNode, cm); + } + + /** + * This method processes both the RecordConstructor construct and the + * SetOfRecords operator. The two are essentially identical except for which + * builtin operator is used. + */ + private final ExprNode processRcdForms(UniqueString operator, TreeNode treeNode, TreeNode children[], ModuleNode cm) + throws AbortException { + // handles RcdConstructor or SetOfRcds + int length = (children.length - 1) / 2; + + // Create an array of pairs to handle all of the fields mentioned in the form + ExprNode[] fieldPairs = new ExprNode[length]; + // Create an array of Unique Strings to check uniqueness of fields. Not very + // efficient + // but a HashTable may be overkill most of the time. + // Remember a label is a string. + UniqueString[] labels = new UniqueString[length]; + + // For each field in the RcdConstructor or SetOfRecords + for (int lvi = 0; lvi < length; lvi++) { + TreeNode syntaxTreeNode[] = children[2 * lvi + 1].heirs(); + + // Create a pair of SemanticNodes to represent one record component + ExprNode sops[] = new ExprNode[2]; + + // The first one gets a new StringNode indicating the field name + sops[0] = new StringNode(syntaxTreeNode[0], false); + labels[lvi] = ((StringNode) sops[0]).getRep(); + for (int cmpIndex = 0; cmpIndex < lvi; cmpIndex++) { + if (labels[lvi].compareTo(labels[cmpIndex]) == 0) { + errors.addError(syntaxTreeNode[0].getLocation(), "Non-unique fields in constructor."); + } + } + + // The second one gets the expression indicating the field value (or set of + // values) + sops[1] = generateExpression(syntaxTreeNode[2], cm); + + // Put the $Pair OpApplNode into the fieldPairs array + fieldPairs[lvi] = new OpApplNode(OP_pair, sops, children[2 * lvi + 1], cm); + } + // Create the top-level OpApplNode, for either the SetOfRecords op + // or the RcdConstructor op. + return new OpApplNode(operator, fieldPairs, treeNode, cm); + } + + private final ExprNode processAction(TreeNode treeNode, TreeNode children[], ModuleNode cm) throws AbortException { + UniqueString match = children[0].getUS(); + if (match.equals(S_a)) + match = OP_aa; + else if (match.equals(S_brack)) + match = OP_sa; + else if (match.equals(S_sf)) + match = OP_sf; + else if (match.equals(S_wf)) + match = OP_wf; + + ExprNode ops[] = new ExprNode[2]; + ops[0] = generateExpression(children[1], cm); + ops[1] = generateExpression(children[3], cm); + return new OpApplNode(match, ops, treeNode, cm); + } + + private final ExprNode processExcept(TreeNode treeNode, TreeNode[] children, ModuleNode cm) throws AbortException { + int numExcepts = (children.length - 3) / 2; // number of ExceptSpec's; + ExprNode[] operands = new ExprNode[numExcepts + 1]; // 1 for each ExceptionSpec + first expr + OpApplNode excNode; // Holds OpApplNode for $Except operator + OpApplNode excSpecNode; // Holds $Pair node for ExceptSpec + + // The first operand of the $Except operator is the expression to + // which the exceptions apply Note this first operand is generated + // BEFORE the $Except node is stacked in the next couple of lines, + // because an @ in the first expression does NOT refer to the + // $Except node currently being generated, but to the next outer + // $Except node. + operands[0] = generateExpression(children[1], cm); + + // Create the $Except OpApplNode that will be returned by this + // method. We create it now, and fill out its contents later, + // because we need a reference to it in order to process @ properly. + excNode = new OpApplNode(OP_exc, operands, treeNode, cm); + + // for each of the ExceptSpecs produce another element of the operands array + for (int excSpecIx = 0; excSpecIx < numExcepts; excSpecIx++) { + TreeNode[] syntaxTreeNode = children[3 + 2 * excSpecIx].heirs(); // extract ExceptSpec + ExprNode[] sops = new ExprNode[2]; // Each ExceptionSpec is a $Pair + int slength = syntaxTreeNode.length - 3; // # of ExceptComponents in ExceptSpec + ExprNode[] ssops = new ExprNode[slength]; // to store ExceptComponents + + // Process the LHS of the ExceptSpec + + // for each ExceptComponent of the form .h or [i] or [i,j,k] add + // an arg to $SEQ node and add build up the syntax tree for + // exceptionTarget + for (int excCompIx = 0; excCompIx < slength; excCompIx++) { + // the heirs of an ExceptComponent + TreeNode subSyntaxTreeNode[] = syntaxTreeNode[1 + excCompIx].heirs(); + + if (subSyntaxTreeNode[0].getUS().equals(S_brack)) { + // The first heir is "[" , indicates one or more fcn args; + // add expressions as function args + if (subSyntaxTreeNode.length > 3) { + // if so, must generate a tuple for comma list of fcn args + int sslength = (subSyntaxTreeNode.length - 1) / 2; // number of func args in comma list + ExprNode[] sssops = new ExprNode[sslength]; // holds the comma list of fcn args + + // for each of multiple function args in the ExceptComponent + for (int fArgIx = 0; fArgIx < sslength; fArgIx++) { + sssops[fArgIx] = generateExpression(subSyntaxTreeNode[1 + 2 * fArgIx], cm); + } + + // add one ExceptComponent to vector of ExceptComponents + ssops[excCompIx] = new OpApplNode(OP_tup, sssops, syntaxTreeNode[2 * excCompIx + 1], cm); + } else { + // add one ExceptComponent to vector of ExceptComponents + ssops[excCompIx] = generateExpression(subSyntaxTreeNode[1], cm); + } + } else { + // otherwise a "." indicates record selection; add a + // StringNode operand as record selector add one + // ExceptComponent to vector of ExceptComponents + ssops[excCompIx] = new StringNode(subSyntaxTreeNode[1], false); + } + } // end for (each ExceptionComponent) + + // Create OpAppl for $SEQ applied to array of ExceptComponents + // following record or func expr or ! + // This is the LHS of an ExceptionSpec + sops[0] = new OpApplNode(OP_seq, ssops, children[3 + 2 * excSpecIx], cm); + + // Process the RHS of the ExceptionSpec + + // Create exceptSpec node now, so that it can be available in + // case the RHS expression of this ExceptSpec contains an @ + excSpecNode = new OpApplNode(OP_pair, sops, children[3 + 2 * excSpecIx], cm); + + // Push the except node and the except spec node on stacks so + // that the RHS of the ExceptSpec, which might contain an @, can + // be evaluated in their "context". + excSpecStack.push(excSpecNode); + excStack.push(excNode); + + // Generate the expression constituting the RHS of the + // ExceptionSpec allow @ in the context of this expression. + sops[1] = generateExpression(syntaxTreeNode[syntaxTreeNode.length - 1], cm); + + // Pop them back off + excSpecStack.pop(); + excStack.pop(); + + // Store excSpecNode as another operand of $Except + operands[excSpecIx + 1] = excSpecNode; + } // end for (each ExceptionSpec) + + return excNode; + } // end processExcept() + + /** + * This method generates an expression tree or an OpArgNode as an argument to an + * OpApplNode + * + * mainOp is the operator under that node mainSTN is the "parent" OpApplNode, + * used in case an error message must be generated argPosition is the argument + * position (counting from 0) that treeNode represents argRoot is the argument + * syntax tree that either becomes an ExprNode or an OpArgNode mn is the module + * these expressions are part of + */ + private ExprOrOpArgNode generateExprOrOpArg(SymbolNode mainOp, TreeNode mainSTN, int argPosition, TreeNode argRoot, + ModuleNode mn) throws AbortException { + SymbolNode argOp = null; + // the SymbolNode that heads the argRoot expression + int argArity; + // number of actual arguments under argRoot + int arityExpected; + // arity that mainOp expects of argument number <argPosition> + + if (mainOp == null) { + errors.addError(mainSTN.getLocation(), "Unable to generate expression or operator argument; " + + "this is probably because of previously reported errors."); + return nullOAN; + } + + // Are we sure the "operator" is not a ModuleNode? + if (mainOp instanceof ModuleNode) { + errors.addError(mainSTN.getLocation(), "Module name '" + mainOp.getName() + "' used as operator."); + return nullOAN; + } + + // Are there too many arguments to mainOp? + if (argPosition + 1 > mainOp.getArity()) { + errors.addError(mainSTN.getLocation(), "Too many arguments for operator '" + mainOp.getName() + + "'. There should be only " + mainOp.getArity() + "."); + + return nullOAN; + } + + // For user-defined mainOp check the FormalParamNodes array + // associated with the mainOp to find out whether it expects an + // operator argument or an ordinary expression argument in + // position number argPosition. (Only UserDefined ops can have + // other multi-arg ops passed to them as params, so we can just + // assign arityExpected = 0 in other cases.) + /*********************************************************************** + * In SANY2, mainOp can also be a ModuleInstanceKind, so the if test * was + * modified appropriately. * + ***********************************************************************/ + if ((mainOp.getKind() == UserDefinedOpKind) || (mainOp.getKind() == ModuleInstanceKind)) { + arityExpected = ((OpDefNode) mainOp).getParams()[argPosition].getArity(); + } else { + arityExpected = 0; + } + ; + + // if mainOp expects zero arguments, then it expects an ordinary + // expression arg in this position, so generate an expression. + // Any errors will be found in the generateExpression method. + if (arityExpected == 0) { + return generateExpression(argRoot, mn); + } else { + // otherwise, we are expecting an OpArg or Lambda + /********************************************************************* + * The following test was originally * * if + * (argRoot.getImage().equals("N_OpApplication")) * * However, that was not + * sufficient, because there are lots of other * nodes that represent + * expressions. At best, these caused weird * error messages. At worst, as in + * the case of a number or string, * they caused an array out of bounds + * exception when generateGenID * was called a few lines later. I hope that the + * following test * covers all the cases in which this should be called with + * argRoot * not an expression. * * Change made by LL on 9 May 2007. * + *********************************************************************/ + if (!(argRoot.getImage().equals("N_GeneralId") || argRoot.getImage().equals("N_GenInfixOp") + || argRoot.getImage().equals("N_GenNonExpPrefixOp") || argRoot.getImage().equals("N_GenPostfixOp") + || argRoot.getImage().equals("N_GenPrefixOp") || argRoot.getImage().equals("N_Lambda"))) { + errors.addError(argRoot.getLocation(), + "An expression appears as argument number " + (argPosition + 1) + + " (counting from 1) to operator '" + mainOp.getName() + + "', in a position an operator is required."); + return nullOAN; + } // end if + + if (argRoot.getKind() == N_Lambda) { + /******************************************************************* + * The argument is a lambda expression. * + *******************************************************************/ + argOp = generateLambda(argRoot, mn); + if (arityExpected == argOp.getArity()) { + return new OpArgNode(argOp, argRoot, mn); + } // if + else { + errors.addError(mainSTN.getLocation(), + "Lambda expression with arity " + argOp.getArity() + " used as argument " + + (argPosition + 1) + " of operator `" + mainOp.getName() + + "', \nbut an operator of arity " + arityExpected + " is required."); + return nullOpArg; + } // else + } // if (argRoot.kind == N_Lambda) + else { + /******************************************************************* + * The argument is not a lambda expression. * + *******************************************************************/ + + /******************************************************************* + * If the argument is a GeneralId node, then we use * genIdToSelector and + * selectorToNode to generate the OpArg node. * + *******************************************************************/ + if (argRoot.getKind() == N_GeneralId) { + return (ExprOrOpArgNode) selectorToNode(genIdToSelector((SyntaxTreeNode) argRoot), arityExpected, + false, false, mn); + } + ; + + /******************************************************************* + * If the argument is not a GeneralId node, we use the code from * SANY1 to + * generate the OpArg node. * + *******************************************************************/ + GenID genID = generateGenID(argRoot, mn); + argOp = genID.getFullyQualifiedOp(); + ; + + // If the symbol has not been defined, then indicate an error and + // return a nullOAN, allowing semantic analysis to continue + if (argOp == null) + return nullOAN; + + argArity = argOp.getArity(); + if (arityExpected == argArity && genID.getArgs().length == 0) { + return new OpArgNode(genID.getFullyQualifiedOp(), argRoot, mn); + } else if (genID.getArgs().length > 0) { + // expression (with or without correct number of args) being + // used where operator should be + errors.addError(mainSTN.getLocation(), + "Expression used in argument position " + (argPosition + 1) + + " (counting from 1) of operator `" + mainOp.getName() + + "', whereas an operator of arity " + arityExpected + " is required."); + return nullOpArg; + } else { + // operator of the wrong arity is being passed as argument + errors.addError(mainSTN.getLocation(), + "Operator with incorrect arity passed as argument. " + "\nOperator '" + argOp.getName() + + "' of arity " + argArity + " is argument number " + (argPosition + 1) + + " (counting from 1) to operator `" + mainOp + "', \nbut an operator of arity " + + arityExpected + " was expected."); + return nullOpArg; + } + } // else of non-Lambda expression case + } // end else of if (arityExpected == 0) + } // end generateExprOrOpArgOrLambda + + /** + * Process the General ID syntax tree, i.e. the part that contains an expression + * of the form A(x,y)!B!C(u,v,w)!D (with no params to D) and represents an + * operator. A General ID occurs in the syntax in only a few places: as an + * operator in an operator application; as an operator used as an operand to + * another operator, and as an operator being substituted for a suitable + * constant in module instantiation. + * + * Returns a GenID object, which must be further processed. + */ + private GenID generateGenID(TreeNode syntaxTreeNode, ModuleNode mn) throws AbortException { + return generateGenID(syntaxTreeNode, mn, false); + } + + /************************************************************************* + * It appears that this is called with unaryNegKludge = true only when * + * processing a prefix operator. * + *************************************************************************/ + private GenID generateGenID(TreeNode syntaxTreeNode, ModuleNode mn, boolean unaryNegKludge) throws AbortException { + GenID genID; // Holds components of the generalized ID for this operator + TreeNode[] children = syntaxTreeNode.heirs(); // To contain N_IdPrefix node and the main operator + TreeNode[] prefix = null; // Contains array of prefix elements + // This is the array of N_IdPrefixElements for the operator, + // i.e. A!B(3)!C has 2 N_IdPrefixElements, A and B(3). + TreeNode[] prefixElt; // a prefixElement; 2- or 3-elem array: [op, (args), "!"] + TreeNode[] allArgs = null; // to collect arg arrays from prefix + TreeNode[] argsList = null; // Will hold an arg list tree + + if (children == null || children.length <= 0) { + // almost certainly an @ used outside of EXCEPT, which is detected elsewhere + return null; + } + + prefix = children[0].heirs(); + + // Allocate object to hold the Generized ID that is part of this OpAppl + genID = new GenID(syntaxTreeNode); + + // Number of elements in the prefix + int len = prefix.length; + + // Allocate array of SyntaxTreeNodes, one for each prefix element + allArgs = new SyntaxTreeNode[len]; + + // Process all of the prefix elements; construct the compound + // identifier (with embedded !-characters), and also accumulate an + // array of argument arrays (allArgs) for each prefix element + for (int i = 0; i < len; i++) { + // prefixElt becomes a 2- or 3-element array containing + // [operator, args (optional), and "!"] + prefixElt = prefix[i].heirs(); + + // Append the next part of compound identifier name and a "!" + genID.append(prefixElt[0].getImage()); + genID.append("!"); + + // allArgs[i] = array of arg syntax trees for next prefix + // element (if any) or "!" (if not) + allArgs[i] = prefixElt[1]; // Note: whether this is args or a "!" is checked below + } + + // Append the primary (rightmost) operator name to compoundID; + // calling "finalAppend" signals that the appending is finished, + // i.e. that the string can be converted to a UniqueString and to + // a SymbolNode inside the genID object + genID.finalAppend(children[1].getImage(), unaryNegKludge); + + // for each argument in each potential argument list in the prefix, + // generate the expression or opArg corresponding to it + + // for each argument list in prefix + int iarg = 0; + for (int i = 0; i < allArgs.length; i++) { + // if there is an actual arg list here (instead of a "!" or null) + if (allArgs[i] != null && allArgs[i].isKind(N_OpArgs)) { + // pick up array of arg list syntax elements + argsList = allArgs[i].heirs(); + + // The odd numbered syntax elements are the args expressions; + // the even numbered ones are parens and commas. + for (int ia = 1; ia < argsList.length; ia += 2) { + // Each arg may be an ordinary expression, or it may be an OpArg; + // produce appropriate semantic tree or node for it. + // Note that operators can be used in place of expressions in only two contexts: + // as argument to suitable user-defined ops, and in the RHS of a substitution + // in module instantiation + genID.addArg( + generateExprOrOpArg(genID.getFullyQualifiedOp(), syntaxTreeNode, iarg, argsList[ia], mn)); + iarg++; + } + } + } + + // "finalize the GenID object (i.e. convert argument vector to array) + genID.finalizeID(); + return genID; + } + + /************************************************************************* + * Added by LL on 27 March 2007. * * A lambda expression is represented as an + * OpDefNode. Since it is * always used only as an operator argument, this + * OpDefNode will appear * in an OpArgNode. * + *************************************************************************/ + private final OpDefNode generateLambda(TreeNode syntaxTreeNode, ModuleNode cm) throws AbortException { + TreeNode[] children = syntaxTreeNode.heirs(); + int arity = (children.length - 2) / 2; + /********************************************************************* + * children = <<"LAMBDA", arg_1, ",", ... , "," , arg_arity, ":", * body>> * so + * children.length = 3 + arity + arity-1 * so arity = (children.length - 2) / 2. + * * + *********************************************************************/ + Context ctxt = new Context(moduleTable, errors); + symbolTable.pushContext(ctxt); + /********************************************************************* + * The context ctxt will hold the parameters of the lambda * expression, which + * may appear in its body. * + *********************************************************************/ + FormalParamNode[] params = new FormalParamNode[arity]; + int argPos = 1; + /********************************************************************* + * children[argPos] is the next parameter's identifier node. * + *********************************************************************/ + for (int i = 0; i < arity; i++) { + /********************************************************************* + * Set params[i] to the FormalParamNode for the i-th formal * parameter. * + *********************************************************************/ + params[i] = new FormalParamNode(children[argPos].getUS(), 0, // parameters of a lambda expression + // have arity 0. + children[argPos], symbolTable, cm); + argPos = argPos + 2; + } // for + // /*********************************************************************** // * Convert the FormalParamNode array params to the OpDeclNode array * // * odn. * @@ -4531,2903 +4245,2754 @@ OpDefNode node = (OpDefNode) vec.elementAt(i); // null, // SymbolTable // params[i].stn); // TreeNode // }; // for - pushFormalParams(params) ; - /******************************************************************* - * Push formal parameters on Last(LS).paramSeq for processing the * - * body - *******************************************************************/ - ExprNode body = generateExpression( children[children.length - 1], cm ); - /********************************************************************* - * Generate the lambda expression's body. * - *********************************************************************/ - popFormalParams() ; - - symbolTable.popContext(); - /********************************************************************* - * Restore original context. * - *********************************************************************/ - return new OpDefNode( - S_lambda, // The operator name is "LAMBDA" - UserDefinedOpKind, // The node kind for a lambda expression - params, // the array of formal parameters - false , // localness, which is meaningless - body, // the body (an expression node) - cm, // the module - null, // the symbol table, which is null for an - // OpDefNode representing a lambda expression. - syntaxTreeNode, - true, // Is defined. Its value should not matter. - null ) ; // Source - } // generateLambda - - /** - * Generates an OpApplNode or an OpArgNode for a SyntaxTreeNode, - * according to whether the value of "typeExpected" is either opAppl - * or opArg. - */ - private final ExprNode generateOpAppl(TreeNode syntaxTreeNode, ModuleNode cm) - throws AbortException { - TreeNode primaryArgs = null; - // points to syntax tree of primary arg list (if any); otherwise null - boolean isOpApp = syntaxTreeNode.isKind( N_OpApplication ) ; - // true ==> to indicate this is an OpAppl; must have primary args - // false ==> operator used as an argument (OpArg); has no primary args - int primaryArgCount = 0; - // total # of arguments, including those of prefix, if any - int len; - // number of prefix elements - TreeNode[] children = syntaxTreeNode.heirs(); - // temp used for finding the prefix - TreeNode [] prefix; - // array of prefix elements - TreeNode[] allArgs = null; - // to collect arg arrays from both prefix and the main op (if any) - TreeNode [] prefixElt; - // a prefixElement; 2 or 3 elem array: [op, (args), "!"] - UniqueString symbol; - // UniqueString name of the fully-qualified operator - SymbolNode fullOperator; - // The SymbolNode for the fully-qualified operator - int iarg = 0; - // loop counter for actual args (as opposed to - // arg syntax elements like commas and parens) - TreeNode[] argsList = null; - // Will hold an array of arg syntax trees for primary operator - ExprOrOpArgNode[] args = null; - // Will hold an array of arg semantic trees for primary operator - - // Process the Generized ID that is the operator for this OpAppl - GenID genID = generateGenID(children[0], cm); - - // Set up pointers to OpAppl's primary args - primaryArgs = children[1]; - // Array of argument list syntax elements for the main - // (rightmost) operator, including parens and commas; - // should be an N_OpArgs node - - // calc number of primary args; - // args are interspersed w/ parens & commas--hence the /2 - primaryArgCount = primaryArgs.heirs().length / 2; - - if (genID == null || genID.getFullyQualifiedOp() == null) { - // if operator is @ or an unresolved symbol; error has already - // been generated inside genID - return nullOAN; - } - - args = new ExprOrOpArgNode[primaryArgCount]; - // Array to hold semantic trees for primary args - - // pick up array of arg list syntax elements - argsList = primaryArgs.heirs(); - - // The odd numbered syntax elements are the args expressions; the - // even numbered ones are parens and commas. - // for each arg in this arg list ... - for ( int ia = 1; ia < argsList.length; ia += 2 ) { - // Each arg may be an ordinary expression, or it may be an OpArg; - // produce appropriate semantic tree or node for it. - // Note that operators can be used in place of expressions - // in only two contexts: - // as argument to suitable user-defined ops, and in the RHS - // of a substitution in module instantiation - args[iarg] = generateExprOrOpArg(genID.getFullyQualifiedOp(), - syntaxTreeNode, - iarg, argsList[ia], cm); - iarg++; // count the actual args - } // end for - - // Concatenate the list of args in the GenID object to the - // primary arg list just created - Vector genIDArgList = genID.getArgsVector(); - ExprOrOpArgNode[] finalArgList = - new ExprOrOpArgNode[genIDArgList.size() + iarg]; - - // Copy the args from the prefix - for (int i = 0; i < genIDArgList.size(); i++) { - finalArgList[i] = (ExprOrOpArgNode)(genIDArgList.elementAt(i)); - } - // Copy the primary args - for (int i = 0, j = genIDArgList.size(); i < iarg; i++, j++ ) { - finalArgList[j] = args[i]; - } - - // return an OpApplNode constructed from the fully-qualified - // operator and the final arg list - return new OpApplNode(genID.getFullyQualifiedOp(), finalArgList, - syntaxTreeNode, cm); - } // end generateOpAppl() - - /** - * Process a named, parameterixed instantiation, e.g. of the form - * D(p1,...pn) = INSTANCE M WITH a1 <- e1 ,..., ar <- er - */ - /************************************************************************* - * Note: the returned value does not seem to be used. * - *************************************************************************/ - private final OpDefNode processModuleDefinition( - TreeNode treeNode, - Vector defs, - /******************************************************* - * This is non-null when called from inside a LET, in * - * which case the OpDef node is appended to defs. If * - * null, the OpDef node is appended to the * - * module-level lists of such nodes. * - *******************************************************/ - Vector insts, - /******************************************************* - * If non-null, then a vector of InstanceNode objects * - * to which the current instance node is to be * - * appended. If null, cm.appendInstance is called to * - * put the InstanceNode onto the module-level lists of * - * such nodes. * - *******************************************************/ - ModuleNode cm) - throws AbortException { - // Start with a LHS for an instance: we must extract from it name - // and possibly parameters. Then we need the external Context of - // the module and to extract all non-local, non-builtin - // symbols. Then build the proper symbol list and add it. - - // We must remember to identify explicitly whether or not the new - // nodes would be local. - - // assert treeNode.isKind(N_ModuleDefinition) - // - // Note that this code does nothing about THEOREMS and ASSUMES in - // modules being instantiated - boolean localness = treeNode.zero() != null; - TreeNode[] children = treeNode.one()[0].heirs(); // heirs of IdentLHS - UniqueString name = children[0].getUS(); - - // processing of LHS of the definition, i.e. the name and parameters - FormalParamNode[] args = nullParam; - Context parmCtxt = null; - - // If the operator being defined as a module instance has any parameters - if (children.length > 1) { - // Create new array of FormalParamNodes for the new operator - args = new FormalParamNode[ children.length /2 -1 ]; - - // Push a new context in current module's SymbolTable - parmCtxt = new Context(moduleTable, errors); - symbolTable.pushContext(parmCtxt); - - // For each formal parameter declared for the op being defined - for (int i = 0; i < args.length; i++) { - TreeNode child = children[2 + 2 * i]; - UniqueString id = null; - int count = 0; - - if ( child.isKind(N_IdentDecl)) { - id = child.heirs()[0].getUS(); - count = (child.heirs().length -1)/ 2; - } - else if ( child.isKind(N_InfixDecl)) { - id = child.heirs()[1].getUS(); - count = 2; - } - else if ( child.isKind(N_PrefixDecl)) { - id = child.heirs()[0].getUS(); - count = 1; - } - else if ( child.isKind(N_PostfixDecl)) { - id = child.heirs()[1].getUS(); - count = 1; - } - else { - errors.addAbort( - treeNode.getLocation(), - "Internal error: Error in formal params part of parse tree.", - true); - } - - // If there was no error - if ( id != null ) { - // Create a new FormalParamNode for the defined Op and put - // it in the SymbolTable - args[i] = new FormalParamNode(id, count, child, symbolTable, cm); - } // end if - } // end for - } // end if - - // processing RHS of the definition, starting with identification - // of module being instantiated, followed by processing of the - // WITH clause (if any) - children = treeNode.one()[2].heirs(); // heirs of NonLocalInstance - - // Find the Context and ModuleNode for the module being instantiated - Context instanceeCtxt = this.getContext(children[1].getUS()); - ModuleNode instanceeModule = symbolTable.resolveModule(children[1].getUS()); - - if (instanceeCtxt == null) { - errors.addError( - children[1].getLocation(), - "Module " + children[1].getImage() + " does not have a context."); - return nullODN; - } - - if (instanceeModule == null) { - errors.addError(children[1].getLocation(), - "Module name " + children[1].getImage() + - " is not known" + " in current context."); - return nullODN; - } - - /* - * Set isInstantiated field of instancee. Added by LL 23 July 2013 - */ - instanceeModule.setInstantiated(true) ; - - - // Create a SubstInNode that will be used to wrap each definition - // body in the module being defined. "children" is the array of - // explicit substitution clauses used in the module definition. - // Both instanceeCtxt and symbolTable are involved here since for - // each substitution c <- e, c must be resolved in the - // instanceeCtxt, and e must be interpreted in symbolTable - SubstInNode substIn = processSubst(treeNode, children, symbolTable, - instanceeCtxt, instanceeModule, cm); - - // We are done with the local context (if one was created because - // of parameters) - if (parmCtxt != null) symbolTable.popContext(); - - // Create a vector of all of the OpDefNodes in the instancee module - /*********************************************************************** - * I have no idea why the module's getOpDefs method isn't used here. * - ***********************************************************************/ - Vector elts = instanceeCtxt.getByClass( OpDefNode.class ); - - // For each definition in the instancee module, create a - // corresponding definition in the instancer module - for (int i = 0; i < elts.size(); i++) { - // Find the OpDefNode to be instantiated - OpDefNode odn = (OpDefNode)elts.elementAt(i); - - /********************************************************************** - * Ignore it if it is local or a builtin def. * - **********************************************************************/ - if ( !odn.isLocal() - && ( (odn.getKind() == UserDefinedOpKind) - || (odn.getKind() == ModuleInstanceKind) )) { - // Create the new name prepended with "name!" - String compoundID = name + "!" + odn.getName(); - UniqueString qualifiedName = UniqueString.uniqueStringOf(compoundID); - - // Copy parameters for the op being defined - FormalParamNode [] fpn = odn.getParams(); - FormalParamNode [] params = new FormalParamNode[fpn.length + args.length]; - System.arraycopy(args, 0, params, 0, args.length); - System.arraycopy(fpn, 0, params, args.length, fpn.length); - - OpDefNode newOdn ; - if (odn.getKind() == UserDefinedOpKind) { - if (substIn.getSubsts().length > 0) { - // If there are substitutions, then the body of the new - // definition instance must be wrapped in a SUBST-IN node. - // Create the "wrapping" SubstInNode as a clone of "subst" - // above, but with a body from the OpDefNode in the module - // being instantiated - SubstInNode substInNode = - new SubstInNode(treeNode, substIn.getSubsts(), odn.getBody(), - cm, instanceeModule); - - // Create the OpDefNode for the new instance of this - // definition; because of the new operator name, cm is the - // module of origin for purposes of deciding of two defs are - // "the same" or "different" - newOdn = new OpDefNode(qualifiedName, UserDefinedOpKind, params, - localness, substInNode, cm, symbolTable, - treeNode, true, odn.getSource()); - setOpDefNodeRecursionFields(newOdn, cm) ; - newOdn.setLabels(odn.getLabelsHT()) ; - } // if (substIn.getSubsts().length > 0) - else { - // no SUBST-IN node required; but because of the new - // operator name, cm is the module of origin for purposes of - // deciding of two defs are "the same" or "different" - newOdn = new OpDefNode(qualifiedName, UserDefinedOpKind, params, - localness, odn.getBody(), cm, symbolTable, - treeNode, true, odn.getSource()); - setOpDefNodeRecursionFields(newOdn, cm) ; - newOdn.setLabels(odn.getLabelsHT()) ; - } // else - } // if (odn.kind == UserDefinedOpKind) - else { - /***************************************************************** - * This is a ModuleInstanceKind node. * - *****************************************************************/ - newOdn = new OpDefNode( - qualifiedName, params, localness, - odn.getOriginallyDefinedInModuleNode(), symbolTable, - treeNode, odn.getSource()); - } ; // else - // defs is non-null iff this module definition is in the Let - // part of a Let-In expression. Add this newly created OpDef - // to either the LET list or the module cm's definition list. - if (defs == null) { - cm.appendDef(newOdn); - } - else { - defs.addElement(newOdn); - } - } // if (!odn.isLocal()&& ... ) - } // for - - /********************************************************************** - * Import the ThmOrAssumpDefNode objects to the current module. The * - * following code for doing this was copied and modified without * - * much thinking from the code above for OpDefNode objects * - **********************************************************************/ - // Create a vector of all of the ThmOrAssumpDefNodes in the - // instancee module - Vector taelts = instanceeCtxt.getByClass( ThmOrAssumpDefNode.class ); - - // For each definition in the instancee module, create a - // corresponding definition in the instancer module - for (int i = 0; i < taelts.size(); i++) { - // Find the ThmOrAssumpDefNode to be instantiated - ThmOrAssumpDefNode taOdn = (ThmOrAssumpDefNode)taelts.elementAt(i); - - /********************************************************************* - * There are no builtin ThmOrAssumpDefNode objects. * - *********************************************************************/ - // Ignore it if it is local - if (!taOdn.isLocal()) { - // Create the new name prepended with "name!" - String compoundID = name + "!" + taOdn.getName(); - UniqueString qualifiedName = UniqueString.uniqueStringOf(compoundID); - - // Copy parameters for the op being defined - /******************************************************************* - * Theorem or assumption definitions have no parameters. * - *******************************************************************/ - FormalParamNode [] fpn = taOdn.getParams(); - FormalParamNode [] params = - new FormalParamNode[fpn.length + args.length]; - System.arraycopy(args, 0, params, 0, args.length); - System.arraycopy(fpn, 0, params, args.length, fpn.length); - - ThmOrAssumpDefNode newtaOdn; - if (substIn.getSubsts().length > 0) { - // If there are substitutions, then the body of the new - // definition instance must be wrapped in a SUBST-IN node. - // Create the "wrapping" SubstInNode as a clone of "subst" - // above, but with a body from the ThmOrAssumpDefNode in the module - // being instantiated - APSubstInNode substInNode = - new APSubstInNode(treeNode, substIn.getSubsts(), taOdn.getBody(), - cm, instanceeModule); - - // Create the ThmOrAssumpDefNode for the new instance of this - // definition; because of the new operator name, cm is the - // module of origin for purposes of deciding of two defs are - // "the same" or "different" - newtaOdn = new ThmOrAssumpDefNode(qualifiedName, taOdn.isTheorem(), - substInNode, cm, symbolTable, - treeNode, params, instanceeModule, - taOdn.getSource()); - // Following statement added by LL on 30 Oct 2012 to handle locally - // instantiated theorems and assumptions. Added setLabels call - // on 31 Oct 2012. - newtaOdn.setLocal(localness); - newtaOdn.setLabels(taOdn.getLabelsHT()) ; - - /***************************************************************** - * No recursion fields needed for a theorem or assumption * - * because it can't appear in a recursive section. * - *****************************************************************/ - // setThmOrAssumpDefNodeRecursionFields(newtaOdn, cm) ; - } - else { - // no SUBST-IN node required; but because of the new - // operator name, cm is the module of origin for purposes of - // deciding if two defs are "the same" or "different" - newtaOdn = new ThmOrAssumpDefNode(qualifiedName, taOdn.isTheorem(), - taOdn.getBody(), cm, symbolTable, - treeNode, params, instanceeModule, - taOdn.getSource()); - // Following statement added by LL on 30 Oct 2012 to handle locally - // instantiated theorems and assumptions. Added setLabels call - // on 31 Oct 2012. - newtaOdn.setLocal(localness); - newtaOdn.setLabels(taOdn.getLabelsHT()) ; - /***************************************************************** - * No recursion fields needed for theorems or assumptions * - * because they can't appear in a recursive section. * - *****************************************************************/ - // setThmOrAssumpDefNodeRecursionFields(newtaOdn, cm) ; - } - - // defs is non-null iff this module definition is in the Let - // part of a Let-In expression. Add this newly created ThmOrAssumpDef - // to either the LET list or the module cm's definition list. - if (defs == null) { - cm.appendDef(newtaOdn); - } - else { - defs.addElement(newtaOdn); - } - } // if (!taOdn.isLocal()&& ... ) - } // for - - // Create a new InstanceNode to represent this INSTANCE stmt - // in the current module - InstanceNode inst = new InstanceNode(name, localness, args, - instanceeModule, - substIn.getSubsts(), treeNode); - - // Append this new InstanceNode to the vector of InstanceNodes - // being accumulated for this module - if (insts == null) { - cm.appendInstance(inst); - } - else { - insts.addElement(inst); - } - - // Create new OpDefNode with ModuleInstanceKind. The reason for - // doing this is to get the name in the symbol table so the name - // cannot be re-used later in this module for a user-defined - // operator. - return new OpDefNode(name, args, localness, cm, symbolTable, treeNode, - null); - /********************************************************************* - * Note: the module's OpDefNode does not have recursive parameters * - * set. If the module definition statement occurs in a recursive * - * section, then it is the instantiated definitions that are * - * recursive, not the module definition itself. (The name under * - * which the module is instantiated cannot appear in a RECURSIVE * - * statement.) * - *********************************************************************/ - - } // end processModuleDefinition() - - /** - * From a particular explicit substitution (substTarget <- substValue) - * check the legality of the substTarget, and if OK, generate the - * appropriate type of node for substValue - */ - private ExprOrOpArgNode generateSubst( - Context instanceeCtxt, - TreeNode substTarget, - TreeNode substValue, - ModuleNode mn) - throws AbortException { - SymbolNode targetSymbol = instanceeCtxt.getSymbol(substTarget.getUS()); - - // if the targetSymbol cannot be found in the instancee context, - // or if it does not correspond to a declaration, then it is an - // illegal substitution target - - if (targetSymbol == null || ! (targetSymbol instanceof OpDeclNode) ) { - errors.addError( - substTarget.getLocation(), - "Identifier '" + substTarget.getUS() + "' is not a legal" + - " target of a substitution. \nA legal target must be a declared" + - " CONSTANT or VARIABLE in the module being instantiated." + - " \n(Also, check for warnings about multiple declarations of" + - " this same identifier.)"); - return nullOAN; - } - - // but if the symbol is found, then if it has arity 0, the RHS - // should be an expression, if arity > 0, the the RHS should be an OpArg - ExprOrOpArgNode returnObject; - - if ( targetSymbol.getArity() == 0 ) { - // if the target of the substitution has arity 0, - // then we expect an expression to be substituted for it - returnObject = generateExpression(substValue, mn); - } - else { - // if the target of the substitution has arity > 0, - // then and operator must be substituted for it - returnObject = generateOpArg(targetSymbol, substValue, mn); - - // and it better have the same arity as the target - if ( ((OpArgNode)returnObject).getArity() != targetSymbol.getArity() ) { - errors.addError(substValue.getLocation(), - "An operator must be substituted for symbol '" + - targetSymbol.getName() + "', and it must have arity " + - targetSymbol.getArity() + "."); - } - } - return returnObject; - } // end generateSubst() - - /** - * Return an OpArgNode constructed from a GeneralId tree to be used - * in the RHS of a substitution - */ - private OpArgNode generateOpArg(SymbolNode targetSymbol, - TreeNode opArgSyntaxNode, - ModuleNode mn) - throws AbortException { - /*********************************************************************** - * If this is a lambda expressiion, then just get it and go. * - ***********************************************************************/ - if (opArgSyntaxNode.isKind(N_Lambda)) { - return new OpArgNode(generateLambda(opArgSyntaxNode, mn), - opArgSyntaxNode, mn) ; - } ; - // First, make sure that an operator ID is present, and not an expression - if ( ! ( opArgSyntaxNode.isKind(N_GeneralId) || - opArgSyntaxNode.isKind(N_GenInfixOp) || - opArgSyntaxNode.isKind(N_GenPrefixOp) || - opArgSyntaxNode.isKind(N_GenNonExpPrefixOp) || - /************************************************************ - * This last disjunct was added by LL on 9 May 2007. * - * * - * The original parser phase produced an N_GenPrefixOp * - * here, but an N_GenNonExpPrefixOp for the syntactically * - * identical situation in an operator argument. The new * - * TLA+2 parser produces N_GenNonExpPrefixOp nodes here, * - * but I left the N_GenPrefixOp case in here just in case * - * this is called somewhere else that I haven't found. * - ************************************************************/ - - opArgSyntaxNode.isKind(N_GenPostfixOp) - ) - ) { - errors.addError(opArgSyntaxNode.getLocation(), - "Arity " + targetSymbol.getArity() + - " operator (not an expression) is expected" + - " \nto substitute for CONSTANT '" + - targetSymbol.getName() + "'." ); - return nullOpArg; - } - - /*********************************************************************** - * If the argument is a GeneralId node, then we use * - * genIdToSelector and selectorToNode to generate the OpArg node. * - ***********************************************************************/ - if (opArgSyntaxNode.getKind() == N_GeneralId) { - /********************************************************************* - * First, a sanity check to make sure we're never looking for an * - * expression argument. * - *********************************************************************/ - if (targetSymbol.getArity() <= 0) { - errors.addAbort(opArgSyntaxNode.getLocation(), - "Internal error: expected to find arity > 0.", true); - } ; - - LevelNode ln = selectorToNode(genIdToSelector( - (SyntaxTreeNode) opArgSyntaxNode), - targetSymbol.getArity(), false, false, mn); - - /******************************************************************* - * Added 23 February 2009: It appears that, in case of an error, * - * selectorToNode can return something other than an OpArgNode * - * here. (In particular, an OpApplNode.) Rather than tix * - * selectorToNode, I am adding a kludge to simply ignore the * - * problem and hope that it can be caused only by some other * - * error. If no error has been found, then we abort and debug if * - * this is encountered. * - *******************************************************************/ - if (!(ln instanceof OpArgNode)) { - if (errors.getNumErrors() > 0) { return nullOpArg; } - errors.addAbort(opArgSyntaxNode.getLocation(), - "Internal error: " + - "Expected an operator argument but " + - "found something else."); - }; - return (OpArgNode) ln ; - } ; - - /******************************************************************* - * If the argument is not a GeneralId node, we use the code from * - * SANY1 to generate the OpArg node. * - *******************************************************************/ - - // Assemble the (possibly compound) generalized identifier, and resolve it. - GenID genID = generateGenID(opArgSyntaxNode, mn); - - // If the fully-qualified op is undefined, then a message has already been - // put in errors, but we must insert a nullOpArgNode in the tree. - if (genID.getFullyQualifiedOp() != null && genID.getArgs().length == 0 ) { - // Create an OpArgNode from it. - return new OpArgNode(genID.getFullyQualifiedOp(), opArgSyntaxNode, mn); - } - else if ( genID.getArgs().length > 0 ) { - // Expression being used where Operator is required - errors.addError(opArgSyntaxNode.getLocation(), - "Arity " + targetSymbol.getArity() + - " operator (not an expression) is expected" + - " to substitute for CONSTANT '" + - targetSymbol.getName() + "'." ); - return nullOpArg; - } - else { - return nullOpArg; - } - } // end generateOpArg() - - /** - * Process the substitutions clause of a module definition or instantiation; - * returns a SubstInNode that can be used as a template for the wrapper that - * must be present around each OpDefNode body created from the module - * instantiation or module definition. - */ - private SubstInNode processSubst( - TreeNode treeNode, - TreeNode[] substNodes, - // array of subst nodes [c1 <- e1, ... ,cn <- en] - SymbolTable instancerST, - // SymbolTable in which the ei must be resolved - Context instanceeCtxt, - // Context in which the ci must be resolved - ModuleNode instanceeModule, - // the ModuleNode of the module in which ci must be resolved - ModuleNode mn) throws AbortException { - TreeNode[] children; // find the substitution part of the syntax tree - - // Create a vector of all declarations of CONSTANTS and VARIABLES - // in the context of the module being instantiated (instancee). - // These are all the symbols that must have substitutions defined - // for them in the instantiation, either explictly or implicitly. - Vector decls = instanceeCtxt.getByClass( OpDeclNode.class ); - - // Create a SubstInNode to be used as a template for the SubstInNodes - // in the body of every newly instantiated OpDef in the module. - // The substitutions in the returned SubstInNode object will be the - // implicit substitutions of the form c<-c for all CONSTANTS and - // VARIABLES c that are BOTH declared in instancee and for which the - // same name is declared or defined in instancer. Note the instancerST - // must be passed to this constructor because with a default substitution - // LHS<-RHS the LHS is resolved in the instanceeCtxt, (done - // in the previous line) and the RHS is resolved in the instancerST. - SubstInNode substIn = - new SubstInNode(treeNode, instancerST, decls, mn, instanceeModule); - - // For each explicit substitution in the syntax tree, overwrite or add - // the corresponding default entry in SubstInNode just created - for (int i = 3; i < substNodes.length; i += 2) { - // pick up array of syntax elements for one substitution, - // e.g. ["c","<-",expr] - TreeNode sc[] = substNodes[i].heirs(); - - // substRHS is the expression "exp" in "c <- exp"; this stmt first - // checks that c is properly declared in the instancee context, - // and then generates an ExprOrOpArgNode. If c is a constant - // with parameters, then an OpArgNode is returned; otherwise it is - // an ExprNode. - ExprOrOpArgNode substRHS = - generateSubst(instanceeCtxt, sc[0], sc[2], mn); - - // Overwrite an implicit substitution if there is one, or add a new one, - // checking for duplicate substitutions for the same symbol - substIn.addExplicitSubstitute(instanceeCtxt, sc[0].getUS(), - sc[2], substRHS ); - } - - // Check if substitution is complete, i.e. that all constants and vars - // have been substituted for. - substIn.matchAll( decls ); - return substIn; - } // end processSubst - - /** - * This method treats *unnamed* INSTANCE stmts - * NOTE: this code does nothing with ASSUMES or THEOREMS - */ - /************************************************************************* - * However, SANY2 imports ThmOrAssumpDef nodes. * - *************************************************************************/ - /** - * @param treeNode - * @param cm - * @param topLevel - * @return - * @throws AbortException - */ -/** - * @param treeNode - * @param cm - * @param topLevel - * @return - * @throws AbortException - */ -/** - * @param treeNode - * @param cm - * @param topLevel - * @return - * @throws AbortException - */ -/** - * @param treeNode - * @param cm - * @param topLevel - * @return - * @throws AbortException - */ -private final InstanceNode - generateInstance(TreeNode treeNode, ModuleNode cm, boolean topLevel) - throws AbortException { - /********************************************************************** - * topLevel argument is true for a top-level INSTANCE statement, and * - * false elsewise--that is, for an INSTANCE inside a proof. * - **********************************************************************/ - TreeNode[] children ; - if (topLevel) { - children = treeNode.one()[0].heirs(); } - // skip one generation below NonLocalInstance - // because we know zero defines local in a - // top-level INSTANCE - else {children = treeNode.heirs();} ; - // id of module being instanced - UniqueString moduleId = children[1].getUS(); - - // If this module instance is declared "LOCAL" then all of the - // definitions in it must be instanced as if they were "LOCAL" - boolean localness = treeNode.local(); - - // Create a list of all declarations for module moduleId. - // Match them against either something in the substitutions or - // something in the current context (symbol table) for the - // substitution. Check that the symbol does occur in the module - // and as a declaration. - Context instanceeCtxt = this.getContext(moduleId); - if (instanceeCtxt == null) { - errors.addAbort(children[1].getLocation(), - "Internal error: No context available for module `" + - moduleId.toString() + "'.", true); - } ; - // Try to find the ModuleNode for the module being instanced in - // the symbolTable - ModuleNode instanceeModuleNode = symbolTable.resolveModule(moduleId); - - // It must be an external module if it isn't in the symbolTable; - // try to find it in moduleTable (it cannot be in both places, or - // a name conflict would have resulted) - if (instanceeModuleNode == null) { - instanceeModuleNode = moduleTable.getModuleNode(moduleId); - } - - if (instanceeModuleNode == null) { - errors.addAbort(children[1].getLocation(), - "Could not find module " + moduleId.toString(), - false); - } - - /* - * Set isInstantiated field of instancee. Added by LL 23 July 2013 - */ - instanceeModuleNode.setInstantiated(true) ; - - // Create the SubstInNode that will act as a template "wrapper" - // for each definition in the module being instantiated; this - // SubstInNode itself gets discarded after being used as template - // however many times is necessary - SubstInNode subst = processSubst(treeNode, children, symbolTable, - instanceeCtxt, instanceeModuleNode, cm); - - // Create a vector of all of the OpDefNodes in the module being - // instantiated - Vector defs = instanceeCtxt.getByClass(OpDefNode.class); - - OpDefNode odn; // OpDefNode in module being instantiated (instancee) - OpDefNode newOdn; // Its counterpart current module (instancer) - SubstInNode substInTemplate; // Template to be used for any - // SubstInNode wrappers required - - // Duplicate the OpDef records from the module being INSTANCE'd - for (int i = 0; i < defs.size(); i++) { - odn = (OpDefNode)defs.elementAt(i); - // OpDefNode in module being instantiated (instancee) - - // Do not instantiate built-in or local operators, or those - // OpDefNodes created solely to prevent a ModuleName from being - // used as an operator node. - if (odn.getKind() == BuiltInKind || - odn.getKind() == ModuleInstanceKind || - odn.isLocal()) { - continue; - } - - // If there are parameters to the module being instantiated, then - // a SubstInNode is required, and possibly a different module of - // origin should be indicated - if (!instanceeModuleNode.isParameterFree()) { - // Create the OpDefNode for the new instance of this definition - // Note that the new OpDefNode shares the array of FormalParamNodes - // with the old OpDefNode, as well as large parts of its body - // (all but the SubstInNode). Hence, changes by a tool to an Original - // OpDefNode will likely be reflected in all instances of it. - if ( odn.getOriginallyDefinedInModuleNode().isParameterFree() ) { - - /**************************************************************** - * Originally, newOdn was set the way it now is if localness = * - * true. Here's the problem with it. Suppose the instantiated * - * module EXTENDS the Naturals module. Then this will add new * - * OpDefNodes for all the symbols defined in Naturals. If the * - * current module EXTENDS Naturals, this will lead to multiple * - * definitions. So, for nonlocal definitions, we just * - * set newOdn to odn. * - * * - * However, now the problem is: suppose the current module * - * does not EXTEND the Naturals module. Then the operators * - * defined in Naturals, which should be defined in the current * - * module, are not. So, we add them to symbolTable. This does * - * not lead to a multiple definition error because apparently * - * it's the addSymbol method that is smart enough to detect if * - * we are adding a definition that comes from the same source as * - * the original one. * - * * - * This fix was made by LL on 16 Feb 2009. * - * * - * On 6 June 2010, LL add "&& topLevel" to the following `if' * - * test. This was needed because an INSTANCE inside a proof * - * was producing a "Multiple declarations or definition" * - * warning if the INSTANCEd module and the current module both * - * EXTENDed Naturals. This fix seems to do the right thing, * - * but I have not extensively tested it and I have no idea what * - * problems may remain. * - ****************************************************************/ - if (localness && topLevel ) { - newOdn = new OpDefNode( odn.getName(), UserDefinedOpKind, odn.getParams(), - localness, odn.getBody(), - odn.getOriginallyDefinedInModuleNode(), - symbolTable, treeNode, true, odn.getSource() ); - /*************************************************************** - * The following statement was added by LL on 16 Feb 2009, by * - * analogy with the corresponding code about 45 lines below. I * - * have no idea if it was originally omitted for a good reason. * - ***************************************************************/ - newOdn.setLabels(odn.getLabelsHT()) ; - } - else { - newOdn = odn; - symbolTable.addSymbol(odn.getName(), odn); - } - } - else { - // Create the "wrapping" SubstInNode as a clone of "subst" above, - // but with a body from the OpDefNode in the module being - // instantiated - substInTemplate = new SubstInNode(treeNode, subst.getSubsts(), - odn.getBody(), cm, - instanceeModuleNode); - newOdn = new OpDefNode(odn.getName(), UserDefinedOpKind, - odn.getParams(), localness, - substInTemplate, cm, symbolTable, treeNode, - true, odn.getSource()); - newOdn.setLabels(odn.getLabelsHT()) ; - - - } - } - else { - // There are no parameters to the instancee module; this - // means that a SubstInNode is not necessary, and also that - // the new operator should be considered to be "originally - // defined in" the same module as the old one for purposes of - // telling whether they are "the same" or different definitions - - // Create an OpDefNode whose body is the same as the instancer's. - - if (localness) { - /**************************************************************** - * See the comments about the similar change made to the * - * setting of newOdn in the `then' clause, just above. * - * This entire change was made by LL on 16 Feb 2009. * - ****************************************************************/ - newOdn = new OpDefNode(odn.getName(), UserDefinedOpKind, odn.getParams(), - localness, odn.getBody(), - odn.getOriginallyDefinedInModuleNode(), - symbolTable, treeNode, true, odn.getSource()); - newOdn.setLabels(odn.getLabelsHT()) ; - } - else { - newOdn = odn; - symbolTable.addSymbol(odn.getName(), odn); - } - } - cm.appendDef(newOdn); - setOpDefNodeRecursionFields(newOdn, cm) ; - } // end for - - /********************************************************************** - * Import the ThmOrAssumpDefNode objects to the current module. The * - * following code for doing this was copied and modified without * - * thinking from the code above for OpDefNode objects * - **********************************************************************/ - Vector tadefs = instanceeCtxt.getByClass(ThmOrAssumpDefNode.class); - - ThmOrAssumpDefNode tadn; - // ThmOrAssumpDefNode in module being instantiated (instancee) - ThmOrAssumpDefNode newTadn; - // Its counterpart current module (instancer) - APSubstInNode tasubstInTemplate; // Template to be used for any - // SubstInNode wrappers required - - // Duplicate the OpDef records from the module being INSTANCE'd - for (int i = 0; i < tadefs.size(); i++) { - tadn = (ThmOrAssumpDefNode)tadefs.elementAt(i); - // ThmOrAssumpDefNode in module being instantiated (instancee) - - // Following statement added by LL on 30 Oct 2012 to handle locally - // instantiated theorems and assumptions. - if (tadn.isLocal()) { - continue ; - } - - // If there are parameters to the module being instantiated, then - // a SubstInNode is required, and possibly a different module of - // origin should be indicated - if (!instanceeModuleNode.isParameterFree()) { - // Create the ThmOrAssumpDefNode for the new instance of this - // definition. Note that the new ThmOrAssumpDefNode shares the - // array of FormalParamNodes with the old ThmOrAssumpDefNode, - // as well as large parts of its body (all but the SubstInNode). - // Hence, changes by a tool to an Original ThmOrAssumpDefNode will - // likely be reflected in all instances of it. - if ( tadn.getOriginallyDefinedInModuleNode().isParameterFree() ) { - // Following if/else added by LL on 30 Oct 2012 to handle locally - // instantiated theorems and assumptions. - if (localness && topLevel ) { - newTadn = new ThmOrAssumpDefNode(tadn.getName(), tadn.isTheorem(), - tadn.getBody(), cm, symbolTable, treeNode, - tadn.getParams(), instanceeModuleNode, - tadn.getSource()) ; - newTadn.setLocal(true); - } - else { - newTadn = tadn; - // On 30 Oct 2012, LL noticed that code was added on 16 Feb 2009 - // to the corresponding place in the code for an OpDefNode, but that - // nothing was added here. I suspect that something should have been - // that the 2009 code should also have been added here, but wasn't-- - // perhaps because there's no getLabelsHT method for a ThmOrAssumpDefNode. - // This may mean that there's a bug in handling labels in the - // instantiated theorem or assumption. - } - /* - new ThmOrAssumpDefNode( tadn.getName(), UserDefinedOpKind, - tadn.getParams(), - localness, tasubstInTemplate, - tadn.getOriginallyDefinedInModuleNode(), - symbolTable, treeNode, true ); - */ - } - else { - // Create the "wrapping" SubstInNode as a clone of "subst" above, - // but with a body from the ThmOrAssumpDefNode in the module being - // instantiated - tasubstInTemplate = new APSubstInNode(treeNode, subst.getSubsts(), - tadn.getBody() , cm, - instanceeModuleNode); - newTadn = new ThmOrAssumpDefNode(tadn.getName(), tadn.isTheorem(), - tasubstInTemplate, cm, symbolTable, - treeNode, tadn.getParams(), - instanceeModuleNode, tadn.getSource()); - // Following if/else added by LL on 30 Oct 2012 to handle locally - // instantiated theorems and assumptions. - newTadn.setLocal(localness) ; - // cm.appendDef(newTadn); - newTadn.setLabels(tadn.getLabelsHT()) ; - - } - } - else { - // There are no parameters to the instancee module; this - // means that a SubstInNode is not necessary, and also that - // the new operator should be considered to be "originally - // defined in" the same module as the old one for purposes of - // telling whether they are "the same" or different definitions - - // Create a ThmOrAssumpDefNode whose body is the same as - // the instancer's. - if (localness && topLevel) { - newTadn = - new ThmOrAssumpDefNode(tadn.getName(), tadn.isTheorem(), - tadn.getBody(), - tadn.getOriginallyDefinedInModuleNode(), - symbolTable, treeNode, tadn.getParams(), - instanceeModuleNode, tadn.getSource()); - // Following if/else added by LL on 30 Oct 2012 to handle locally - // instantiated theorems and assumptions. - newTadn.setLocal(localness) ; - newTadn.setLabels(tadn.getLabelsHT()) ; - } - else { - newTadn = tadn; - symbolTable.addSymbol(tadn.getName(), tadn); - } - } - if (topLevel) {cm.appendDef(newTadn);} ; - /******************************************************************** - * No recursion fields needed for theorems or assumptions because * - * they can't appear in a recursive section. * - ********************************************************************/ - // setOpDefNodeRecursionFields(newTadn, cm) ; - } // end for - - // Create a new InstanceNode to represent this INSTANCE stmt in - // the current module - InstanceNode inst = - new InstanceNode(null /*no name*/, localness, null /*no parms*/, - instanceeModuleNode, subst.getSubsts(), treeNode); - - // Append this new InstanceNode to the vector of InstanceNodes - // being accumulated for this module - if (topLevel){ cm.appendInstance(inst); } ; - return inst; - - } // void generateInstance - - - private final void processTheorem(TreeNode stn, ModuleNode cm) - throws AbortException { - ThmOrAssumpDefNode tadn = null ; - LevelNode body ; - ProofNode proof = null ; - int bodyIndex = stn.heirs().length - 1 ; - boolean isAssumeProve = false ; - /********************************************************************* - * Set true if the body is an ASSUME/PROVE. * - *********************************************************************/ - boolean hasProof = - (stn.heirs()[bodyIndex].getKind() == N_Proof) - || (stn.heirs()[bodyIndex].getKind() == N_TerminalProof) ; - if (hasProof) {bodyIndex--;} ; - - if (bodyIndex > 1) { - /********************************************************************* - * If this is a named theorem, start fresh processing of labels, * - * create the object. * - *********************************************************************/ - pushLS(); - UniqueString name = stn.heirs()[1].getUS() ; - tadn = new ThmOrAssumpDefNode(name, stn) ; - } ; - if (stn.zero()[bodyIndex].isKind(N_AssumeProve)) - { /******************************************************************* - * Theorem statement is an ASSUME/PROVE. Must set currentGoal. * - *******************************************************************/ - currentGoal = tadn ; - isAssumeProve = true ; - - /******************************************************************* - * We want the symbols declared in top-level NEW statements of the * - * ASSUME clause to be visible in the proof. Therefore, we do the * - * context push for the top-level AssumeProveNode here instead of * - * in generateAssumeProve. * - *******************************************************************/ - symbolTable.pushContext(new Context(moduleTable, errors)) ; - body = generateAssumeProve(stn.heirs()[bodyIndex], cm); - currentGoal = null ; - } - else { /**************************************************************** - * Theorem statement must be an ExprNode. * - ****************************************************************/ - body = generateExpression(stn.heirs()[bodyIndex], cm); - } ; - if (bodyIndex > 1) { - /********************************************************************** - * The theorem node has the form * - * THEOREM Ident == statement * - **********************************************************************/ - Context assumeContext = null; - if (isAssumeProve) { - /******************************************************************** - * If the body is an ASSUME/PROVE, we must save in assumeContext * - * the context containing the symbols declared in the ASSUME * - * clause and pop it, so Ident is made visible outside the proof. * - ********************************************************************/ - assumeContext = symbolTable.getContext() ; - symbolTable.popContext() ; - } ; - UniqueString name = stn.heirs()[1].getUS() ; - tadn.construct(true, body, cm, symbolTable, null) ; - tadn.setLabels(popLabelNodeSet()) ; - cm.appendDef(tadn) ; - if (isAssumeProve) { - /******************************************************************** - * After putting Ident into the symbol table, we push the ASSUME * - * context back onto symboltable. * - ********************************************************************/ - symbolTable.pushContext(assumeContext) ; - }; - } ; // if (bodyIndex > 1) - - /************************************************************************ - * Note: If this is a named theorem, then the name has been added to * - * symbolTable so it can be referred to within the theorem's proof (if * - * it has one). * - ************************************************************************/ - if (hasProof) {proof = generateProof(stn.heirs()[bodyIndex+1], cm);} ; - - if (isAssumeProve) { - symbolTable.popContext(); - /******************************************************************** - * Pop the context containing ASSUME declarations. * - ********************************************************************/ - ((AssumeProveNode) body).inProof = false ; - /******************************************************************** - * Reset the AssumeProve node's inProof field. * - ********************************************************************/ - } ; - cm.addTheorem(stn, body, proof, tadn) ; - } // ProcessTheorem - - private final void processAssumption(TreeNode stn, ModuleNode cm) - throws AbortException { - ThmOrAssumpDefNode tadn = null ; - int lastIndex = stn.heirs().length - 1 ; - if (lastIndex > 1) {pushLS();} ; - /********************************************************************* - * If this is a named assumption, start fresh processing of labels. * - *********************************************************************/ - ExprNode expr = generateExpression(stn.heirs()[lastIndex], cm); - - if (lastIndex > 1) { - /********************************************************************** - * The assumption node has the form * - * ASSUME Ident == expression * - **********************************************************************/ - UniqueString name = stn.heirs()[1].getUS() ; - tadn = new ThmOrAssumpDefNode(name, false, expr, cm, symbolTable, stn, - null, null, null) ; - tadn.setLabels(popLabelNodeSet()) ; - cm.appendDef(tadn) ; - } ; - cm.addAssumption(stn, expr, symbolTable, tadn); - return; - } // processAssumption - - - private final ProofNode generateProof(TreeNode stn, ModuleNode cm) - /*********************************************************************** - * Node stn is of kind N_TerminalProof or an N_Proof. The heirs of an * - * N_Proof node consist of an optional PROOF token followed by a * - * seequence of N_ProofStep nodes. The heirs of an N_ProofStep node * - * are a StartStep() token, a statement body, and an optional proof. * - * A statement body is one of the following node kinds: * - * * - * Have no proof: * - * N_DefStep N_UseOrHide N_NonLocalInstance N_HaveStep, * - * N_TakeStep N_WitnessStep * - * * - * Have a proof * - * N_QEDStep N_PickStep N_CaseStep N_AssertStep * - * * - * Each step produces the following kind of semantic node: * - * * - * N_DefStep : DefStepNode * - * * - * N_UseOrHide : UseOrNideNode * - * * - * N_NonLocalInstance : InstanceNode * - * * - * Others: TheoremNode * - * The type of statement is indicated by the body of the * - * node. For any step other than an N_AssertStep, the body * - * is an OpApplNode with the following dummy operator: * - * * - * N_HaveStep : $Have * - * N_CaseStep : $Pfcase * - * N_TakeStep : $Take * - * N_PickStep : $Pick * - * N_WitnessStep : $Witness * - * N_QEDStep : $Qed * - ***********************************************************************/ - throws AbortException { - int numberOfPops = 0; - /********************************************************************* - * For each SUFFICES ASSUME/PROVE step, we push a new context onto * - * the symbol table containing declarations of any NEW symbols. * - * These need to be popped off when through processing the proof. * - *********************************************************************/ - if (stn.getKind() == N_TerminalProof) { - return generateLeafProof(stn, cm) ;} ; - - Context pfCtxt = new Context(moduleTable, errors) ; - symbolTable.pushContext(pfCtxt); - /********************************************************************* - * Create a new sub-Context for the proof. * - *********************************************************************/ - - TreeNode heirs[] = stn.heirs() ; - int offset = 0 ; - if (heirs[0].getKind() == TLAplusParserConstants.PROOF) { offset = 1 ; } ; - LevelNode[] steps = new LevelNode[heirs.length - offset] ; - Vector iVec = new Vector() ; - /********************************************************************* - * A vector to hold the InstanceNodes generated by steps of the form * - * Id == INSTANCE ... so they can be level checked. * - *********************************************************************/ - - /*********************************************************************** - * At the beginning of each loop iteration, the variable prevIsInFix * - * equals true iff the previous step consists of a formula whose main * - * operator is an infix operator, including an "@-step" of the form "@ * - * infix-op expression". If it is true, then rhSide is the ExprNode * - * of the infix-op's right-hand-side expression. * - ***********************************************************************/ - boolean prevIsInfix = false ; - ExprNode prevRHS = null ; - - for (int i = offset ; i < heirs.length; i++) { - /********************************************************************* - * Process proof step i. * - *********************************************************************/ - boolean isAssumeProve = false ; - /******************************************************************* - * Will be set true for an ASSUME/PROVE, so we can reset the * - * node's inProof field. * - *******************************************************************/ - boolean isSuffices = false ; - /******************************************************************* - * Will be set to true for a SUFFICES statement. This is used to * - * do the right thing with ASSUME declarations in an ASSUME/PROVE * - * step, and to set the suffices field of the ThmOrAssumpDefNode * - * if this is a named step. * - *******************************************************************/ - - /********************************************************************* - * For an ASSUME/PROVE, we set assumeContext to a context containing * - * the declarations from the outer-most NEWs of the ASSUME. For an * - * ordinary ASSUME/PROVE, this context is pushed onto the symbol * - * table for statement's processing the proof. For a SUFFICE * - * ASSUME/PROVE, it is pushed onto the symbol table after processing * - * the statement's proof so the outermost NEW declarations are * - * visible for the rest of the current proof. * - * The handling of the assumeContext was changed on 1 Jul 2009 * - * because SUFFICE ASSUME/PROVE was not being handled properly. * - *********************************************************************/ - Context assumeContext = null; - - /********************************************************************* - * For "PICK x : exp", the symbol x is declared in exp, undeclared * - * in the proof of the step, and declared afterwards. (There'd be a * - * similar problem with TAKE if it took a proof.) The following * - * variables are used to manipulate this, with pickContext holding * - * the declarations of the symbols introduced by the PICK step. * - *********************************************************************/ - boolean isPick = false; - Context pickContext = null; - - /********************************************************************* - * Set stepNumSTN, stepBodySTN, and stepPfSTN to the syntax tree * - * nodes of the step number, the body, and the proof (the latter * - * equal to null if there's no proof. * - *********************************************************************/ - TreeNode pfStepSTN = heirs[i] ; - TreeNode stepNumSTN = pfStepSTN.heirs()[0] ; - TreeNode stepBodySTN = pfStepSTN.heirs()[1] ; - TreeNode stepPfSTN = null ; - if (pfStepSTN.heirs().length > 2) {stepPfSTN = pfStepSTN.heirs()[2];}; - - LevelNode pfNumNode = null ; - boolean makePfNumNode = true ; - /******************************************************************* - * For a numbered step that doesn't produce a TheoremNode, * - * pfNumNode is set to a node that will be the stepNode of a * - * NumberedProofStepKind OpDefNode. * - *******************************************************************/ - /* - * On 23 Feb 2014 LL added the following code to make step numbers - * like <*>13 illegal, so <+> and <*> can be used only for unnamed - * steps. It would be most natural to make the change age the - * parsing level by changing the JavaCC code. However, that code - * is a Kludge that it is best not to touch unless absolutely necessary. - * Also, detecting the error here allows multiple instances of the - * error to be reported in a single execution of the parser. - * - * The modification is based on the following empirical observation: - * - * - For a step number like "<3>13." - * stepNumSTN.image = stepNumSTN.originalImage = "<3>13." - * - For a step number like "<*>13.", - * stepNumSTN.image = "<3>13." and stepNumSTN.originalImage = "<*>13." - * - For unnumbered steps, - * stepNumSTN.originalImage = null and stepNumSTN.image = the actual token. - */ - SyntaxTreeNode STN = (SyntaxTreeNode) stepNumSTN ; - if ( (STN.originalImage != null) - && (STN.originalImage != STN.image) - ) { - String oimage = STN.originalImage.toString() ; - if ( (! oimage.equals(STN.image.toString())) - && ( (oimage.charAt(1) == '*') || (oimage.charAt(1) == '+') ) - ) { - errors.addError(stepNumSTN.getLocation(), - "<*> and <+> cannot be used for a named step."); - } - } - - /********************************************************************* - * Set stepNum to the step number, or null if its an unnumbered step. * - *********************************************************************/ - UniqueString stepNum = null ; - switch (stepNumSTN.getKind()) { - // LL: On 25 Feb 2010 I discovered the following comment here: - // XXXXXX xyz: need to add the following case - // The ProofImplicitStepLexeme case (something like <*>3) seems to be - // handled properly in tests. I presume that this is an obsolete - // comment that I didn't remove when I added the case to the code. - case TLAplusParserConstants.ProofImplicitStepLexeme : - case TLAplusParserConstants.ProofStepLexeme : - stepNum = stepNumSTN.getUS() ; - break ; - case TLAplusParserConstants.ProofStepDotLexeme : - String stNum = stepNumSTN.getUS().toString() ; - stepNum = UniqueString.uniqueStringOf( - stNum.substring(0, stNum.indexOf("."))) ; - break ; - default : - makePfNumNode = false ; - break ; - } ; // switch - - /********************************************************************* - * If this is a numbered step, process labels. * - *********************************************************************/ - if (stepNum != null) { - pushLS(); - } ; - - /******************************************************************** - * Construct the ThmOrOpDefNode if needed. (We need to do it now * - * because we have to set currentGoal before we generate the body.) * - ********************************************************************/ - ThmOrAssumpDefNode tadn = null ; - if (stepNum != null) { - tadn = new ThmOrAssumpDefNode(stepNum, stepNumSTN) ; - } ; - - /********************************************************************* - * Set prevIsInfix false unless this is an AssertStep node. * - *********************************************************************/ - int stepKind = stepBodySTN.getKind(); - if (stepKind != N_AssertStep) {prevIsInfix = false ;} ; - - switch (stepKind) { - case N_DefStep : - /***************************************************************** - * Set defSTNs to the array of heirs, and defOffSet so that * - * defSTNs[defOffSet] ... defSTNs[defSTNs.length-1] is the * - * sequence of definitions. * - *****************************************************************/ - TreeNode[] defSTNs = stepBodySTN.heirs(); - int defOffSet = 0 ; - if (defSTNs[0].getKind() == DEFINE) {defOffSet = 1;}; - - OpDefNode[] defs = new OpDefNode[defSTNs.length - defOffSet] ; - /*************************************************************** - * Will be set to the sequence of OpDefNodes for the * - * definitions. * - ***************************************************************/ - for (int j = defOffSet; j < defSTNs.length ; j++) { - TreeNode defSTN = defSTNs[j]; ; - Vector vec = new Vector() ; - switch (defSTN.getKind()) { - /*************************************************************** - * Need to check if it's an operator, function, or module * - * definition. * - ***************************************************************/ - case N_FunctionDefinition : - processFunction(defSTN, vec, cm) ; - break ; - case N_ModuleDefinition : - /************************************************************* - * The call to processModuleDefinition sets defsVec to a * - * vector of all the definitions it makes, and adds the new * - * InstanceNode to iVec. For now, we're just throwing * - * away defsVec. (If defsVec were null, then * - * processModuleDefinition would add these definitions to to * - * the module's list of top-level definitions.) * - *************************************************************/ - Vector defsVec = new Vector() ; - vec.addElement( - processModuleDefinition(defSTN, defsVec, iVec, cm)) ; - break ; - case N_OperatorDefinition : - processOperator(defSTN, vec, cm) ; - /************************************************************* - * processOperator creates an OpDefNode, puts an entry for * - * it in symbolTable, and adds the OpDefNode to vec. * - *************************************************************/ - break ; - }; // switch (def.getKind()) - defs[j - defOffSet] = (OpDefNode) vec.elementAt(0); - }; // for j - pfNumNode = new DefStepNode(stepBodySTN, stepNum, defs) ; - steps[i - offset] = pfNumNode ; - break ; - - case N_UseOrHide : - UseOrHideNode uohn = generateUseOrHide(stepBodySTN, cm) ; - - // Added by LL on 16 Jun 2010 so location returnedby getLocation() will - // include the step number. - uohn.stn = pfStepSTN; - - uohn.setStepName(stepNum); // Added 6 June 2010 by LL. - - if (uohn.facts.length + uohn.defs.length == 0) { - errors.addError(stepBodySTN.getLocation(), - "Empty USE or HIDE statement."); - }; - uohn.factCheck(); - // Added 4 Mar 2009. - pfNumNode = uohn ; - steps[i - offset] = pfNumNode ; - break ; - - case N_NonLocalInstance : - // Code to set step name added by LL on 6 June 2010 - InstanceNode inst = generateInstance(stepBodySTN, cm, false); - inst.setStepName(stepNum); - pfNumNode = inst; - steps[i - offset] = pfNumNode ; - break ; - - default : - makePfNumNode = false ; - TreeNode[] bodyHeirs = stepBodySTN.heirs() ; - LevelNode body = null ; - /*************************************************************** - * This will be set to the body of the TheoremNode or * - * ThmOrAssumpDefNode. * - ***************************************************************/ - UniqueString op = null ; - ExprNode[] args ; - /*************************************************************** - * For anything but an N_Assert node, body is set to an * - * OpApplNode having these as its operator and arguments. * - ***************************************************************/ - - switch (stepBodySTN.getKind()) { - case N_AssertStep : - int bodyNext = 0 ; - if (bodyHeirs[0].getKind() == - TLAplusParserConstants.SUFFICES) { - bodyNext = 1 ; - isSuffices = true ; - /************************************************************ - * We can't have an "@" in a SUFFICES step. * - ************************************************************/ - } ; - if (bodyHeirs[bodyNext].getKind() == N_AssumeProve) { - /************************************************************ - * This is an AssumeProve node. * - ************************************************************/ - isAssumeProve = true ; - - /************************************************************ - * For an ASSUME/PROVE, we need save the symbol * - * declarations from top-level NEW statements in the ASSUME * - * to make them visible only in the statement's proof for * - * an ordinary ASSUME/PROVE, and only after the statement's * - * proof for a SUFFICES ASSUME/PROVE. * - ************************************************************/ - symbolTable.pushContext(new Context(moduleTable, errors)) ; - - currentGoal = tadn ; - /********************************************************** - * Need to set currentGoal before generating the * - * AssumeProve node. * - **********************************************************/ - body = generateAssumeProve(bodyHeirs[bodyNext], cm); - - if (isSuffices) { ((AssumeProveNode) body).setSuffices(); }; - /********************************************************** - * Added 16 Feb 2009 by LL. * - **********************************************************/ - currentGoal = null ; - assumeContext = symbolTable.getContext() ; - symbolTable.popContext() ; - prevIsInfix = false ; - } - else { - /************************************************************ - * This is an ordinary expression. * - ************************************************************/ - TreeNode curExpr = bodyHeirs[bodyNext] ; - - /************************************************************ - * Special handling of SUFFICES added by LL 16 Feb 2009. * - ************************************************************/ - if (isSuffices) { - args = new ExprNode[1] ; - args[0] = generateExpression(curExpr, cm) ; - body = new OpApplNode(OP_suffices, args, stepBodySTN, cm) ; - } // if (isSuffices) - else { - /************************************************************ - * If the current expression is an infix expression, set * - * curLHS to its current LHS, otherwise set it to null. * - ************************************************************/ - SyntaxTreeNode curLHS = null ; - if (curExpr.getKind() == N_InfixExpr) { - curLHS = (SyntaxTreeNode) curExpr.heirs()[0] ; - } ; - /************************************************************ - * If prevIsInfix is true and curLHS is an "@", then * - * process it, using as the right-hand side a new $Nop node * - * with prevRHS as its argument. * - ************************************************************/ - if ( prevIsInfix - && (curLHS != null) - && (curLHS.heirs().length > 0) - /*************************************************** - * This test added 25 Feb 2010 because if curLHS * - * is a string, then curLHS.heirs() is a * - * zero-length array, so the following test threw * - * an out-of-bounds array index exception. Note * - * that curLHS.heirs() should never be null, * - * because the heirs() method can never return * - * null. * - ***************************************************/ - && (((SyntaxTreeNode) - curLHS.heirs()[0]).heirs().length == 0) - && (curLHS.heirs()).length > 1 - /*************************************************** - * This test added 2 Mar 2009 to fix following * - * bug. When we are here and the left-hand side * - * is something like a number, then curLHS.heirs() * - * seems to have length 1 and the following test * - * causes an ArrayIndexOverflowException. * - ***************************************************/ - && (curLHS.heirs()[1].getKind() == IDENTIFIER) - && (curLHS.heirs()[1].getUS() == AtUS)) { - - /********************************************************** - * The following code obtained by a simple modification * - * of the N_InfixExpr case of generateExpression. * - **********************************************************/ - TreeNode[] children = curExpr.heirs() ; - GenID genID = generateGenID(children[1], cm); - ExprNode[]sns = new ExprNode[2]; - SymbolNode opn = - symbolTable.resolveSymbol( - Operators.resolveSynonym(genID.getCompoundIDUS())); - if ( opn == null ) { - errors.addError(curExpr.getLocation(), - "Couldn't resolve infix operator symbol `" + - genID.getCompoundIDUS() + "'." ); - return null; - } ; - sns[1] = generateExpression( children[2], cm ); - /********************************************************** - * Set sns[1] to a new $Nop OpApplNode whose argument is * - * prevRHS. * - **********************************************************/ - ExprNode[] nopArgs = new ExprNode[1] ; - nopArgs[0] = prevRHS ; - sns[0] = new OpApplNode(OP_nop, nopArgs, curLHS, cm) ; - body = new OpApplNode(opn, sns, curExpr, cm); - } // if ( prevIsInfix ...) - else { // this is not an @-step - body = generateExpression(curExpr, cm) ; - } ; - - /************************************************************ - * If this is an infix ioperator, set prevIsInfix true and * - * prevRHS equal to its right-hand argument, else set * - * prevIsInfix false. * - ************************************************************/ - prevIsInfix = false ; - if ( (curLHS != null) - /******************************************************* - * The following conjuncts should be true unless there * - * was an error in the expression. * - *******************************************************/ - && (body != null) - && (body.getKind() == OpApplKind) - && ( ((OpApplNode) body).getArgs().length > 1)) { - prevIsInfix = true ; - prevRHS = (ExprNode) ((OpApplNode) body).getArgs()[1] ; - } - } - }; // else This is an ordinary expression. - break ; - - case N_HaveStep : - case N_CaseStep : - if (stepBodySTN.getKind() == N_HaveStep) {op = OP_have ;} - else {op = OP_pfcase ;} ; - args = new ExprNode[1] ; - args[0] = generateExpression(bodyHeirs[1], cm) ; - body = new OpApplNode(op, args, stepBodySTN, cm) ; - break ; - - case N_TakeStep : - case N_PickStep : - if (stepBodySTN.getKind() == N_TakeStep) {op = OP_take ;} - else { - op = OP_pick ; - isPick = true; - /************************************************************ - * Push a new context onto the symbolTable stack to get the * - * declarations of the PICK symbols. * - ************************************************************/ - symbolTable.pushContext(new Context(moduleTable, errors)) ; - } ; - - if (bodyHeirs[1].getKind() == N_QuantBound) { - /************************************************************ - * The introduced identifiers are bounded--e.g., "TAKE id * - * \in Set". * - ************************************************************/ - - /************************************************************ - * Set quants to the number of N_QuantBound nodes. * - ************************************************************/ - int quants = 1 ; - int nextTok = 2; - while ( (nextTok < bodyHeirs.length) - && (bodyHeirs[nextTok].getKind() - == TLAplusParserConstants.COMMA)) { - quants++ ; - nextTok = nextTok + 2 ; - }; - FormalParamNode[][] params = new FormalParamNode[quants][0]; - boolean[] bt = new boolean[quants] ; - ExprNode[] paramBounds = new ExprNode[quants]; - processQuantBoundArgs( - bodyHeirs, 1, params, bt, paramBounds, cm) ; - - if (isPick) { - /********************************************************** - * Save the declarations in pickContext. * - **********************************************************/ - pickContext = symbolTable.getContext() ; - /********************************************************** - * This is a PICK step; get the ": expr". * - **********************************************************/ - nextTok++ ; // Skip over the ":" - args = new ExprNode[1] ; - pushFormalParams(flattenParams(params)) ; - /******************************************************** - * Push the bound variables on Last(LS).paramSeq and * - * process the body of the PICK. * - ********************************************************/ - args[0] = generateExpression(bodyHeirs[nextTok], cm) ; - popFormalParams() ; - symbolTable.popContext() ; - /******************************************************** - * Remove the bound symbols from the symbol table so * - * they're undefined for the proof of the PICK. * - ********************************************************/ - } - else { - /********************************************************** - * This is a TAKE step. * - **********************************************************/ - args = new ExprNode[0] ; - }; - body = new OpApplNode(op, null, args, params, - bt, paramBounds, stepBodySTN, cm) ; - } - else { - /************************************************************ - * The introduced identifiers are unbounded--e.g., "TAKE * - * id1, id2". * - ************************************************************/ - - /************************************************************ - * Set ids to the number of introduced identifiers. * - ************************************************************/ - int ids = 1 ; - while ( (2*ids < bodyHeirs.length) - && (bodyHeirs[2*ids].getKind() == - TLAplusParserConstants.COMMA)) {ids++;} ; - - /************************************************************ - * Set params to the array of new FormalParamNodes for the * - * identifiers. The identifiers are added to the current * - * symbol table. * - ************************************************************/ - FormalParamNode[] params = new FormalParamNode[ids]; - for (int j = 0 ; j < ids ; j++) { - params[j] = new FormalParamNode( - bodyHeirs[2*j + 1].getUS(), 0, - bodyHeirs[2*j + 1], symbolTable, cm); - } ; - - if (isPick) { - /********************************************************** - * Save the declarations in pickContext. * - **********************************************************/ - pickContext = symbolTable.getContext() ; - - /********************************************************** - * This is a PICK step; get the ": expr". * - **********************************************************/ - pushFormalParams(params) ; - /******************************************************** - * Push formal parameters on Last(LS).paramSeq for * - * processing the body. * - ********************************************************/ - args = new ExprNode[1] ; - args[0] = generateExpression(bodyHeirs[2*ids + 1], cm) ; - popFormalParams() ; - symbolTable.popContext() ; - /******************************************************** - * Remove the bound symbols from the symbol table so * - * they're undefined for the proof of the PICK. * - ********************************************************/ - } - else { - /********************************************************** - * This is a TAKE step. * - **********************************************************/ - args = new ExprNode[0] ; - }; - body = new OpApplNode(op, args, params, stepBodySTN, cm) ; - } ; - - break ; - - case N_WitnessStep : - /************************************************************** - * Set ids to the number of expressions. * - **************************************************************/ - int ids = 1 ; - while ( (2 * ids < bodyHeirs.length) - && (bodyHeirs[2 * ids].getKind() == - TLAplusParserConstants.COMMA)) {ids++;} ; - - args = new ExprNode[ids] ; - for (int j = 0 ; j < ids ; j++) { - args[j] = generateExpression(bodyHeirs[2*j + 1], cm); - } ; - body = new OpApplNode(OP_witness, args, stepBodySTN, cm) ; - break ; - - case N_QEDStep : - args = new ExprNode[0] ; - body = new OpApplNode(OP_qed, args, stepBodySTN, cm) ; - break ; - - default : - errors.addAbort( - stn.getLocation(), - "Internal error: Unexpected SyntaxTreeNode kind: " - + heirs[i].getKind()) ; - break ; - }; // switch - - - /******************************************************************** - * Set the fields of the ThmOrOpDefNode if there is one, including * - * the suffices field. * - ********************************************************************/ - if (stepNum != null) { - tadn.construct(true, body, cm, symbolTable, null) ; - tadn.setLabels(popLabelNodeSet()) ; - if (isSuffices) { tadn.setSuffices() ;} ; - } ; - /*********************************************************************** - * Set proof to the proof, or to null if there is none. There is no * - * check made to see if this is a kind of step that should have a * - * proof. Thus, adding a proof to something like a WITNESS statement * - * requires changing only the parsing phase (specified by tla+.jj). * - ***********************************************************************/ - ProofNode proof = null ; - if (stepPfSTN != null) { - /****************************************************************** - * For an ordinary ASSUME/PROVE, must make the ASSUME's * - * declarations visible in the statement's proof. * - ******************************************************************/ - if (isAssumeProve && !isSuffices) { - symbolTable.pushContext(assumeContext) ; - } ; - proof = generateProof(stepPfSTN, cm); - if (isAssumeProve && !isSuffices) {symbolTable.popContext(); } ; - } ; - - /******************************************************************** - * For a SUFFICES ASSUME/PROVE, must make the ASSUME's declarations * - * visible after the proof. This is done by pushing assumeContext * - * onto the symbol table and incrementing numberOfPops so it will * - * be popped at the end of the proof. * - ********************************************************************/ - if (isAssumeProve && isSuffices) { - numberOfPops++ ; - symbolTable.pushContext(assumeContext) ; } ; - if (isAssumeProve) { ((AssumeProveNode) body).inProof = false ; } ; - /****************************************************************** - * For an ASSUME/PROVE, set the inProof field to false. * - ******************************************************************/ - TheoremNode thm = new TheoremNode(stepBodySTN, body, cm, proof, tadn); - - // Added by LL on 16 Jun 2010 so location returnedby getLocation() will - // include the step number. - thm.stn = pfStepSTN; - - thm.suffices = isSuffices ; - steps[i - offset] = thm; - }; // switch - if (makePfNumNode) { - /******************************************************************* - * Make an OpDefNode for the numbered step and add any label * - * declarations to it. * - *******************************************************************/ - OpDefNode nodeMadeOnlyToBePutInSymbolTable = - new OpDefNode(stepNum, pfNumNode, cm, symbolTable, pfStepSTN) ; - nodeMadeOnlyToBePutInSymbolTable.setLabels(popLabelNodeSet()) ; - } - if (isPick) { - /******************************************************************* - * We have to take the symbol declarations from pickContext and * - * put them into the current symbol table. * - *******************************************************************/ - Enumeration e = pickContext.content() ; - while (e.hasMoreElements()) { - SymbolNode sym = ((Context.Pair)(e.nextElement())).getSymbol(); - symbolTable.addSymbol(sym.getName(), sym) ; - } - } - } ; // for i - InstanceNode[] insts = new InstanceNode[iVec.size()] ; - for (int i = 0 ; i < insts.length; i++) { - insts[i] = (InstanceNode) iVec.elementAt(i) ; - }; - - /*********************************************************************** - * Pop the contexts that were pushed onto the symbol table for SUFFICE * - * ASSUME/PROVE steps. * - ***********************************************************************/ - for (int i = 0; i < numberOfPops; i++) { - // Added by LL on 24 June 2010 - // Need to add the symbols in the context being popped to pfCtxt - // so they will be put into the context of the NonLeafProofNode. - Context topContext = symbolTable.getContext(); - Enumeration e = topContext.content() ; - while (e.hasMoreElements()) { - SymbolNode sym = ((Context.Pair)(e.nextElement())).getSymbol(); - pfCtxt.addSymbolToContext(sym.getName(), sym) ; - } - symbolTable.popContext(); - }; - symbolTable.popContext(); - return new NonLeafProofNode(stn, steps, insts, pfCtxt); - /********************************************************************* - * Pop the sub-Context. * - *********************************************************************/ - } // generateProof - -/*************************************************************************** -* The following method is not used and I have no idea why it's still * -* here. * -***************************************************************************/ - private final LevelNode generateNumerableStep( - TreeNode stn, + pushFormalParams(params); + /******************************************************************* + * Push formal parameters on Last(LS).paramSeq for processing the * body + *******************************************************************/ + ExprNode body = generateExpression(children[children.length - 1], cm); + /********************************************************************* + * Generate the lambda expression's body. * + *********************************************************************/ + popFormalParams(); + + symbolTable.popContext(); + /********************************************************************* + * Restore original context. * + *********************************************************************/ + return new OpDefNode(S_lambda, // The operator name is "LAMBDA" + UserDefinedOpKind, // The node kind for a lambda expression + params, // the array of formal parameters + false, // localness, which is meaningless + body, // the body (an expression node) + cm, // the module + null, // the symbol table, which is null for an + // OpDefNode representing a lambda expression. + syntaxTreeNode, true, // Is defined. Its value should not matter. + null); // Source + } // generateLambda + + /** + * Generates an OpApplNode or an OpArgNode for a SyntaxTreeNode, according to + * whether the value of "typeExpected" is either opAppl or opArg. + */ + private final ExprNode generateOpAppl(TreeNode syntaxTreeNode, ModuleNode cm) throws AbortException { + TreeNode primaryArgs = null; + // points to syntax tree of primary arg list (if any); otherwise null + boolean isOpApp = syntaxTreeNode.isKind(N_OpApplication); + // true ==> to indicate this is an OpAppl; must have primary args + // false ==> operator used as an argument (OpArg); has no primary args + int primaryArgCount = 0; + // total # of arguments, including those of prefix, if any + int len; + // number of prefix elements + TreeNode[] children = syntaxTreeNode.heirs(); + // temp used for finding the prefix + TreeNode[] prefix; + // array of prefix elements + TreeNode[] allArgs = null; + // to collect arg arrays from both prefix and the main op (if any) + TreeNode[] prefixElt; + // a prefixElement; 2 or 3 elem array: [op, (args), "!"] + UniqueString symbol; + // UniqueString name of the fully-qualified operator + SymbolNode fullOperator; + // The SymbolNode for the fully-qualified operator + int iarg = 0; + // loop counter for actual args (as opposed to + // arg syntax elements like commas and parens) + TreeNode[] argsList = null; + // Will hold an array of arg syntax trees for primary operator + ExprOrOpArgNode[] args = null; + // Will hold an array of arg semantic trees for primary operator + + // Process the Generized ID that is the operator for this OpAppl + GenID genID = generateGenID(children[0], cm); + + // Set up pointers to OpAppl's primary args + primaryArgs = children[1]; + // Array of argument list syntax elements for the main + // (rightmost) operator, including parens and commas; + // should be an N_OpArgs node + + // calc number of primary args; + // args are interspersed w/ parens & commas--hence the /2 + primaryArgCount = primaryArgs.heirs().length / 2; + + if (genID == null || genID.getFullyQualifiedOp() == null) { + // if operator is @ or an unresolved symbol; error has already + // been generated inside genID + return nullOAN; + } + + args = new ExprOrOpArgNode[primaryArgCount]; + // Array to hold semantic trees for primary args + + // pick up array of arg list syntax elements + argsList = primaryArgs.heirs(); + + // The odd numbered syntax elements are the args expressions; the + // even numbered ones are parens and commas. + // for each arg in this arg list ... + for (int ia = 1; ia < argsList.length; ia += 2) { + // Each arg may be an ordinary expression, or it may be an OpArg; + // produce appropriate semantic tree or node for it. + // Note that operators can be used in place of expressions + // in only two contexts: + // as argument to suitable user-defined ops, and in the RHS + // of a substitution in module instantiation + args[iarg] = generateExprOrOpArg(genID.getFullyQualifiedOp(), syntaxTreeNode, iarg, argsList[ia], cm); + iarg++; // count the actual args + } // end for + + // Concatenate the list of args in the GenID object to the + // primary arg list just created + Vector genIDArgList = genID.getArgsVector(); + ExprOrOpArgNode[] finalArgList = new ExprOrOpArgNode[genIDArgList.size() + iarg]; + + // Copy the args from the prefix + for (int i = 0; i < genIDArgList.size(); i++) { + finalArgList[i] = (ExprOrOpArgNode) (genIDArgList.elementAt(i)); + } + // Copy the primary args + for (int i = 0, j = genIDArgList.size(); i < iarg; i++, j++) { + finalArgList[j] = args[i]; + } + + // return an OpApplNode constructed from the fully-qualified + // operator and the final arg list + return new OpApplNode(genID.getFullyQualifiedOp(), finalArgList, syntaxTreeNode, cm); + } // end generateOpAppl() + + /** + * Process a named, parameterixed instantiation, e.g. of the form D(p1,...pn) = + * INSTANCE M WITH a1 <- e1 ,..., ar <- er + */ + /************************************************************************* + * Note: the returned value does not seem to be used. * + *************************************************************************/ + private final OpDefNode processModuleDefinition(TreeNode treeNode, Vector defs, + /******************************************************* + * This is non-null when called from inside a LET, in * which case the OpDef + * node is appended to defs. If * null, the OpDef node is appended to the * + * module-level lists of such nodes. * + *******************************************************/ + Vector insts, + /******************************************************* + * If non-null, then a vector of InstanceNode objects * to which the current + * instance node is to be * appended. If null, cm.appendInstance is called to * + * put the InstanceNode onto the module-level lists of * such nodes. * + *******************************************************/ + ModuleNode cm) throws AbortException { + // Start with a LHS for an instance: we must extract from it name + // and possibly parameters. Then we need the external Context of + // the module and to extract all non-local, non-builtin + // symbols. Then build the proper symbol list and add it. + + // We must remember to identify explicitly whether or not the new + // nodes would be local. + + // assert treeNode.isKind(N_ModuleDefinition) + // + // Note that this code does nothing about THEOREMS and ASSUMES in + // modules being instantiated + boolean localness = treeNode.zero() != null; + TreeNode[] children = treeNode.one()[0].heirs(); // heirs of IdentLHS + UniqueString name = children[0].getUS(); + + // processing of LHS of the definition, i.e. the name and parameters + FormalParamNode[] args = nullParam; + Context parmCtxt = null; + + // If the operator being defined as a module instance has any parameters + if (children.length > 1) { + // Create new array of FormalParamNodes for the new operator + args = new FormalParamNode[children.length / 2 - 1]; + + // Push a new context in current module's SymbolTable + parmCtxt = new Context(moduleTable, errors); + symbolTable.pushContext(parmCtxt); + + // For each formal parameter declared for the op being defined + for (int i = 0; i < args.length; i++) { + TreeNode child = children[2 + 2 * i]; + UniqueString id = null; + int count = 0; + + if (child.isKind(N_IdentDecl)) { + id = child.heirs()[0].getUS(); + count = (child.heirs().length - 1) / 2; + } else if (child.isKind(N_InfixDecl)) { + id = child.heirs()[1].getUS(); + count = 2; + } else if (child.isKind(N_PrefixDecl)) { + id = child.heirs()[0].getUS(); + count = 1; + } else if (child.isKind(N_PostfixDecl)) { + id = child.heirs()[1].getUS(); + count = 1; + } else { + errors.addAbort(treeNode.getLocation(), + "Internal error: Error in formal params part of parse tree.", true); + } + + // If there was no error + if (id != null) { + // Create a new FormalParamNode for the defined Op and put + // it in the SymbolTable + args[i] = new FormalParamNode(id, count, child, symbolTable, cm); + } // end if + } // end for + } // end if + + // processing RHS of the definition, starting with identification + // of module being instantiated, followed by processing of the + // WITH clause (if any) + children = treeNode.one()[2].heirs(); // heirs of NonLocalInstance + + // Find the Context and ModuleNode for the module being instantiated + Context instanceeCtxt = this.getContext(children[1].getUS()); + ModuleNode instanceeModule = symbolTable.resolveModule(children[1].getUS()); + + if (instanceeCtxt == null) { + errors.addError(children[1].getLocation(), + "Module " + children[1].getImage() + " does not have a context."); + return nullODN; + } + + if (instanceeModule == null) { + errors.addError(children[1].getLocation(), + "Module name " + children[1].getImage() + " is not known" + " in current context."); + return nullODN; + } + + /* + * Set isInstantiated field of instancee. Added by LL 23 July 2013 + */ + instanceeModule.setInstantiated(true); + + // Create a SubstInNode that will be used to wrap each definition + // body in the module being defined. "children" is the array of + // explicit substitution clauses used in the module definition. + // Both instanceeCtxt and symbolTable are involved here since for + // each substitution c <- e, c must be resolved in the + // instanceeCtxt, and e must be interpreted in symbolTable + SubstInNode substIn = processSubst(treeNode, children, symbolTable, instanceeCtxt, instanceeModule, cm); + + // We are done with the local context (if one was created because + // of parameters) + if (parmCtxt != null) + symbolTable.popContext(); + + // Create a vector of all of the OpDefNodes in the instancee module + /*********************************************************************** + * I have no idea why the module's getOpDefs method isn't used here. * + ***********************************************************************/ + Vector elts = instanceeCtxt.getByClass(OpDefNode.class); + + // For each definition in the instancee module, create a + // corresponding definition in the instancer module + for (int i = 0; i < elts.size(); i++) { + // Find the OpDefNode to be instantiated + OpDefNode odn = (OpDefNode) elts.elementAt(i); + + /********************************************************************** + * Ignore it if it is local or a builtin def. * + **********************************************************************/ + if (!odn.isLocal() && ((odn.getKind() == UserDefinedOpKind) || (odn.getKind() == ModuleInstanceKind))) { + // Create the new name prepended with "name!" + String compoundID = name + "!" + odn.getName(); + UniqueString qualifiedName = UniqueString.uniqueStringOf(compoundID); + + // Copy parameters for the op being defined + FormalParamNode[] fpn = odn.getParams(); + FormalParamNode[] params = new FormalParamNode[fpn.length + args.length]; + System.arraycopy(args, 0, params, 0, args.length); + System.arraycopy(fpn, 0, params, args.length, fpn.length); + + OpDefNode newOdn; + if (odn.getKind() == UserDefinedOpKind) { + if (substIn.getSubsts().length > 0) { + // If there are substitutions, then the body of the new + // definition instance must be wrapped in a SUBST-IN node. + // Create the "wrapping" SubstInNode as a clone of "subst" + // above, but with a body from the OpDefNode in the module + // being instantiated + SubstInNode substInNode = new SubstInNode(treeNode, substIn.getSubsts(), odn.getBody(), cm, + instanceeModule); + + // Create the OpDefNode for the new instance of this + // definition; because of the new operator name, cm is the + // module of origin for purposes of deciding of two defs are + // "the same" or "different" + newOdn = new OpDefNode(qualifiedName, UserDefinedOpKind, params, localness, substInNode, cm, + symbolTable, treeNode, true, odn.getSource()); + setOpDefNodeRecursionFields(newOdn, cm); + newOdn.setLabels(odn.getLabelsHT()); + } // if (substIn.getSubsts().length > 0) + else { + // no SUBST-IN node required; but because of the new + // operator name, cm is the module of origin for purposes of + // deciding of two defs are "the same" or "different" + newOdn = new OpDefNode(qualifiedName, UserDefinedOpKind, params, localness, odn.getBody(), cm, + symbolTable, treeNode, true, odn.getSource()); + setOpDefNodeRecursionFields(newOdn, cm); + newOdn.setLabels(odn.getLabelsHT()); + } // else + } // if (odn.kind == UserDefinedOpKind) + else { + /***************************************************************** + * This is a ModuleInstanceKind node. * + *****************************************************************/ + newOdn = new OpDefNode(qualifiedName, params, localness, odn.getOriginallyDefinedInModuleNode(), + symbolTable, treeNode, odn.getSource()); + } + ; // else + // defs is non-null iff this module definition is in the Let + // part of a Let-In expression. Add this newly created OpDef + // to either the LET list or the module cm's definition list. + if (defs == null) { + cm.appendDef(newOdn); + } else { + defs.addElement(newOdn); + } + } // if (!odn.isLocal()&& ... ) + } // for + + /********************************************************************** + * Import the ThmOrAssumpDefNode objects to the current module. The * following + * code for doing this was copied and modified without * much thinking from the + * code above for OpDefNode objects * + **********************************************************************/ + // Create a vector of all of the ThmOrAssumpDefNodes in the + // instancee module + Vector taelts = instanceeCtxt.getByClass(ThmOrAssumpDefNode.class); + + // For each definition in the instancee module, create a + // corresponding definition in the instancer module + for (int i = 0; i < taelts.size(); i++) { + // Find the ThmOrAssumpDefNode to be instantiated + ThmOrAssumpDefNode taOdn = (ThmOrAssumpDefNode) taelts.elementAt(i); + + /********************************************************************* + * There are no builtin ThmOrAssumpDefNode objects. * + *********************************************************************/ + // Ignore it if it is local + if (!taOdn.isLocal()) { + // Create the new name prepended with "name!" + String compoundID = name + "!" + taOdn.getName(); + UniqueString qualifiedName = UniqueString.uniqueStringOf(compoundID); + + // Copy parameters for the op being defined + /******************************************************************* + * Theorem or assumption definitions have no parameters. * + *******************************************************************/ + FormalParamNode[] fpn = taOdn.getParams(); + FormalParamNode[] params = new FormalParamNode[fpn.length + args.length]; + System.arraycopy(args, 0, params, 0, args.length); + System.arraycopy(fpn, 0, params, args.length, fpn.length); + + ThmOrAssumpDefNode newtaOdn; + if (substIn.getSubsts().length > 0) { + // If there are substitutions, then the body of the new + // definition instance must be wrapped in a SUBST-IN node. + // Create the "wrapping" SubstInNode as a clone of "subst" + // above, but with a body from the ThmOrAssumpDefNode in the module + // being instantiated + APSubstInNode substInNode = new APSubstInNode(treeNode, substIn.getSubsts(), taOdn.getBody(), cm, + instanceeModule); + + // Create the ThmOrAssumpDefNode for the new instance of this + // definition; because of the new operator name, cm is the + // module of origin for purposes of deciding of two defs are + // "the same" or "different" + newtaOdn = new ThmOrAssumpDefNode(qualifiedName, taOdn.isTheorem(), substInNode, cm, symbolTable, + treeNode, params, instanceeModule, taOdn.getSource()); + // Following statement added by LL on 30 Oct 2012 to handle locally + // instantiated theorems and assumptions. Added setLabels call + // on 31 Oct 2012. + newtaOdn.setLocal(localness); + newtaOdn.setLabels(taOdn.getLabelsHT()); + + /***************************************************************** + * No recursion fields needed for a theorem or assumption * because it can't + * appear in a recursive section. * + *****************************************************************/ + // setThmOrAssumpDefNodeRecursionFields(newtaOdn, cm) ; + } else { + // no SUBST-IN node required; but because of the new + // operator name, cm is the module of origin for purposes of + // deciding if two defs are "the same" or "different" + newtaOdn = new ThmOrAssumpDefNode(qualifiedName, taOdn.isTheorem(), taOdn.getBody(), cm, + symbolTable, treeNode, params, instanceeModule, taOdn.getSource()); + // Following statement added by LL on 30 Oct 2012 to handle locally + // instantiated theorems and assumptions. Added setLabels call + // on 31 Oct 2012. + newtaOdn.setLocal(localness); + newtaOdn.setLabels(taOdn.getLabelsHT()); + /***************************************************************** + * No recursion fields needed for theorems or assumptions * because they can't + * appear in a recursive section. * + *****************************************************************/ + // setThmOrAssumpDefNodeRecursionFields(newtaOdn, cm) ; + } + + // defs is non-null iff this module definition is in the Let + // part of a Let-In expression. Add this newly created ThmOrAssumpDef + // to either the LET list or the module cm's definition list. + if (defs == null) { + cm.appendDef(newtaOdn); + } else { + defs.addElement(newtaOdn); + } + } // if (!taOdn.isLocal()&& ... ) + } // for + + // Create a new InstanceNode to represent this INSTANCE stmt + // in the current module + InstanceNode inst = new InstanceNode(name, localness, args, instanceeModule, substIn.getSubsts(), treeNode); + + // Append this new InstanceNode to the vector of InstanceNodes + // being accumulated for this module + if (insts == null) { + cm.appendInstance(inst); + } else { + insts.addElement(inst); + } + + // Create new OpDefNode with ModuleInstanceKind. The reason for + // doing this is to get the name in the symbol table so the name + // cannot be re-used later in this module for a user-defined + // operator. + return new OpDefNode(name, args, localness, cm, symbolTable, treeNode, null); + /********************************************************************* + * Note: the module's OpDefNode does not have recursive parameters * set. If the + * module definition statement occurs in a recursive * section, then it is the + * instantiated definitions that are * recursive, not the module definition + * itself. (The name under * which the module is instantiated cannot appear in a + * RECURSIVE * statement.) * + *********************************************************************/ + + } // end processModuleDefinition() + + /** + * From a particular explicit substitution (substTarget <- substValue) check the + * legality of the substTarget, and if OK, generate the appropriate type of node + * for substValue + */ + private ExprOrOpArgNode generateSubst(Context instanceeCtxt, TreeNode substTarget, TreeNode substValue, + ModuleNode mn) throws AbortException { + SymbolNode targetSymbol = instanceeCtxt.getSymbol(substTarget.getUS()); + + // if the targetSymbol cannot be found in the instancee context, + // or if it does not correspond to a declaration, then it is an + // illegal substitution target + + if (targetSymbol == null || !(targetSymbol instanceof OpDeclNode)) { + errors.addError(substTarget.getLocation(), "Identifier '" + substTarget.getUS() + "' is not a legal" + + " target of a substitution. \nA legal target must be a declared" + + " CONSTANT or VARIABLE in the module being instantiated." + + " \n(Also, check for warnings about multiple declarations of" + " this same identifier.)"); + return nullOAN; + } + + // but if the symbol is found, then if it has arity 0, the RHS + // should be an expression, if arity > 0, the the RHS should be an OpArg + ExprOrOpArgNode returnObject; + + if (targetSymbol.getArity() == 0) { + // if the target of the substitution has arity 0, + // then we expect an expression to be substituted for it + returnObject = generateExpression(substValue, mn); + } else { + // if the target of the substitution has arity > 0, + // then and operator must be substituted for it + returnObject = generateOpArg(targetSymbol, substValue, mn); + + // and it better have the same arity as the target + if (((OpArgNode) returnObject).getArity() != targetSymbol.getArity()) { + errors.addError(substValue.getLocation(), "An operator must be substituted for symbol '" + + targetSymbol.getName() + "', and it must have arity " + targetSymbol.getArity() + "."); + } + } + return returnObject; + } // end generateSubst() + + /** + * Return an OpArgNode constructed from a GeneralId tree to be used in the RHS + * of a substitution + */ + private OpArgNode generateOpArg(SymbolNode targetSymbol, TreeNode opArgSyntaxNode, ModuleNode mn) + throws AbortException { + /*********************************************************************** + * If this is a lambda expressiion, then just get it and go. * + ***********************************************************************/ + if (opArgSyntaxNode.isKind(N_Lambda)) { + return new OpArgNode(generateLambda(opArgSyntaxNode, mn), opArgSyntaxNode, mn); + } + ; + // First, make sure that an operator ID is present, and not an expression + if (!(opArgSyntaxNode.isKind(N_GeneralId) || opArgSyntaxNode.isKind(N_GenInfixOp) + || opArgSyntaxNode.isKind(N_GenPrefixOp) || opArgSyntaxNode.isKind(N_GenNonExpPrefixOp) || + /************************************************************ + * This last disjunct was added by LL on 9 May 2007. * * The original parser + * phase produced an N_GenPrefixOp * here, but an N_GenNonExpPrefixOp for the + * syntactically * identical situation in an operator argument. The new * TLA+2 + * parser produces N_GenNonExpPrefixOp nodes here, * but I left the + * N_GenPrefixOp case in here just in case * this is called somewhere else that + * I haven't found. * + ************************************************************/ + + opArgSyntaxNode.isKind(N_GenPostfixOp))) { + errors.addError(opArgSyntaxNode.getLocation(), + "Arity " + targetSymbol.getArity() + " operator (not an expression) is expected" + + " \nto substitute for CONSTANT '" + targetSymbol.getName() + "'."); + return nullOpArg; + } + + /*********************************************************************** + * If the argument is a GeneralId node, then we use * genIdToSelector and + * selectorToNode to generate the OpArg node. * + ***********************************************************************/ + if (opArgSyntaxNode.getKind() == N_GeneralId) { + /********************************************************************* + * First, a sanity check to make sure we're never looking for an * expression + * argument. * + *********************************************************************/ + if (targetSymbol.getArity() <= 0) { + errors.addAbort(opArgSyntaxNode.getLocation(), "Internal error: expected to find arity > 0.", true); + } + ; + + LevelNode ln = selectorToNode(genIdToSelector((SyntaxTreeNode) opArgSyntaxNode), targetSymbol.getArity(), + false, false, mn); + + /******************************************************************* + * Added 23 February 2009: It appears that, in case of an error, * + * selectorToNode can return something other than an OpArgNode * here. (In + * particular, an OpApplNode.) Rather than tix * selectorToNode, I am adding a + * kludge to simply ignore the * problem and hope that it can be caused only by + * some other * error. If no error has been found, then we abort and debug if * + * this is encountered. * + *******************************************************************/ + if (!(ln instanceof OpArgNode)) { + if (errors.getNumErrors() > 0) { + return nullOpArg; + } + errors.addAbort(opArgSyntaxNode.getLocation(), + "Internal error: " + "Expected an operator argument but " + "found something else."); + } + ; + return (OpArgNode) ln; + } + ; + + /******************************************************************* + * If the argument is not a GeneralId node, we use the code from * SANY1 to + * generate the OpArg node. * + *******************************************************************/ + + // Assemble the (possibly compound) generalized identifier, and resolve it. + GenID genID = generateGenID(opArgSyntaxNode, mn); + + // If the fully-qualified op is undefined, then a message has already been + // put in errors, but we must insert a nullOpArgNode in the tree. + if (genID.getFullyQualifiedOp() != null && genID.getArgs().length == 0) { + // Create an OpArgNode from it. + return new OpArgNode(genID.getFullyQualifiedOp(), opArgSyntaxNode, mn); + } else if (genID.getArgs().length > 0) { + // Expression being used where Operator is required + errors.addError(opArgSyntaxNode.getLocation(), + "Arity " + targetSymbol.getArity() + " operator (not an expression) is expected" + + " to substitute for CONSTANT '" + targetSymbol.getName() + "'."); + return nullOpArg; + } else { + return nullOpArg; + } + } // end generateOpArg() + + /** + * Process the substitutions clause of a module definition or instantiation; + * returns a SubstInNode that can be used as a template for the wrapper that + * must be present around each OpDefNode body created from the module + * instantiation or module definition. + */ + private SubstInNode processSubst(TreeNode treeNode, TreeNode[] substNodes, + // array of subst nodes [c1 <- e1, ... ,cn <- en] + SymbolTable instancerST, + // SymbolTable in which the ei must be resolved + Context instanceeCtxt, + // Context in which the ci must be resolved + ModuleNode instanceeModule, + // the ModuleNode of the module in which ci must be resolved + ModuleNode mn) throws AbortException { + TreeNode[] children; // find the substitution part of the syntax tree + + // Create a vector of all declarations of CONSTANTS and VARIABLES + // in the context of the module being instantiated (instancee). + // These are all the symbols that must have substitutions defined + // for them in the instantiation, either explictly or implicitly. + Vector decls = instanceeCtxt.getByClass(OpDeclNode.class); + + // Create a SubstInNode to be used as a template for the SubstInNodes + // in the body of every newly instantiated OpDef in the module. + // The substitutions in the returned SubstInNode object will be the + // implicit substitutions of the form c<-c for all CONSTANTS and + // VARIABLES c that are BOTH declared in instancee and for which the + // same name is declared or defined in instancer. Note the instancerST + // must be passed to this constructor because with a default substitution + // LHS<-RHS the LHS is resolved in the instanceeCtxt, (done + // in the previous line) and the RHS is resolved in the instancerST. + SubstInNode substIn = new SubstInNode(treeNode, instancerST, decls, mn, instanceeModule); + + // For each explicit substitution in the syntax tree, overwrite or add + // the corresponding default entry in SubstInNode just created + for (int i = 3; i < substNodes.length; i += 2) { + // pick up array of syntax elements for one substitution, + // e.g. ["c","<-",expr] + TreeNode sc[] = substNodes[i].heirs(); + + // substRHS is the expression "exp" in "c <- exp"; this stmt first + // checks that c is properly declared in the instancee context, + // and then generates an ExprOrOpArgNode. If c is a constant + // with parameters, then an OpArgNode is returned; otherwise it is + // an ExprNode. + ExprOrOpArgNode substRHS = generateSubst(instanceeCtxt, sc[0], sc[2], mn); + + // Overwrite an implicit substitution if there is one, or add a new one, + // checking for duplicate substitutions for the same symbol + substIn.addExplicitSubstitute(instanceeCtxt, sc[0].getUS(), sc[2], substRHS); + } + + // Check if substitution is complete, i.e. that all constants and vars + // have been substituted for. + substIn.matchAll(decls); + return substIn; + } // end processSubst + + /** + * This method treats *unnamed* INSTANCE stmts NOTE: this code does nothing with + * ASSUMES or THEOREMS + */ + /************************************************************************* + * However, SANY2 imports ThmOrAssumpDef nodes. * + *************************************************************************/ + /** + * @param treeNode + * @param cm + * @param topLevel + * @return + * @throws AbortException + */ + /** + * @param treeNode + * @param cm + * @param topLevel + * @return + * @throws AbortException + */ + /** + * @param treeNode + * @param cm + * @param topLevel + * @return + * @throws AbortException + */ + /** + * @param treeNode + * @param cm + * @param topLevel + * @return + * @throws AbortException + */ + private final InstanceNode generateInstance(TreeNode treeNode, ModuleNode cm, boolean topLevel) + throws AbortException { + /********************************************************************** + * topLevel argument is true for a top-level INSTANCE statement, and * false + * elsewise--that is, for an INSTANCE inside a proof. * + **********************************************************************/ + TreeNode[] children; + if (topLevel) { + children = treeNode.one()[0].heirs(); + } + // skip one generation below NonLocalInstance + // because we know zero defines local in a + // top-level INSTANCE + else { + children = treeNode.heirs(); + } + ; + // id of module being instanced + UniqueString moduleId = children[1].getUS(); + + // If this module instance is declared "LOCAL" then all of the + // definitions in it must be instanced as if they were "LOCAL" + boolean localness = treeNode.local(); + + // Create a list of all declarations for module moduleId. + // Match them against either something in the substitutions or + // something in the current context (symbol table) for the + // substitution. Check that the symbol does occur in the module + // and as a declaration. + Context instanceeCtxt = this.getContext(moduleId); + if (instanceeCtxt == null) { + errors.addAbort(children[1].getLocation(), + "Internal error: No context available for module `" + moduleId.toString() + "'.", true); + } + ; + // Try to find the ModuleNode for the module being instanced in + // the symbolTable + ModuleNode instanceeModuleNode = symbolTable.resolveModule(moduleId); + + // It must be an external module if it isn't in the symbolTable; + // try to find it in moduleTable (it cannot be in both places, or + // a name conflict would have resulted) + if (instanceeModuleNode == null) { + instanceeModuleNode = moduleTable.getModuleNode(moduleId); + } + + if (instanceeModuleNode == null) { + errors.addAbort(children[1].getLocation(), "Could not find module " + moduleId.toString(), false); + } + + /* + * Set isInstantiated field of instancee. Added by LL 23 July 2013 + */ + instanceeModuleNode.setInstantiated(true); + + // Create the SubstInNode that will act as a template "wrapper" + // for each definition in the module being instantiated; this + // SubstInNode itself gets discarded after being used as template + // however many times is necessary + SubstInNode subst = processSubst(treeNode, children, symbolTable, instanceeCtxt, instanceeModuleNode, cm); + + // Create a vector of all of the OpDefNodes in the module being + // instantiated + Vector defs = instanceeCtxt.getByClass(OpDefNode.class); + + OpDefNode odn; // OpDefNode in module being instantiated (instancee) + OpDefNode newOdn; // Its counterpart current module (instancer) + SubstInNode substInTemplate; // Template to be used for any + // SubstInNode wrappers required + + // Duplicate the OpDef records from the module being INSTANCE'd + for (int i = 0; i < defs.size(); i++) { + odn = (OpDefNode) defs.elementAt(i); + // OpDefNode in module being instantiated (instancee) + + // Do not instantiate built-in or local operators, or those + // OpDefNodes created solely to prevent a ModuleName from being + // used as an operator node. + if (odn.getKind() == BuiltInKind || odn.getKind() == ModuleInstanceKind || odn.isLocal()) { + continue; + } + + // If there are parameters to the module being instantiated, then + // a SubstInNode is required, and possibly a different module of + // origin should be indicated + if (!instanceeModuleNode.isParameterFree()) { + // Create the OpDefNode for the new instance of this definition + // Note that the new OpDefNode shares the array of FormalParamNodes + // with the old OpDefNode, as well as large parts of its body + // (all but the SubstInNode). Hence, changes by a tool to an Original + // OpDefNode will likely be reflected in all instances of it. + if (odn.getOriginallyDefinedInModuleNode().isParameterFree()) { + + /**************************************************************** + * Originally, newOdn was set the way it now is if localness = * true. Here's + * the problem with it. Suppose the instantiated * module EXTENDS the Naturals + * module. Then this will add new * OpDefNodes for all the symbols defined in + * Naturals. If the * current module EXTENDS Naturals, this will lead to + * multiple * definitions. So, for nonlocal definitions, we just * set newOdn to + * odn. * * However, now the problem is: suppose the current module * does not + * EXTEND the Naturals module. Then the operators * defined in Naturals, which + * should be defined in the current * module, are not. So, we add them to + * symbolTable. This does * not lead to a multiple definition error because + * apparently * it's the addSymbol method that is smart enough to detect if * we + * are adding a definition that comes from the same source as * the original + * one. * * This fix was made by LL on 16 Feb 2009. * * On 6 June 2010, LL add + * "&& topLevel" to the following `if' * test. This was needed because an + * INSTANCE inside a proof * was producing a "Multiple declarations or + * definition" * warning if the INSTANCEd module and the current module both * + * EXTENDed Naturals. This fix seems to do the right thing, * but I have not + * extensively tested it and I have no idea what * problems may remain. * + ****************************************************************/ + if (localness && topLevel) { + newOdn = new OpDefNode(odn.getName(), UserDefinedOpKind, odn.getParams(), localness, + odn.getBody(), odn.getOriginallyDefinedInModuleNode(), symbolTable, treeNode, true, + odn.getSource()); + /*************************************************************** + * The following statement was added by LL on 16 Feb 2009, by * analogy with the + * corresponding code about 45 lines below. I * have no idea if it was + * originally omitted for a good reason. * + ***************************************************************/ + newOdn.setLabels(odn.getLabelsHT()); + } else { + newOdn = odn; + symbolTable.addSymbol(odn.getName(), odn); + } + } else { + // Create the "wrapping" SubstInNode as a clone of "subst" above, + // but with a body from the OpDefNode in the module being + // instantiated + substInTemplate = new SubstInNode(treeNode, subst.getSubsts(), odn.getBody(), cm, + instanceeModuleNode); + newOdn = new OpDefNode(odn.getName(), UserDefinedOpKind, odn.getParams(), localness, + substInTemplate, cm, symbolTable, treeNode, true, odn.getSource()); + newOdn.setLabels(odn.getLabelsHT()); + + } + } else { + // There are no parameters to the instancee module; this + // means that a SubstInNode is not necessary, and also that + // the new operator should be considered to be "originally + // defined in" the same module as the old one for purposes of + // telling whether they are "the same" or different definitions + + // Create an OpDefNode whose body is the same as the instancer's. + + if (localness) { + /**************************************************************** + * See the comments about the similar change made to the * setting of newOdn in + * the `then' clause, just above. * This entire change was made by LL on 16 Feb + * 2009. * + ****************************************************************/ + newOdn = new OpDefNode(odn.getName(), UserDefinedOpKind, odn.getParams(), localness, odn.getBody(), + odn.getOriginallyDefinedInModuleNode(), symbolTable, treeNode, true, odn.getSource()); + newOdn.setLabels(odn.getLabelsHT()); + } else { + newOdn = odn; + symbolTable.addSymbol(odn.getName(), odn); + } + } + cm.appendDef(newOdn); + setOpDefNodeRecursionFields(newOdn, cm); + } // end for + + /********************************************************************** + * Import the ThmOrAssumpDefNode objects to the current module. The * following + * code for doing this was copied and modified without * thinking from the code + * above for OpDefNode objects * + **********************************************************************/ + Vector tadefs = instanceeCtxt.getByClass(ThmOrAssumpDefNode.class); + + ThmOrAssumpDefNode tadn; + // ThmOrAssumpDefNode in module being instantiated (instancee) + ThmOrAssumpDefNode newTadn; + // Its counterpart current module (instancer) + APSubstInNode tasubstInTemplate; // Template to be used for any + // SubstInNode wrappers required + + // Duplicate the OpDef records from the module being INSTANCE'd + for (int i = 0; i < tadefs.size(); i++) { + tadn = (ThmOrAssumpDefNode) tadefs.elementAt(i); + // ThmOrAssumpDefNode in module being instantiated (instancee) + + // Following statement added by LL on 30 Oct 2012 to handle locally + // instantiated theorems and assumptions. + if (tadn.isLocal()) { + continue; + } + + // If there are parameters to the module being instantiated, then + // a SubstInNode is required, and possibly a different module of + // origin should be indicated + if (!instanceeModuleNode.isParameterFree()) { + // Create the ThmOrAssumpDefNode for the new instance of this + // definition. Note that the new ThmOrAssumpDefNode shares the + // array of FormalParamNodes with the old ThmOrAssumpDefNode, + // as well as large parts of its body (all but the SubstInNode). + // Hence, changes by a tool to an Original ThmOrAssumpDefNode will + // likely be reflected in all instances of it. + if (tadn.getOriginallyDefinedInModuleNode().isParameterFree()) { + // Following if/else added by LL on 30 Oct 2012 to handle locally + // instantiated theorems and assumptions. + if (localness && topLevel) { + newTadn = new ThmOrAssumpDefNode(tadn.getName(), tadn.isTheorem(), tadn.getBody(), cm, + symbolTable, treeNode, tadn.getParams(), instanceeModuleNode, tadn.getSource()); + newTadn.setLocal(true); + } else { + newTadn = tadn; + // On 30 Oct 2012, LL noticed that code was added on 16 Feb 2009 + // to the corresponding place in the code for an OpDefNode, but that + // nothing was added here. I suspect that something should have been + // that the 2009 code should also have been added here, but wasn't-- + // perhaps because there's no getLabelsHT method for a ThmOrAssumpDefNode. + // This may mean that there's a bug in handling labels in the + // instantiated theorem or assumption. + } + /* + * new ThmOrAssumpDefNode( tadn.getName(), UserDefinedOpKind, tadn.getParams(), + * localness, tasubstInTemplate, tadn.getOriginallyDefinedInModuleNode(), + * symbolTable, treeNode, true ); + */ + } else { + // Create the "wrapping" SubstInNode as a clone of "subst" above, + // but with a body from the ThmOrAssumpDefNode in the module being + // instantiated + tasubstInTemplate = new APSubstInNode(treeNode, subst.getSubsts(), tadn.getBody(), cm, + instanceeModuleNode); + newTadn = new ThmOrAssumpDefNode(tadn.getName(), tadn.isTheorem(), tasubstInTemplate, cm, + symbolTable, treeNode, tadn.getParams(), instanceeModuleNode, tadn.getSource()); + // Following if/else added by LL on 30 Oct 2012 to handle locally + // instantiated theorems and assumptions. + newTadn.setLocal(localness); + // cm.appendDef(newTadn); + newTadn.setLabels(tadn.getLabelsHT()); + + } + } else { + // There are no parameters to the instancee module; this + // means that a SubstInNode is not necessary, and also that + // the new operator should be considered to be "originally + // defined in" the same module as the old one for purposes of + // telling whether they are "the same" or different definitions + + // Create a ThmOrAssumpDefNode whose body is the same as + // the instancer's. + if (localness && topLevel) { + newTadn = new ThmOrAssumpDefNode(tadn.getName(), tadn.isTheorem(), tadn.getBody(), + tadn.getOriginallyDefinedInModuleNode(), symbolTable, treeNode, tadn.getParams(), + instanceeModuleNode, tadn.getSource()); + // Following if/else added by LL on 30 Oct 2012 to handle locally + // instantiated theorems and assumptions. + newTadn.setLocal(localness); + newTadn.setLabels(tadn.getLabelsHT()); + } else { + newTadn = tadn; + symbolTable.addSymbol(tadn.getName(), tadn); + } + } + if (topLevel) { + cm.appendDef(newTadn); + } + ; + /******************************************************************** + * No recursion fields needed for theorems or assumptions because * they can't + * appear in a recursive section. * + ********************************************************************/ + // setOpDefNodeRecursionFields(newTadn, cm) ; + } // end for + + // Create a new InstanceNode to represent this INSTANCE stmt in + // the current module + InstanceNode inst = new InstanceNode(null /* no name */, localness, null /* no parms */, instanceeModuleNode, + subst.getSubsts(), treeNode); + + // Append this new InstanceNode to the vector of InstanceNodes + // being accumulated for this module + if (topLevel) { + cm.appendInstance(inst); + } + ; + return inst; + + } // void generateInstance + + private final void processTheorem(TreeNode stn, ModuleNode cm) throws AbortException { + ThmOrAssumpDefNode tadn = null; + LevelNode body; + ProofNode proof = null; + int bodyIndex = stn.heirs().length - 1; + boolean isAssumeProve = false; + /********************************************************************* + * Set true if the body is an ASSUME/PROVE. * + *********************************************************************/ + boolean hasProof = (stn.heirs()[bodyIndex].getKind() == N_Proof) + || (stn.heirs()[bodyIndex].getKind() == N_TerminalProof); + if (hasProof) { + bodyIndex--; + } + ; + + if (bodyIndex > 1) { + /********************************************************************* + * If this is a named theorem, start fresh processing of labels, * create the + * object. * + *********************************************************************/ + pushLS(); + UniqueString name = stn.heirs()[1].getUS(); + tadn = new ThmOrAssumpDefNode(name, stn); + } + ; + if (stn.zero()[bodyIndex] + .isKind(N_AssumeProve)) { /******************************************************************* + * Theorem statement is an ASSUME/PROVE. Must set currentGoal. * + *******************************************************************/ + currentGoal = tadn; + isAssumeProve = true; + + /******************************************************************* + * We want the symbols declared in top-level NEW statements of the * ASSUME + * clause to be visible in the proof. Therefore, we do the * context push for + * the top-level AssumeProveNode here instead of * in generateAssumeProve. * + *******************************************************************/ + symbolTable.pushContext(new Context(moduleTable, errors)); + body = generateAssumeProve(stn.heirs()[bodyIndex], cm); + currentGoal = null; + } else { /**************************************************************** + * Theorem statement must be an ExprNode. * + ****************************************************************/ + body = generateExpression(stn.heirs()[bodyIndex], cm); + } + ; + if (bodyIndex > 1) { + /********************************************************************** + * The theorem node has the form * THEOREM Ident == statement * + **********************************************************************/ + Context assumeContext = null; + if (isAssumeProve) { + /******************************************************************** + * If the body is an ASSUME/PROVE, we must save in assumeContext * the context + * containing the symbols declared in the ASSUME * clause and pop it, so Ident + * is made visible outside the proof. * + ********************************************************************/ + assumeContext = symbolTable.getContext(); + symbolTable.popContext(); + } + ; + UniqueString name = stn.heirs()[1].getUS(); + tadn.construct(true, body, cm, symbolTable, null); + tadn.setLabels(popLabelNodeSet()); + cm.appendDef(tadn); + if (isAssumeProve) { + /******************************************************************** + * After putting Ident into the symbol table, we push the ASSUME * context back + * onto symboltable. * + ********************************************************************/ + symbolTable.pushContext(assumeContext); + } + ; + } + ; // if (bodyIndex > 1) + + /************************************************************************ + * Note: If this is a named theorem, then the name has been added to * + * symbolTable so it can be referred to within the theorem's proof (if * it has + * one). * + ************************************************************************/ + if (hasProof) { + proof = generateProof(stn.heirs()[bodyIndex + 1], cm); + } + ; + + if (isAssumeProve) { + symbolTable.popContext(); + /******************************************************************** + * Pop the context containing ASSUME declarations. * + ********************************************************************/ + ((AssumeProveNode) body).inProof = false; + /******************************************************************** + * Reset the AssumeProve node's inProof field. * + ********************************************************************/ + } + ; + cm.addTheorem(stn, body, proof, tadn); + } // ProcessTheorem + + private final void processAssumption(TreeNode stn, ModuleNode cm) throws AbortException { + ThmOrAssumpDefNode tadn = null; + int lastIndex = stn.heirs().length - 1; + if (lastIndex > 1) { + pushLS(); + } + ; + /********************************************************************* + * If this is a named assumption, start fresh processing of labels. * + *********************************************************************/ + ExprNode expr = generateExpression(stn.heirs()[lastIndex], cm); + + if (lastIndex > 1) { + /********************************************************************** + * The assumption node has the form * ASSUME Ident == expression * + **********************************************************************/ + UniqueString name = stn.heirs()[1].getUS(); + tadn = new ThmOrAssumpDefNode(name, false, expr, cm, symbolTable, stn, null, null, null); + tadn.setLabels(popLabelNodeSet()); + cm.appendDef(tadn); + } + ; + cm.addAssumption(stn, expr, symbolTable, tadn); + return; + } // processAssumption + + private final ProofNode generateProof(TreeNode stn, ModuleNode cm) + /*********************************************************************** + * Node stn is of kind N_TerminalProof or an N_Proof. The heirs of an * N_Proof + * node consist of an optional PROOF token followed by a * seequence of + * N_ProofStep nodes. The heirs of an N_ProofStep node * are a StartStep() + * token, a statement body, and an optional proof. * A statement body is one of + * the following node kinds: * * Have no proof: * N_DefStep N_UseOrHide + * N_NonLocalInstance N_HaveStep, * N_TakeStep N_WitnessStep * * Have a proof * + * N_QEDStep N_PickStep N_CaseStep N_AssertStep * * Each step produces the + * following kind of semantic node: * * N_DefStep : DefStepNode * * N_UseOrHide + * : UseOrNideNode * * N_NonLocalInstance : InstanceNode * * Others: TheoremNode + * * The type of statement is indicated by the body of the * node. For any step + * other than an N_AssertStep, the body * is an OpApplNode with the following + * dummy operator: * * N_HaveStep : $Have * N_CaseStep : $Pfcase * N_TakeStep : + * $Take * N_PickStep : $Pick * N_WitnessStep : $Witness * N_QEDStep : $Qed * + ***********************************************************************/ + throws AbortException { + int numberOfPops = 0; + /********************************************************************* + * For each SUFFICES ASSUME/PROVE step, we push a new context onto * the symbol + * table containing declarations of any NEW symbols. * These need to be popped + * off when through processing the proof. * + *********************************************************************/ + if (stn.getKind() == N_TerminalProof) { + return generateLeafProof(stn, cm); + } + ; + + Context pfCtxt = new Context(moduleTable, errors); + symbolTable.pushContext(pfCtxt); + /********************************************************************* + * Create a new sub-Context for the proof. * + *********************************************************************/ + + TreeNode heirs[] = stn.heirs(); + int offset = 0; + if (heirs[0].getKind() == TLAplusParserConstants.PROOF) { + offset = 1; + } + ; + LevelNode[] steps = new LevelNode[heirs.length - offset]; + Vector iVec = new Vector(); + /********************************************************************* + * A vector to hold the InstanceNodes generated by steps of the form * Id == + * INSTANCE ... so they can be level checked. * + *********************************************************************/ + + /*********************************************************************** + * At the beginning of each loop iteration, the variable prevIsInFix * equals + * true iff the previous step consists of a formula whose main * operator is an + * infix operator, including an "@-step" of the form "@ * infix-op expression". + * If it is true, then rhSide is the ExprNode * of the infix-op's + * right-hand-side expression. * + ***********************************************************************/ + boolean prevIsInfix = false; + ExprNode prevRHS = null; + + for (int i = offset; i < heirs.length; i++) { + /********************************************************************* + * Process proof step i. * + *********************************************************************/ + boolean isAssumeProve = false; + /******************************************************************* + * Will be set true for an ASSUME/PROVE, so we can reset the * node's inProof + * field. * + *******************************************************************/ + boolean isSuffices = false; + /******************************************************************* + * Will be set to true for a SUFFICES statement. This is used to * do the right + * thing with ASSUME declarations in an ASSUME/PROVE * step, and to set the + * suffices field of the ThmOrAssumpDefNode * if this is a named step. * + *******************************************************************/ + + /********************************************************************* + * For an ASSUME/PROVE, we set assumeContext to a context containing * the + * declarations from the outer-most NEWs of the ASSUME. For an * ordinary + * ASSUME/PROVE, this context is pushed onto the symbol * table for statement's + * processing the proof. For a SUFFICE * ASSUME/PROVE, it is pushed onto the + * symbol table after processing * the statement's proof so the outermost NEW + * declarations are * visible for the rest of the current proof. * The handling + * of the assumeContext was changed on 1 Jul 2009 * because SUFFICE ASSUME/PROVE + * was not being handled properly. * + *********************************************************************/ + Context assumeContext = null; + + /********************************************************************* + * For "PICK x : exp", the symbol x is declared in exp, undeclared * in the + * proof of the step, and declared afterwards. (There'd be a * similar problem + * with TAKE if it took a proof.) The following * variables are used to + * manipulate this, with pickContext holding * the declarations of the symbols + * introduced by the PICK step. * + *********************************************************************/ + boolean isPick = false; + Context pickContext = null; + + /********************************************************************* + * Set stepNumSTN, stepBodySTN, and stepPfSTN to the syntax tree * nodes of the + * step number, the body, and the proof (the latter * equal to null if there's + * no proof. * + *********************************************************************/ + TreeNode pfStepSTN = heirs[i]; + TreeNode stepNumSTN = pfStepSTN.heirs()[0]; + TreeNode stepBodySTN = pfStepSTN.heirs()[1]; + TreeNode stepPfSTN = null; + if (pfStepSTN.heirs().length > 2) { + stepPfSTN = pfStepSTN.heirs()[2]; + } + ; + + LevelNode pfNumNode = null; + boolean makePfNumNode = true; + /******************************************************************* + * For a numbered step that doesn't produce a TheoremNode, * pfNumNode is set to + * a node that will be the stepNode of a * NumberedProofStepKind OpDefNode. * + *******************************************************************/ + /* + * On 23 Feb 2014 LL added the following code to make step numbers like <*>13 + * illegal, so <+> and <*> can be used only for unnamed steps. It would be most + * natural to make the change age the parsing level by changing the JavaCC code. + * However, that code is a Kludge that it is best not to touch unless absolutely + * necessary. Also, detecting the error here allows multiple instances of the + * error to be reported in a single execution of the parser. + * + * The modification is based on the following empirical observation: + * + * - For a step number like "<3>13." stepNumSTN.image = stepNumSTN.originalImage + * = "<3>13." - For a step number like "<*>13.", stepNumSTN.image = "<3>13." and + * stepNumSTN.originalImage = "<*>13." - For unnumbered steps, + * stepNumSTN.originalImage = null and stepNumSTN.image = the actual token. + */ + SyntaxTreeNode STN = (SyntaxTreeNode) stepNumSTN; + if ((STN.originalImage != null) && (STN.originalImage != STN.image)) { + String oimage = STN.originalImage.toString(); + if ((!oimage.equals(STN.image.toString())) + && ((oimage.charAt(1) == '*') || (oimage.charAt(1) == '+'))) { + errors.addError(stepNumSTN.getLocation(), "<*> and <+> cannot be used for a named step."); + } + } + + /********************************************************************* + * Set stepNum to the step number, or null if its an unnumbered step. * + *********************************************************************/ + UniqueString stepNum = null; + switch (stepNumSTN.getKind()) { + // LL: On 25 Feb 2010 I discovered the following comment here: + // XXXXXX xyz: need to add the following case + // The ProofImplicitStepLexeme case (something like <*>3) seems to be + // handled properly in tests. I presume that this is an obsolete + // comment that I didn't remove when I added the case to the code. + case TLAplusParserConstants.ProofImplicitStepLexeme: + case TLAplusParserConstants.ProofStepLexeme: + stepNum = stepNumSTN.getUS(); + break; + case TLAplusParserConstants.ProofStepDotLexeme: + String stNum = stepNumSTN.getUS().toString(); + stepNum = UniqueString.uniqueStringOf(stNum.substring(0, stNum.indexOf("."))); + break; + default: + makePfNumNode = false; + break; + } + ; // switch + + /********************************************************************* + * If this is a numbered step, process labels. * + *********************************************************************/ + if (stepNum != null) { + pushLS(); + } + ; + + /******************************************************************** + * Construct the ThmOrOpDefNode if needed. (We need to do it now * because we + * have to set currentGoal before we generate the body.) * + ********************************************************************/ + ThmOrAssumpDefNode tadn = null; + if (stepNum != null) { + tadn = new ThmOrAssumpDefNode(stepNum, stepNumSTN); + } + ; + + /********************************************************************* + * Set prevIsInfix false unless this is an AssertStep node. * + *********************************************************************/ + int stepKind = stepBodySTN.getKind(); + if (stepKind != N_AssertStep) { + prevIsInfix = false; + } + ; + + switch (stepKind) { + case N_DefStep: + /***************************************************************** + * Set defSTNs to the array of heirs, and defOffSet so that * defSTNs[defOffSet] + * ... defSTNs[defSTNs.length-1] is the * sequence of definitions. * + *****************************************************************/ + TreeNode[] defSTNs = stepBodySTN.heirs(); + int defOffSet = 0; + if (defSTNs[0].getKind() == DEFINE) { + defOffSet = 1; + } + ; + + OpDefNode[] defs = new OpDefNode[defSTNs.length - defOffSet]; + /*************************************************************** + * Will be set to the sequence of OpDefNodes for the * definitions. * + ***************************************************************/ + for (int j = defOffSet; j < defSTNs.length; j++) { + TreeNode defSTN = defSTNs[j]; + ; + Vector vec = new Vector(); + switch (defSTN.getKind()) { + /*************************************************************** + * Need to check if it's an operator, function, or module * definition. * + ***************************************************************/ + case N_FunctionDefinition: + processFunction(defSTN, vec, cm); + break; + case N_ModuleDefinition: + /************************************************************* + * The call to processModuleDefinition sets defsVec to a * vector of all the + * definitions it makes, and adds the new * InstanceNode to iVec. For now, we're + * just throwing * away defsVec. (If defsVec were null, then * + * processModuleDefinition would add these definitions to to * the module's list + * of top-level definitions.) * + *************************************************************/ + Vector defsVec = new Vector(); + vec.addElement(processModuleDefinition(defSTN, defsVec, iVec, cm)); + break; + case N_OperatorDefinition: + processOperator(defSTN, vec, cm); + /************************************************************* + * processOperator creates an OpDefNode, puts an entry for * it in symbolTable, + * and adds the OpDefNode to vec. * + *************************************************************/ + break; + } + ; // switch (def.getKind()) + defs[j - defOffSet] = (OpDefNode) vec.elementAt(0); + } + ; // for j + pfNumNode = new DefStepNode(stepBodySTN, stepNum, defs); + steps[i - offset] = pfNumNode; + break; + + case N_UseOrHide: + UseOrHideNode uohn = generateUseOrHide(stepBodySTN, cm); + + // Added by LL on 16 Jun 2010 so location returnedby getLocation() will + // include the step number. + uohn.stn = pfStepSTN; + + uohn.setStepName(stepNum); // Added 6 June 2010 by LL. + + if (uohn.facts.length + uohn.defs.length == 0) { + errors.addError(stepBodySTN.getLocation(), "Empty USE or HIDE statement."); + } + ; + uohn.factCheck(); + // Added 4 Mar 2009. + pfNumNode = uohn; + steps[i - offset] = pfNumNode; + break; + + case N_NonLocalInstance: + // Code to set step name added by LL on 6 June 2010 + InstanceNode inst = generateInstance(stepBodySTN, cm, false); + inst.setStepName(stepNum); + pfNumNode = inst; + steps[i - offset] = pfNumNode; + break; + + default: + makePfNumNode = false; + TreeNode[] bodyHeirs = stepBodySTN.heirs(); + LevelNode body = null; + /*************************************************************** + * This will be set to the body of the TheoremNode or * ThmOrAssumpDefNode. * + ***************************************************************/ + UniqueString op = null; + ExprNode[] args; + /*************************************************************** + * For anything but an N_Assert node, body is set to an * OpApplNode having + * these as its operator and arguments. * + ***************************************************************/ + + switch (stepBodySTN.getKind()) { + case N_AssertStep: + int bodyNext = 0; + if (bodyHeirs[0].getKind() == TLAplusParserConstants.SUFFICES) { + bodyNext = 1; + isSuffices = true; + /************************************************************ + * We can't have an "@" in a SUFFICES step. * + ************************************************************/ + } + ; + if (bodyHeirs[bodyNext].getKind() == N_AssumeProve) { + /************************************************************ + * This is an AssumeProve node. * + ************************************************************/ + isAssumeProve = true; + + /************************************************************ + * For an ASSUME/PROVE, we need save the symbol * declarations from top-level + * NEW statements in the ASSUME * to make them visible only in the statement's + * proof for * an ordinary ASSUME/PROVE, and only after the statement's * proof + * for a SUFFICES ASSUME/PROVE. * + ************************************************************/ + symbolTable.pushContext(new Context(moduleTable, errors)); + + currentGoal = tadn; + /********************************************************** + * Need to set currentGoal before generating the * AssumeProve node. * + **********************************************************/ + body = generateAssumeProve(bodyHeirs[bodyNext], cm); + + if (isSuffices) { + ((AssumeProveNode) body).setSuffices(); + } + ; + /********************************************************** + * Added 16 Feb 2009 by LL. * + **********************************************************/ + currentGoal = null; + assumeContext = symbolTable.getContext(); + symbolTable.popContext(); + prevIsInfix = false; + } else { + /************************************************************ + * This is an ordinary expression. * + ************************************************************/ + TreeNode curExpr = bodyHeirs[bodyNext]; + + /************************************************************ + * Special handling of SUFFICES added by LL 16 Feb 2009. * + ************************************************************/ + if (isSuffices) { + args = new ExprNode[1]; + args[0] = generateExpression(curExpr, cm); + body = new OpApplNode(OP_suffices, args, stepBodySTN, cm); + } // if (isSuffices) + else { + /************************************************************ + * If the current expression is an infix expression, set * curLHS to its current + * LHS, otherwise set it to null. * + ************************************************************/ + SyntaxTreeNode curLHS = null; + if (curExpr.getKind() == N_InfixExpr) { + curLHS = (SyntaxTreeNode) curExpr.heirs()[0]; + } + ; + /************************************************************ + * If prevIsInfix is true and curLHS is an "@", then * process it, using as the + * right-hand side a new $Nop node * with prevRHS as its argument. * + ************************************************************/ + if (prevIsInfix && (curLHS != null) && (curLHS.heirs().length > 0) + /*************************************************** + * This test added 25 Feb 2010 because if curLHS * is a string, then + * curLHS.heirs() is a * zero-length array, so the following test threw * an + * out-of-bounds array index exception. Note * that curLHS.heirs() should never + * be null, * because the heirs() method can never return * null. * + ***************************************************/ + && (((SyntaxTreeNode) curLHS.heirs()[0]).heirs().length == 0) + && (curLHS.heirs()).length > 1 + /*************************************************** + * This test added 2 Mar 2009 to fix following * bug. When we are here and the + * left-hand side * is something like a number, then curLHS.heirs() * seems to + * have length 1 and the following test * causes an ArrayIndexOverflowException. + * * + ***************************************************/ + && (curLHS.heirs()[1].getKind() == IDENTIFIER) + && (curLHS.heirs()[1].getUS() == AtUS)) { + + /********************************************************** + * The following code obtained by a simple modification * of the N_InfixExpr + * case of generateExpression. * + **********************************************************/ + TreeNode[] children = curExpr.heirs(); + GenID genID = generateGenID(children[1], cm); + ExprNode[] sns = new ExprNode[2]; + SymbolNode opn = symbolTable + .resolveSymbol(Operators.resolveSynonym(genID.getCompoundIDUS())); + if (opn == null) { + errors.addError(curExpr.getLocation(), "Couldn't resolve infix operator symbol `" + + genID.getCompoundIDUS() + "'."); + return null; + } + ; + sns[1] = generateExpression(children[2], cm); + /********************************************************** + * Set sns[1] to a new $Nop OpApplNode whose argument is * prevRHS. * + **********************************************************/ + ExprNode[] nopArgs = new ExprNode[1]; + nopArgs[0] = prevRHS; + sns[0] = new OpApplNode(OP_nop, nopArgs, curLHS, cm); + body = new OpApplNode(opn, sns, curExpr, cm); + } // if ( prevIsInfix ...) + else { // this is not an @-step + body = generateExpression(curExpr, cm); + } + ; + + /************************************************************ + * If this is an infix ioperator, set prevIsInfix true and * prevRHS equal to + * its right-hand argument, else set * prevIsInfix false. * + ************************************************************/ + prevIsInfix = false; + if ((curLHS != null) + /******************************************************* + * The following conjuncts should be true unless there * was an error in the + * expression. * + *******************************************************/ + && (body != null) && (body.getKind() == OpApplKind) + && (((OpApplNode) body).getArgs().length > 1)) { + prevIsInfix = true; + prevRHS = (ExprNode) ((OpApplNode) body).getArgs()[1]; + } + } + } + ; // else This is an ordinary expression. + break; + + case N_HaveStep: + case N_CaseStep: + if (stepBodySTN.getKind() == N_HaveStep) { + op = OP_have; + } else { + op = OP_pfcase; + } + ; + args = new ExprNode[1]; + args[0] = generateExpression(bodyHeirs[1], cm); + body = new OpApplNode(op, args, stepBodySTN, cm); + break; + + case N_TakeStep: + case N_PickStep: + if (stepBodySTN.getKind() == N_TakeStep) { + op = OP_take; + } else { + op = OP_pick; + isPick = true; + /************************************************************ + * Push a new context onto the symbolTable stack to get the * declarations of + * the PICK symbols. * + ************************************************************/ + symbolTable.pushContext(new Context(moduleTable, errors)); + } + ; + + if (bodyHeirs[1].getKind() == N_QuantBound) { + /************************************************************ + * The introduced identifiers are bounded--e.g., "TAKE id * \in Set". * + ************************************************************/ + + /************************************************************ + * Set quants to the number of N_QuantBound nodes. * + ************************************************************/ + int quants = 1; + int nextTok = 2; + while ((nextTok < bodyHeirs.length) + && (bodyHeirs[nextTok].getKind() == TLAplusParserConstants.COMMA)) { + quants++; + nextTok = nextTok + 2; + } + ; + FormalParamNode[][] params = new FormalParamNode[quants][0]; + boolean[] bt = new boolean[quants]; + ExprNode[] paramBounds = new ExprNode[quants]; + processQuantBoundArgs(bodyHeirs, 1, params, bt, paramBounds, cm); + + if (isPick) { + /********************************************************** + * Save the declarations in pickContext. * + **********************************************************/ + pickContext = symbolTable.getContext(); + /********************************************************** + * This is a PICK step; get the ": expr". * + **********************************************************/ + nextTok++; // Skip over the ":" + args = new ExprNode[1]; + pushFormalParams(flattenParams(params)); + /******************************************************** + * Push the bound variables on Last(LS).paramSeq and * process the body of the + * PICK. * + ********************************************************/ + args[0] = generateExpression(bodyHeirs[nextTok], cm); + popFormalParams(); + symbolTable.popContext(); + /******************************************************** + * Remove the bound symbols from the symbol table so * they're undefined for the + * proof of the PICK. * + ********************************************************/ + } else { + /********************************************************** + * This is a TAKE step. * + **********************************************************/ + args = new ExprNode[0]; + } + ; + body = new OpApplNode(op, null, args, params, bt, paramBounds, stepBodySTN, cm); + } else { + /************************************************************ + * The introduced identifiers are unbounded--e.g., "TAKE * id1, id2". * + ************************************************************/ + + /************************************************************ + * Set ids to the number of introduced identifiers. * + ************************************************************/ + int ids = 1; + while ((2 * ids < bodyHeirs.length) + && (bodyHeirs[2 * ids].getKind() == TLAplusParserConstants.COMMA)) { + ids++; + } + ; + + /************************************************************ + * Set params to the array of new FormalParamNodes for the * identifiers. The + * identifiers are added to the current * symbol table. * + ************************************************************/ + FormalParamNode[] params = new FormalParamNode[ids]; + for (int j = 0; j < ids; j++) { + params[j] = new FormalParamNode(bodyHeirs[2 * j + 1].getUS(), 0, bodyHeirs[2 * j + 1], + symbolTable, cm); + } + ; + + if (isPick) { + /********************************************************** + * Save the declarations in pickContext. * + **********************************************************/ + pickContext = symbolTable.getContext(); + + /********************************************************** + * This is a PICK step; get the ": expr". * + **********************************************************/ + pushFormalParams(params); + /******************************************************** + * Push formal parameters on Last(LS).paramSeq for * processing the body. * + ********************************************************/ + args = new ExprNode[1]; + args[0] = generateExpression(bodyHeirs[2 * ids + 1], cm); + popFormalParams(); + symbolTable.popContext(); + /******************************************************** + * Remove the bound symbols from the symbol table so * they're undefined for the + * proof of the PICK. * + ********************************************************/ + } else { + /********************************************************** + * This is a TAKE step. * + **********************************************************/ + args = new ExprNode[0]; + } + ; + body = new OpApplNode(op, args, params, stepBodySTN, cm); + } + ; + + break; + + case N_WitnessStep: + /************************************************************** + * Set ids to the number of expressions. * + **************************************************************/ + int ids = 1; + while ((2 * ids < bodyHeirs.length) + && (bodyHeirs[2 * ids].getKind() == TLAplusParserConstants.COMMA)) { + ids++; + } + ; + + args = new ExprNode[ids]; + for (int j = 0; j < ids; j++) { + args[j] = generateExpression(bodyHeirs[2 * j + 1], cm); + } + ; + body = new OpApplNode(OP_witness, args, stepBodySTN, cm); + break; + + case N_QEDStep: + args = new ExprNode[0]; + body = new OpApplNode(OP_qed, args, stepBodySTN, cm); + break; + + default: + errors.addAbort(stn.getLocation(), + "Internal error: Unexpected SyntaxTreeNode kind: " + heirs[i].getKind()); + break; + } + ; // switch + + /******************************************************************** + * Set the fields of the ThmOrOpDefNode if there is one, including * the + * suffices field. * + ********************************************************************/ + if (stepNum != null) { + tadn.construct(true, body, cm, symbolTable, null); + tadn.setLabels(popLabelNodeSet()); + if (isSuffices) { + tadn.setSuffices(); + } + ; + } + ; + /*********************************************************************** + * Set proof to the proof, or to null if there is none. There is no * check made + * to see if this is a kind of step that should have a * proof. Thus, adding a + * proof to something like a WITNESS statement * requires changing only the + * parsing phase (specified by tla+.jj). * + ***********************************************************************/ + ProofNode proof = null; + if (stepPfSTN != null) { + /****************************************************************** + * For an ordinary ASSUME/PROVE, must make the ASSUME's * declarations visible + * in the statement's proof. * + ******************************************************************/ + if (isAssumeProve && !isSuffices) { + symbolTable.pushContext(assumeContext); + } + ; + proof = generateProof(stepPfSTN, cm); + if (isAssumeProve && !isSuffices) { + symbolTable.popContext(); + } + ; + } + ; + + /******************************************************************** + * For a SUFFICES ASSUME/PROVE, must make the ASSUME's declarations * visible + * after the proof. This is done by pushing assumeContext * onto the symbol + * table and incrementing numberOfPops so it will * be popped at the end of the + * proof. * + ********************************************************************/ + if (isAssumeProve && isSuffices) { + numberOfPops++; + symbolTable.pushContext(assumeContext); + } + ; + if (isAssumeProve) { + ((AssumeProveNode) body).inProof = false; + } + ; + /****************************************************************** + * For an ASSUME/PROVE, set the inProof field to false. * + ******************************************************************/ + TheoremNode thm = new TheoremNode(stepBodySTN, body, cm, proof, tadn); + + // Added by LL on 16 Jun 2010 so location returnedby getLocation() will + // include the step number. + thm.stn = pfStepSTN; + + thm.suffices = isSuffices; + steps[i - offset] = thm; + } + ; // switch + if (makePfNumNode) { + /******************************************************************* + * Make an OpDefNode for the numbered step and add any label * declarations to + * it. * + *******************************************************************/ + OpDefNode nodeMadeOnlyToBePutInSymbolTable = new OpDefNode(stepNum, pfNumNode, cm, symbolTable, + pfStepSTN); + nodeMadeOnlyToBePutInSymbolTable.setLabels(popLabelNodeSet()); + } + if (isPick) { + /******************************************************************* + * We have to take the symbol declarations from pickContext and * put them into + * the current symbol table. * + *******************************************************************/ + Enumeration e = pickContext.content(); + while (e.hasMoreElements()) { + SymbolNode sym = ((Context.Pair) (e.nextElement())).getSymbol(); + symbolTable.addSymbol(sym.getName(), sym); + } + } + } + ; // for i + InstanceNode[] insts = new InstanceNode[iVec.size()]; + for (int i = 0; i < insts.length; i++) { + insts[i] = (InstanceNode) iVec.elementAt(i); + } + ; + + /*********************************************************************** + * Pop the contexts that were pushed onto the symbol table for SUFFICE * + * ASSUME/PROVE steps. * + ***********************************************************************/ + for (int i = 0; i < numberOfPops; i++) { + // Added by LL on 24 June 2010 + // Need to add the symbols in the context being popped to pfCtxt + // so they will be put into the context of the NonLeafProofNode. + Context topContext = symbolTable.getContext(); + Enumeration e = topContext.content(); + while (e.hasMoreElements()) { + SymbolNode sym = ((Context.Pair) (e.nextElement())).getSymbol(); + pfCtxt.addSymbolToContext(sym.getName(), sym); + } + symbolTable.popContext(); + } + ; + symbolTable.popContext(); + return new NonLeafProofNode(stn, steps, insts, pfCtxt); + /********************************************************************* + * Pop the sub-Context. * + *********************************************************************/ + } // generateProof + + /*************************************************************************** + * The following method is not used and I have no idea why it's still * here. * + ***************************************************************************/ + private final LevelNode generateNumerableStep(TreeNode stn, // TreeNode stmt, // UniqueString stepNum, // TreeNode proof, - ModuleNode cm) - throws AbortException { - /*********************************************************************** - * Used to generate the step for a NumerableStep or QEDStep token. * - * Returns a TheoremNode or ThmOrAssumpDefNode. * - * * - * The parsing into the syntactic tree makes it hard to rationalize * - * processing of an N_NumerableStep node because there are two * - * different ways such a step can be parsed * - * * - * 1. [step number] (sequence of tokens of the statement) [proof] * - * 2. [step number] (NonExprBody) [proof] * - * * - * where, in case 2, the NonExprBody node contains all the tokens of * - * the statement except the optional step number. Here are the * - * classes into which the different kinds of NumerableStep nodes fall. * - * * - * 1. CASE, (step number) expression. * - * * - * 2. WITNESS, TAKE, PICK, HAVE, ASSUME/PROVE, SUFFICES, * - * and PROVE expression * - ***********************************************************************/ -errors.addAbort(stn.getLocation(), "Uses generateNumerable_Step") ; - UniqueString stepNum = null ; - TreeNode[] heirs = stn.heirs() ; - int nextTok = 0 ; - ThmOrAssumpDefNode tadn = null ; - boolean isSuffices = false ; - boolean isAssumeProve = false ; - /********************************************************************* - * Set true if this is an ASSUME/PROVE step, in which case some * - * messing with the symbol table is necessary so that symbols * - * declared in the ASSUME are visible in the proof of this step. * - * They should be visible in the rest of the proof iff this is a * - * SUFFICES ASSUME... step. * - *********************************************************************/ - Context assumeContext = null; - /********************************************************************* - * For use with an ASSUME/PROVE. * - *********************************************************************/ - LevelNode body = null ; - /********************************************************************* - * This will be set to the body of the TheoremNode or * - * ThmOrAssumpDefNode. * - *********************************************************************/ - - /*********************************************************************** - * We now save the values of heirs and nextTok. If the tokens of the * - * statement are inside a NonExprBody node, we set heirs to the * - * children of that node, nextTok to 0, and hasNonExprBody true. * - * After processing the statement, we will reset heirs and set nextTok * - * to the its saved value plus 1. * - ***********************************************************************/ - TreeNode[] savedHeirs = heirs ; - int savedNextTok = nextTok ; - boolean hasNonExprBody = false ; - if (heirs[nextTok].getKind() == N_NonExprBody) { - heirs = heirs[nextTok].heirs() ; - nextTok = 0 ; - hasNonExprBody = true ; - } ; - - /*********************************************************************** - * If next token is "SUFFICES", then skip over it and set isSuffices * - * true. With the current grammar, this is the case only for an * - * ordinary assertion (a statement or ASSUME/PROVE). * - ***********************************************************************/ - if (heirs[nextTok].getKind() == TLAplusParserConstants.SUFFICES) { - nextTok++ ; - isSuffices = true ; - } ; - - /*********************************************************************** - * Skip over the next token if it's "PROVE". With current grammar, * - * this only happens when nextTok = 0. * - ***********************************************************************/ - if (heirs[nextTok].getKind() == TLAplusParserConstants.PROVE) { - nextTok++ ; - } ; - - /*********************************************************************** - * If body will be an OpApplNode with a dummy operator (like $Pick), * - * then set op to the operator's name and move past the determining * - * token (like "PICK"). Otherwise, the body will be an ordinary * - * assertion and op is left null. * - ***********************************************************************/ - UniqueString op = null ; - switch (heirs[nextTok].getKind()) { - case TLAplusParserConstants.QED : - body = new OpApplNode(OP_qed, new ExprNode[0], heirs[nextTok], cm) ; - nextTok++ ; - break ; - - case TLAplusParserConstants.CASE : - case TLAplusParserConstants.HAVE : - op = OP_have ; - if (heirs[nextTok].getKind() == TLAplusParserConstants.CASE) { - op = OP_pfcase ; - }; - nextTok++ ; - ExprNode[] args = new ExprNode[1] ; - args[0] = generateExpression(heirs[nextTok], cm) ; - body = new OpApplNode(op, args, heirs[nextTok], cm) ; - nextTok++ ; - break ; - - case TLAplusParserConstants.TAKE : - case TLAplusParserConstants.PICK : - op = OP_take ; - if (heirs[nextTok].getKind() == TLAplusParserConstants.PICK) { - op = OP_pick ; - }; - nextTok++ ; - - if (heirs[nextTok].getKind() == N_QuantBound) { - int offset = nextTok; - /***************************************************************** - * The introduced identifiers are bounded--e.g., * - * "TAKE id \in Set". * - *****************************************************************/ - /***************************************************************** - * Set quants to the number of N_QuantBound nodes. * - *****************************************************************/ - int quants = 1 ; - nextTok++ ; - while ( (nextTok < heirs.length) - && (heirs[nextTok].getKind() - == TLAplusParserConstants.COMMA)) { - quants++ ; - nextTok = nextTok + 2 ; - }; - FormalParamNode[][] params = new FormalParamNode[quants][0]; - boolean[] bt = new boolean[quants] ; - ExprNode[] paramBounds = new ExprNode[quants]; - processQuantBoundArgs(heirs, offset, params, bt, paramBounds, cm) ; + ModuleNode cm) throws AbortException { + /*********************************************************************** + * Used to generate the step for a NumerableStep or QEDStep token. * Returns a + * TheoremNode or ThmOrAssumpDefNode. * * The parsing into the syntactic tree + * makes it hard to rationalize * processing of an N_NumerableStep node because + * there are two * different ways such a step can be parsed * * 1. [step number] + * (sequence of tokens of the statement) [proof] * 2. [step number] + * (NonExprBody) [proof] * * where, in case 2, the NonExprBody node contains all + * the tokens of * the statement except the optional step number. Here are the * + * classes into which the different kinds of NumerableStep nodes fall. * * 1. + * CASE, (step number) expression. * * 2. WITNESS, TAKE, PICK, HAVE, + * ASSUME/PROVE, SUFFICES, * and PROVE expression * + ***********************************************************************/ + errors.addAbort(stn.getLocation(), "Uses generateNumerable_Step"); + UniqueString stepNum = null; + TreeNode[] heirs = stn.heirs(); + int nextTok = 0; + ThmOrAssumpDefNode tadn = null; + boolean isSuffices = false; + boolean isAssumeProve = false; + /********************************************************************* + * Set true if this is an ASSUME/PROVE step, in which case some * messing with + * the symbol table is necessary so that symbols * declared in the ASSUME are + * visible in the proof of this step. * They should be visible in the rest of + * the proof iff this is a * SUFFICES ASSUME... step. * + *********************************************************************/ + Context assumeContext = null; + /********************************************************************* + * For use with an ASSUME/PROVE. * + *********************************************************************/ + LevelNode body = null; + /********************************************************************* + * This will be set to the body of the TheoremNode or * ThmOrAssumpDefNode. * + *********************************************************************/ + + /*********************************************************************** + * We now save the values of heirs and nextTok. If the tokens of the * statement + * are inside a NonExprBody node, we set heirs to the * children of that node, + * nextTok to 0, and hasNonExprBody true. * After processing the statement, we + * will reset heirs and set nextTok * to the its saved value plus 1. * + ***********************************************************************/ + TreeNode[] savedHeirs = heirs; + int savedNextTok = nextTok; + boolean hasNonExprBody = false; + if (heirs[nextTok].getKind() == N_NonExprBody) { + heirs = heirs[nextTok].heirs(); + nextTok = 0; + hasNonExprBody = true; + } + ; + + /*********************************************************************** + * If next token is "SUFFICES", then skip over it and set isSuffices * true. + * With the current grammar, this is the case only for an * ordinary assertion + * (a statement or ASSUME/PROVE). * + ***********************************************************************/ + if (heirs[nextTok].getKind() == TLAplusParserConstants.SUFFICES) { + nextTok++; + isSuffices = true; + } + ; + + /*********************************************************************** + * Skip over the next token if it's "PROVE". With current grammar, * this only + * happens when nextTok = 0. * + ***********************************************************************/ + if (heirs[nextTok].getKind() == TLAplusParserConstants.PROVE) { + nextTok++; + } + ; + + /*********************************************************************** + * If body will be an OpApplNode with a dummy operator (like $Pick), * then set + * op to the operator's name and move past the determining * token (like + * "PICK"). Otherwise, the body will be an ordinary * assertion and op is left + * null. * + ***********************************************************************/ + UniqueString op = null; + switch (heirs[nextTok].getKind()) { + case TLAplusParserConstants.QED: + body = new OpApplNode(OP_qed, new ExprNode[0], heirs[nextTok], cm); + nextTok++; + break; + + case TLAplusParserConstants.CASE: + case TLAplusParserConstants.HAVE: + op = OP_have; + if (heirs[nextTok].getKind() == TLAplusParserConstants.CASE) { + op = OP_pfcase; + } + ; + nextTok++; + ExprNode[] args = new ExprNode[1]; + args[0] = generateExpression(heirs[nextTok], cm); + body = new OpApplNode(op, args, heirs[nextTok], cm); + nextTok++; + break; + + case TLAplusParserConstants.TAKE: + case TLAplusParserConstants.PICK: + op = OP_take; + if (heirs[nextTok].getKind() == TLAplusParserConstants.PICK) { + op = OP_pick; + } + ; + nextTok++; + + if (heirs[nextTok].getKind() == N_QuantBound) { + int offset = nextTok; + /***************************************************************** + * The introduced identifiers are bounded--e.g., * "TAKE id \in Set". * + *****************************************************************/ + /***************************************************************** + * Set quants to the number of N_QuantBound nodes. * + *****************************************************************/ + int quants = 1; + nextTok++; + while ((nextTok < heirs.length) && (heirs[nextTok].getKind() == TLAplusParserConstants.COMMA)) { + quants++; + nextTok = nextTok + 2; + } + ; + FormalParamNode[][] params = new FormalParamNode[quants][0]; + boolean[] bt = new boolean[quants]; + ExprNode[] paramBounds = new ExprNode[quants]; + processQuantBoundArgs(heirs, offset, params, bt, paramBounds, cm); // ExprNode[] args ; - if (op == OP_pick) { - /**************************************************************** - * This is a PICK step; get the ": expr". * - ****************************************************************/ - nextTok++ ; // Skip over the ":" - args = new ExprNode[1] ; - args[0] = generateExpression(heirs[nextTok], cm) ; - nextTok++ ; - } - else { - /**************************************************************** - * This is a TAKE step. * - ****************************************************************/ - args = new ExprNode[0] ; - }; - body = - new OpApplNode(op, null, args, params, bt, paramBounds, stn, cm) ; - } - else { - /***************************************************************** - * The introduced identifiers are unbounded--e.g., * - * "TAKE id1, id2". * - *****************************************************************/ - /***************************************************************** - * Set ids to the number of introduced identifiers. * - *****************************************************************/ - int ids = 1 ; - while ( (nextTok + 2*ids - 1 < heirs.length) - && (heirs[nextTok + 2*ids - 1].getKind() == - TLAplusParserConstants.COMMA)) {ids++;} ; - - /***************************************************************** - * Set params to the array of new FormalParamNodes for the * - * identifiers. The identifiers are added to the current symbol * - * table. * - *****************************************************************/ - FormalParamNode[] params = new FormalParamNode[ids]; - for (int i = 0 ; i < ids ; i++) { - params[i] = new FormalParamNode( - heirs[2*i + nextTok].getUS(), 0, - heirs[2*i + nextTok], symbolTable, cm); - } ; - /***************************************************************** - * Skip over the identifier-list tokens. * - *****************************************************************/ - nextTok = nextTok + 2*ids - 1; + if (op == OP_pick) { + /**************************************************************** + * This is a PICK step; get the ": expr". * + ****************************************************************/ + nextTok++; // Skip over the ":" + args = new ExprNode[1]; + args[0] = generateExpression(heirs[nextTok], cm); + nextTok++; + } else { + /**************************************************************** + * This is a TAKE step. * + ****************************************************************/ + args = new ExprNode[0]; + } + ; + body = new OpApplNode(op, null, args, params, bt, paramBounds, stn, cm); + } else { + /***************************************************************** + * The introduced identifiers are unbounded--e.g., * "TAKE id1, id2". * + *****************************************************************/ + /***************************************************************** + * Set ids to the number of introduced identifiers. * + *****************************************************************/ + int ids = 1; + while ((nextTok + 2 * ids - 1 < heirs.length) + && (heirs[nextTok + 2 * ids - 1].getKind() == TLAplusParserConstants.COMMA)) { + ids++; + } + ; + + /***************************************************************** + * Set params to the array of new FormalParamNodes for the * identifiers. The + * identifiers are added to the current symbol * table. * + *****************************************************************/ + FormalParamNode[] params = new FormalParamNode[ids]; + for (int i = 0; i < ids; i++) { + params[i] = new FormalParamNode(heirs[2 * i + nextTok].getUS(), 0, heirs[2 * i + nextTok], + symbolTable, cm); + } + ; + /***************************************************************** + * Skip over the identifier-list tokens. * + *****************************************************************/ + nextTok = nextTok + 2 * ids - 1; // ExprNode[] args ; - if (op == OP_pick) { - /**************************************************************** - * This is a PICK step; get the ": expr". * - ****************************************************************/ - nextTok++ ; // Skip over the ":" - args = new ExprNode[1] ; - args[0] = generateExpression(heirs[nextTok], cm) ; - nextTok++ ; - } - else { - /**************************************************************** - * This is a TAKE step. * - ****************************************************************/ - args = new ExprNode[0] ; - }; - body = - new OpApplNode(op, args, params, stn, cm) ; - } ; - break ; - - case TLAplusParserConstants.WITNESS : - nextTok++ ; - - /******************************************************************* - * Set ids to the number of expressions. * - *******************************************************************/ - int ids = 1 ; - while ( (nextTok + 2*ids - 1 < heirs.length) - && (heirs[nextTok + 2*ids - 1].getKind() == - TLAplusParserConstants.COMMA)) {ids++;} ; - ExprNode[] exprs = new ExprNode[ids] ; - for (int i = 0 ; i < ids ; i++) { - exprs[i] = generateExpression(heirs[2*i + nextTok], cm); - } ; - - body = new OpApplNode(OP_tup, exprs, stn, cm) ; - nextTok = nextTok + 2*ids - 1; - break ; - - default: - /******************************************************************* - * This is an ordinary assertion--either an ExprNode or an * - * AssumeProve node. * - *******************************************************************/ - if (heirs[nextTok].getKind() == N_AssumeProve) { - /***************************************************************** - * This is an AssumeProve node. * - * * - * For an ordinary ASSUME/PROVE, we need to save symbol * - * declarations from top-level NEW statements in the ASSUME to * - * make them visible only in the proof. If this is a SUFFICES * - * ASSUME/PROVE, we don't need to do that because we want those * - * symbol declarations to be visible outside the PROVE clause as * - * well. * - *****************************************************************/ - isAssumeProve = true ; - if (!isSuffices) { - symbolTable.pushContext(new Context(moduleTable, errors)) ; + if (op == OP_pick) { + /**************************************************************** + * This is a PICK step; get the ": expr". * + ****************************************************************/ + nextTok++; // Skip over the ":" + args = new ExprNode[1]; + args[0] = generateExpression(heirs[nextTok], cm); + nextTok++; + } else { + /**************************************************************** + * This is a TAKE step. * + ****************************************************************/ + args = new ExprNode[0]; + } + ; + body = new OpApplNode(op, args, params, stn, cm); + } + ; + break; + + case TLAplusParserConstants.WITNESS: + nextTok++; + + /******************************************************************* + * Set ids to the number of expressions. * + *******************************************************************/ + int ids = 1; + while ((nextTok + 2 * ids - 1 < heirs.length) + && (heirs[nextTok + 2 * ids - 1].getKind() == TLAplusParserConstants.COMMA)) { + ids++; + } + ; + ExprNode[] exprs = new ExprNode[ids]; + for (int i = 0; i < ids; i++) { + exprs[i] = generateExpression(heirs[2 * i + nextTok], cm); + } + ; + + body = new OpApplNode(OP_tup, exprs, stn, cm); + nextTok = nextTok + 2 * ids - 1; + break; + + default: + /******************************************************************* + * This is an ordinary assertion--either an ExprNode or an * AssumeProve node. * + *******************************************************************/ + if (heirs[nextTok].getKind() == N_AssumeProve) { + /***************************************************************** + * This is an AssumeProve node. * * For an ordinary ASSUME/PROVE, we need to + * save symbol * declarations from top-level NEW statements in the ASSUME to * + * make them visible only in the proof. If this is a SUFFICES * ASSUME/PROVE, we + * don't need to do that because we want those * symbol declarations to be + * visible outside the PROVE clause as * well. * + *****************************************************************/ + isAssumeProve = true; + if (!isSuffices) { + symbolTable.pushContext(new Context(moduleTable, errors)); // System.out.println("here") ; - } ; + } + ; // System.out.println("here and there") ; - body = generateAssumeProve(heirs[nextTok], cm); - if (!isSuffices) { - assumeContext = symbolTable.getContext() ; - symbolTable.popContext() ; - } ; - } - else { - /***************************************************************** - * This is an ordinary expression. * - *****************************************************************/ - body = generateExpression(heirs[nextTok], cm) ; - }; - nextTok++ ; - break ; - } ; // switch - - /*********************************************************************** - * Complete the ThmOrOpDefNode, if there is one. * - ***********************************************************************/ - if (stepNum != null) { - // SZA: the next line commented, to prevent a NullPointerException - // The method is not executed anyways. - // tadn.construct(true, body, cm, symbolTable, null) ; - } ; - - - /*********************************************************************** - * Restore heirs and nextTok if the statement's tokens were inside a * - * NonExprBody node. * - ***********************************************************************/ - if (hasNonExprBody) { - heirs = savedHeirs ; - nextTok = savedNextTok + 1; - } ; - - /*********************************************************************** - * Set proof to the proof, or to null if there is none. There is no * - * check made to see if this is a kind of step that should have a * - * proof. Thus, adding a proof to something like a WITNESS statement * - * requires changing only the parsing phase (specified by tla+.jj). * - ***********************************************************************/ - ProofNode proof = null ; - if (heirs.length > nextTok) { - if (isAssumeProve && !isSuffices) { - symbolTable.pushContext(assumeContext) ; - } ; - proof = generateProof(heirs[nextTok], cm); - if (isAssumeProve && !isSuffices) { - symbolTable.popContext(); - } ; - } ; - - TheoremNode thm = new TheoremNode(stn, body, cm, proof, tadn); - thm.suffices = isSuffices ; - return thm; - - } // generateNumerableStep - - private final LeafProofNode generateLeafProof(TreeNode stn, ModuleNode cm) - throws AbortException { - TreeNode heirs[] = stn.heirs() ; - LevelNode[] facts ; - SymbolNode[] defs ; - int nextTok = 0 ; - boolean omitted = false ; - - /*********************************************************************** - * Skip over an optional "PROOF" (which can occur in a BY statement). * - ***********************************************************************/ - if (heirs[0].getKind() == TLAplusParserConstants.PROOF) { - nextTok++ ; - } ; - - boolean isOnly = false ; - - if (heirs[nextTok].getKind() == TLAplusParserConstants.BY) { - /********************************************************************* - * For a BY proof, call generate a UseOrHideNode and use its facts * - * and defs field. * - *********************************************************************/ - UseOrHideNode uh = generateUseOrHide(stn, cm) ; - isOnly = uh.isOnly ; - facts = uh.facts; - defs = uh.defs; - /********************************************************************* - * The following check added by LL on 16 Feb 2009. * - *********************************************************************/ - if (facts.length + defs.length == 0) { - errors.addError(stn.getLocation(), "Empty BY"); - }; - } - else{ - facts = new LevelNode[0]; - defs = new SymbolNode[0]; - if (heirs[nextTok].getKind() == TLAplusParserConstants.OMITTED) { - omitted = true ; }; - } ; - return new LeafProofNode(stn, facts, defs, omitted, isOnly) ; - } - - UseOrHideNode - generateUseOrHide(TreeNode stn, ModuleNode cm) throws AbortException { - /*********************************************************************** - * Since a BY statement currently has essentially the same syntax as * - * USE and HIDE, this is also used to parse a BY statement. If given * - * a BY statement, it returns a UseOrHideNode that will be used to * - * form the LeafProofNode (and then thrown away). * - ***********************************************************************/ - int kind = UseKind ; - TreeNode heirs[] = stn.heirs() ; - - boolean isOnly = false; - /********************************************************************* - * True iff this is an "ONLY" step--either a BY ONLY or a USE ONLY. * - * However, we may decide not to include USE ONLY in the language, * - * which wil require a simple modification of the javacc code. * - *********************************************************************/ - - if (heirs[0].getKind() == TLAplusParserConstants.HIDE) - {kind = HideKind; } ; - - int nextTok = 1 ; - - /*********************************************************************** - * Skip over an optional "PROOF" (which can occur in a BY statement). * - ***********************************************************************/ - if (heirs[0].getKind() == TLAplusParserConstants.PROOF) { - nextTok++ ; - } ; - - if (nextTok >= heirs.length) { - errors.addError(stn.getLocation(), "Empty BY, USE, or HIDE"); - return new UseOrHideNode(kind, stn, new LevelNode[0], new SymbolNode[0], isOnly); - } - Vector vec = new Vector() ; - /********************************************************************* - * To hold the facts and then the defs. * - *********************************************************************/ - - if (heirs[nextTok].getKind() == TLAplusParserConstants.ONLY) { - isOnly = true; - nextTok++ ; - } ; - - /*********************************************************************** - * Get the facts. * - ***********************************************************************/ - while ( (nextTok < heirs.length) - && (heirs[nextTok].getKind() != TLAplusParserConstants.DF)) { - if (heirs[nextTok].getKind() == TLAplusParserConstants.MODULE) { - nextTok++ ; - UniqueString moduleId = heirs[nextTok].getUS(); - ModuleNode moduleNode = symbolTable.resolveModule(moduleId); - - /******************************************************************* - * The following added 16 Oct 2007 to allow a fact to be the * - * current module. This will probably mean to use or hide facts * - * introduced so far in the current module. * - *******************************************************************/ - if ((moduleNode == null) && (moduleId == cm.getName())) { - moduleNode = cm ;} ; - if (moduleNode != null) { vec.addElement(moduleNode) ; } - else { - errors.addError( - heirs[nextTok].getLocation(), - "Module `" + moduleId + - "' used without being extended or instantiated."); - } - } // if - else { - /******************************************************************* - * If the this token is an N_GeneralId, then we generate it here * - * using selectorToNode so we can call that method with the isFact * - * argument true. Otherwise, we just call generateExpression. * - *******************************************************************/ - if (heirs[nextTok].getKind() == N_GeneralId) { - /***************************************************************** - * Here, we are using the fact that a theorem name must be a * - * GeneralId and not something like an N_GenInfixOp. * - *****************************************************************/ - vec.addElement( - selectorToNode(genIdToSelector((SyntaxTreeNode) heirs[nextTok]), - 0, true, false, cm)) ; - } - else if (heirs[nextTok].getKind() == N_AssumeProve) { - vec.addElement(generateAssumeProve(heirs[nextTok], cm)) ; - } - else { - vec.addElement(generateExpression(heirs[nextTok], cm)) ; - } // else - } // else - nextTok++ ; - if ( (nextTok < heirs.length) - && (heirs[nextTok].getKind() == TLAplusParserConstants.COMMA)) { - nextTok++ ; - }; - } ; // while - LevelNode[] facts = new LevelNode[vec.size()] ; - for (int i = 0 ; i < vec.size() ; i++) { - facts[i] = (LevelNode) vec.elementAt(i) ; - } ; - - /*********************************************************************** - * Get the defs. * - ***********************************************************************/ - SymbolNode[] defs ; - if (nextTok >= heirs.length) { - defs = new SymbolNode[0] ; - } - else { - vec = new Vector() ; - nextTok++ ; - while (nextTok < heirs.length) { - if (heirs[nextTok].getKind() == TLAplusParserConstants.MODULE) { - nextTok++ ; - UniqueString moduleId = heirs[nextTok].getUS(); - ModuleNode moduleNode = symbolTable.resolveModule(moduleId); - - /***************************************************************** - * The following added 16 Oct 2007 to allow a fact to be the * - * current module. This will probably mean to use or hide * - * definitions introduced so far in the current module. * - *****************************************************************/ - if ((moduleNode == null) && (moduleId == cm.getName())) { - moduleNode = cm ;} ; - if (moduleNode != null) { vec.addElement(moduleNode) ; } - else { - errors.addError( - heirs[nextTok].getLocation(), - "Module `" + moduleId + - "' used without being extended or instantiated."); - } - } // if - else { - Selector sel = genIdToSelector((SyntaxTreeNode) heirs[nextTok]) ; - SemanticNode selToNd = selectorToNode(sel, -1, false, true, cm); - if ( (selToNd instanceof OpDefNode) - || ( (selToNd instanceof ThmOrAssumpDefNode) - && // This conjunct added 4 Feb 2015 by LL to forbid step - // names in a DEF clause. - (((ThmOrAssumpDefNode) selToNd).getName().toString().charAt(0) != '<' ) - )) { - SymbolNode def = (SymbolNode) selToNd; - vec.addElement(def) ; - } - else { - /*************************************************************** - * This error is redundant, because it should have been caught * - * in selectorToNode. But a little redundancy can't hurt. * - * But a little redundancy can't hurt. * - ***************************************************************/ - errors.addError( - heirs[nextTok].getLocation(), - "DEF clause entry should describe a defined operator.") ; - } // else - } // else - nextTok++ ; - if ( (nextTok < heirs.length) - && (heirs[nextTok].getKind() == TLAplusParserConstants.COMMA)) { - nextTok++ ; - }; - } ; // while - defs = new SymbolNode[vec.size()] ; - for (int i = 0 ; i < vec.size() ; i++) { - defs[i] = (SymbolNode) vec.elementAt(i) ; - } ; - } // else of if (nextTok >= heirs.length) - return new UseOrHideNode(kind, stn, facts, defs, isOnly) ; - } // generateUseOrHide - - void processRecursive(TreeNode tn, ModuleNode cm) { - /*********************************************************************** - * Process a RECURSIVE statement. Creates an OpDefNode for each of * - * the declared operators. * - ***********************************************************************/ - TreeNode[] children = tn.heirs() ; - - /*********************************************************************** - * Increment unresolvedCnt unresolvedSum, and, if necessary, * - * recursiveSectionCount. This needs to be done before the calls to * - * startOpDefNode so that we are in a recursive section when it is * - * called. Note that for * - * * - * RECURSIVE op_1, ... , op_n * - * * - * children is the sequence of tokens * - * * - * "RECURSIVE", op_1, ",", ... , "," , op_n * - * * - * so children.length = 2*n and n = children.length / 2. * - ***********************************************************************/ - if (unresolvedSum == 0) {recursiveSectionCount ++; }; - int numOfDecls = children.length / 2 ; - unresolvedCnt[curLevel] = unresolvedCnt[curLevel] + numOfDecls ; - unresolvedSum = unresolvedSum + numOfDecls; - - for (int i = 1 ; i < children.length; i = i+2) { - /********************************************************************* - * Process the i-th declaration in the RECURSIVE statement. * - *********************************************************************/ - TreeNode declNode = children[i] ; - - /********************************************************************* - * Set odn to an OpDeclNode made from the i-th declaration. * - *********************************************************************/ - OpDeclNode odn = - buildParameter(declNode, - ConstantDeclKind, // Value of argument doesn't matter. - ConstantLevel, // Value of argument doesn't matter. - cm, - false) ; // Not adding OpDeclNode to symbolTable - - /********************************************************************* - * Set params to the array of parameters for the declared operator's * - * OpDefNode. * - *********************************************************************/ - FormalParamNode[] params = new FormalParamNode[odn.getArity()] ; - for (int ip = 0 ; ip < params.length ; ip++) { - params[ip] = new FormalParamNode(null, 0, null, null, cm) ; - } ; - OpDefNode node = startOpDefNode(odn.getName(), - declNode, - UserDefinedOpKind, - params, - false, //localness - cm, - symbolTable); - /****************************************************************** - * This node isn't saved anywhere. It will be found either by * - * looking up the operator when the N_OperatorDefinition node is * - * encountered, or else by looking through the module's * - * recursiveDecls vector when it is discovered that some operator * - * has been declared but not defined (by finding unresolvedCnt > * - * 0 when coming to the end of a LET or of the module). * - ******************************************************************/ - cm.recursiveOpDefNodes.addElement(node) ; - - } // for (int i = 1 ...) - } - - - -/*************************************************************************** -* LABEL HANDLING * -* * -* The following methods are used to create the labels fields of OpDefNode * -* and LabelNode objects. They are specified in terms of an abstract data * -* object LS which is an element of * -* * -* Seq( [labels : SUBSET LabelNode, * -* paramSeq : Seq (SUBSET OpDeclNode)] ) * -* * -* Thus, for all i \in 1..Len(LS) : * -* LS[i].labels is a set of LabelNode objects * -* LS[i].paramSeq is a sequence of sets of OpDeclNode objects. * -* * -* Initially, LS equals the empty sequence << >>. * -* * -* We define * -* Front(LS) == [i \in 1..Len(LS-1) |-> LS[i]] * -* Last(LS) == LS[Len(LS)] * -* * -* LS is a stack containing one element for each OpDefNode and LabelNode * -* within which we are currently processing nodes, where Last(LS) is the * -* inner-most such node. Last(LS).paramSeq is a stack containing one * -* element for each node that comes between the the preceding OpDefNode or * -* LabelNode and the current node that introduces bound variables. * -* Last(LS).paramSeq[j] is the set of bound variables introduced by the * -* j-th such node, where Last(Last(LS).paramSeq) is the most recent such * -* set of bound variables. * -***************************************************************************/ - -/*************************************************************************** -* Code for handling labels was added in the following methods: * -* generateAssumeProve * -* generateLambda * -* processTheorem * -* processAssumption * -* processOperator * -* processFunction * -* processChoose * -* processBoundQuant * -* processUnboundQuant * -* processSubsetOf * -* processSetOfAll * -* processFcnConst * -* generateExpression * -* generateProof * -***************************************************************************/ - - LabelNode generateLabel(TreeNode stn, ModuleNode cm) - throws AbortException { - - boolean isAssumeProve = false ; - /********************************************************************* - * Set true iff this is a labeled ASSUME/PROVE. * - *********************************************************************/ - - if (!inOpDefNode()) { - errors.addError(stn.getLocation(), - "Label not in definition or proof step."); - return nullLabelNode ; - }; - - if (noLabelsAllowed()) { - errors.addError( - stn.getLocation(), - "Label not allowed within scope of declaration in " + - "nested ASSUME/PROVE."); - return nullLabelNode ; - } ; - - /*********************************************************************** - * For now at least, and probably forever, we are not allowing an * - * expression to be labeled if it lies inside an EXCEPT clause--which * - * means if it is used where an "@" could appear. The test for this * - * is taken from generateExpression. * - ***********************************************************************/ - if (!((excStack.empty() || excSpecStack.empty()))) { - errors.addError(stn.getLocation(), - "Labels inside EXCEPT clauses are not yet implemented."); - return nullLabelNode ; - } ; - - TreeNode[] labelExpChildren = stn.heirs() ; - - /*********************************************************************** - * We first process the body, since we need it to create the LabelNode. * - ***********************************************************************/ - pushLS() ; - LevelNode body ; - if (labelExpChildren[2].getKind() == N_AssumeProve) { - body = generateAssumeProve(labelExpChildren[2], cm) ; - isAssumeProve = true ; - } - else { body = generateExpression(labelExpChildren[2], cm) ; } ; - Hashtable ht = popLabelNodeSet() ; - - /*********************************************************************** - * We now create the LabelNode. * - ***********************************************************************/ - UniqueString name ; - FormalParamNode[] params; - /********************************************************************* - * The label's name and parameter list. * - *********************************************************************/ - if (labelExpChildren[0].getKind() == N_GeneralId) { - /********************************************************************* - * There are no arguments to the label. * - *********************************************************************/ - name = labelExpChildren[0].heirs()[1].getUS() ; - params = new FormalParamNode[0] ; - } - else { - /********************************************************************** - * The label should be represented as an N_OpApplication node. * - **********************************************************************/ - if (labelExpChildren[0].getKind() != N_OpApplication) - { throw new WrongInvocationException("Label has unexpected syntax tree kind.") ; }; - - TreeNode[] opApplChildren = labelExpChildren[0].heirs() ; - name = opApplChildren[0].heirs()[1].getUS() ; - TreeNode[] opArgsChildren = opApplChildren[1].heirs() ; - int numOfParams = (opArgsChildren.length - 1) / 2 ; - params = new FormalParamNode[numOfParams] ; - for (int i = 0; i < numOfParams; i++) { - /******************************************************************* - * We have to get the FormalParamNode objects for the parameter * - * array params by looking them up in the current context, since * - * they could be either ConstantLevel (normal case) or StateLevel * - * (if they're introduced in a \AA or \EE expression). * - *******************************************************************/ - TreeNode argSyntaxNode = opArgsChildren[2*i + 1]; - UniqueString argName = argSyntaxNode.heirs()[1].getUS() ; - SymbolNode argNode = symbolTable.resolveSymbol(argName) ; - FormalParamNode arg = null ; - if (argNode instanceof FormalParamNode) - {arg = (FormalParamNode) argNode ;} - else {errors.addError(argSyntaxNode.getLocation(), - "Illegal parameter " + argName.toString() + - " of label `" + name.toString() + "'."); - arg = new FormalParamNode(argName, 0, argSyntaxNode, null, cm); - /********************************************************** - * Create a dummy FormalParamNode to prevent a null * - * pointer exception in later processing. * - **********************************************************/ - }; - params[i] = arg ; - } // for - } ; // else - SemanticNode cg = null ; - if (assumeProveDepth > 0) {cg = currentGoal;} ; - LabelNode retVal = new LabelNode(stn, name, params, currentGoal, - currentGoalClause, body, isAssumeProve) ; - retVal.setLabels(ht) ; - boolean ignore = formalParamsEqual(retVal) ; - /********************************************************************* - * We throw away the return value because if there's an error, it is * - * reported by the method. * - *********************************************************************/ - if (!addLabelNodeToSet(retVal)) { - errors.addError(stn.getLocation(), - "Duplicate label `" + name.toString() + "'."); - } ; - return retVal ; - } // generateLabel - - Vector LSlabels = new Vector() ; - /*********************************************************************** - * LSlabels.elementAt(i) is a HashTable that represents * - * LS.labels[i+1]. The values in the Hashtable are LabelNode objects, * - * where ln.getName() is the key of object ln. The empty set is * - * represented by null. * - ***********************************************************************/ - - Vector LSparamSeq = new Vector() ; - /*********************************************************************** - * LsparamSeq.elementAt(i) is a Vector whose elements are of type * - * FormalParamNode[] that represents LS.paramSeq[i+1]. In particular * - * LS.paramSeq[i+1][j+1] equals * - * LET foo == (FormalParamNode[]) * - * ((Vector) LsparamSeq.elementAt(i))).elementAt(j) * - * IN { (FormalParamNode) foo[j] : j \in 0 .. foo.length} * - ***********************************************************************/ - - void pushLS() { - /*********************************************************************** - * Implements LS' = Append(LS, [labels |-> {}, paramSeq |-> << >>]) * - * That is, it pushes an "empty" record onto the end of LS. * - ***********************************************************************/ - LSlabels.addElement(null) ; - LSparamSeq.addElement(new Vector()) ; - } - - Hashtable popLabelNodeSet() { - /*********************************************************************** - * Implements LS' = Front(LS) * - * return Last(LS).labels * - ***********************************************************************/ - int size = LSlabels.size() ; - if (size == 0) - {throw new WrongInvocationException("popLabelNodeSet called on empty stack.");} ; - Hashtable retVal = (Hashtable) LSlabels.elementAt(size-1) ; - LSlabels.removeElementAt(size-1) ; - LSparamSeq.removeElementAt(size-1) ; - return retVal ; - } - - boolean addLabelNodeToSet(LabelNode ln) { - /*********************************************************************** - * Implements * - * LET succ == \A eln \in Last(LS).labels : eln.name # ln.name * - * IN LS' = IF succ THEN [LS EXCEPT * - * ![Len(LS)].labels = @ \cup {ln}] * - * ELSE Labeling Stack * - * return succ * - * That is, it adds LabelNode ln to Last(LS).labels iff there is not * - * already a LabelNode with the same name in it, otherwise do nothing. * - * It returns true iff ln was added. * - ***********************************************************************/ - int size = LSlabels.size() ; - if (size == 0) - {throw new WrongInvocationException("addLabelNodeToSet called on empty stack.");} ; - Hashtable ht = (Hashtable) LSlabels.elementAt(size-1) ; - if (ht == null) { - ht = new Hashtable() ; - LSlabels.setElementAt(ht, size-1) ; - } - boolean retVal = ! ht.containsKey(ln.getName()) ; - if (retVal) { ht.put(ln.getName(), ln); } ; - return retVal ; - } - - void pushFormalParams(FormalParamNode[] odns) { - /*********************************************************************** - * Implements * - * * - * LS' = IF LS # << >> * - * THEN [LS EXCEPT ![Len(LS)].paramSeq = * - * Append(@, {odns[i] : i \in DOMAIN odns)] * - * ELSE LS * - * * - * That is, if LS is not empty, then it pushes the set of * - * FormalParamNode objects in the array odns onto the end of * - * Last(LS).paramSeq. * - ***********************************************************************/ - if (!inOpDefNode()){return;} ; - Vector lastFormalParams = - (Vector) LSparamSeq.elementAt(LSparamSeq.size() - 1) ; - lastFormalParams.addElement(odns) ; - } - - void popFormalParams() { - /*********************************************************************** - * Implements * - * * - * LS' = IF LS # << >> * - * THEN [LS EXCEPT ![Len(LS)].paramSeq = Front(@)] * - * ELSE LS * - * That is, if LS is not empty, then it removes the last item from * - * Last(LS).paramSeq. * - ***********************************************************************/ - if (!inOpDefNode()){return;} ; - Vector lastFormalParams = - (Vector) LSparamSeq.elementAt(LSparamSeq.size() - 1) ; - int size = lastFormalParams.size() ; - if (size == 0) - {throw new WrongInvocationException("popFormalParams called on empty stack.");} ; - lastFormalParams.removeElementAt(size - 1); - } - - boolean formalParamsEqual(LabelNode ln) { - /*********************************************************************** - * Returns LET odns == ln.params * - * IN /\ \A i, j \in DOMAIN odns : * - * (i # j) => odns[i] # odns[j] * - * /\ {odns[i] : i \in DOMAINE odns} = * - * UNION {Last(LS).parmSeq[i] : * - * i \in DOMAIN Last(LS).parmSeq} * - * That is, it returns true iff all the FormalParamNode objects in * - * odns are distinct and the set of all thos objects equals the union * - * of all the sets in the sequence Last(LS).paramSeq. * - * * - * If this returns false, then an error message explains why. * - ***********************************************************************/ - boolean retVal = true ; - HashSet opParams = new HashSet() ; - FormalParamNode[] odns = ln.params ; - for (int i = 0; i < odns.length; i++) { - if (!opParams.add(odns[i])) { - retVal = false; - errors.addError(ln.stn.getLocation(), - "Repeated formal parameter " + - odns[i].getName().toString() + " \nin label `" + - ln.getName().toString() + "'.") ; - }; - } // for ; - Vector lastFormalParams = - (Vector) LSparamSeq.elementAt(LSparamSeq.size() - 1) ; - int size = lastFormalParams.size() ; - for (int i = 0 ; i < size; i++) { - FormalParamNode[] ops = (FormalParamNode[]) lastFormalParams.elementAt(i); - for (int j = 0 ; j < ops.length ; j++) { - if (! opParams.remove(ops[j])) { - retVal = false; - errors.addError(ln.stn.getLocation(), - "Label " + ln.getName().toString() + - " must contain formal parameter `" + - ops[j].getName().toString() + "'.") ; - }; - } // for j; - } // for i; - if (!opParams.isEmpty()) { - retVal = false ; - Iterator iter = opParams.iterator(); - String res = "Label " + ln.getName().toString() + - " declares extra parameter(s) "; - while (iter.hasNext()){ - FormalParamNode nd = (FormalParamNode) iter.next() ; - res = res + nd.getName().toString() + " "; - } // while - errors.addError(ln.stn.getLocation(), res) ; - } // if - return retVal ; - } - - boolean inOpDefNode() { return LSlabels.size() > 0; } - /*********************************************************************** - * Returns true iff LS is not equal to the empty sequence << >>. * - ***********************************************************************/ - - FormalParamNode[] flattenParams(FormalParamNode[][] array) { - /*********************************************************************** - * Flatten a 2-dimensional array of FormalParamNodes into a * - * 1-dimensional array. Used because the oan.boundedBoundSymbols * - * field for an OpApplNode is such a 2-dimensional array, since * - * "bounded bound symbols" may be tuples. * - ***********************************************************************/ - int size = 0 ; - for (int i = 0 ; i < array.length; i++) { size = size + array[i].length; } ; - FormalParamNode[] res = new FormalParamNode[size] ; - int k = 0 ; - for (int i = 0 ; i < array.length; i++) { - for (int j = 0 ; j < array[i].length; j++) { - res[k] = array[i][j] ; - k++ ; - } ; // for j - } ; // for i - return res ; - } - -/*************************************************************************** -* Recursion Processing * -* * -* This is the code for setting the fields of OpDefNode objects that are * -* related to recursion--namely, letInLevel, inRecursive, * -* inRecursiveSection. See the description of these fields in * -* semantic/OpDefNode.java to see what they mean. * -* * -* A tool might also want to compute the strongly connected components of * -* the dependency graph--the graph of OpDefNode objects containing an edge * -* from n to m iff m's operator appears in the body of n. * -* * -* I originally thought that the dependency graph could be created while * -* the semantic graph was being constructed, and that the strongly * -* connected components could be computed incrementally by running a * -* connected-component algorithm at the end of each recursive section. I * -* thought that could be done easily by creating an OpDefNode for a * -* definition before processing the definition's body, and adding to a * -* field in that OpDefNode a list of the OpDefNodes that appeared in * -* OpApplNodes in the body. However, that didn't work because of * -* definitions like * -* * -* f == LET f == ... * -* IN ... * -* * -* So, I abandoned that idea. It's probably easiest to compute the * -* connected components after the module is processed. * -* * -* If we ever do want to construct those connected components, we can run * -* a version of Tarjan's algorithm for computing strongly connected * -* components of a directed graph. A +cal coding of this algorithm is in * -* the file Tarjan.tla, a copy of which appears as a comment at the end of * -* this file. * -***************************************************************************/ - -/*************************************************************************** -* The fields. * -***************************************************************************/ - final int MaxLetInLevel = 100 ; - /*********************************************************************** - * It seems safe to assume that LETs won't be nested more than 100 * - * deep, so we can use arrays instead of having to deal with vectors * - * to allow arbitrary depths. * - ***********************************************************************/ - - int curLevel = 0 ; - /*********************************************************************** - * The current LET/IN nesting level--that is, the number of LET/IN * - * statements within which the nodes we are currently processing lie. * - ***********************************************************************/ - - int[] unresolvedCnt = new int[MaxLetInLevel] ; - /*********************************************************************** - * For i \in 0..curLevel, the value of unresolvedCnt[i] is the number * - * of operators declared in RECURSIVE statements at let/in level i that * - * have not yet been defined. * - ***********************************************************************/ - - int unresolvedSum = 0; - /*********************************************************************** - * Equals the sum from i = 0 to curLevel of unresolvedCnt[i]. * - ***********************************************************************/ - - int recursiveSectionCount = 0 ; - /*********************************************************************** - * This field is incremented whenever a new recursive section is * - * begun--that is, when unresolvedSum changes from 0 to a positive * - * value (which must be 1). * - ***********************************************************************/ + body = generateAssumeProve(heirs[nextTok], cm); + if (!isSuffices) { + assumeContext = symbolTable.getContext(); + symbolTable.popContext(); + } + ; + } else { + /***************************************************************** + * This is an ordinary expression. * + *****************************************************************/ + body = generateExpression(heirs[nextTok], cm); + } + ; + nextTok++; + break; + } + ; // switch + + /*********************************************************************** + * Complete the ThmOrOpDefNode, if there is one. * + ***********************************************************************/ + if (stepNum != null) { + // SZA: the next line commented, to prevent a NullPointerException + // The method is not executed anyways. + // tadn.construct(true, body, cm, symbolTable, null) ; + } + ; + + /*********************************************************************** + * Restore heirs and nextTok if the statement's tokens were inside a * + * NonExprBody node. * + ***********************************************************************/ + if (hasNonExprBody) { + heirs = savedHeirs; + nextTok = savedNextTok + 1; + } + ; + + /*********************************************************************** + * Set proof to the proof, or to null if there is none. There is no * check made + * to see if this is a kind of step that should have a * proof. Thus, adding a + * proof to something like a WITNESS statement * requires changing only the + * parsing phase (specified by tla+.jj). * + ***********************************************************************/ + ProofNode proof = null; + if (heirs.length > nextTok) { + if (isAssumeProve && !isSuffices) { + symbolTable.pushContext(assumeContext); + } + ; + proof = generateProof(heirs[nextTok], cm); + if (isAssumeProve && !isSuffices) { + symbolTable.popContext(); + } + ; + } + ; + + TheoremNode thm = new TheoremNode(stn, body, cm, proof, tadn); + thm.suffices = isSuffices; + return thm; + + } // generateNumerableStep + + private final LeafProofNode generateLeafProof(TreeNode stn, ModuleNode cm) throws AbortException { + TreeNode heirs[] = stn.heirs(); + LevelNode[] facts; + SymbolNode[] defs; + int nextTok = 0; + boolean omitted = false; + + /*********************************************************************** + * Skip over an optional "PROOF" (which can occur in a BY statement). * + ***********************************************************************/ + if (heirs[0].getKind() == TLAplusParserConstants.PROOF) { + nextTok++; + } + ; + + boolean isOnly = false; + + if (heirs[nextTok].getKind() == TLAplusParserConstants.BY) { + /********************************************************************* + * For a BY proof, call generate a UseOrHideNode and use its facts * and defs + * field. * + *********************************************************************/ + UseOrHideNode uh = generateUseOrHide(stn, cm); + isOnly = uh.isOnly; + facts = uh.facts; + defs = uh.defs; + /********************************************************************* + * The following check added by LL on 16 Feb 2009. * + *********************************************************************/ + if (facts.length + defs.length == 0) { + errors.addError(stn.getLocation(), "Empty BY"); + } + ; + } else { + facts = new LevelNode[0]; + defs = new SymbolNode[0]; + if (heirs[nextTok].getKind() == TLAplusParserConstants.OMITTED) { + omitted = true; + } + ; + } + ; + return new LeafProofNode(stn, facts, defs, omitted, isOnly); + } + + UseOrHideNode generateUseOrHide(TreeNode stn, ModuleNode cm) throws AbortException { + /*********************************************************************** + * Since a BY statement currently has essentially the same syntax as * USE and + * HIDE, this is also used to parse a BY statement. If given * a BY statement, + * it returns a UseOrHideNode that will be used to * form the LeafProofNode (and + * then thrown away). * + ***********************************************************************/ + int kind = UseKind; + TreeNode heirs[] = stn.heirs(); + + boolean isOnly = false; + /********************************************************************* + * True iff this is an "ONLY" step--either a BY ONLY or a USE ONLY. * However, + * we may decide not to include USE ONLY in the language, * which wil require a + * simple modification of the javacc code. * + *********************************************************************/ + + if (heirs[0].getKind() == TLAplusParserConstants.HIDE) { + kind = HideKind; + } + ; + + int nextTok = 1; + + /*********************************************************************** + * Skip over an optional "PROOF" (which can occur in a BY statement). * + ***********************************************************************/ + if (heirs[0].getKind() == TLAplusParserConstants.PROOF) { + nextTok++; + } + ; + + if (nextTok >= heirs.length) { + errors.addError(stn.getLocation(), "Empty BY, USE, or HIDE"); + return new UseOrHideNode(kind, stn, new LevelNode[0], new SymbolNode[0], isOnly); + } + Vector vec = new Vector(); + /********************************************************************* + * To hold the facts and then the defs. * + *********************************************************************/ + + if (heirs[nextTok].getKind() == TLAplusParserConstants.ONLY) { + isOnly = true; + nextTok++; + } + ; + + /*********************************************************************** + * Get the facts. * + ***********************************************************************/ + while ((nextTok < heirs.length) && (heirs[nextTok].getKind() != TLAplusParserConstants.DF)) { + if (heirs[nextTok].getKind() == TLAplusParserConstants.MODULE) { + nextTok++; + UniqueString moduleId = heirs[nextTok].getUS(); + ModuleNode moduleNode = symbolTable.resolveModule(moduleId); + + /******************************************************************* + * The following added 16 Oct 2007 to allow a fact to be the * current module. + * This will probably mean to use or hide facts * introduced so far in the + * current module. * + *******************************************************************/ + if ((moduleNode == null) && (moduleId == cm.getName())) { + moduleNode = cm; + } + ; + if (moduleNode != null) { + vec.addElement(moduleNode); + } else { + errors.addError(heirs[nextTok].getLocation(), + "Module `" + moduleId + "' used without being extended or instantiated."); + } + } // if + else { + /******************************************************************* + * If the this token is an N_GeneralId, then we generate it here * using + * selectorToNode so we can call that method with the isFact * argument true. + * Otherwise, we just call generateExpression. * + *******************************************************************/ + if (heirs[nextTok].getKind() == N_GeneralId) { + /***************************************************************** + * Here, we are using the fact that a theorem name must be a * GeneralId and not + * something like an N_GenInfixOp. * + *****************************************************************/ + vec.addElement( + selectorToNode(genIdToSelector((SyntaxTreeNode) heirs[nextTok]), 0, true, false, cm)); + } else if (heirs[nextTok].getKind() == N_AssumeProve) { + vec.addElement(generateAssumeProve(heirs[nextTok], cm)); + } else { + vec.addElement(generateExpression(heirs[nextTok], cm)); + } // else + } // else + nextTok++; + if ((nextTok < heirs.length) && (heirs[nextTok].getKind() == TLAplusParserConstants.COMMA)) { + nextTok++; + } + ; + } + ; // while + LevelNode[] facts = new LevelNode[vec.size()]; + for (int i = 0; i < vec.size(); i++) { + facts[i] = (LevelNode) vec.elementAt(i); + } + ; + + /*********************************************************************** + * Get the defs. * + ***********************************************************************/ + SymbolNode[] defs; + if (nextTok >= heirs.length) { + defs = new SymbolNode[0]; + } else { + vec = new Vector(); + nextTok++; + while (nextTok < heirs.length) { + if (heirs[nextTok].getKind() == TLAplusParserConstants.MODULE) { + nextTok++; + UniqueString moduleId = heirs[nextTok].getUS(); + ModuleNode moduleNode = symbolTable.resolveModule(moduleId); + + /***************************************************************** + * The following added 16 Oct 2007 to allow a fact to be the * current module. + * This will probably mean to use or hide * definitions introduced so far in the + * current module. * + *****************************************************************/ + if ((moduleNode == null) && (moduleId == cm.getName())) { + moduleNode = cm; + } + ; + if (moduleNode != null) { + vec.addElement(moduleNode); + } else { + errors.addError(heirs[nextTok].getLocation(), + "Module `" + moduleId + "' used without being extended or instantiated."); + } + } // if + else { + Selector sel = genIdToSelector((SyntaxTreeNode) heirs[nextTok]); + SemanticNode selToNd = selectorToNode(sel, -1, false, true, cm); + if ((selToNd instanceof OpDefNode) || ((selToNd instanceof ThmOrAssumpDefNode) && // This conjunct + // added 4 Feb + // 2015 by LL to + // forbid step + // names in a + // DEF clause. + (((ThmOrAssumpDefNode) selToNd).getName().toString().charAt(0) != '<'))) { + SymbolNode def = (SymbolNode) selToNd; + vec.addElement(def); + } else { + /*************************************************************** + * This error is redundant, because it should have been caught * in + * selectorToNode. But a little redundancy can't hurt. * But a little redundancy + * can't hurt. * + ***************************************************************/ + errors.addError(heirs[nextTok].getLocation(), + "DEF clause entry should describe a defined operator."); + } // else + } // else + nextTok++; + if ((nextTok < heirs.length) && (heirs[nextTok].getKind() == TLAplusParserConstants.COMMA)) { + nextTok++; + } + ; + } + ; // while + defs = new SymbolNode[vec.size()]; + for (int i = 0; i < vec.size(); i++) { + defs[i] = (SymbolNode) vec.elementAt(i); + } + ; + } // else of if (nextTok >= heirs.length) + return new UseOrHideNode(kind, stn, facts, defs, isOnly); + } // generateUseOrHide + + void processRecursive(TreeNode tn, ModuleNode cm) { + /*********************************************************************** + * Process a RECURSIVE statement. Creates an OpDefNode for each of * the + * declared operators. * + ***********************************************************************/ + TreeNode[] children = tn.heirs(); + + /*********************************************************************** + * Increment unresolvedCnt unresolvedSum, and, if necessary, * + * recursiveSectionCount. This needs to be done before the calls to * + * startOpDefNode so that we are in a recursive section when it is * called. + * Note that for * * RECURSIVE op_1, ... , op_n * * children is the sequence of + * tokens * * "RECURSIVE", op_1, ",", ... , "," , op_n * * so children.length = + * 2*n and n = children.length / 2. * + ***********************************************************************/ + if (unresolvedSum == 0) { + recursiveSectionCount++; + } + ; + int numOfDecls = children.length / 2; + unresolvedCnt[curLevel] = unresolvedCnt[curLevel] + numOfDecls; + unresolvedSum = unresolvedSum + numOfDecls; + + for (int i = 1; i < children.length; i = i + 2) { + /********************************************************************* + * Process the i-th declaration in the RECURSIVE statement. * + *********************************************************************/ + TreeNode declNode = children[i]; + + /********************************************************************* + * Set odn to an OpDeclNode made from the i-th declaration. * + *********************************************************************/ + OpDeclNode odn = buildParameter(declNode, ConstantDeclKind, // Value of argument doesn't matter. + ConstantLevel, // Value of argument doesn't matter. + cm, false); // Not adding OpDeclNode to symbolTable + + /********************************************************************* + * Set params to the array of parameters for the declared operator's * + * OpDefNode. * + *********************************************************************/ + FormalParamNode[] params = new FormalParamNode[odn.getArity()]; + for (int ip = 0; ip < params.length; ip++) { + params[ip] = new FormalParamNode(null, 0, null, null, cm); + } + ; + OpDefNode node = startOpDefNode(odn.getName(), declNode, UserDefinedOpKind, params, false, // localness + cm, symbolTable); + /****************************************************************** + * This node isn't saved anywhere. It will be found either by * looking up the + * operator when the N_OperatorDefinition node is * encountered, or else by + * looking through the module's * recursiveDecls vector when it is discovered + * that some operator * has been declared but not defined (by finding + * unresolvedCnt > * 0 when coming to the end of a LET or of the module). * + ******************************************************************/ + cm.recursiveOpDefNodes.addElement(node); + + } // for (int i = 1 ...) + } + + /*************************************************************************** + * LABEL HANDLING * * The following methods are used to create the labels fields + * of OpDefNode * and LabelNode objects. They are specified in terms of an + * abstract data * object LS which is an element of * * Seq( [labels : SUBSET + * LabelNode, * paramSeq : Seq (SUBSET OpDeclNode)] ) * * Thus, for all i \in + * 1..Len(LS) : * LS[i].labels is a set of LabelNode objects * LS[i].paramSeq is + * a sequence of sets of OpDeclNode objects. * * Initially, LS equals the empty + * sequence << >>. * * We define * Front(LS) == [i \in 1..Len(LS-1) |-> LS[i]] * + * Last(LS) == LS[Len(LS)] * * LS is a stack containing one element for each + * OpDefNode and LabelNode * within which we are currently processing nodes, + * where Last(LS) is the * inner-most such node. Last(LS).paramSeq is a stack + * containing one * element for each node that comes between the the preceding + * OpDefNode or * LabelNode and the current node that introduces bound + * variables. * Last(LS).paramSeq[j] is the set of bound variables introduced by + * the * j-th such node, where Last(Last(LS).paramSeq) is the most recent such * + * set of bound variables. * + ***************************************************************************/ + + /*************************************************************************** + * Code for handling labels was added in the following methods: * + * generateAssumeProve * generateLambda * processTheorem * processAssumption * + * processOperator * processFunction * processChoose * processBoundQuant * + * processUnboundQuant * processSubsetOf * processSetOfAll * processFcnConst * + * generateExpression * generateProof * + ***************************************************************************/ + + LabelNode generateLabel(TreeNode stn, ModuleNode cm) throws AbortException { + + boolean isAssumeProve = false; + /********************************************************************* + * Set true iff this is a labeled ASSUME/PROVE. * + *********************************************************************/ + + if (!inOpDefNode()) { + errors.addError(stn.getLocation(), "Label not in definition or proof step."); + return nullLabelNode; + } + ; + + if (noLabelsAllowed()) { + errors.addError(stn.getLocation(), + "Label not allowed within scope of declaration in " + "nested ASSUME/PROVE."); + return nullLabelNode; + } + ; + + /*********************************************************************** + * For now at least, and probably forever, we are not allowing an * expression + * to be labeled if it lies inside an EXCEPT clause--which * means if it is used + * where an "@" could appear. The test for this * is taken from + * generateExpression. * + ***********************************************************************/ + if (!((excStack.empty() || excSpecStack.empty()))) { + errors.addError(stn.getLocation(), "Labels inside EXCEPT clauses are not yet implemented."); + return nullLabelNode; + } + ; + + TreeNode[] labelExpChildren = stn.heirs(); + + /*********************************************************************** + * We first process the body, since we need it to create the LabelNode. * + ***********************************************************************/ + pushLS(); + LevelNode body; + if (labelExpChildren[2].getKind() == N_AssumeProve) { + body = generateAssumeProve(labelExpChildren[2], cm); + isAssumeProve = true; + } else { + body = generateExpression(labelExpChildren[2], cm); + } + ; + Hashtable ht = popLabelNodeSet(); + + /*********************************************************************** + * We now create the LabelNode. * + ***********************************************************************/ + UniqueString name; + FormalParamNode[] params; + /********************************************************************* + * The label's name and parameter list. * + *********************************************************************/ + if (labelExpChildren[0].getKind() == N_GeneralId) { + /********************************************************************* + * There are no arguments to the label. * + *********************************************************************/ + name = labelExpChildren[0].heirs()[1].getUS(); + params = new FormalParamNode[0]; + } else { + /********************************************************************** + * The label should be represented as an N_OpApplication node. * + **********************************************************************/ + if (labelExpChildren[0].getKind() != N_OpApplication) { + throw new WrongInvocationException("Label has unexpected syntax tree kind."); + } + ; + + TreeNode[] opApplChildren = labelExpChildren[0].heirs(); + name = opApplChildren[0].heirs()[1].getUS(); + TreeNode[] opArgsChildren = opApplChildren[1].heirs(); + int numOfParams = (opArgsChildren.length - 1) / 2; + params = new FormalParamNode[numOfParams]; + for (int i = 0; i < numOfParams; i++) { + /******************************************************************* + * We have to get the FormalParamNode objects for the parameter * array params + * by looking them up in the current context, since * they could be either + * ConstantLevel (normal case) or StateLevel * (if they're introduced in a \AA + * or \EE expression). * + *******************************************************************/ + TreeNode argSyntaxNode = opArgsChildren[2 * i + 1]; + UniqueString argName = argSyntaxNode.heirs()[1].getUS(); + SymbolNode argNode = symbolTable.resolveSymbol(argName); + FormalParamNode arg = null; + if (argNode instanceof FormalParamNode) { + arg = (FormalParamNode) argNode; + } else { + errors.addError(argSyntaxNode.getLocation(), + "Illegal parameter " + argName.toString() + " of label `" + name.toString() + "'."); + arg = new FormalParamNode(argName, 0, argSyntaxNode, null, cm); + /********************************************************** + * Create a dummy FormalParamNode to prevent a null * pointer exception in later + * processing. * + **********************************************************/ + } + ; + params[i] = arg; + } // for + } + ; // else + SemanticNode cg = null; + if (assumeProveDepth > 0) { + cg = currentGoal; + } + ; + LabelNode retVal = new LabelNode(stn, name, params, currentGoal, currentGoalClause, body, isAssumeProve); + retVal.setLabels(ht); + boolean ignore = formalParamsEqual(retVal); + /********************************************************************* + * We throw away the return value because if there's an error, it is * reported + * by the method. * + *********************************************************************/ + if (!addLabelNodeToSet(retVal)) { + errors.addError(stn.getLocation(), "Duplicate label `" + name.toString() + "'."); + } + ; + return retVal; + } // generateLabel + + Vector LSlabels = new Vector(); + /*********************************************************************** + * LSlabels.elementAt(i) is a HashTable that represents * LS.labels[i+1]. The + * values in the Hashtable are LabelNode objects, * where ln.getName() is the + * key of object ln. The empty set is * represented by null. * + ***********************************************************************/ + + Vector LSparamSeq = new Vector(); + + /*********************************************************************** + * LsparamSeq.elementAt(i) is a Vector whose elements are of type * + * FormalParamNode[] that represents LS.paramSeq[i+1]. In particular * + * LS.paramSeq[i+1][j+1] equals * LET foo == (FormalParamNode[]) * ((Vector) + * LsparamSeq.elementAt(i))).elementAt(j) * IN { (FormalParamNode) foo[j] : j + * \in 0 .. foo.length} * + ***********************************************************************/ + + void pushLS() { + /*********************************************************************** + * Implements LS' = Append(LS, [labels |-> {}, paramSeq |-> << >>]) * That is, + * it pushes an "empty" record onto the end of LS. * + ***********************************************************************/ + LSlabels.addElement(null); + LSparamSeq.addElement(new Vector()); + } + + Hashtable popLabelNodeSet() { + /*********************************************************************** + * Implements LS' = Front(LS) * return Last(LS).labels * + ***********************************************************************/ + int size = LSlabels.size(); + if (size == 0) { + throw new WrongInvocationException("popLabelNodeSet called on empty stack."); + } + ; + Hashtable retVal = (Hashtable) LSlabels.elementAt(size - 1); + LSlabels.removeElementAt(size - 1); + LSparamSeq.removeElementAt(size - 1); + return retVal; + } + + boolean addLabelNodeToSet(LabelNode ln) { + /*********************************************************************** + * Implements * LET succ == \A eln \in Last(LS).labels : eln.name # ln.name * IN + * LS' = IF succ THEN [LS EXCEPT * ![Len(LS)].labels = @ \cup {ln}] * ELSE + * Labeling Stack * return succ * That is, it adds LabelNode ln to + * Last(LS).labels iff there is not * already a LabelNode with the same name in + * it, otherwise do nothing. * It returns true iff ln was added. * + ***********************************************************************/ + int size = LSlabels.size(); + if (size == 0) { + throw new WrongInvocationException("addLabelNodeToSet called on empty stack."); + } + ; + Hashtable ht = (Hashtable) LSlabels.elementAt(size - 1); + if (ht == null) { + ht = new Hashtable(); + LSlabels.setElementAt(ht, size - 1); + } + boolean retVal = !ht.containsKey(ln.getName()); + if (retVal) { + ht.put(ln.getName(), ln); + } + ; + return retVal; + } + + void pushFormalParams(FormalParamNode[] odns) { + /*********************************************************************** + * Implements * * LS' = IF LS # << >> * THEN [LS EXCEPT ![Len(LS)].paramSeq = * + * Append(@, {odns[i] : i \in DOMAIN odns)] * ELSE LS * * That is, if LS is not + * empty, then it pushes the set of * FormalParamNode objects in the array odns + * onto the end of * Last(LS).paramSeq. * + ***********************************************************************/ + if (!inOpDefNode()) { + return; + } + ; + Vector lastFormalParams = (Vector) LSparamSeq.elementAt(LSparamSeq.size() - 1); + lastFormalParams.addElement(odns); + } + + void popFormalParams() { + /*********************************************************************** + * Implements * * LS' = IF LS # << >> * THEN [LS EXCEPT ![Len(LS)].paramSeq = + * Front(@)] * ELSE LS * That is, if LS is not empty, then it removes the last + * item from * Last(LS).paramSeq. * + ***********************************************************************/ + if (!inOpDefNode()) { + return; + } + ; + Vector lastFormalParams = (Vector) LSparamSeq.elementAt(LSparamSeq.size() - 1); + int size = lastFormalParams.size(); + if (size == 0) { + throw new WrongInvocationException("popFormalParams called on empty stack."); + } + ; + lastFormalParams.removeElementAt(size - 1); + } + + boolean formalParamsEqual(LabelNode ln) { + /*********************************************************************** + * Returns LET odns == ln.params * IN /\ \A i, j \in DOMAIN odns : * (i # j) => + * odns[i] # odns[j] * /\ {odns[i] : i \in DOMAINE odns} = * UNION + * {Last(LS).parmSeq[i] : * i \in DOMAIN Last(LS).parmSeq} * That is, it returns + * true iff all the FormalParamNode objects in * odns are distinct and the set + * of all thos objects equals the union * of all the sets in the sequence + * Last(LS).paramSeq. * * If this returns false, then an error message explains + * why. * + ***********************************************************************/ + boolean retVal = true; + HashSet opParams = new HashSet(); + FormalParamNode[] odns = ln.params; + for (int i = 0; i < odns.length; i++) { + if (!opParams.add(odns[i])) { + retVal = false; + errors.addError(ln.stn.getLocation(), "Repeated formal parameter " + odns[i].getName().toString() + + " \nin label `" + ln.getName().toString() + "'."); + } + ; + } // for ; + Vector lastFormalParams = (Vector) LSparamSeq.elementAt(LSparamSeq.size() - 1); + int size = lastFormalParams.size(); + for (int i = 0; i < size; i++) { + FormalParamNode[] ops = (FormalParamNode[]) lastFormalParams.elementAt(i); + for (int j = 0; j < ops.length; j++) { + if (!opParams.remove(ops[j])) { + retVal = false; + errors.addError(ln.stn.getLocation(), "Label " + ln.getName().toString() + + " must contain formal parameter `" + ops[j].getName().toString() + "'."); + } + ; + } // for j; + } // for i; + if (!opParams.isEmpty()) { + retVal = false; + Iterator iter = opParams.iterator(); + String res = "Label " + ln.getName().toString() + " declares extra parameter(s) "; + while (iter.hasNext()) { + FormalParamNode nd = (FormalParamNode) iter.next(); + res = res + nd.getName().toString() + " "; + } // while + errors.addError(ln.stn.getLocation(), res); + } // if + return retVal; + } + + boolean inOpDefNode() { + return LSlabels.size() > 0; + } + + /*********************************************************************** + * Returns true iff LS is not equal to the empty sequence << >>. * + ***********************************************************************/ + + FormalParamNode[] flattenParams(FormalParamNode[][] array) { + /*********************************************************************** + * Flatten a 2-dimensional array of FormalParamNodes into a * 1-dimensional + * array. Used because the oan.boundedBoundSymbols * field for an OpApplNode is + * such a 2-dimensional array, since * "bounded bound symbols" may be tuples. * + ***********************************************************************/ + int size = 0; + for (int i = 0; i < array.length; i++) { + size = size + array[i].length; + } + ; + FormalParamNode[] res = new FormalParamNode[size]; + int k = 0; + for (int i = 0; i < array.length; i++) { + for (int j = 0; j < array[i].length; j++) { + res[k] = array[i][j]; + k++; + } + ; // for j + } + ; // for i + return res; + } + + /*************************************************************************** + * Recursion Processing * * This is the code for setting the fields of OpDefNode + * objects that are * related to recursion--namely, letInLevel, inRecursive, * + * inRecursiveSection. See the description of these fields in * + * semantic/OpDefNode.java to see what they mean. * * A tool might also want to + * compute the strongly connected components of * the dependency graph--the + * graph of OpDefNode objects containing an edge * from n to m iff m's operator + * appears in the body of n. * * I originally thought that the dependency graph + * could be created while * the semantic graph was being constructed, and that + * the strongly * connected components could be computed incrementally by + * running a * connected-component algorithm at the end of each recursive + * section. I * thought that could be done easily by creating an OpDefNode for a + * * definition before processing the definition's body, and adding to a * field + * in that OpDefNode a list of the OpDefNodes that appeared in * OpApplNodes in + * the body. However, that didn't work because of * definitions like * * f == + * LET f == ... * IN ... * * So, I abandoned that idea. It's probably easiest to + * compute the * connected components after the module is processed. * * If we + * ever do want to construct those connected components, we can run * a version + * of Tarjan's algorithm for computing strongly connected * components of a + * directed graph. A +cal coding of this algorithm is in * the file Tarjan.tla, + * a copy of which appears as a comment at the end of * this file. * + ***************************************************************************/ + + /*************************************************************************** + * The fields. * + ***************************************************************************/ + final int MaxLetInLevel = 100; + /*********************************************************************** + * It seems safe to assume that LETs won't be nested more than 100 * deep, so we + * can use arrays instead of having to deal with vectors * to allow arbitrary + * depths. * + ***********************************************************************/ + + int curLevel = 0; + /*********************************************************************** + * The current LET/IN nesting level--that is, the number of LET/IN * statements + * within which the nodes we are currently processing lie. * + ***********************************************************************/ + + int[] unresolvedCnt = new int[MaxLetInLevel]; + /*********************************************************************** + * For i \in 0..curLevel, the value of unresolvedCnt[i] is the number * of + * operators declared in RECURSIVE statements at let/in level i that * have not + * yet been defined. * + ***********************************************************************/ + + int unresolvedSum = 0; + /*********************************************************************** + * Equals the sum from i = 0 to curLevel of unresolvedCnt[i]. * + ***********************************************************************/ + + int recursiveSectionCount = 0; + /*********************************************************************** + * This field is incremented whenever a new recursive section is * begun--that + * is, when unresolvedSum changes from 0 to a positive * value (which must be + * 1). * + ***********************************************************************/ // Code to construct dependence graph removed by LL on 7 Apr 2007 // OpDefNode[] defStack = new OpDefNode[MaxLetInLevel] ; @@ -7458,59 +7023,51 @@ errors.addAbort(stn.getLocation(), "Uses generateNumerable_Step") ; // * Perhaps some other use will be found for it, in which case it * // * should be put into the module's ModuleNode object. * // ***********************************************************************/ - - int max_dfs = 0; - /*********************************************************************** - * A variable used in the Tarjan algorithm. * - ***********************************************************************/ - - Vector nstack = new Vector(10) ; - /*********************************************************************** - * A vector of OpDefNode objects, representing the stack of the Tarjan * - * algorithm. * - ***********************************************************************/ - - int moduleNestingLevel = -1 ; - /*********************************************************************** - * When processing a module, this is its nesting level--that is, its * - * depth in the tree of inner modules of the outermost module. * - ***********************************************************************/ -/*************************************************************************** -* Methods. * -***************************************************************************/ - OpDefNode startOpDefNode(UniqueString us, - TreeNode tn, - int kind, - FormalParamNode[] params, - boolean localness, - ModuleNode oModNode, - SymbolTable st - ) { - /*********************************************************************** - * Called to create an OpDefNode for a RECURSIVE declaration. The * - * params argument should be an array of dummy FormalParamNode * - * objects, with null names. The OpDefNode's setParams method should * - * be used to add the actual formal parameter's to the object. * - * * - * Note: for an operator declared in a RECURSIVE statement, this * - * method is called when processing that statement with the Treenode * - * tn equal to the syntax tree node for the declaration. * - ***********************************************************************/ - OpDefNode odn = new OpDefNode(us, UserDefinedOpKind, params, localness, - null, // the expression - oModNode, st, tn, false, null) ; + int max_dfs = 0; + /*********************************************************************** + * A variable used in the Tarjan algorithm. * + ***********************************************************************/ + + Vector nstack = new Vector(10); + /*********************************************************************** + * A vector of OpDefNode objects, representing the stack of the Tarjan * + * algorithm. * + ***********************************************************************/ + + int moduleNestingLevel = -1; + + /*********************************************************************** + * When processing a module, this is its nesting level--that is, its * depth in + * the tree of inner modules of the outermost module. * + ***********************************************************************/ + + /*************************************************************************** + * Methods. * + ***************************************************************************/ + OpDefNode startOpDefNode(UniqueString us, TreeNode tn, int kind, FormalParamNode[] params, boolean localness, + ModuleNode oModNode, SymbolTable st) { + /*********************************************************************** + * Called to create an OpDefNode for a RECURSIVE declaration. The * params + * argument should be an array of dummy FormalParamNode * objects, with null + * names. The OpDefNode's setParams method should * be used to add the actual + * formal parameter's to the object. * * Note: for an operator declared in a + * RECURSIVE statement, this * method is called when processing that statement + * with the Treenode * tn equal to the syntax tree node for the declaration. * + ***********************************************************************/ + OpDefNode odn = new OpDefNode(us, UserDefinedOpKind, params, localness, null, // the expression + oModNode, st, tn, false, null); // was incrementing it twice // unresolvedCnt[curLevel] ++ ; // unresolvedSum = unresolvedSum ++ ; - oModNode.recursiveDecls.addElement(odn) ; + oModNode.recursiveDecls.addElement(odn); - odn.letInLevel = curLevel ; - odn.inRecursive = true ; - odn.inRecursiveSection = true ; - odn.recursiveSection = recursiveSectionCount; - oModNode.opDefsInRecursiveSection.addElement(odn) ; + odn.letInLevel = curLevel; + odn.inRecursive = true; + odn.inRecursiveSection = true; + odn.recursiveSection = recursiveSectionCount; + oModNode.opDefsInRecursiveSection.addElement(odn); // the participating and nbrs field have been removed from OpDefNode objects // odn.participating = true ; @@ -7521,29 +7078,25 @@ errors.addAbort(stn.getLocation(), "Uses generateNumerable_Step") ; // defStack[defStackLen] = odn ; // defStackLen ++ ; // } ; - return odn; - } - - void endOpDefNode(OpDefNode nd, - ExprNode exp, - TreeNode stn) { - /*********************************************************************** - * Called to complete the creation of an OpDefNode nd that was created * - * by an invocation of startOpDefNode. If we are processing recursive * - * definitions, then it modifies unresolvedCnt and unresolvedSum if * - * necessary. * - * * - * When called for an operator not declared in a RECURSIVE statement, * - * the TreeNode stn is the same one that startOpDefNode was called * - * with to create the OpDefNode. * - ***********************************************************************/ - nd.isDefined = true ; - - /*********************************************************************** - * Set the node's body and syntax-tree node. * - ***********************************************************************/ - nd.setBody(exp) ; - nd.stn = stn ; + return odn; + } + + void endOpDefNode(OpDefNode nd, ExprNode exp, TreeNode stn) { + /*********************************************************************** + * Called to complete the creation of an OpDefNode nd that was created * by an + * invocation of startOpDefNode. If we are processing recursive * definitions, + * then it modifies unresolvedCnt and unresolvedSum if * necessary. * * When + * called for an operator not declared in a RECURSIVE statement, * the TreeNode + * stn is the same one that startOpDefNode was called * with to create the + * OpDefNode. * + ***********************************************************************/ + nd.isDefined = true; + + /*********************************************************************** + * Set the node's body and syntax-tree node. * + ***********************************************************************/ + nd.setBody(exp); + nd.stn = stn; // Code to construct dependence graph removed by LL on 7 Apr 2007 // if (unresolvedSum > 0) { @@ -7553,9 +7106,9 @@ errors.addAbort(stn.getLocation(), "Uses generateNumerable_Step") ; // defStackLen -- ; // } ; - if (nd.inRecursive) { - unresolvedCnt[curLevel] -- ; - unresolvedSum -- ; + if (nd.inRecursive) { + unresolvedCnt[curLevel]--; + unresolvedSum--; // Code to construct dependence graph removed by LL on 7 Apr 2007 // if (unresolvedSum == 0) { // tarjan() ; @@ -7564,12 +7117,13 @@ errors.addAbort(stn.getLocation(), "Uses generateNumerable_Step") ; // } ; // for // participants = new Vector(100) ; // } // if (unresolvedSum == 0) - if (unresolvedSum < 0) { - throw new WrongInvocationException("Defined more recursive operators than were declared " + - "in RECURSIVE statements.") ; - }; - } // if (nd.inRecursive) - } + if (unresolvedSum < 0) { + throw new WrongInvocationException( + "Defined more recursive operators than were declared " + "in RECURSIVE statements."); + } + ; + } // if (nd.inRecursive) + } // Code to construct dependence graph removed by LL on 7 Apr 2007 // void registerSymbolNode(SymbolNode nd) { @@ -7610,72 +7164,64 @@ errors.addAbort(stn.getLocation(), "Uses generateNumerable_Step") ; // return nd ; // } // findSymbol - - void setOpDefNodeRecursionFields(OpDefNode odn, ModuleNode cm) { - /*********************************************************************** - * Called to set the field odn.letInLevel and the fields * - * odn.recursiveSection and odn.inRecursiveSection if they need * - * non-default values, and to add odn to cm.opDefsInRecursiveSection * - * if necessary. * - ***********************************************************************/ - odn.letInLevel = curLevel ; - if (unresolvedSum > 0) { - odn.recursiveSection = recursiveSectionCount; - odn.inRecursiveSection = (unresolvedCnt[curLevel] > 0) ; - cm.opDefsInRecursiveSection.addElement(odn) ; - } - - } - - void checkIfInRecursiveSection(TreeNode tn, String type) { - /*********************************************************************** - * Report an error if we are in a recursive section--between an * - * operator's declaration in a RECURSIVE statement and its definition. * - ***********************************************************************/ - if (unresolvedSum > 0) { - errors.addError(tn.getLocation(), - type + " may not appear within " + - "a recursive definition section." - ) ; - } - } - - void checkForUndefinedRecursiveOps(ModuleNode cm) { - /*********************************************************************** - * Called at the end of a LET clause and at the end of processing a * - * module to check for operators that were declared in a RECURSIVE * - * statement but not defined. It calls errors.addError to report any * - * that it finds. * - ***********************************************************************/ - /*********************************************************************** - * The number of operators declared in RECURSIVE statements within the * - * LET but not defined equals unresolvedCnt[curLevel]. * - ***********************************************************************/ - if (unresolvedCnt[curLevel] > 0) { - /********************************************************************* - * Go through the module's recursiveDecls vector to find all symbols * - * declared at the current LET/IN level but not defined. * - *********************************************************************/ - for (int i = 0 ; i < cm.recursiveDecls.size() ; i++) { - OpDefNode odn = (OpDefNode) cm.recursiveDecls.elementAt(i) ; - if ( (odn.letInLevel == curLevel) - && odn.inRecursive - && (! odn.isDefined)) { - errors.addError(odn.getTreeNode().getLocation(), - "Symbol " + odn.getName().toString() + - " declared in RECURSIVE statement but not defined." - ) ; - } ; - }; // for - - unresolvedSum = unresolvedSum - unresolvedCnt[curLevel] ; - /******************************************************************* - * Need to update unresolvedSum correct because we are effectively * - * setting unresolvedCnt[curLevel] to 0. * - *******************************************************************/ - }; // if (unresolvedCnt[curLevel] > 0) - } - + void setOpDefNodeRecursionFields(OpDefNode odn, ModuleNode cm) { + /*********************************************************************** + * Called to set the field odn.letInLevel and the fields * odn.recursiveSection + * and odn.inRecursiveSection if they need * non-default values, and to add odn + * to cm.opDefsInRecursiveSection * if necessary. * + ***********************************************************************/ + odn.letInLevel = curLevel; + if (unresolvedSum > 0) { + odn.recursiveSection = recursiveSectionCount; + odn.inRecursiveSection = (unresolvedCnt[curLevel] > 0); + cm.opDefsInRecursiveSection.addElement(odn); + } + + } + + void checkIfInRecursiveSection(TreeNode tn, String type) { + /*********************************************************************** + * Report an error if we are in a recursive section--between an * operator's + * declaration in a RECURSIVE statement and its definition. * + ***********************************************************************/ + if (unresolvedSum > 0) { + errors.addError(tn.getLocation(), type + " may not appear within " + "a recursive definition section."); + } + } + + void checkForUndefinedRecursiveOps(ModuleNode cm) { + /*********************************************************************** + * Called at the end of a LET clause and at the end of processing a * module to + * check for operators that were declared in a RECURSIVE * statement but not + * defined. It calls errors.addError to report any * that it finds. * + ***********************************************************************/ + /*********************************************************************** + * The number of operators declared in RECURSIVE statements within the * LET but + * not defined equals unresolvedCnt[curLevel]. * + ***********************************************************************/ + if (unresolvedCnt[curLevel] > 0) { + /********************************************************************* + * Go through the module's recursiveDecls vector to find all symbols * declared + * at the current LET/IN level but not defined. * + *********************************************************************/ + for (int i = 0; i < cm.recursiveDecls.size(); i++) { + OpDefNode odn = (OpDefNode) cm.recursiveDecls.elementAt(i); + if ((odn.letInLevel == curLevel) && odn.inRecursive && (!odn.isDefined)) { + errors.addError(odn.getTreeNode().getLocation(), + "Symbol " + odn.getName().toString() + " declared in RECURSIVE statement but not defined."); + } + ; + } + ; // for + + unresolvedSum = unresolvedSum - unresolvedCnt[curLevel]; + /******************************************************************* + * Need to update unresolvedSum correct because we are effectively * setting + * unresolvedCnt[curLevel] to 0. * + *******************************************************************/ + } + ; // if (unresolvedCnt[curLevel] > 0) + } // void tarjan() { // /*********************************************************************** @@ -7687,4489 +7233,2300 @@ errors.addAbort(stn.getLocation(), "Uses generateNumerable_Step") ; //***************************************************************************/ // } -} - -/************************ file Tarjan.tla *********************************** ------------------------------- MODULE Tarjan -------------------------------- -(***************************************************************************) -(* This version of Tarjan's algorithm for computing the strongly *) -(* connected components of a directed graph was adapted from the version *) -(* of 27 Mar 2007 on the Wikipedia page *) -(* *) -(* http://en.wikipedia.org/wiki/Tarjan's_strongly_connected_components_algorithm *) -(* *) -(* One modification is that the set of connected components it returns *) -(* does not contain a singleton {n} unless n has an edge pointing to *) -(* itself. *) -(* *) -(* This is an implementation of the algorithm in Tarjan.tla with data *) -(* structures that correspond to the ones used in the algorithm for *) -(* constructing dependency components in semantic/Generator. *) -(* *) -(* *) -(* I have not rigorously verified this algorithm, but I think I understand *) -(* why it works. I have tested it only on the 10 graphs at the end of *) -(* this file. *) -(***************************************************************************) - -EXTENDS Integers, Sequences, TLC - -CONSTANT N, \* The number of nodes - Neighbors \* A sequence of edges. - -Node == 1..N - -ASSUME Neighbors \in Seq(Node \X Node) - -Min(a, b) == IF a < b THEN a ELSE b - -Front(s) == [i \in 1 .. (Len(s)-1) |-> s[i]] - (*************************************************************************) - (* The sequence obtained from a sequence s by deleting the last item. *) - (*************************************************************************) - -Last(s) == s[Len(s)] - (*************************************************************************) - (* The last item in the sequence s. *) - (*************************************************************************) - -(***************************************************************************) -(* I believe the algorithm works because it maintains the following *) -(* invariants (at reasonable points in the code). We say that node *) -(* nstack[i] precedes nstack[j] iff i < j. *) -(* *) -(* - A node n is in nstack iff n.dfs \geq 0 *) -(* *) -(* - For all nodes n # m in nstack, if n precedes m then: *) -(* /\ n.dfs < m.dfs. *) -(* /\ there is a path from n to m in the graph. *) -(* *) -(* - For every node n in nstack, *) -(* /\ n.lowlink \leq n.dfs and *) -(* /\ there is a node m in nstack such that *) -(* 1. n.lowlink = m.dfs. *) -(* 2. if m # n then there is a path from n to m in the graph. *) -(* /\ For every node m in the same connected component as n: *) -(* m.examined = TRUE implies m is in nstack *) -(***************************************************************************) - -(************************* ---algorithm Tarjan2 -variables nstack = << >> ; - (***************************************************************) - (* The stack of nodes, where nstack[1] is the bottom of the *) - (* stack. *) - (***************************************************************) - max_dfs = 0 ; - (***************************************************************) - (* Incremented by 1 every time a new element is added to the *) - (* stack. *) - (***************************************************************) - - data = [nd \in Node |-> [lowlink |-> -1, - dfs |-> -1, - examined |-> FALSE, - nbrs |-> << >>, - (*****************************************) - (* A sequence of nodes to which there *) - (* are edges from this node. The *) - (* same node may appear multiple *) - (* times. *) - (*****************************************) - nxtDep |-> -1 ]] ; - (***************************************************************) - (* The information maintained for the nodes. Note that *) - (* *) - (* - Node nd is on nstack iff data[nd].dfs \geq 0, in *) - (* which case nstack[data[nd].dfs + 1] = nd. *) - (* *) - (* - nbrs is set from Neighbors by the initialization code. *) - (* *) - (* - nxtDep represents the nextDependency field of the *) - (* OpDefNode object, where a value of -1 represents *) - (* null. Following nxtDep pointers leads in a cycle of *) - (* the node's connected component. See the comments on *) - (* the nextDependency field in semantic/OpDefNode. *) - (***************************************************************) - - idx ; \* A counter variable. - -procedure tarjan(n) - variable nxt ; \* A counter variable - nxtnbr ; \* An abbreviation - - begin - data[n].lowlink := max_dfs || - data[n].dfs := max_dfs || - data[n].examined := TRUE ; - max_dfs := max_dfs + 1; - nstack := Append(nstack, n) ; - nxt := 1 ; - while nxt \leq Len(data[n].nbrs) do - nxtnbr := data[n].nbrs[nxt] ; - if ~ data[nxtnbr].examined - then (***************************************************************) - (* nxtnbr is unexamined *) - (***************************************************************) - call tarjan(nxtnbr) ; - data[n].lowlink := Min(data[n].lowlink, data[nxtnbr].lowlink) ; - - elsif data[nxtnbr].dfs \geq 0 - then (**********************************************************) - (* nxtnbr is in nstack *) - (**********************************************************) - data[n].lowlink := Min(data[n].lowlink, data[nxtnbr].dfs) ; - end if ; - nxt := nxt + 1; - end while ; - if data[n].lowlink = data[n].dfs - then (*****************************************************************) - (* The set of all nodes on nstack from n to the end form a *) - (* connected component. *) - (*****************************************************************) - nxtnbr := Last(nstack) ; \* save last node on stack. - while n # Last(nstack) do - data[Last(nstack)].nxtDep := nstack[Len(nstack)-1] || - data[Last(nstack)].dfs := -1 ; - (*************************************************************) - (* Make nxtDep field of last node on stack point to next to *) - (* last node. *) - (*************************************************************) - nstack := Front(nstack) ; - end while ; - if \/ n # nxtnbr - \/ \E i \in 1..Len(data[n].nbrs) : n = data[n].nbrs[i] - then (**********************************************************) - (* Form a connected component consisting of just n only *) - (* if n is a neighbor of itself. *) - (**********************************************************) - data[n].nxtDep := nxtnbr; - end if; - data[n].dfs := -1 ; - nstack := Front(nstack) ; - end if ; - data[n].nbrs := << >> ; - (**********************************************************************) - (* Can garbage collect the sequence of neighbors. *) - (**********************************************************************) - return ; -end procedure - -begin - (************************************************************************) - (* Initialize the nbrs field of data elements from Neighbors. *) - (************************************************************************) - idx := 1 ; - while idx \leq Len(Neighbors) do - with edge = Neighbors[idx] do - data[edge[1]].nbrs := Append(data[edge[1]].nbrs, edge[2]) ; - end with ; - idx := idx + 1; - end while ; - - (************************************************************************) - (* while there is an unexamined node, call tarjan(nd) *) - (* for any unexamined node nd. *) - (************************************************************************) - idx := 1 ; - while idx \leq N do - if data[idx].lowlink < 0 - then (***************************************************************) - (* idx is unexamined *) - (***************************************************************) - call tarjan(idx) ; - end if; - idx := idx + 1 ; - end while ; - - (************************************************************************) - (* print result. *) - (************************************************************************) - idx := 1 ; - while idx \leq N do - if data[idx].nxtDep \geq 0 - then print <<idx, "->", data[idx].nxtDep>> - end if ; - idx := idx+1; - end while -end algorithm -***********************) - -\* BEGIN TRANSLATION -\* END TRANSLATION - -\**** Test data - -Node1 == {1, 2, 3, 4} - -Nbrs1 == << <<1, 2>>, \* components 1 and 3,4 - <<1, 2>>, \* components 1 and 3,4 - <<1, 1>>, - <<1, 1>>, - <<3, 4>> , - <<3, 4>> , - <<4, 3>> , - <<4, 3>>, - <<4, 3>> - >> - -Nbrs2 == << <<1, 2>>, \* components 1 and 3,4 - <<1, 1>>, - <<1, 3>>, - <<3, 4>>, - <<4, 3>> - >> -Nbrs3 == << <<1, 2>>, \* components 1 and 3,4 - <<1, 1>>, - <<3, 1>>, - <<3, 4>>, - <<4, 3>> - >> -Nbrs4 == << <<1, 3>>, \* - <<1, 8>>, \* componetns: 2,3,4,5 and 7,8,9 - <<1, 9>>, - <<1, 9>>, - <<1, 9>>, - <<3, 2>>, - <<3, 2>>, - <<2, 4>>, - <<4, 5>>, - <<4, 10>>, - <<5, 3>>, - <<8, 7>>, - <<8, 7>>, - <<9, 8>>, - <<7, 9>>, - <<7, 9>>, - <<9, 10>>, - <<9, 10>>, - <<9, 10>> - >> - -Nbrs5 == << <<1, 8>>, \* components 7,8,9 - <<1, 9>>, - <<4, 10>>, - <<8, 7>>, - <<9, 8>>, - <<7, 9>>, - <<9, 10>> - >> - -Nbrs6 == << <<6, 4>>, \* components 2,3,4,5 - <<3, 4>>, - <<4, 5>>, - <<5, 2>>, - <<2, 3>>, - <<5, 1>>, - <<2, 1>> - >> - -Nbrs7 == << <<1, 7>>, \* 1 component with 1..9 \ {3} - <<1, 9>>, - <<2, 1>>, - <<3, 2>>, - <<3, 6>>, - <<4, 6>>, - <<5, 2>>, - <<6, 5>>, - <<6, 8>>, - <<7, 5>>, - <<7, 8>>, - <<8, 4>>, - <<8, 10>>, - <<9, 7>>, - <<9, 10>> - >> -Nbrs8 == << <<1, 7>>, \* 1 component with 1..8 \ {3} - <<2, 1>>, - <<3, 2>>, - <<3, 2>>, - <<3, 2>>, - <<3, 6>>, - <<4, 6>>, - <<5, 2>>, - <<6, 5>>, - <<6, 8>>, - <<6, 8>>, - <<6, 8>>, - <<7, 5>>, - <<7, 8>>, - <<8, 4>>, - <<8, 10>>, - <<9, 7>>, - <<9, 10>> - >> -Nbrs9 == << \* 4,6, 7,8,9,10 - <<2, 1>>, - <<3, 2>>, - <<3, 6>>, - <<4, 6>>, - <<4, 6>>, - <<4, 6>>, - <<5, 2>>, - <<6, 5>>, - <<6, 8>>, - <<7, 5>>, - <<7, 5>>, - <<7, 5>>, - <<7, 5>>, - <<7, 8>>, - <<8, 4>>, - <<8, 10>>, - <<9, 7>>, - <<10, 9>> - >> -Nbrs == << \* 1, 3, 4, 5 and 6 - <<1, 4>>, - <<2, 1>>, - <<2, 3>>, - <<2, 3>>, - <<2, 3>>, - <<2, 5>>, - <<3, 4>>, - <<4, 1>>, - <<4, 5>>, - <<4, 5>>, - <<4, 5>>, - <<5, 3>>, - <<6, 5>>, - <<6, 6>> - >> - -============================================================================= - -************************** end file Tarjan.tla ******************************/ - -/************************* file Subexpression.tla ********************** -last modified on Fri 13 November 2009 at 14:11:05 PST by lamport ------------------------- MODULE Subexpression --------------------------- - - -(***************************************************************************) -(* NAMING *) -(* *) -(* There are four kinds of Nameable Objects: *) -(* - Expressions *) -(* - Operators that take an argument *) -(* (and that can appear as operator arguments) *) -(* - Operator definitions (named in BY/USE/HIDE statements) *) -(* - ASSUME/PROVEs. *) -(* *) -(* There are three relevant kinds of primitive names that appear in a *) -(* TLA+ module. *) -(* *) -(* Defined Module Name (ModuleName): *) -(* It appears to the left of "== INSTANCE...". It may or may not *) -(* contain arguments. *) -(* Primitive Operator Name (PrimOpName): *) -(* It appear to the left of the "==" in a definition, theorem *) -(* or assumption. It may or may not contain arguments. *) -(* Label: *) -(* It appears before a "::" in an expression. *) -(* *) -(* Examples of possible primitive names are "Foo" and "Bar(x+1, 42)". *) -(* *) -(* The Built-In Operator names (like "SUBSET") will not be considered to *) -(* be primitive names in the following. *) -(* *) -(* To these explicit names we add the following Operand Selector (OpSel) *) -(* names: *) -(* - numbers, *) -(* - "<<", ">>", *) -(* - Things of the form (exp_1, ... , exp_n) where the exp_i are *) -(* expressions or operator arguments and n > 0. *) -(* - "@", *) -(* *) -(* A Compound Name is a sequence of primitive names, operand selectors, *) -(* and ":"s separated by "!"s. *) -(* *) -(* In TLA+1, the general way of naming a non-built-in operator is with *) -(* what I will call an Elementary Operator Name (ElemOpName) which has *) -(* the following syntax. For simplicity, I will ignore the "!"s in the *) -(* `formal' syntax. *) -(* *) -(* ElemOpName ::= (ModuleName)* PrimOpName *) -(* *) -(* We consider this to name both the operator and its definition (the *) -(* expression to the right of the "=="). How the two meanings are *) -(* disambiguated will be explained below. *) -(* *) -(* If EON is an ElemOpName and Lab_1, ... , Lab_n are a sequence of *) -(* label names, then EON ! Lab_1 ! ... ! Lab_N names the expression *) -(* labeled by Lab_N, where each Lab_(i+1) must be the name of a labeled *) -(* expression within the expression labeled by Lab_i with no labels *) -(* between Lab_i and Lab_(i+1). Thus a name described by *) -(* *) -(* ElemOpName (Label)* *) -(* *) -(* describes an operator definition or a labeled expression. To such a *) -(* name, we can append a sequence of OperandSelectors. By rules *) -(* described later, the resulting name selects a subexpression (or *) -(* ASSUME/PROVE node) or Operator (appearing as an operator argument) *) -(* from the body of the operator definition or labeled expression. If *) -(* the labeled expression is a LET/IN, then to this name can be appended *) -(* an ElemOpName defined in the LET part, and the naming process can then *) -(* be iterated. The full syntax of a General Name is: *) -(* *) -(* OperatorName ::= ElemOpName *) -(* | ElemOpName (Label)* ((OpSel)+ | ":") ElemOpName *) -(* *) -(* GeneralName ::= OperatorName (":" | (Label)* (OpSeq)* ) *) -(* *) -(* Note that ":" is used in two ways: *) -(* *) -(* - An OperatorName not followed by ":" names the operator; one *) -(* followed by ":" names the operator's definition. *) -(* - Suppose a general name GN ending in a label names a LET/IN *) -(* expression and EON is an ElemOpName. Then GN !: !EON names *) -(* an operator defined in the LET. The general name GN ! Foo would *) -(* name an expression labeled by Foo inside the IN clause. *) -(***************************************************************************) - - -(***************************************************************************) -(* This +cal program describes an algorithm used in generating the *) -(* semantic tree for a parse tree consisting of a GeneralId node or an *) -(* OpApplication node whose first child is a GeneralId node. Two examples *) -(* are *) -(* *) -(* Foo(x)!Bar!<<!(a,b)!G *) -(* Foo(x)!Bar!<<!(a,b)!G(c) *) -(* *) -(* We assume that these are processed to yield two sequences of nodes: *) -(* op and args. The sequences for the first are *) -(* *) -(* op = << "Foo", "Bar", "<<", "null", "G" >> *) -(* args = << <<x>>, << >>, << >>, <<a, b>>, <<>> >> *) -(* *) -(* and for the second are *) -(* *) -(* op = << "Foo", "Bar", "<<", "null", "G" >> *) -(* args = << <<x>>, << >>, << >>, <<a, b>>, <<c>> >> *) -(* *) -(* where each symbol in args represents the semantic node produced by that *) -(* symbol. *) -(* *) -(* The other input to the algortithm is an integer expectedArity. There *) -(* are three cases: *) -(* *) -(* expectedArity = 0 : *) -(* The parser is expecting an expression. This is the case *) -(* when the expression occurs in the first part of a USE *) -(* or HIDE statement. *) -(* *) -(* expectedArity > 0 : The parser is expecting an operator *) -(* argument of this arity. *) -(* *) -(* expectedArity = -1 : *) -(* The parser is expecting the name of a definition. This is *) -(* the case when the expression occurs in the DEF clause of a USE *) -(* or HIDE or BY statement. *) -(* *) -(* If expectedArity # 0, then args equals a sequence each of whose *) -(* element is the empty sequence. *) -(***************************************************************************) - -(***************************************************************************) -(* Note: This spec is assuming that proof-step numbers are treated like *) -(* operator names. That is, a step *) -(* *) -(* <3>2. foo > bar *) -(* *) -(* will be represented by a ThmOrAssumpDefKind node just as if it were *) -(* *) -(* THEOREM <3>2 == foo > bar *) -(* *) -(* If this is not the case, then additional code needs to be added to deal *) -(* with numbered steps. But however it is handled, the "<3>2" will be a *) -(* name in the current symbol table. *) -(***************************************************************************) - -EXTENDS Integers, Sequences, TLC - -(***************************************************************************) -(* SeqSeqToSeq(ss) is defined to be the concatenation of the sequences *) -(* that are the elements of the sequence ss. *) -(***************************************************************************) -RECURSIVE SeqSeqToSeq(_) -SeqSeqToSeq(ss) == IF ss = << >> - THEN << >> - ELSE Head(ss) \o SeqSeqToSeq(Tail(ss)) - -(***************************************************************************) -(* These are kinds of semantic nodes that can be encountered by the *) -(* algorithm. See tlasany/sematic/ASTConstants.java. *) -(***************************************************************************) -ConstantDeclKind == "ConstantDecl" -VariableDeclKind == "VariableDecl" -BoundSymbolKind == "BoundSymbol" -UserDefinedOpKind == "UserDefinedOp" -ModuleInstanceKind == "ModuleInstance" -BuiltInKind == "BuiltIn" -OpArgKind == "OpArg" -OpApplKind == "OpAppl" -LetInKind == "LetIn" -FormalParamKind == "FormalParam" -TheoremKind == "Theorem" -SubstInKind == "SubstIn" -AssumeProveKind == "AssumeProve" -ProofKind == "Proof" -NumeralKind == "Numeral" -DecimalKind == "Decimal" -StringKind == "String" -AtNodeKind == "AtNode" -AssumeKind == "Assume" -InstanceKind == "Instance" -ThmOrAssumpDefKind == "ThmOrAssumpDef" -LabelKind == "Label" -APSubstInKind == "APSubstIn" - -CONSTANT - NodeId, \* The set of all node identifiers. - Node, \* A mapping from NodeId to nodes (which are records). - GlobalContext, \* The initial context. - null, \* A special value not equal to anything else. - debug \* Setting to TRUE causes TLC to print a trace if the - \* algorithm reports an error. - -(***************************************************************************) -(* CONTEXTS *) -(* *) -(* A context is a mapping from names to NodeIds. There are actually three *) -(* different kinds of mappings from names to nodes in the implementation *) -(* that are all represented here as contexts: *) -(* - The symbolTable of the Generator. *) -(* - The context field of a LetInNode *) -(* - The labels field of a LabelNode *) -(***************************************************************************) -IsContext(C) == - (*************************************************************************) - (* True iff C is a context. *) - (*************************************************************************) - /\ DOMAIN C \subseteq STRING - /\ \A str \in DOMAIN C : C[str] \in NodeId - -LookUp(nm, ctxt) == - (*************************************************************************) - (* The value assigned to name nm by context cxt, or null if there is no *) - (* value assigned. *) - (*************************************************************************) - IF nm \in DOMAIN ctxt THEN Node[ctxt[nm]] ELSE null - -Param == [name : STRING, arity : Nat] - (*************************************************************************) - (* The set of parameters, which can appear in operator definitions. *) - (*************************************************************************) - -(***************************************************************************) -(* For simplicity, we assume that argument numbers range from 1 to 9, and *) -(* we define the following mappings from to and from argument numbers to *) -(* their string representations. *) -(***************************************************************************) -NumberOp == {"1", "2", "3", "4", "5", "6", "7", "8", "9"} -NumericVal(numOp) == CASE numOp = "1" -> 1 - [] numOp = "2" -> 2 - [] numOp = "3" -> 3 - [] numOp = "4" -> 4 - [] numOp = "5" -> 5 - [] numOp = "6" -> 6 - [] numOp = "7" -> 7 - [] numOp = "8" -> 8 - [] numOp = "9" -> 9 - -NumToString(num) == CASE num = 1 -> "1" - [] num = 2 -> "2" - [] num = 3 -> "3" - [] num = 4 -> "4" - [] num = 5 -> "5" - [] num = 6 -> "6" - [] num = 7 -> "7" - [] num = 8 -> "8" - [] num = 9 -> "9" - -IsName(op) == - (*************************************************************************) - (* A name is something other than an argument selector that can appear *) - (* in a compound name. *) - (*************************************************************************) - op \in STRING \ ({"<<", ">>", "@", ":", "null"} \cup NumberOp) - - -ArgNum(op, arity) == - (*************************************************************************) - (* The argument number chosen by argument selector op for an operator *) - (* application with arity arguments. It equals -1 if op is not a legal *) - (* argument selector for this arity. *) - (*************************************************************************) - CASE op \in NumberOp -> - IF NumericVal(op) <= arity THEN NumericVal(op) ELSE -1 - [] op = "<<" -> IF arity > 0 THEN 1 ELSE -1 - [] op = ">>" -> CASE arity = 1 -> 1 - [] arity = 2 -> 2 - [] OTHER -> -1 - [] OTHER -> -1 - -Arity(node) == - (*************************************************************************) - (* The arity of a node--that is, the number of arguments it takes. *) - (*************************************************************************) - IF node.kind \in {UserDefinedOpKind, BuiltInKind, ModuleInstanceKind, - ThmOrAssumpDefKind, OpArgKind, LabelKind, - ConstantDeclKind, FormalParamKind} - THEN node.arity - ELSE 0 - -ParamArity(node, i) == - (*************************************************************************) - (* The arity of the i-th parameter of the node of kind *) - (* UserDefinedOpKind, ThmOrAssumpDefKind, ModuleInstanceKind, *) - (* ConstantDeclKind, or FormalParamKind *) - (*************************************************************************) - IF node.kind \in {ConstantDeclKind, FormalParamKind} - THEN 0 - ELSE node.params[i].arity ------------------------------------------------------------------------------ - -(***************************************************************************) -(* The following assumptions define the data types--in particular, the *) -(* record components that each node type must contain. *) -(***************************************************************************) -ASSUME IsContext(GlobalContext) - -ASSUME DOMAIN Node = NodeId - -ASSUME -/\ \A id \in NodeId : - LET node == Node[id] IN - /\ node.kind \in - {ConstantDeclKind, VariableDeclKind, BoundSymbolKind, - UserDefinedOpKind, ModuleInstanceKind, BuiltInKind, - OpArgKind, OpApplKind, LetInKind, FormalParamKind, - TheoremKind, SubstInKind, AssumeProveKind, ProofKind, - NumeralKind, DecimalKind, StringKind, AtNodeKind, - AssumeKind, InstanceKind, ThmOrAssumpDefKind, LabelKind, - APSubstInKind} - -ASSUME -/\ \A id \in NodeId : - LET node == Node[id] IN - /\ node.kind \in {UserDefinedOpKind, BuiltInKind, ModuleInstanceKind, - ThmOrAssumpDefKind, LabelKind, ConstantDeclKind, - FormalParamKind} - => /\ node.name \in STRING - /\ node.arity \in Nat \cup {-1} - /\ (node.arity = -1) => (node.kind = BuiltInKind) - -ASSUME - \A id \in NodeId : - LET node == Node[id] IN - node.kind \in {UserDefinedOpKind, ThmOrAssumpDefKind} => - /\ node.body \in NodeId - /\ IsContext(node.labels) - /\ node.params \in Seq(Param) - /\ Len(node.params) = node.arity - /\ node.defined \in BOOLEAN - /\ node.source \in {null} \cup NodeId - \* The original definition (before instantiation) - \* or null if this is it. - -ASSUME - \A id \in NodeId : - LET node == Node[id] IN - /\ node.kind = OpApplKind => - /\ node.operands \in Seq(NodeId) - /\ node.operator \in NodeId - /\ node.unboundedBoundSymbols \in Seq(STRING) - /\ node.boundedBoundSymbols \in Seq(Seq(STRING)) - (**************************************************************) - (* These are actually arrays and arrays of arrays of *) - (* FormalParamNode objects (formerly OpDeclNode objects). *) - (**************************************************************) - /\ node.ranges \in Seq(NodeId) - /\ Len(node.ranges) = Len(node.boundedBoundSymbols) - -ASSUME - \A id \in NodeId : - LET node == Node[id] IN - /\ node.kind = LetInKind => - /\ node.body \in NodeId - /\ IsContext(node.context) - -ASSUME - \A id \in NodeId : - LET node == Node[id] IN - /\ node.kind = OpArgKind => - /\ node.op \in NodeId - /\ IsName(node.name) - /\ (node.arity \in Nat) /\ (node.arity > 0) - -ASSUME - \A id \in NodeId : - LET node == Node[id] IN - /\ node.kind = LabelKind => - /\ node.body \in NodeId - /\ IsContext(node.labels) - /\ node.params \in Seq(Param) - -ASSUME - \A id \in NodeId : - LET node == Node[id] IN - /\ node.kind = SubstInKind => - /\ node.body \in NodeId - /\ node.subst \in STRING \* Just to identify it. - -ASSUME - \A id \in NodeId : - LET node == Node[id] IN - /\ node.kind = AssumeProveKind => - /\ node.assumes\in Seq(NodeId) - /\ node.prove \in NodeId - - -RECURSIVE ExpandNode(_) -ExpandNode(node) == - CASE node.kind \in {UserDefinedOpKind, LetInKind} -> - [node EXCEPT !.body = ExpandNode(Node[@])] - [] node.kind = OpApplKind -> - [node EXCEPT - !.operands = [i \in DOMAIN @ |-> ExpandNode(Node[@[i]])], - !.operator = ExpandNode(Node[@]), - !.ranges = [i \in DOMAIN @ |-> ExpandNode(Node[@[i]])]] - [] OTHER -> node - - ------------------------------------------------------------------------------ -(***************************************************************************) -(* Test Data *) -(* *) -(* The algorithm has been tested on the following sets of data, where each *) -(* operator MCxxx is either substituted for constant parameter xxx or is *) -(* used as initial value of the algorithm's input variable xxx. *) -(***************************************************************************) - - -(***************************** - - -(***************************************************************************) -(* ------------------------ MODULE M ------------------------ *) -(* CONSTANT C *) -(* Op(Arg(_), p) == \A x \in {1,2}, <<y, z>> \in {3} : *) -(* lab(x,y,z) :: LET a + b == <<Arg(a), b>> *) -(* IN 1 + label2 :: p + C *) -(* Foo(u) == "FooBody(u)" *) -(* ============================================================= *) -(* *) -(* Inst(d) == INSTANCE M WITH C <- "expr(d)" *) -(***************************************************************************) - -\* MCNode == -\* 1 :> [kind |-> UserDefinedOpKind, \* Op(Arg(_), p) == ... -\* name |-> "Op", -\* body |-> 3, -\* labels |-> "lab" :> 4, -\* arity |-> 2, -\* params |-> <<[name |-> "Arg", arity |-> 1], -\* [name |-> "p", arity |-> 0]>>, -\* defined |-> TRUE, -\* source |-> null ] -\* @@ -\* 2 :> [kind |-> BuiltInKind, -\* name |-> "$BoundedForall", -\* arity |-> -1 -\* ] -\* @@ -\* 3 :> [kind |-> OpApplKind, \* \A x \in {1,2}, <<y, z>> \in {3} : ... -\* operands |-> << 4 >>, -\* operator |-> 2, -\* unboundedBoundSymbols |-> <<>>, -\* boundedBoundSymbols |-> << <<"x">>, <<"y", "z">> >>, -\* ranges |-> << 19, 21 >>] -\* @@ -\* 4 :> [kind |-> LabelKind, \* lab(x,y,z) :: LET a + b == <<Arg(a), b>> -\* name |-> "lab", \* IN 1 + label2 :: p + C -\* body |-> 5, -\* arity |-> 3, -\* params |-> <<[name |-> "x", arity |->0], -\* [name |-> "y", arity |->0], -\* [name |-> "z", arity |->0] >>, -\* labels |-> "label2" :> 15 ] -\* @@ -\* 5 :> [kind |-> LetInKind, \* LET a + b == <<Arg(a), b>> -\* context |-> "+" :> 6, \* IN 1 + label2 :: p + C -\* body |-> 13 ] -\* @@ -\* 6 :> [kind |-> UserDefinedOpKind, \* a + b == <<Arg(a), b>> -\* name |-> "+", -\* body |-> 7, -\* labels |-> << >>, -\* arity |-> 2, -\* params |-> <<[name |-> "a", arity |-> 0], -\* [name |-> "b", arity |-> 0]>>, -\* defined |-> TRUE, -\* source |-> null ] -\* @@ -\* 7 :> [kind |-> OpApplKind, \* <<Arg(a), b>> -\* operands |-> << 9, 12 >>, -\* operator |-> 8, -\* unboundedBoundSymbols |-> <<>>, -\* boundedBoundSymbols |-> << >>, -\* ranges |-> << >>] -\* @@ -\* 8 :> [kind |-> BuiltInKind, -\* name |-> "$Tuple", -\* arity |-> -1 -\* ] -\* @@ -\* 9 :> [kind |-> OpApplKind, \* Arg(a) -\* operands |-> <<11 >>, -\* operator |-> 10, -\* unboundedBoundSymbols |-> <<>>, -\* boundedBoundSymbols |-> << >>, -\* ranges |-> << >>] -\* @@ -\* 10 :> [kind |-> FormalParamKind, -\* name |-> "Arg", -\* arity |-> 1] -\* -\* @@ -\* 11 :> [kind |-> FormalParamKind, -\* name |-> "a", -\* arity |-> 0] -\* @@ -\* 12 :> [kind |-> FormalParamKind, -\* name |-> "b", -\* arity |-> 0] -\* @@ -\* 13 :> [kind |-> OpApplKind, \* 1 + label2 :: p + C -\* operands |-> <<14, 15>>, -\* operator |-> 6, -\* unboundedBoundSymbols |-> <<>>, -\* boundedBoundSymbols |-> << >>, -\* ranges |-> << >>] -\* @@ -\* 14 :> [kind |-> NumeralKind, -\* val |-> 1] -\* @@ -\* 15 :> [kind |-> LabelKind, \* label2 :: p + C -\* name |-> "label2", -\* body |-> 16, -\* arity |-> 0, -\* params |-> <<>>, -\* labels |-> << >>] -\* @@ -\* 16 :> [kind |-> OpApplKind, \* p + C -\* operands |-> <<17, 18>>, -\* operator |-> 6, -\* unboundedBoundSymbols |-> <<>>, -\* boundedBoundSymbols |-> << >>, -\* ranges |-> << >>] -\* @@ -\* 17 :> [kind |-> FormalParamKind, -\* name |-> "p", -\* arity |-> 0] -\* @@ -\* 18 :> [kind |-> ConstantDeclKind, -\* name |-> "C", -\* arity |-> 0 ] -\* @@ -\* 19 :> [kind |-> OpApplKind, \* {1,2} -\* operands |-> << 14, 20 >>, -\* operator |-> 8, \* Actually, specifying <<1, 2>> -\* unboundedBoundSymbols |-> << >>, -\* boundedBoundSymbols |-> << >>, -\* ranges |-> << >>] -\* @@ -\* 20 :> [kind |-> NumeralKind, -\* val |-> 2] -\* @@ -\* 21 :> [kind |-> OpApplKind, \* {1,2} -\* operands |-> << 22>>, -\* operator |-> 8, \* Actually, specifying <<1, 2>> -\* unboundedBoundSymbols |-> << >>, -\* boundedBoundSymbols |-> << >>, -\* ranges |-> << >>] -\* @@ -\* 22 :> [kind |-> NumeralKind, -\* val |-> 3] -\* @@ -\* 23 :> [kind |-> UserDefinedOpKind, \* Foo(u) == "FooBody(u)" -\* name |-> "Foo", -\* body |-> 24, -\* labels |-> << >>, -\* arity |-> 1, -\* params |-> <<[name |-> "u", arity |-> 0]>>, -\* defined |-> TRUE, -\* source |-> null ] -\* @@ -\* 24 :> [kind |-> StringKind, -\* val |-> "FooBody(u)"] -\* @@ -\* 25 :> [kind |-> StringKind, -\* val |-> "string"] -\* @@ -\* 26 :> [kind |-> ModuleInstanceKind, \* Inst(d) == INSTANCE M WITH C <- "expr(d)" -\* name |-> "Inst", -\* arity |-> 1, -\* params |-> <<[name |-> "d", arity |-> 0] >>, -\* defined |-> TRUE ] -\* @@ -\* 27 :> [kind |-> SubstInKind, -\* body |-> 24, -\* subst |-> "expr(d)"] -\* @@ -\* 28 :> [kind |-> UserDefinedOpKind, \* Foo(u) == "FooBody(u)" -\* name |-> "Inst!Foo", -\* body |-> 27, -\* labels |-> << >>, -\* arity |-> 2, -\* params |-> << [name |-> "d", arity |-> 0], -\* [name |-> "u", arity |-> 0]>>, -\* defined |-> TRUE, -\* source |-> 23 ] -\* @@ -\* 29 :> [kind |-> UserDefinedOpKind, \* Op(Arg(_), p) == ... -\* name |-> "Inst!Op", -\* body |-> 30, -\* labels |-> "lab" :> 4, -\* arity |-> 3, -\* params |-> <<[name |-> "d", arity |-> 0], -\* [name |-> "Arg", arity |-> 1], -\* [name |-> "p", arity |-> 0]>>, -\* defined |-> TRUE, -\* source |-> 1 ] -\* @@ -\* 30 :> [kind |-> SubstInKind, -\* body |-> 3, -\* subst |-> "expr(d)"] -\* @@ -\* 31 :> [kind |-> StringKind, -\* val |-> "Argm"] -\* -\* MCNodeId == 1..31 -\* -\* MCGlobalContext == -\* "Op" :> 1 -\* @@ -\* "Foo" :> 23 -\* @@ -\* "Inst" :> 26 -\* @@ -\* "Inst!Foo" :> 28 -\* @@ -\* "Inst!Op" :> 29 -\* -\* \* Foo("string") -\* \* MCops == <<"Foo">> -\* \* MCargs == << <<25>>>> -\* \* MCexpectedArity == 0 -\* -\* \* Op(Foo, "string") -\* \* MCops == <<"Op">> -\* \* MCargs == << <<23, 25>> >> -\* \* MCexpectedArity == 0 -\* -\* \* Op(Foo, "string")!lab(1,2,3) -\* \* MCops == <<"Op", "lab">> -\* \* MCargs == << <<23, 25>> , <<14, 20, 22 >> >> -\* \* MCexpectedArity == 0 -\* -\* \* Op(Foo, "string")!lab(1,2,3)!<< -\* \* MCops == <<"Op", "lab", "<<">> -\* \* MCargs == << <<23, 25>> , <<14, 20, 22>> , << >> >> -\* \* MCexpectedArity == 0 -\* -\* \* Op(Foo, "string")!lab(1,2,3)!<<!>> -\* \* MCops == <<"Op", "lab", "<<", ">>">> -\* \* MCargs == << <<23, 25>> , <<14, 20, 22>> , << >>, << >> >> -\* \* MCexpectedArity == 0 -\* -\* \* Op(Foo, "string")!lab(1,2,3)!label2 -\* \* MCops == <<"Op", "lab", "label2">> -\* \* MCargs == << <<23, 25>> , <<14, 20, 22>> , << >> >> -\* \* MCexpectedArity == 0 -\* -\* \* Op(Foo, "string")!lab(1,2,3)!label2!<< -\* \* MCops == <<"Op", "lab", "label2", "<<">> -\* \* MCargs == << <<23, 25>> , <<14, 20, 22>> , << >>, << >> >> -\* \* MCexpectedArity == 0 -\* -\* \* Op(Foo, "string")!lab(1,2,3)!:!+(3,2) -\* \* MCops == <<"Op", "lab", ":", "+" >> -\* \* MCargs == << <<23, 25>> , <<14, 20, 22>> , << >> , <<22, 20 >> >> -\* \* MCexpectedArity == 0 -\* -\* \* Op(Foo, "string")!lab(1,2,3)!:!+(3,2)!>> -\* \* MCops == <<"Op", "lab", ":", "+", ">>" >> -\* \* MCargs == << <<23, 25>> , <<14, 20, 22>> , << >> , <<22, 20 >>, << >> >> -\* \* MCexpectedArity == 0 -\* -\* \* Op(Foo, "string")!(1,2,3) -\* \* MCops == <<"Op", "null" >> -\* \* MCargs == << <<23, 25>> , <<14, 20, 22>> >> -\* \* MCexpectedArity == 0 -\* -\* \* Op(Foo, "string")!(1,2,3)!>>!>>!<< -\* \* MCops == <<"Op", "null", ">>", ">>", "<<" >> -\* \* MCargs == << <<23, 25>> , <<14, 20, 22>>, << >>, << >>, << >> >> -\* \* MCexpectedArity == 0 -\* -\* \* Op(Foo, "string")!<<!>> -\* \* MCops == <<"Op", "<<", ">>" >> -\* \* MCargs == << <<23, 25>>, << >>, << >> >> -\* \* MCexpectedArity == 0 -\* -\* \* Inst("Argm")!Foo("string") -\* \* MCops == <<"Inst", "Foo">> -\* \* MCargs == << <<31>>, <<25>>>> -\* \* MCexpectedArity == 0 -\* -\* \* Inst("Argm")!Op(Foo, "string") -\* \* MCops == <<"Inst", "Op">> -\* \* MCargs == << <<31>>, <<23, 25>> >> -\* \* MCexpectedArity == 0 -\* -\* \* Inst("Argm")!Op(Foo, "string")!lab(1,2,3) -\* \* MCops == <<"Inst", "Op", "lab">> -\* \* MCargs == << <<31>>, <<23, 25>> , <<14, 20, 22 >> >> -\* \* MCexpectedArity == 0 -\* -\* \* Inst("Argm")!Op(Foo, "string")!lab(1,2,3)!<< -\* \* MCops == <<"Inst", "Op", "lab", "<<">> -\* \* MCargs == << <<31>>, <<23, 25>> , <<14, 20, 22>> , << >> >> -\* \* MCexpectedArity == 0 -\* -\* \* Inst("Argm")!Op(Foo, "string")!lab(1,2,3)!<<!>> -\* \* MCops == <<"Inst", "Op", "lab", "<<", ">>">> -\* \* MCargs == << <<31>>, <<23, 25>> , <<14, 20, 22>> , << >>, << >> >> -\* \* MCexpectedArity == 0 -\* -\* \* Inst("Argm")!Op(Foo, "string")!lab(1,2,3)!label2 -\* \* MCops == <<"Inst", "Op", "lab", "label2">> -\* \* MCargs == << <<31>>, <<23, 25>> , <<14, 20, 22>> , << >> >> -\* \* MCexpectedArity == 0 -\* -\* \* Inst("Argm")!Op(Foo, "string")!lab(1,2,3)!label2!<< -\* \* MCops == <<"Inst", "Op", "lab", "label2", "<<">> -\* \* MCargs == << <<31>>, <<23, 25>> , <<14, 20, 22>> , << >>, << >> >> -\* \* MCexpectedArity == 0 -\* -\* \* Inst("Argm")!Op(Foo, "string")!lab(1,2,3)!:!+(3,2) -\* \* MCops == <<"Inst", "Op", "lab", ":", "+" >> -\* \* MCargs == << <<31>>, <<23, 25>> , <<14, 20, 22>> , << >> , <<22, 20 >> >> -\* \* MCexpectedArity == 0 -\* -\* \* Inst("Argm")!Op(Foo, "string")!lab(1,2,3)!:!+(3,2)!>> -\* \* MCops == <<"Inst", "Op", "lab", ":", "+", ">>" >> -\* \* MCargs == << <<31>>, <<23, 25>> , <<14, 20, 22>> , << >> , <<22, 20 >>, << >> >> -\* \* MCexpectedArity == 0 -\* -\* \* Inst("Argm")!Op(Foo, "string")!(1,2,3) -\* \* MCops == <<"Inst", "Op", "null" >> -\* \* MCargs == << <<31>>, <<23, 25>> , <<14, 20, 22>> >> -\* \* MCexpectedArity == 0 -\* -\* \* Inst("Argm")!Op(Foo, "string")!(1,2,3)!>>!>>!<< -\* \* MCops == <<"Inst", "Op", "null", ">>", ">>", "<<" >> -\* \* MCargs == << <<31>>, <<23, 25>> , <<14, 20, 22>>, << >>, << >>, << >> >> -\* \* MCexpectedArity == 0 -\* -\* \* Inst("Argm")!Op(Foo, "string")!<<!>> -\* \* MCops == <<"Inst", "Op", "<<", ">>" >> -\* \* MCargs == << <<31>>, <<23, 25>>, << >>, << >> >> -\* \* MCexpectedArity == 0 - -*****************************************) - -(***************************************************************************) -(* ------------------------ MODULE M ------------------------ *) -(* CONSTANT C, Arg1(_) *) -(* Op(Arg, p) == \A x \in {1,2}, <<y, z>> \in {3} : *) -(* lab(x,y,z) :: LET a + b == <<Arg1(a), b>> *) -(* IN 1 + label2 :: p + C *) -(* Bar(AOp(_)) == AOp(1) *) -(* Foo(u) == Bar(Arg1) *) -(* Foo2 == Bar(LAMBDA m, n : <<m, n>>) *) -(* THEOREM Thm == ASSUME Arg1(1) , Bar(Arg1) PROVE << C, Bar(Arg1)>> *) -(* UU == lab:: C *) -(* ============================================================= *) -(* *) -(* Foo3 == \A x : LET Bar3 == 1 IN 2 *) -(* Inst(d) == INSTANCE M WITH C <- "expr(d)" *) -(***************************************************************************) - - -MCNode == - 1 :> [kind |-> UserDefinedOpKind, \* Op(Arg, p) == ... - name |-> "Op", - body |-> 3, - labels |-> "lab" :> 4, - arity |-> 2, - params |-> <<[name |-> "Arg", arity |-> 0], - [name |-> "p", arity |-> 0]>>, - defined |-> TRUE, - source |-> null ] -@@ - 2 :> [kind |-> BuiltInKind, - name |-> "$BoundedForall", - arity |-> -1 - ] -@@ - 3 :> [kind |-> OpApplKind, \* \A x \in {1,2}, <<y, z>> \in {3} : ... - operands |-> << 4 >>, - operator |-> 2, - unboundedBoundSymbols |-> <<>>, - boundedBoundSymbols |-> << <<"x">>, <<"y", "z">> >>, - ranges |-> << 19, 21 >>] -@@ - 4 :> [kind |-> LabelKind, \* lab(x,y,z) :: LET a + b == <<Arg(a), b>> - name |-> "lab", \* IN 1 + label2 :: p + C - body |-> 5, - arity |-> 3, - params |-> <<[name |-> "x", arity |->0], - [name |-> "y", arity |->0], - [name |-> "z", arity |->0] >>, - labels |-> "label2" :> 15 ] -@@ - 5 :> [kind |-> LetInKind, \* LET a + b == <<Arg(a), b>> - context |-> "+" :> 6, \* IN 1 + label2 :: p + C - body |-> 13 ] -@@ - 6 :> [kind |-> UserDefinedOpKind, \* a + b == <<Arg(a), b>> - name |-> "+", - body |-> 7, - labels |-> << >>, - arity |-> 2, - params |-> <<[name |-> "a", arity |-> 0], - [name |-> "b", arity |-> 0]>>, - defined |-> TRUE, - source |-> null ] -@@ - 7 :> [kind |-> OpApplKind, \* <<Arg(a), b>> - operands |-> << 9, 12 >>, - operator |-> 8, - unboundedBoundSymbols |-> <<>>, - boundedBoundSymbols |-> << >>, - ranges |-> << >>] -@@ - 8 :> [kind |-> BuiltInKind, - name |-> "$Tuple", - arity |-> -1 - ] -@@ - 9 :> [kind |-> OpApplKind, \* Arg1(a) - operands |-> <<11 >>, - operator |-> 10, - unboundedBoundSymbols |-> <<>>, - boundedBoundSymbols |-> << >>, - ranges |-> << >>] -@@ -10 :> [kind |-> ConstantDeclKind, - name |-> "Arg1", - arity |-> 1] - -@@ -11 :> [kind |-> FormalParamKind, - name |-> "a", - arity |-> 0] -@@ -12 :> [kind |-> FormalParamKind, - name |-> "b", - arity |-> 0] -@@ -13 :> [kind |-> OpApplKind, \* 1 + label2 :: p + C - operands |-> <<14, 15>>, - operator |-> 6, - unboundedBoundSymbols |-> <<>>, - boundedBoundSymbols |-> << >>, - ranges |-> << >>] -@@ -14 :> [kind |-> NumeralKind, - val |-> 1] -@@ -15 :> [kind |-> LabelKind, \* label2 :: p + C - name |-> "label2", - body |-> 16, - arity |-> 0, - params |-> <<>>, - labels |-> << >>] -@@ -16 :> [kind |-> OpApplKind, \* p + C - operands |-> <<17, 18>>, - operator |-> 6, - unboundedBoundSymbols |-> <<>>, - boundedBoundSymbols |-> << >>, - ranges |-> << >>] -@@ -17 :> [kind |-> FormalParamKind, - name |-> "p", - arity |-> 0] -@@ -18 :> [kind |-> ConstantDeclKind, - name |-> "C", - arity |-> 0 ] -@@ -19 :> [kind |-> OpApplKind, \* {1,2} - operands |-> << 14, 20 >>, - operator |-> 8, \* Actually, specifying <<1, 2>> - unboundedBoundSymbols |-> << >>, - boundedBoundSymbols |-> << >>, - ranges |-> << >>] -@@ -20 :> [kind |-> NumeralKind, - val |-> 2] -@@ -21 :> [kind |-> OpApplKind, \* {1,2} - operands |-> << 22>>, - operator |-> 8, \* Actually, specifying <<1, 2>> - unboundedBoundSymbols |-> << >>, - boundedBoundSymbols |-> << >>, - ranges |-> << >>] -@@ -22 :> [kind |-> NumeralKind, - val |-> 3] -@@ -23 :> [kind |-> UserDefinedOpKind, \* Foo(u) == Bar(Arg1) - name |-> "Foo", - body |-> 24, - labels |-> << >>, - arity |-> 1, - params |-> <<[name |-> "u", arity |-> 0]>>, - defined |-> TRUE, - source |-> null ] -@@ -24 :> [kind |-> OpApplKind, \* Bar(Arg1) - operands |-> <<34>>, - operator |-> 32, - unboundedBoundSymbols |-> <<>>, - boundedBoundSymbols |-> << >>, - ranges |-> << >>] -@@ -25 :> [kind |-> StringKind, - val |-> "string"] -@@ -26 :> [kind |-> ModuleInstanceKind, \* Inst(d) == INSTANCE M WITH C <- "expr(d)" - name |-> "Inst", - arity |-> 1, - params |-> <<[name |-> "d", arity |-> 0] >>, - defined |-> TRUE ] -@@ -27 :> [kind |-> SubstInKind, - body |-> 24, - subst |-> "expr(d)"] -@@ -28 :> [kind |-> UserDefinedOpKind, \* Foo(u) == "FooBody(u)" - name |-> "Inst!Foo", - body |-> 27, - labels |-> << >>, - arity |-> 2, - params |-> << [name |-> "d", arity |-> 0], - [name |-> "u", arity |-> 0]>>, - defined |-> TRUE, - source |-> 23 ] -@@ -29 :> [kind |-> UserDefinedOpKind, \* Op(Arg, p) == ... - name |-> "Inst!Op", - body |-> 30, - labels |-> "lab" :> 4, - arity |-> 3, - params |-> <<[name |-> "d", arity |-> 0], - [name |-> "Arg", arity |-> 0], - [name |-> "p", arity |-> 0]>>, - defined |-> TRUE, - source |-> 1 ] -@@ -30 :> [kind |-> SubstInKind, - body |-> 3, - subst |-> "expr(d)"] -@@ -31 :> [kind |-> StringKind, - val |-> "Argm"] -@@ -32 :> [kind |-> UserDefinedOpKind, \* Bar(AOp(_)) == AOp(1) - name |-> "Bar", - body |-> 33, - labels |-> << >>, - arity |-> 1, - params |-> <<[name |-> "AOp", arity |-> 1]>>, - defined |-> TRUE, - source |-> null ] -@@ -33 :> [kind |-> OpApplKind, \* Arg1(1) - operands |-> <<14>>, - operator |-> 10, - unboundedBoundSymbols |-> <<>>, - boundedBoundSymbols |-> << >>, - ranges |-> << >>] -@@ -34 :> [kind |-> OpArgKind, - op |-> 10, - arity |-> 1, - name |-> "Arg1"] -@@ -35 :> [kind |-> UserDefinedOpKind, \* Foo2 == Bar(LAMBDA m, n : <<m, n>>) - name |-> "Foo2", - body |-> 36, - labels |-> << >>, - arity |-> 0, - params |-> <<>>, - defined |-> TRUE, - source |-> null ] -@@ -36 :> [kind |-> OpApplKind, \* Bar(LAMBDA m, n : <<m, n>>) - operands |-> <<41>>, - operator |-> 32, - unboundedBoundSymbols |-> <<>>, - boundedBoundSymbols |-> << >>, - ranges |-> << >>] -@@ -37 :> [kind |-> UserDefinedOpKind, \* LAMBDA m, n : <<m, n>> - name |-> "LAMBDA", - body |-> 38, - labels |-> << >>, - arity |-> 2, - params |-> <<[name |-> "m", arity |-> 0], - [name |-> "n", arity |-> 0]>>, - defined |-> TRUE, - source |-> null ] -@@ -38 :> [kind |-> OpApplKind, \* <<m, n>> - operands |-> << 39, 40 >>, - operator |-> 8, - unboundedBoundSymbols |-> <<>>, - boundedBoundSymbols |-> << >>, - ranges |-> << >>] -@@ -39 :> [kind |-> FormalParamKind, - name |-> "m", - arity |-> 0] -@@ -40 :> [kind |-> FormalParamKind, - name |-> "n", - arity |-> 0] -@@ -41 :> [kind |-> OpArgKind, \* LAMBDA m, n : <<m, n>> - op |-> 37, - arity |-> 2, - name |-> "LAMBDA"] -@@ -42 :> [kind |-> UserDefinedOpKind, \* Inst!Foo2 == Bar(LAMBDA m, n : <<m, n>>) - name |-> "Inst!Foo2", - body |-> 43, - labels |-> << >>, - arity |-> 1, - params |-> <<[name |-> "d", arity |-> 0]>>, - defined |-> TRUE, - source |-> 35 ] -@@ -43 :> [kind |-> SubstInKind, - body |-> 36, - subst |-> "expr(d)"] -@@ -44 :> [kind |-> ThmOrAssumpDefKind, \* Thm == ASSUME Arg1(1) ... - name |-> "Thm", - body |-> 45, - labels |-> << >>, - arity |-> 0, - params |-> << >> , - defined |-> TRUE, - source |-> null ] -@@ -45 :> [kind |-> AssumeProveKind, - assumes |-> <<33, 24>>, - prove |-> 46 ] -@@ -46 :> [kind |-> OpApplKind, \* <<c, Bar(Arg1)>> - operands |-> << 59, 24 >>, - operator |-> 8, - unboundedBoundSymbols |-> <<>>, - boundedBoundSymbols |-> << >>, - ranges |-> << >>] -@@ -47 :> [kind |-> ThmOrAssumpDefKind, \* Thm == ASSUME Arg1(1) ... - name |-> "Inst!Thm", - body |-> 48, - labels |-> << >>, - arity |-> 1, - params |-> << [name |-> "d", arity |-> 0]>> , - defined |-> TRUE, - source |-> 44 ] -@@ -48 :> [kind |-> SubstInKind, - body |-> 45, - subst |-> "expr(d)"] -@@ -49 :> [kind |-> UserDefinedOpKind, \* Foo3 == \A x : LET ... - name |-> "Foo3", - body |-> 51, - labels |-> << >>, - arity |-> 0, - params |-> <<>>, - defined |-> TRUE, - source |-> null ] -@@ -50 :> [kind |-> LetInKind, \* Foo3 == \A x : LET Bar3 == 1 IN 2 - context |-> "Bar3" :> 53, - body |-> 13 ] -@@ -51 :> [kind |-> OpApplKind, \* \A x : LET Bar3 == 1 IN 2 - operands |-> << 50 >>, - operator |-> 52, - unboundedBoundSymbols |-> <<"x">>, - boundedBoundSymbols |-> << >>, - ranges |-> << >>] -@@ -52 :> [kind |-> BuiltInKind, \* \A x : - name |-> "$UnboundedForall", - arity |-> -1] -@@ -53 :> [kind |-> UserDefinedOpKind, \* Bar3 == 1 - name |-> "Bar3", - body |-> 14, - labels |-> << >>, - arity |-> 0, - params |-> <<>>, - defined |-> TRUE, - source |-> null ] -@@ -54 :> [kind |-> FormalParamKind, \* AOp - name |-> "AOp", - arity |-> 1] -@@ -55 :> [kind |-> UserDefinedOpKind, \* Bar(AOp(_)) == AOp(1) - name |-> "Inst!Bar", - body |-> 56, - labels |-> << >>, - arity |-> 2, - params |-> <<[name |-> "d", arity |-> 0], - [name |-> "AOp", arity |-> 1]>>, - defined |-> TRUE, - source |-> null ] -@@ -56 :> [kind |-> SubstInKind, - body |-> 33, - subst |-> "expr(d)"] -@@ -57 :> [kind |-> UserDefinedOpKind, \* UU == lab:: C - name |-> "UU", - body |-> 58, - labels |-> "lab":> 58, - arity |-> 0, - params |-> <<>>, - defined |-> TRUE, - source |-> null ] -@@ -58 :> [kind |-> LabelKind, \* lab :: C - name |-> "lab", - body |-> 59, - arity |-> 0, - params |-> <<>>, - labels |-> << >>] - -@@ -59 :> [kind |-> OpApplKind, \* C - operands |-> <<>>, - operator |-> 18, - unboundedBoundSymbols |-> <<>>, - boundedBoundSymbols |-> << >>, - ranges |-> << >>] -@@ -60 :> [kind |-> UserDefinedOpKind, \* UU == lab:: C - name |-> "Inst!UU", - body |-> 58, - labels |-> "lab":> 58, - arity |-> 1, - params |-> <<[name |-> "d", arity |-> 0] >>, - defined |-> TRUE, - source |-> null ] - - - MCNodeId == 1..60 - -MCGlobalContext == - "Op" :> 1 -@@ - "C" :> 18 -@@ - "UU" :> 57 -@@ - "Foo" :> 23 -@@ - "Inst" :> 26 -@@ - "Inst!Foo" :> 28 -@@ - "Inst!Bar" :> 55 -@@ - "Inst!Op" :> 29 -@@ - "Inst!UU" :> 60 -@@ - "Foo2" :> 35 -@@ - "Inst!Foo2" :> 42 -@@ - "Thm" :> 44 -@@ - "Inst!Thm" :> 47 -@@ - "Foo3" :> 49 -@@ - "Bar" :> 32 -@@ - "Arg1" :> 10 -@@ - "AOp" :> 54 -@@ - "p" :> 17 - -\* Foo -\* MCops == <<"Foo">> -\* MCargs == << << >> >> -\* MCexpectedArity == 1 - -\* Op() -\* MCops == <<"Op">> -\* MCargs == << <<>> >> -\* MCexpectedArity == 2 - -\* Op!lab -\* MCops == <<"Op", "lab">> -\* MCargs == << <<>> , <<>> >> -\* MCexpectedArity == 5 - -\* Op!lab!<< -\* MCops == <<"Op", "lab", "<<">> -\* MCargs == << <<>> , <<>> , << >> >> -\* MCexpectedArity == 5 - -\* Op!lab!<<!>> -\* MCops == <<"Op", "lab", "<<", ">>">> -\* MCargs == << << >> , << >> , << >>, << >> >> -\* MCexpectedArity == 5 - -\* Op!lab!label2 -\* MCops == <<"Op", "lab", "label2">> -\* MCargs == << << >> , << >> , << >> >> -\* MCexpectedArity == 5 - -\* Op!lab!label2!<< -\* MCops == <<"Op", "lab", "label2", "<<">> -\* MCargs == << << >> , << >> , << >>, << >> >> -\* MCexpectedArity == 5 - -\* Op!lab!:!+ -\* MCops == <<"Op", "lab", ":", "+" >> -\* MCargs == << << >> , << >> , << >> , << >> >> -\* MCexpectedArity == 7 - -\* Op!lab!:!+ -\* MCops == <<"Op", "lab", ":", "+", ">>" >> -\* MCargs == << << >> , << >> , << >> , << >>, << >> >> -\* MCexpectedArity == 7 - -\* Op!@ -\* MCops == <<"Op", "@" >> -\* MCargs == << << >> , << >> >> -\* MCexpectedArity == 5 - -\* Op!@!>>!>>!<< -\* MCops == <<"Op", "@", ">>", ">>", "<<" >> -\* MCargs == << << >> , << >>, << >>, << >>, << >> >> -\* MCexpectedArity == 5 - -\* Op!<<!>> -\* MCops == <<"Op", "<<", ">>" >> -\* MCargs == << << >>, << >>, << >> >> -\* MCexpectedArity == 2 - -\* Inst!Foo -\* MCops == <<"Inst", "Foo">> -\* MCargs == << << >>, << >> >> -\* MCexpectedArity == 2 - -\* Inst!Op -\* MCops == <<"Inst", "Op">> -\* MCargs == << << >>, << >> >> -\* MCexpectedArity == 3 - -\* Inst!Op!lab -\* MCops == <<"Inst", "Op", "lab">> -\* MCargs == << << >>, << >> , << >> >> -\* MCexpectedArity == 6 - -\* Inst!Op!lab!<< -\* MCops == <<"Inst", "Op", "lab", "<<">> -\* MCargs == << << >>, << >> , << >> , << >> >> -\* MCexpectedArity == 6 - -\* Inst!Op!lab!<<!>> -\* MCops == <<"Inst", "Op", "lab", "<<", ">>">> -\* MCargs == << << >>, << >> , << >> , << >>, << >> >> -\* MCexpectedArity == 6 - -\* Inst!Op!lab!label2 -\* MCops == <<"Inst", "Op", "lab", "label2">> -\* MCargs == << << >>, << >> , << >> , << >> >> -\* MCexpectedArity == 6 - -\* Inst!Op!lab!label2!<< -\* MCops == <<"Inst", "Op", "lab", "label2", "<<">> -\* MCargs == << << >>, << >> , << >> , << >>, << >> >> -\* MCexpectedArity == 6 - -\* Inst!Op!lab!:!+ -\* MCops == <<"Inst", "Op", "lab", ":", "+" >> -\* MCargs == << << >>, << >> , << >> , << >> , << >> >> -\* MCexpectedArity == 8 - -\* Inst!Op!lab!:!+!>> -\* MCops == <<"Inst", "Op", "lab", ":", "+", ">>" >> -\* MCargs == << << >>, << >> , << >> , << >> , << >>, << >> >> -\* MCexpectedArity == 8 - -\* Inst!Op!@ -\* MCops == <<"Inst", "Op", "@" >> -\* MCargs == << << >>, << >> , << >> >> -\* MCexpectedArity == 6 - -\* Inst!Op!@!>>!>>!<< -\* MCops == <<"Inst", "Op", "@", ">>", ">>", "<<" >> -\* MCargs == << << >>, << >> , << >>, << >>, << >>, << >> >> -\* MCexpectedArity == 6 - -\* Inst!Op!<<!>> -\* MCops == <<"Inst", "Op", "<<", ">>" >> -\* MCargs == << << >>, << >>, << >>, << >> >> -\* MCexpectedArity == 3 - -\* Foo!1 -\* MCops == <<"Foo", "1">> -\* MCargs == << << >>, << >> >> -\* MCexpectedArity == 2 - -\* Inst!Foo!1 -\* MCops == <<"Inst", "Foo", "1">> -\* MCargs == << << >>, << >>, << >> >> -\* MCexpectedArity == 3 - -\* Foo2!1 -\* MCops == <<"Foo2", "1">> -\* MCargs == << << >>, << >> >> -\* MCexpectedArity == 2 - -\* Foo2!1!(1, 2) -\* MCops == <<"Foo2", "1", "null">> -\* MCargs == << << >>, << >>, <<14, 20>> >> -\* MCexpectedArity == 0 - -\* Foo2!1!(1, 2)!2 -\* MCops == <<"Foo2", "1", "null", "2">> -\* MCargs == << << >>, << >>, <<14, 20>>, << >> >> -\* MCexpectedArity == 0 - -\* Inst!Foo2!1 -\* MCops == <<"Inst", "Foo2", "1">> -\* MCargs == << << >>, << >>, << >> >> -\* MCexpectedArity == 3 - -\* Inst!Foo2!1 -\* MCops == <<"Inst", "Foo2", "1", "@">> -\* MCargs == << << >>, << >>, << >>, << >> >> -\* MCexpectedArity == 3 - -\* Inst!Foo2!1!2 -\* MCops == <<"Inst", "Foo2", "1", "@", "2">> -\* MCargs == << << >>, << >>, << >>, << >>, << >> >> -\* MCexpectedArity == 3 - -\* Inst("Argm")!Foo2!1!(1, 2) -\* MCops == <<"Inst", "Foo2", "1", "null">> -\* MCargs == << <<31>>, << >>, << >>, <<14, 20>> >> -\* MCexpectedArity == 0 - -\* Inst("Argm")!Foo2!1!(1, 2)!2 -\* MCops == <<"Inst", "Foo2", "1", "null", "2">> -\* MCargs == << <<31>>, << >>, << >>, <<14, 20>>, << >> >> -\* MCexpectedArity == 0 - -\* Thm!1 -\* MCops == <<"Thm", "1" >> -\* MCargs == << << >>, << >> >> -\* MCexpectedArity == 0 - -\* Thm!2 -\* MCops == <<"Thm", "2" >> -\* MCargs == << << >>, << >> >> -\* MCexpectedArity == 0 - -\* Thm!3!<< -\* MCops == <<"Thm", "3", "<<" >> -\* MCargs == << << >>, << >>, << >> >> -\* MCexpectedArity == 0 - -\* Inst("Argm")!Thm!1 -\* MCops == <<"Inst", "Thm", "1" >> -\* MCargs == << <<31>>, << >>, << >> >> -\* MCexpectedArity == 0 - -\* Inst("Argm")!Thm!2 -\* MCops == <<"Inst", "Thm", "2" >> -\* MCargs == << <<31>>, << >>, << >> >> -\* MCexpectedArity == 0 - -\* Inst("Argm")!Thm!3!<< -\* MCops == <<"Inst", "Thm", "3", "<<" >> -\* MCargs == << <<31>>, << >>, << >>, << >> >> -\* MCexpectedArity == 0 - -\* Inst("Argm")!UU -\* MCops == <<"Inst", "UU">> -\* MCargs == << <<31>>, << >> >> -\* MCexpectedArity == 0 - -\* Inst("Argm")!Bar(Arg1)!1<< -\* MCops == <<"Inst", "Bar", "1" >> -\* MCargs == << <<31>>, <<10 >>, << >> >> -\* MCexpectedArity == 0 - -\* Inst("Argm")!Bar(Arg1)!1<< -\* MCops == <<"Inst", "Bar", "1" >> -\* MCargs == << <<31>>, <<10 >>, << >> >> -\* MCexpectedArity == 0 - -\* Inst("Argm")!Thm \* Note: doesn't report an error because -\* MCops == <<"Inst", "Thm" >> \* Inst!Thm isn't expanded -\* MCargs == << <<31>>, << >> >> -\* MCexpectedArity == 0 - -\* Inst("Argm")!Thm!: ERROR! -\* MCops == <<"Inst", "Thm", ":" >> -\* MCargs == << <<31>>, << >>, << >> >> -\* MCexpectedArity == 0 - - -\* Foo2 -\* MCops == <<"Foo2">> -\* MCargs == <<<< >> >> -\* MCexpectedArity == 0 - -\* Foo3!(3)!:Bar3 -\* MCops == <<"Foo3", "null", "Bar3">> -\* MCargs == << << >>, <<22>>, << >> >> -\* MCexpectedArity == 0 - -\* Bar(Arg1) -\* \* MCops == <<"Bar">> -\* \* MCargs == << <<10>> >> -\* \* MCexpectedArity == 0 - -\* Arg1(C) -\* MCops == <<"Arg1">> -\* MCargs == << <<18>> >> -\* MCexpectedArity == 0 - -\* AOp -\* MCops == <<"AOp">> -\* MCargs == << <<18 >> >> -\* MCexpectedArity == 0 - -\* C -\* MCops == <<"C">> -\* MCargs == << << >> >> -\* MCexpectedArity == 0 - -\* p -\* MCops == <<"p">> -\* MCargs == << << >> >> -\* MCexpectedArity == 0 - -\* Foo3 -\* MCops == <<"Foo3">> -\* MCargs == << << >> >> -\* MCexpectedArity == 0 - -\***** THIS IS A BUG--NOT HANDLED CORRECTLY ****** -\* UU!lab -\* MCops == <<"UU", "lab">> -\* MCargs == << << >>, << >> >> -\* MCexpectedArity == 0 - -\* Op(2, 2)!(2, 2, 2) -\* MCops == <<"Op", "null">> -\* MCargs == << <<20, 20>>, <<20, 20, 20 >> >> -\* MCexpectedArity == 0 - -\* Foo3!(2) -\* MCops == <<"Foo3", "null">> -\* MCargs == << << >>, <<20>> >> -\* MCexpectedArity == 0 - -\* Op(2, 2)!(2,2,2)!+(2, 2) -\* MCops == <<"Op", "null", "+">> -\* MCargs == << <<20,20 >>, <<20,20,20>> , <<20,20>>>> -\* MCexpectedArity == 0 - -\* MCops == <<"Op">> -\* MCargs == << << >> >> -\* MCexpectedArity == -1 - -\* MCops == <<"Inst", "Op">> -\* MCargs == << << >> , << >> >> -\* MCexpectedArity == -1 - -\* MCops == <<"Inst", "Thm">> -\* MCargs == << << >> , << >> >> -\* MCexpectedArity == -1 - -\* MCops == <<"Inst", "Op", "@", "+">> -\* MCargs == << << >> , << >> , << >> , << >> >> -\* MCexpectedArity == -1 - -\* MCops == <<"Op", "lab", ":", "+">> -\* MCargs == << << >> , << >>, << >>, << >> >> -\* MCexpectedArity == 7 - -\* MCops == <<"Op", "lab", ":", "+">> -\* MCargs == << << >> , << >>, << >>, << >> >> -\* MCexpectedArity == -1 - -\* MCops == <<"Foo2">> -\* MCargs == << << >> >> -\* MCexpectedArity == 0 - -\* MCops == <<"Foo3", ":">> -\* MCargs == << << >>, << >> >> -\* MCexpectedArity == 0 - -\* MCops == <<"Op", "lab", ":", "+">> -\* MCargs == << << >>, << >>, << >>, << >> >> -\* MCexpectedArity == -1 - -\* MCops == <<"Inst", "Thm">> -\* MCargs == << <<14 >>, << >> >> -\* MCexpectedArity == 0 - -MCops == <<"Inst", "Thm", ":">> -MCargs == << <<14 >>, << >>, << >> >> -MCexpectedArity == 0 -MCdebug == FALSE \* TRUE - -(********************************* -(***************************************************************************) -(* Module M *) -(* CONSTANT C *) -(* Op(Arg(_), p) == \A x \in {1,2}, <<y, z>> \in {3} : *) -(* lab(x,y,z) :: LET a + b == <<Arg(a), b>> *) -(* IN 1 + label2 :: p + C *) -(* *) -(* Foo(u) == "FooBody(u)" *) -(* Inst(w) == INSTANCE M WITH C <- <<w, CONST>> *) -(***************************************************************************) - -\* MCNode == -\* 1 :> [kind |-> NumeralKind, -\* val |-> 1] -\* @@ -\* 2 :> [kind |-> BuiltInKind, \* \E -\* name |-> "$UnboundedExists", -\* arity |-> -1] -\* -\* @@ -\* 3 :> [kind |-> OpApplKind, \* \E x : Node 1 -\* operands |-> <<1>>, -\* operator |-> 2, -\* unboundedBoundSymbols |-> <<"x">>, -\* boundedBoundSymbols |-> << >>, -\* ranges |-> << >>] -\* @@ -\* 4 :> [kind |-> UserDefinedOpKind, \* Inst!UserOp(p1, OpP) == Node 8 -\* name |-> "Inst!UserOp", -\* body |-> 8, -\* labels |-> "Label1" :> 7, -\* arity |-> 2, -\* params |-> <<[name |-> "p1", arity |-> 0], -\* [name |-> "OpP", arity |-> 0]>>, -\* defined |-> TRUE, -\* source |-> 40 ] -\* @@ -\* 5 :> [kind |-> ModuleInstanceKind, \* Inst(p1) == INSTANCE -\* name |-> "Inst", -\* arity |-> 1, -\* params |-> <<[name |-> "p1", arity |-> 0] >>, -\* defined |-> TRUE ] -\* @@ -\* 6 :> [kind |-> UserDefinedOpKind, \* 1ArgOp(1OpP(_, _)) == Node 1 -\* name |-> "1ArgOp", -\* body |-> 41, -\* labels |-> << >>, -\* arity |-> 1, -\* params |-> <<[name |-> "1OpP", arity |-> 0]>>, -\* defined |-> TRUE, -\* source |-> null ] -\* @@ -\* 7 :> [kind |-> LabelKind, -\* name |-> "Label1", -\* body |-> 14, -\* arity |-> 1, -\* params |-> <<[name |-> "labParam1", arity |-> 0]>>, -\* labels |-> "Label2" :> 9 -\* ] -\* @@ -\* 8 :> [kind |-> SubstInKind, -\* body |-> 11, -\* subst |-> "x <- something(p1)"] -\* -\* @@ -\* 9 :> [kind |-> LabelKind, -\* name |-> "Label2", -\* body |-> 15, -\* arity |-> 1, -\* params |-> <<[name |-> "labParam2", arity |-> 0]>>, -\* labels |-> << >> -\* ] -\* @@ -\* 10 :> [kind |-> NumeralKind, -\* val |-> 10] -\* @@ -\* 11 :> [kind |-> SubstInKind, -\* body |-> 15, -\* subst |-> "y <- something(p1)"] -\* @@ -\* 12 :> [kind |-> StringKind, -\* val |-> "Body of UserOp (p1, OpP)"] -\* @@ -\* 13 :> [kind |-> StringKind, -\* val |-> "Body of Label2(labParam2)"] -\* @@ -\* 14 :> [kind |-> StringKind, -\* val |-> "Body of Label1(labParam1)"] -\* @@ -\* 15 :> [kind |-> LetInKind, -\* context |-> "1ArgOp" :> 6, -\* body |-> 16] -\* @@ -\* 16 :> [kind |-> StringKind, -\* val |-> "Body of Let/In"] -\* @@ -\* 17 :> [kind |-> UserDefinedOpKind, \* 1ArgOp("1OpP") == Node 1 -\* name |-> "Foo", -\* body |-> 18, -\* labels |-> << >>, -\* arity |-> 0, -\* params |-> << >> , \* <<[name |-> "FooPar1", arity |-> 0]>>, -\* defined |-> TRUE, -\* source |-> null] -\* @@ -\* 18 :> [kind |-> OpApplKind, -\* operands |-> <<42 >>, -\* operator |-> 21, -\* unboundedBoundSymbols |-> <<>>, -\* boundedBoundSymbols |-> << >>, -\* ranges |-> << >>] -\* @@ -\* 19 :> [kind |-> StringKind, -\* val |-> "Arg1"] -\* @@ -\* 20 :> [kind |-> StringKind, -\* val |-> "Arg2"] -\* @@ -\* 21 :> [kind |-> UserDefinedOpKind, \* Foo2 == [rcd-lab1 |-> rcd-comp1, -\* name |-> "Foo2", \* ... -\* body |-> 34, \* rcd-lab3 |-> rcd-comp3] -\* labels |-> << >>, -\* arity |-> 1, -\* params |-> <<[name |-> "p1", arity |-> 1]>>, -\* defined |-> TRUE, -\* source |-> null ] -\* @@ -\* 22 :> [kind |-> OpApplKind, -\* operands |-> <<10 (* 25 *) , 28, 31>>, -\* operator |-> 23, -\* unboundedBoundSymbols |-> <<>>, -\* boundedBoundSymbols |-> << >>, -\* ranges |-> << >>] -\* @@ -\* 23 :> [kind |-> BuiltInKind, -\* name |-> "$Except", \* "$Case", \* "$SetOfRcds", -\* arity |-> -1 -\* ] -\* @@ -\* 24 :> [kind |-> BuiltInKind, -\* name |-> "$Pair", -\* arity |-> 2 -\* ] -\* @@ -\* 25 :> [kind |-> OpApplKind, -\* operands |-> <<26,27>>, -\* operator |-> 24, -\* unboundedBoundSymbols |-> <<>>, -\* boundedBoundSymbols |-> << >>, -\* ranges |-> << >>] -\* @@ -\* 26 :> [kind |-> StringKind, -\* val |-> "rcd-lab1"] -\* @@ -\* 27 :> [kind |-> StringKind, -\* val |-> "rcd-comp1"] -\* -\* @@ -\* 28 :> [kind |-> OpApplKind, -\* operands |-> <<29,30>>, -\* operator |-> 24, -\* unboundedBoundSymbols |-> <<>>, -\* boundedBoundSymbols |-> << >>, -\* ranges |-> << >>] -\* @@ -\* 29 :> [kind |-> StringKind, -\* val |-> "rcd-lab2"] -\* @@ -\* 30 :> [kind |-> StringKind, -\* val |-> "rcd-comp2"] -\* @@ -\* 31 :> [kind |-> OpApplKind, -\* operands |-> <<32,33>>, -\* operator |-> 24, -\* unboundedBoundSymbols |-> <<>>, -\* boundedBoundSymbols |-> << >>, -\* ranges |-> << >>] -\* @@ -\* 32 :> [kind |-> StringKind, -\* val |-> "rcd-lab3"] -\* @@ -\* 33 :> [kind |-> StringKind, -\* val |-> "rcd-comp3"] -\* @@ -\* 34 :> [kind |-> OpApplKind, -\* operands |-> <<35>>, -\* operator |-> 36, -\* unboundedBoundSymbols |-> <<>>, -\* boundedBoundSymbols |-> << <<"b1", "b2">>, <<"b3">> >>, -\* ranges |-> <<37, 38 >>] -\* @@ -\* 35 :> [kind |-> StringKind, -\* val |-> "Fcn of b1, b2, b3"] -\* @@ -\* 36 :> [kind |-> BuiltInKind, -\* name |-> "$Pair", -\* arity |-> 2 -\* ] -\* @@ -\* 37 :> [kind |-> StringKind, -\* val |-> "Range 1"] -\* @@ -\* 38 :> [kind |-> StringKind, -\* val |-> "Range 2"] -\* @@ -\* 39 :> [kind |-> StringKind, -\* val |-> "Arg3"] -\* @@ -\* 40 :> [kind |-> UserDefinedOpKind, \* Inst!UserOp(p1, OpP) == Node 8 -\* name |-> "UserOp", -\* body |-> 8, -\* labels |-> "Label1" :> 7, -\* arity |-> 1, -\* params |-> <<[name |-> "OpP", arity |-> 1]>>, -\* defined |-> TRUE, -\* source |-> null ] -\* @@ -\* 41 :> [kind |-> StringKind, -\* val |-> "Body of 1ArgOp may dep on (x, y)"] -\* @@ -\* 42 :> [kind |-> OpArgKind, -\* op |-> 43, -\* arity |-> 1, -\* name |-> "Foo3"] -\* @@ -\* 43 :> [kind |-> UserDefinedOpKind, \* 1ArgOp("1OpP") == Node 1 -\* name |-> "Foo3", -\* body |-> 18, -\* labels |-> << >>, -\* arity |-> 0, -\* params |-> << >>, \* <<[name |-> "FooPar1", arity |-> 0]>>, -\* defined |-> TRUE, -\* source |-> null] -\* -\* MCNodeId == 1..43 -\* -\* MCGlobalContext == -\* "Inst!UserOp" :> 4 -\* @@ -\* "Inst" :> 5 -\* @@ -\* "Foo" :> 17 -\* @@ -\* "Foo2" :> 21 -\* @@ -\* "Foo3" :> 43 -\* \* @@ -\* \* "1ArgOp" :> 6 -\* -\* -\* MCops == <<"Inst", "UserOp">> \* , "1">> -\* MCargs == << <<1 >> , <<1>> >> - -\* \* << <<1>>, <<19, 20, 33 >> >> \* <<1>>, <<10>>, << >>, <<4>> >> -\* -\* MCexpectedArity == 0 \* -1 \* 5 -***************************) - ------------------------------------------------------------------------------ -(***************************************************************************) -(* THE ALGORITHM *) -(***************************************************************************) -(************************************** ---algorithm Subexpression -variables - (*************************************************************************) - (* The input variables. *) - (*************************************************************************) - ops = MCops, - args = MCargs, - (***********************************************************************) - (* The sequences op and args described above. *) - (***********************************************************************) - expectedArity = MCexpectedArity, - (***********************************************************************) - (* The arity of the operator expected, or -1 if expecting a definition *) - (* name. It is 0 iff an expression is expected. The result should be *) - (* an OpArg node if expectedArity > 0, it should be an expression node *) - (* if expectedArity = 0, and it should be a UserDefinedOpKind or a *) - (* ThmOrAssumpDefKind if expectedArity < 0. *) - (***********************************************************************) - - (*************************************************************************) - (* The major variables of the algorithm. *) - (*************************************************************************) - substInPrefix = << >> , - (***********************************************************************) - (* The sequence of SubstInNode or APSubstInNode sequence that will be *) - (* appended to body of the resulting OpApplNode *) - (***********************************************************************) - params = << >> , - (***********************************************************************) - (* The sequence of FormalParamNode objects that are the formal *) - (* parameters if this produces a Lambda expression. A Lambda *) - (* expression will be produced iff this is non-empty *) - (***********************************************************************) - allArgs = << >> , - (***********************************************************************) - (* The sequence of all arguments. *) - (***********************************************************************) - curNode = null, - (***********************************************************************) - (* If params = << >>, then the OpDefNode for the OpApplNode that is *) - (* produced. If params # << >>, then the Expr node that will form the *) - (* body of the Lambda expression. *) - (* *) - (* Note: in the Java implementation, this will actually be a ref to *) - (* the node--in terms of this spec, a NodeId. *) - (***********************************************************************) - subExprOf = "null", - (***********************************************************************) - (* The node UserDefinedOpDefKind or ThmOrAssumpDefKind within which *) - (* this subexpression is defined. *) - (***********************************************************************) - result = null, - (***********************************************************************) - (* The actual output node. *) - (***********************************************************************) - - (*************************************************************************) - (* The local variables. *) - (*************************************************************************) - idx = 1, - (***********************************************************************) - (* The element of the arrays op and args that the algorithm is *) - (* currently examining. *) - (***********************************************************************) - mode = "FindingOpName", - (***********************************************************************) - (* The current mode describing what kind of selector it is expecting *) - (* next. Its other possible values are "FollowingLabels" and *) - (* "FindingSubExpr". *) - (***********************************************************************) - prevMode = "", - (***********************************************************************) - (* The mode for the previously examined selector. *) - (***********************************************************************) - curContext = GlobalContext, - (***********************************************************************) - (* The context for looking up operator or label names. *) - (***********************************************************************) - curName = "" , - (***********************************************************************) - (* When looking up an operator name, which may be something like *) - (* "Foo!Bar!Baz", this is the part that has been found so far. *) - (***********************************************************************) - opDefArityFound = 0, - opDefArgs = << >>, - (***********************************************************************) - (* The total arity and the sequence of arguments found so far for the *) - (* current operator--for example, opDefArityFound will equal 2 if the *) - (* algorithm has so far processed "Foo(a, b)!Bar" *) - (***********************************************************************) - firstFindingOpName = TRUE, - (***********************************************************************) - (* True iff have entered the FindingOpName only once (initially). *) - (***********************************************************************) - opNode, - newName, - newNode, - nodeArity, - temp, - tempArgs - - -(***************************************************************************) -(* A macro used for reporting an error and terminating the execution. *) -(***************************************************************************) -macro Error(msg) begin print msg ; - if debug then (**************************************) - (* Force the +cal translator to *) - (* insert a label so the error trace *) - (* shows the final state. *) - (**************************************) - idx := idx ; - idx := idx ; - assert FALSE - else goto Done - end if - end macro ; - - begin - - (*************************************************************************) - (* An assertion that checks that ops and args are consistent with each *) - (* other and with the value of expectedArity. In an implementation, *) - (* some of these should be guaranteed by the context (e.g., Len(ops) = *) - (* Len(args) while others will be checked as part of the processing for *) - (* the particular value of idx. *) - (*************************************************************************) - assert /\ expectedArity \in Int - /\ ops \in Seq(STRING) - /\ args \in Seq(Seq(NodeId)) - /\ Len(ops) = Len(args) - /\ \A i \in 1..Len(ops) : - /\ \/ ops[i] \in {"<<", ">>", "@", ":"} \cup NumberOp - \/ expectedArity # 0 - => args[i] = << >> - /\ (ops[i] = "null") => (Len(args[i]) > 0) ; - - (*************************************************************************) - (* The outer "for" loop on idx. *) - (*************************************************************************) - while idx <= Len(ops) do - if mode = "FindingOpName" then - -\* The following code was modified by LL on 23 Sep 2009 to fix the following -\* bug. The parser produced a bogus error on a reference to an identifier -\* M!N that is imported by an unnamed INSTANCE of a module containing the -\* statement M == INSTANCE ... . The original code essentially just -\* contained the first iteration of the following while loop, and would -\* report an error if it found newNode = null. Thus, it would report -\* an error in the identifier M!N when it found M to be undefined. -\* -\* Corrected by LL on 9 Nov 2009 to handle arguments of those -\* undefined operators. They are put into tempArgs when they are -\* found. - - newNode := null ; - tempArgs := << >> ; - (**********************************************************************) - (* The number of arguments found in the following loop for the *) - (* undefined operators *) - (**********************************************************************) - while newNode = null do - if idx \geq Len(ops) - then Error ("Unknown operator"); - end if; - newName := IF IsName(ops[idx]) - THEN IF curName = "" THEN ops[idx] - ELSE curName \o "!" \o ops[idx] - ELSE null ; - if /\ curName = "" - /\ ~ IsName(ops[idx]) - then (***************************************************************) - (* I think that this error can only happen for idx = 1, and *) - (* should never happen in the implementation because the *) - (* parser should not allow a compound name that doesn't begi *) - (* with a name. *) - (***************************************************************) - Error("Need an operator or label name or step number here") - end if; - newNode := IF newName # null THEN LookUp(newName, curContext) ELSE null ; - if newName = null - then tempArgs := tempArgs \o args[idx]; - idx := idx + 1; - end if; - end while ; - curNode := newNode ; - curName := newName ; - if curNode.kind \in - {UserDefinedOpKind, ThmOrAssumpDefKind, - ModuleInstanceKind, ConstantDeclKind, VariableDeclKind, - FormalParamKind, BuiltInKind, BoundSymbolKind} - then if curNode.kind \in - {ConstantDeclKind, VariableDeclKind, FormalParamKind, - BuiltInKind, BoundSymbolKind} - then if idx # 1 - then Error("Abort: Impossible naming of declaration " - \o "or built-in operator.") - else if Len(ops) # 1 - then Error("Can't take subexpression of this"); - end if - end if - end if ; - nodeArity := Arity(curNode) ; - tempArgs := tempArgs \o args[idx]; - if expectedArity = 0 - then if opDefArityFound + Len(tempArgs) # nodeArity - then Error("Wrong number of arguments") - end if ; - if \E i \in 1..Len(tempArgs[idx]) : - Arity(Node[tempArgs[idx][i]]) # - ParamArity(curNode, i + opDefArityFound) - then Error("Argument has wrong arity") - else opDefArgs := opDefArgs \o tempArgs; - end if ; - else if expectedArity > 0 - then if \E i \in 1..Len(curNode.params) : - curNode.params[i].arity > 0 - then Error("Higher-order operator selected " - \o "as operator argument") - end if -\* else Error( -\* "Abort: Don't yet handle expectedArity < 0") - end if - end if ; - opDefArityFound := nodeArity ; - if curNode.kind \in {UserDefinedOpKind, ThmOrAssumpDefKind, - ConstantDeclKind, VariableDeclKind, - FormalParamKind, BoundSymbolKind} - then if /\ curNode.kind = UserDefinedOpKind - /\ ~ curNode.defined - /\ ~ Len(ops) = 1 - then Error("Can't refer to subexpression of " - \o "operator inside its definition") - end if; - if /\ firstFindingOpName - /\ curNode.kind \in {UserDefinedOpKind, ThmOrAssumpDefKind} - then subExprOf := curNode - end if; - if idx # Len(ops) - then params := params \o curNode.params ; - curName := "" ; - if IsName(ops[idx+1]) - then mode := "FollowingLabels" - else mode := "FindingSubExpr" ; - end if ; - allArgs := allArgs \o opDefArgs ; - opDefArityFound := 0 ; - opDefArgs := << >> ; - newNode := Node[curNode.body] ; - while newNode.kind = SubstInKind do - substInPrefix := substInPrefix \o <<newNode>>; - newNode := Node[newNode.body]; - end while ; - while newNode.kind \in {SubstInKind, APSubstInKind} - (************************************************) - (* Added APSubstInKind test on 13 Nov 2009. *) - (************************************************) - do - substInPrefix := substInPrefix \o <<newNode>>; - newNode := Node[newNode.body]; - end while ; - (********************************************) - (* If the next op is an OpSel, then need to *) - (* skip over SubstInNodes, which are *) - (* invisible to the user. *) - (********************************************) - if mode = "FindingSubExpr" - then curNode := newNode - end if; - end if ; - else \* curNode.kind = ModuleInstanceKind - if (idx = Len(ops)) - then Error("Operator name incomplete") - end if - end if - else Error("Unexpected node kind") ; - end if ; - prevMode := "FindingOpName" ; - - - elsif mode = "FollowingLabels" then - if prevMode = "FindingOpName" - then assert curNode.kind \in {UserDefinedOpKind, ThmOrAssumpDefKind} ; - newNode := LookUp(ops[idx], curNode.labels) ; - else assert curNode.kind = LabelKind ; - newNode := LookUp(ops[idx], curNode.labels) ; - end if ; - if newNode = null - then Error("Label not found") - end if; - curNode := newNode ; - if expectedArity = 0 - then if Len(args[idx]) # curNode.arity - then Error("bad arity") - end if ; - if \E i \in 1..Len(args[idx]) : - Arity(Node[args[idx][i]]) # 0 - then Error("Operator argument given to label") - end if; - allArgs := allArgs \o args[idx] ; - end if; - params := params \o curNode.params ; - if /\ idx < Len(ops) - /\ ~IsName(ops[idx+1]) - then mode := "FindingSubExpr" - end if ; - if \/ mode = "FindingSubExpr" - \/ idx = Len(ops) - then curNode := Node[curNode.body] - end if; - prevMode := "FollowingLabels"; - - - elsif mode = "FindingSubExpr" then - if ops[idx] = ":" then - if \/ prevMode \notin {"FindingOpName", "FollowingLabels"} - \/ ~ \/ /\ idx = Len(ops) - /\ prevMode = "FindingOpName" - \/ /\ idx < Len(ops) - /\ IsName(ops[idx+1]) - then Error("`!:' can be used only after a name and either at the " \o - "end after an operator name or before an operator name.") - end if - - elsif curNode.kind = LetInKind then - if ArgNum(ops[idx], 1) = 1 - then curNode := Node[curNode.body] - else Error("A Let/In has only a single operand") - end if ; - - elsif curNode.kind = OpApplKind then - opNode := Node[curNode.operator] ; - if opNode.kind - \in {FormalParamKind, ConstantDeclKind, UserDefinedOpKind} then - (******************************************************************) - (* Selecting an argument from something of the form Op(...). *) - (******************************************************************) - temp := ArgNum(ops[idx], opNode.arity) ; - if temp = -1 - then Error("Nonexistent operand specified") - else curNode := Node[curNode.operands[temp]] - end if - elsif opNode.kind = BuiltInKind then - \* See configuration/ConfigConstants for a list of all - \* BuiltInKind operators. - if opNode.name \in {"$RcdConstructor", "$SetOfRcds"} then - (*****************************************************************) - (* curNode represents an expression *) - (* [a_1 |-> e_1, ... , a_n |-> e_n] or *) - (* [a_1 : e_1, ... , a_n : e_n] *) - (* Its i-th argument is the $Pair node (a_i, e_i) *) - (*****************************************************************) - temp := ArgNum(ops[idx], Len(curNode.operands)) ; - if temp = -1 - then Error("Incorrect subexpression number") - end if ; - curNode := Node[curNode.operands[temp]] ; - if \/ curNode.kind # OpApplKind - \/ Node[curNode.operator].kind # BuiltInKind - \/ Node[curNode.operator].name # "$Pair" - then Error ("Expecting $Pair(...)") - else curNode := Node[curNode.operands[2]] - end if - - elsif opNode.name \in {"$Case"} then - (*****************************************************************) - (* The i-th clause is a $Pair node, where for the OTHER clause *) - (* the 1st element is null. *) - (*****************************************************************) - if idx = Len(ops) - then Error( "CASE subexpression must be of form !i!j") - end if ; - temp := ArgNum( ops[idx], Len(curNode.operands)) ; - if temp = -1 - then Error("Incorrect subexpression name") - end if ; - curNode := Node[curNode.operands[temp]] ; - if \/ curNode.kind # OpApplKind - \/ Node[curNode.operator].kind # BuiltInKind - \/ Node[curNode.operator].name # "$Pair" - then Error ("Expecting $Pair(...)") - end if ; - idx := idx+1 ; - temp := ArgNum(ops[idx], 2); - if temp = -1 - then Error("Incorrect subexpression name") - end if ; - curNode := Node[curNode.operands[temp]] ; - if curNode = null - then Error("Selecting OTHER") - end if - - elsif opNode.name = "$Except" then - (****************************************************************) - (* For *) - (* [exp_1 ELSE !... = exp_2, ... , !.. = exp_n] *) - (* argument number i chooses exp_i. For i > 1, exp_i is the *) - (* second argument of a $Pair operator. *) - (****************************************************************) - temp := ArgNum(ops[idx], Len(curNode.operands)); - if temp = -1 - then Error("Bad argument selector.") - end if; - curNode := Node[curNode.operands[temp]] ; - if temp > 1 - then if \/ curNode.kind # OpApplKind - \/ Node[curNode.operator].kind # BuiltInKind - \/ Node[curNode.operator].name # "$Pair" - then Error("Unexpected expression node found.") - else curNode := Node[curNode.operands[2]] - end if - end if - - else - (*****************************************************************) - (* Operator handled by standard procedure. *) - (*****************************************************************) - if /\ Len(curNode.unboundedBoundSymbols) = 0 - /\ Len(curNode.boundedBoundSymbols) = 0 - then (**********************************************************) - (* Current subexpression has no bound variables. *) - (**********************************************************) - temp := ArgNum(ops[idx], Len(curNode.operands)) ; - if temp = -1 - then Error("Incorrect subexpression selector") - end if; - curNode := Node[curNode.operands[temp]] ; - else - (**********************************************************) - (* Current subexpression has bound variables. If *) - (* selector is "@" or null, then choosing body and adding *) - (* parameters. Otherwise, must be selecting one of the *) - (* bounds. *) - (**********************************************************) - if ops[idx] \in {"null", "@"} - then (***************************************************) - (* Set temp to the sequence of parameters. *) - (***************************************************) - temp := IF Len(curNode.unboundedBoundSymbols) > 0 - THEN curNode.unboundedBoundSymbols - ELSE SeqSeqToSeq( - curNode.boundedBoundSymbols); - - params := params \o - [i \in 1.. Len(temp) |-> - [name |-> temp[i], arity |-> 0]] ; - allArgs := allArgs \o args[idx] ; - if /\ ops[idx] = "null" - /\ Len(args[idx]) # Len(temp) - then Error("Wrong number of selector arguments"); - end if; - curNode := Node[curNode.operands[1]]; - else temp := ArgNum(ops[idx], Len(curNode.ranges)) ; - if temp = -1 - then Error ("Selecting non-existent range") ; - else curNode := Node[curNode.ranges[temp]] - end if - end if - end if - end if \* opNode.name = ... - - else Error("Applying subexpression chooser to an expr" \o - " with no choosable subexpressions") - - end if \* opNode.kind = ... - - elsif curNode.kind = AssumeProveKind then - temp := ArgNum(ops[idx], 1 + Len(curNode.assumes)) ; - if temp = -1 - then Error("Illegal argument number") - else if temp <= Len(curNode.assumes) - then curNode := Node[curNode.assumes[temp]] - else curNode := Node[curNode.prove] - end if - end if - - elsif curNode.kind = OpArgKind then - (*********************************************************************) - (* The only kind of OpArgNode that has a subpart is a Lambda *) - (* expression. *) - (*********************************************************************) - opNode := Node[curNode.op] ; - if \/ opNode.kind # UserDefinedOpKind - \/ opNode.name # "LAMBDA" then - Error("Selecting from operator argument that has no sub-part") - elsif ops[idx] \notin {"null", "@"} then - Error("Incorrect selection from LAMBDA") - elsif /\ ops[idx] = "null" - /\ Len(args[idx]) # Len(opNode.params) then - Error("Incorrect number of arguments for LAMBDA") - else params := params \o opNode.params ; - allArgs := allArgs \o args[idx] ; - curNode := Node[opNode.body] ; - end if - - elsif curNode.kind \in {UserDefinedOpKind, BuiltInKind} then - Error("Abort: should not have been able to choose this node.") - - elsif curNode.kind \in {AtNodeKind, DecimalKind, NumeralKind, StringKind, - FormalParamKind, ConstantDeclKind, VariableDeclKind, - BoundSymbolKind} then - Error("Selected part has no subexpression") - - elsif curNode.kind = LabelKind then - (*********************************************************************) - (* Skip over label. *) - (*********************************************************************) - curNode := Node[curNode.body] ; - idx := idx - 1 ; - else - Error("Unexpected node kind found in expression") - - end if; \* ops[idx] = ":" - - if idx # Len(ops) - then if IsName(ops[idx+1]) - then while curNode.kind = LabelKind do - curNode := Node[curNode.body] - end while ; - if curNode.kind = LetInKind - then curContext := curNode.context ; - mode := "FindingOpName" ; - firstFindingOpName := FALSE; - else Error("A name not following the selector of a LET") - end if - else if ops[idx+1] = ":" - then Error("!: should not follow an operand selector") - end if ; - end if ; - end if ; \* ops[idx] = ":" - prevMode := "FindingSubExpr"; - - else Error("Bad value of mode") - - end if ; \* mode = ... - - idx := idx + 1; - end while ; - - if curNode.kind = AssumeProveKind - then Error("Selecting ASSUME/PROVE instead of expression") - end if; - - if expectedArity < 0 - then if \/ prevMode # "FindingOpName" - \/ curNode.kind \notin {UserDefinedOpKind, ThmOrAssumpDefKind} - then Error("Should have selected a definition, but didn't.") - end if; - result := curNode ; - goto Finished - end if; - - if expectedArity > 0 - then temp := Len(params) + IF \/ prevMode = "FindingOpName" - \/ curNode.kind = OpArgKind - THEN Arity(curNode) - ELSE 0 ; - if expectedArity # temp - then Error("Expect arity = " \o ToString(expectedArity) - \o ", but found arity = " \o ToString(temp)) - end if - end if; - - - (*************************************************************************) - (* If found an operator def and there are parameters or substitutions, *) - (* then set curNode to the operator applied to new parameters, add those *) - (* parameters to params and add the arguments to allArgs. Note: need to *) - (* do this even if operator takes no parameters because we need to put *) - (* the operator in a LAMBDA expression whose body is an expression *) - (*************************************************************************) - if /\ prevMode = "FindingOpName" - /\ Len(params) + Len(substInPrefix) > 0 - then temp := [i \in 1..Len(curNode.params) |-> - [curNode.params[i] EXCEPT !.name = "New " \o @]] ; - (****************************************************************) - (* temp := new formal parameters for the operator, with the *) - (* same arities as the original parameters. *) - (****************************************************************) - - curNode := [kind |-> OpApplKind, - operands |-> temp, - operator |-> curNode, - unboundedBoundSymbols |-> <<>>, - boundedBoundSymbols |-> << >>, - ranges |-> << >>] ; - params := params \o temp ; - allArgs := allArgs \o opDefArgs ; - end if ; - - if curNode.kind = OpArgKind - then if expectedArity = 0 then - Error("Selected Operator Argument when expression expected.") - elsif expectedArity # Len(params) + curNode.arity then - Error("Selected operator has wrong arity.") - else - if Len(params) + Len(substInPrefix) > 0 - then (********************************************************) - (* If curNode is a LAMBDA, then this will eventually *) - (* produce an OpArg node whose operator is a LAMBDA *) - (* whose body is an OpApplNode that applies the *) - (* curNode's LAMBDA to parameters of the outer LAMBDA. *) - (* This result can be simplified to a LAMBDA whose body *) - (* is the body of curNode. However, that *) - (* simplification is what one will get by selecting the *) - (* body of the LAMBDA, so we keep this complicated *) - (* expression in this case. *) - (********************************************************) - temp := [i \in 1..curNode.arity |-> - [name |-> "NewParam" \o NumToString(i), - arity |-> 0]] ; - curNode := [kind |-> OpApplKind, - operands |-> temp, - operator |-> Node[curNode.op], - unboundedBoundSymbols |-> <<>>, - boundedBoundSymbols |-> << >>, - ranges |-> << >>] ; - params := params \o temp - end if - end if - end if ; - - if curNode.kind \in {UserDefinedOpKind, ConstantDeclKind, VariableDeclKind, - FormalParamKind, BuiltInKind, BoundSymbolKind, - ThmOrAssumpDefKind} then - (***********************************************************************) - (* There are no params or substitutions, so this is an easy case. *) - (***********************************************************************) - if expectedArity > 0 then - result := [kind |-> OpArgKind, - name |-> curNode.name, - op |-> curNode, - arity |-> expectedArity] - elsif expectedArity = 0 then - result := [kind |-> OpApplKind, - operands |-> opDefArgs, - operator |-> curNode, - unboundedBoundSymbols |-> <<>>, - boundedBoundSymbols |-> << >>, - ranges |-> << >>, - subExprOf |-> subExprOf] - end if - elsif curNode.kind = OpArgKind then - (***********************************************************************) - (* There are no params or substitutions, so this is an easy case. *) - (***********************************************************************) - result := curNode ; - else (******************************************************************) - (* curNode should be an expression node. *) - (******************************************************************) - temp := Len(substInPrefix) ; - while temp > 0 do - curNode := [kind |-> substInPrefix[temp].kind, - (********************************************) - (* Changed on 13 Nov 2009 from SubstInKind. *) - (********************************************) - body |-> curNode , - subst |-> substInPrefix[temp].subst ] ; - temp := temp - 1; - end while; - - if expectedArity > 0 - then if Len(params) # expectedArity - then Error ("Selection has wrong arity") - end if ; - result := [kind |-> OpArgKind, - op |-> [kind |-> UserDefinedOpKind, - name |-> "LAMBDA", - body |-> curNode, - params |-> params, - arity |-> Len(params), - defined |-> TRUE, - source |-> null], - name |-> "LAMBDA"] - else if Len(params) # Len(allArgs) - then Error("Abort: number of params # num of args") - end if ; - if Len(params) = 0 - then (******************************************************) - (* This is the one case with expectedArity = 0 in *) - (* which the result is not a newly-constructed node. *) - (* In this case, we construct a dummy label node so *) - (* we have a node to which the implementation can *) - (* attach the syntax node. *) - (******************************************************) - result := [kind |-> LabelKind, - name |-> "$Subexpression", - arity |-> 0, - params |-> << >>, - body |-> curNode, - subExprOf |-> subExprOf] - - else result := [kind |-> OpApplKind, - operator |-> [kind |-> UserDefinedOpKind, - name |-> "LAMBDA", - body |-> curNode, - params |-> params, - arity |-> Len(params), - defined |-> TRUE, - source |-> null], - operands |-> allArgs, - unboundedBoundSymbols |-> <<>>, - boundedBoundSymbols |-> << >>, - ranges |-> << >>, - subExprOf |-> subExprOf] - end if - end if - - end if; - -Finished: - print "Result: " ; - print result ; - -end algorithm -*********************************************************************) -\* BEGIN TRANSLATION -CONSTANT defaultInitValue -VARIABLES ops, args, expectedArity, substInPrefix, params, allArgs, curNode, - subExprOf, result, idx, mode, prevMode, curContext, curName, - opDefArityFound, opDefArgs, firstFindingOpName, opNode, newName, - newNode, nodeArity, temp, tempArgs, pc - -vars == << ops, args, expectedArity, substInPrefix, params, allArgs, curNode, - subExprOf, result, idx, mode, prevMode, curContext, curName, - opDefArityFound, opDefArgs, firstFindingOpName, opNode, newName, - newNode, nodeArity, temp, tempArgs, pc >> - -Init == (* Global variables *) - /\ ops = MCops - /\ args = MCargs - /\ expectedArity = MCexpectedArity - /\ substInPrefix = << >> - /\ params = << >> - /\ allArgs = << >> - /\ curNode = null - /\ subExprOf = "null" - /\ result = null - /\ idx = 1 - /\ mode = "FindingOpName" - /\ prevMode = "" - /\ curContext = GlobalContext - /\ curName = "" - /\ opDefArityFound = 0 - /\ opDefArgs = << >> - /\ firstFindingOpName = TRUE - /\ opNode = defaultInitValue - /\ newName = defaultInitValue - /\ newNode = defaultInitValue - /\ nodeArity = defaultInitValue - /\ temp = defaultInitValue - /\ tempArgs = defaultInitValue - /\ pc = "Lbl_1" - -Lbl_1 == /\ pc = "Lbl_1" - /\ Assert(/\ expectedArity \in Int - /\ ops \in Seq(STRING) - /\ args \in Seq(Seq(NodeId)) - /\ Len(ops) = Len(args) - /\ \A i \in 1..Len(ops) : - /\ \/ ops[i] \in {"<<", ">>", "@", ":"} \cup NumberOp - \/ expectedArity # 0 - => args[i] = << >> - /\ (ops[i] = "null") => (Len(args[i]) > 0), - "Failure of assertion at line line 1906, column 3.") - /\ pc' = "Lbl_2" - /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, - allArgs, curNode, subExprOf, result, idx, mode, - prevMode, curContext, curName, opDefArityFound, - opDefArgs, firstFindingOpName, opNode, newName, - newNode, nodeArity, temp, tempArgs >> - -Lbl_2 == /\ pc = "Lbl_2" - /\ IF idx <= Len(ops) - THEN /\ IF mode = "FindingOpName" - THEN /\ newNode' = null - /\ tempArgs' = << >> - /\ pc' = "Lbl_3" - /\ UNCHANGED << params, allArgs, curNode, idx, - opNode, temp >> - ELSE /\ IF mode = "FollowingLabels" - THEN /\ IF prevMode = "FindingOpName" - THEN /\ Assert(curNode.kind \in {UserDefinedOpKind, ThmOrAssumpDefKind}, - "Failure of assertion at line line 2053, column 13.") - /\ newNode' = LookUp(ops[idx], curNode.labels) - ELSE /\ Assert(curNode.kind = LabelKind, - "Failure of assertion at line line 2055, column 13.") - /\ newNode' = LookUp(ops[idx], curNode.labels) - /\ IF newNode' = null - THEN /\ PrintT("Label not found") - /\ IF debug - THEN /\ idx' = idx - /\ pc' = "Lbl_22" - ELSE /\ pc' = "Done" - /\ UNCHANGED idx - ELSE /\ pc' = "Lbl_23" - /\ UNCHANGED idx - /\ UNCHANGED << params, allArgs, - curNode, opNode, - temp >> - ELSE /\ IF mode = "FindingSubExpr" - THEN /\ IF ops[idx] = ":" - THEN /\ IF \/ prevMode \notin {"FindingOpName", "FollowingLabels"} - \/ ~ \/ /\ idx = Len(ops) - /\ prevMode = "FindingOpName" - \/ /\ idx < Len(ops) - /\ IsName(ops[idx+1]) - THEN /\ PrintT("`!:' can be used only after a name and either at the " \o - "end after an operator name or before an operator name.") - /\ IF debug - THEN /\ idx' = idx - /\ pc' = "Lbl_29" - ELSE /\ pc' = "Done" - /\ UNCHANGED idx - ELSE /\ pc' = "Lbl_63" - /\ UNCHANGED idx - /\ UNCHANGED << params, - allArgs, - curNode, - opNode, - temp >> - ELSE /\ IF curNode.kind = LetInKind - THEN /\ IF ArgNum(ops[idx], 1) = 1 - THEN /\ curNode' = Node[curNode.body] - /\ pc' = "Lbl_63" - /\ UNCHANGED idx - ELSE /\ PrintT("A Let/In has only a single operand") - /\ IF debug - THEN /\ idx' = idx - /\ pc' = "Lbl_30" - ELSE /\ pc' = "Done" - /\ UNCHANGED idx - /\ UNCHANGED curNode - /\ UNCHANGED << params, - allArgs, - opNode, - temp >> - ELSE /\ IF curNode.kind = OpApplKind - THEN /\ opNode' = Node[curNode.operator] - /\ IF opNode'.kind - \in {FormalParamKind, ConstantDeclKind, UserDefinedOpKind} - THEN /\ temp' = ArgNum(ops[idx], opNode'.arity) - /\ IF temp' = -1 - THEN /\ PrintT("Nonexistent operand specified") - /\ IF debug - THEN /\ idx' = idx - /\ pc' = "Lbl_31" - ELSE /\ pc' = "Done" - /\ UNCHANGED idx - /\ UNCHANGED curNode - ELSE /\ curNode' = Node[curNode.operands[temp']] - /\ pc' = "Lbl_63" - /\ UNCHANGED idx - /\ UNCHANGED << params, - allArgs >> - ELSE /\ IF opNode'.kind = BuiltInKind - THEN /\ IF opNode'.name \in {"$RcdConstructor", "$SetOfRcds"} - THEN /\ temp' = ArgNum(ops[idx], Len(curNode.operands)) - /\ IF temp' = -1 - THEN /\ PrintT("Incorrect subexpression number") - /\ IF debug - THEN /\ idx' = idx - /\ pc' = "Lbl_32" - ELSE /\ pc' = "Done" - /\ UNCHANGED idx - ELSE /\ pc' = "Lbl_33" - /\ UNCHANGED idx - /\ UNCHANGED << params, - allArgs, - curNode >> - ELSE /\ IF opNode'.name \in {"$Case"} - THEN /\ IF idx = Len(ops) - THEN /\ PrintT("CASE subexpression must be of form !i!j") - /\ IF debug - THEN /\ idx' = idx - /\ pc' = "Lbl_36" - ELSE /\ pc' = "Done" - /\ UNCHANGED idx - ELSE /\ pc' = "Lbl_37" - /\ UNCHANGED idx - /\ UNCHANGED << params, - allArgs, - curNode, - temp >> - ELSE /\ IF opNode'.name = "$Except" - THEN /\ temp' = ArgNum(ops[idx], Len(curNode.operands)) - /\ IF temp' = -1 - THEN /\ PrintT("Bad argument selector.") - /\ IF debug - THEN /\ idx' = idx - /\ pc' = "Lbl_46" - ELSE /\ pc' = "Done" - /\ UNCHANGED idx - ELSE /\ pc' = "Lbl_47" - /\ UNCHANGED idx - /\ UNCHANGED << params, - allArgs, - curNode >> - ELSE /\ IF /\ Len(curNode.unboundedBoundSymbols) = 0 - /\ Len(curNode.boundedBoundSymbols) = 0 - THEN /\ temp' = ArgNum(ops[idx], Len(curNode.operands)) - /\ IF temp' = -1 - THEN /\ PrintT("Incorrect subexpression selector") - /\ IF debug - THEN /\ idx' = idx - /\ pc' = "Lbl_50" - ELSE /\ pc' = "Done" - /\ UNCHANGED idx - ELSE /\ pc' = "Lbl_51" - /\ UNCHANGED idx - /\ UNCHANGED << params, - allArgs, - curNode >> - ELSE /\ IF ops[idx] \in {"null", "@"} - THEN /\ temp' = (IF Len(curNode.unboundedBoundSymbols) > 0 - THEN curNode.unboundedBoundSymbols - ELSE SeqSeqToSeq( - curNode.boundedBoundSymbols)) - /\ params' = params \o - [i \in 1.. Len(temp') |-> - [name |-> temp'[i], arity |-> 0]] - /\ allArgs' = allArgs \o args[idx] - /\ IF /\ ops[idx] = "null" - /\ Len(args[idx]) # Len(temp') - THEN /\ PrintT("Wrong number of selector arguments") - /\ IF debug - THEN /\ idx' = idx - /\ pc' = "Lbl_52" - ELSE /\ pc' = "Done" - /\ UNCHANGED idx - ELSE /\ pc' = "Lbl_53" - /\ UNCHANGED idx - /\ UNCHANGED curNode - ELSE /\ temp' = ArgNum(ops[idx], Len(curNode.ranges)) - /\ IF temp' = -1 - THEN /\ PrintT("Selecting non-existent range") - /\ IF debug - THEN /\ idx' = idx - /\ pc' = "Lbl_54" - ELSE /\ pc' = "Done" - /\ UNCHANGED idx - /\ UNCHANGED curNode - ELSE /\ curNode' = Node[curNode.ranges[temp']] - /\ pc' = "Lbl_63" - /\ UNCHANGED idx - /\ UNCHANGED << params, - allArgs >> - ELSE /\ PrintT("Applying subexpression chooser to an expr" \o - " with no choosable subexpressions") - /\ IF debug - THEN /\ idx' = idx - /\ pc' = "Lbl_55" - ELSE /\ pc' = "Done" - /\ UNCHANGED idx - /\ UNCHANGED << params, - allArgs, - curNode, - temp >> - ELSE /\ IF curNode.kind = AssumeProveKind - THEN /\ temp' = ArgNum(ops[idx], 1 + Len(curNode.assumes)) - /\ IF temp' = -1 - THEN /\ PrintT("Illegal argument number") - /\ IF debug - THEN /\ idx' = idx - /\ pc' = "Lbl_56" - ELSE /\ pc' = "Done" - /\ UNCHANGED idx - /\ UNCHANGED curNode - ELSE /\ IF temp' <= Len(curNode.assumes) - THEN /\ curNode' = Node[curNode.assumes[temp']] - ELSE /\ curNode' = Node[curNode.prove] - /\ pc' = "Lbl_63" - /\ UNCHANGED idx - /\ UNCHANGED << params, - allArgs, - opNode >> - ELSE /\ IF curNode.kind = OpArgKind - THEN /\ opNode' = Node[curNode.op] - /\ IF \/ opNode'.kind # UserDefinedOpKind - \/ opNode'.name # "LAMBDA" - THEN /\ PrintT("Selecting from operator argument that has no sub-part") - /\ IF debug - THEN /\ idx' = idx - /\ pc' = "Lbl_57" - ELSE /\ pc' = "Done" - /\ UNCHANGED idx - /\ UNCHANGED << params, - allArgs, - curNode >> - ELSE /\ IF ops[idx] \notin {"null", "@"} - THEN /\ PrintT("Incorrect selection from LAMBDA") - /\ IF debug - THEN /\ idx' = idx - /\ pc' = "Lbl_58" - ELSE /\ pc' = "Done" - /\ UNCHANGED idx - /\ UNCHANGED << params, - allArgs, - curNode >> - ELSE /\ IF /\ ops[idx] = "null" - /\ Len(args[idx]) # Len(opNode'.params) - THEN /\ PrintT("Incorrect number of arguments for LAMBDA") - /\ IF debug - THEN /\ idx' = idx - /\ pc' = "Lbl_59" - ELSE /\ pc' = "Done" - /\ UNCHANGED idx - /\ UNCHANGED << params, - allArgs, - curNode >> - ELSE /\ params' = params \o opNode'.params - /\ allArgs' = allArgs \o args[idx] - /\ curNode' = Node[opNode'.body] - /\ pc' = "Lbl_63" - /\ UNCHANGED idx - ELSE /\ IF curNode.kind \in {UserDefinedOpKind, BuiltInKind} - THEN /\ PrintT("Abort: should not have been able to choose this node.") - /\ IF debug - THEN /\ idx' = idx - /\ pc' = "Lbl_60" - ELSE /\ pc' = "Done" - /\ UNCHANGED idx - /\ UNCHANGED curNode - ELSE /\ IF curNode.kind \in {AtNodeKind, DecimalKind, NumeralKind, StringKind, - FormalParamKind, ConstantDeclKind, VariableDeclKind, - BoundSymbolKind} - THEN /\ PrintT("Selected part has no subexpression") - /\ IF debug - THEN /\ idx' = idx - /\ pc' = "Lbl_61" - ELSE /\ pc' = "Done" - /\ UNCHANGED idx - /\ UNCHANGED curNode - ELSE /\ IF curNode.kind = LabelKind - THEN /\ curNode' = Node[curNode.body] - /\ idx' = idx - 1 - /\ pc' = "Lbl_63" - ELSE /\ PrintT("Unexpected node kind found in expression") - /\ IF debug - THEN /\ idx' = idx - /\ pc' = "Lbl_62" - ELSE /\ pc' = "Done" - /\ UNCHANGED idx - /\ UNCHANGED curNode - /\ UNCHANGED << params, - allArgs, - opNode >> - /\ UNCHANGED temp - ELSE /\ PrintT("Bad value of mode") - /\ IF debug - THEN /\ idx' = idx - /\ pc' = "Lbl_68" - ELSE /\ pc' = "Done" - /\ UNCHANGED idx - /\ UNCHANGED << params, - allArgs, - curNode, - opNode, - temp >> - /\ UNCHANGED newNode - /\ UNCHANGED tempArgs - ELSE /\ IF curNode.kind = AssumeProveKind - THEN /\ PrintT("Selecting ASSUME/PROVE instead of expression") - /\ IF debug - THEN /\ idx' = idx - /\ pc' = "Lbl_70" - ELSE /\ pc' = "Done" - /\ UNCHANGED idx - ELSE /\ pc' = "Lbl_71" - /\ UNCHANGED idx - /\ UNCHANGED << params, allArgs, curNode, opNode, newNode, - temp, tempArgs >> - /\ UNCHANGED << ops, args, expectedArity, substInPrefix, subExprOf, - result, mode, prevMode, curContext, curName, - opDefArityFound, opDefArgs, firstFindingOpName, - newName, nodeArity >> - -Lbl_69 == /\ pc = "Lbl_69" - /\ idx' = idx + 1 - /\ pc' = "Lbl_2" - /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, - allArgs, curNode, subExprOf, result, mode, prevMode, - curContext, curName, opDefArityFound, opDefArgs, - firstFindingOpName, opNode, newName, newNode, - nodeArity, temp, tempArgs >> - -Lbl_3 == /\ pc = "Lbl_3" - /\ IF newNode = null - THEN /\ IF idx \geq Len(ops) - THEN /\ PrintT("Unknown operator") - /\ IF debug - THEN /\ idx' = idx - /\ pc' = "Lbl_4" - ELSE /\ pc' = "Done" - /\ UNCHANGED idx - ELSE /\ pc' = "Lbl_5" - /\ UNCHANGED idx - /\ UNCHANGED << curNode, curName >> - ELSE /\ curNode' = newNode - /\ curName' = newName - /\ IF curNode'.kind \in - {UserDefinedOpKind, ThmOrAssumpDefKind, - ModuleInstanceKind, ConstantDeclKind, VariableDeclKind, - FormalParamKind, BuiltInKind, BoundSymbolKind} - THEN /\ IF curNode'.kind \in - {ConstantDeclKind, VariableDeclKind, FormalParamKind, - BuiltInKind, BoundSymbolKind} - THEN /\ IF idx # 1 - THEN /\ PrintT("Abort: Impossible naming of declaration " - \o "or built-in operator.") - /\ IF debug - THEN /\ idx' = idx - /\ pc' = "Lbl_8" - ELSE /\ pc' = "Done" - /\ UNCHANGED idx - ELSE /\ IF Len(ops) # 1 - THEN /\ PrintT("Can't take subexpression of this") - /\ IF debug - THEN /\ idx' = idx - /\ pc' = "Lbl_9" - ELSE /\ pc' = "Done" - /\ UNCHANGED idx - ELSE /\ pc' = "Lbl_10" - /\ UNCHANGED idx - ELSE /\ pc' = "Lbl_10" - /\ UNCHANGED idx - ELSE /\ PrintT("Unexpected node kind") - /\ IF debug - THEN /\ idx' = idx - /\ pc' = "Lbl_20" - ELSE /\ pc' = "Done" - /\ UNCHANGED idx - /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, - allArgs, subExprOf, result, mode, prevMode, - curContext, opDefArityFound, opDefArgs, - firstFindingOpName, opNode, newName, newNode, - nodeArity, temp, tempArgs >> - -Lbl_5 == /\ pc = "Lbl_5" - /\ newName' = IF IsName(ops[idx]) - THEN IF curName = "" THEN ops[idx] - ELSE curName \o "!" \o ops[idx] - ELSE null - /\ IF /\ curName = "" - /\ ~ IsName(ops[idx]) - THEN /\ PrintT("Need an operator or label name or step number here") - /\ IF debug - THEN /\ idx' = idx - /\ pc' = "Lbl_6" - ELSE /\ pc' = "Done" - /\ UNCHANGED idx - ELSE /\ pc' = "Lbl_7" - /\ UNCHANGED idx - /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, - allArgs, curNode, subExprOf, result, mode, prevMode, - curContext, curName, opDefArityFound, opDefArgs, - firstFindingOpName, opNode, newNode, nodeArity, temp, - tempArgs >> - -Lbl_6 == /\ pc = "Lbl_6" - /\ idx' = idx - /\ Assert(FALSE, - "Failure of assertion at line line 1892, column 38 of macro called at line 1956, column 13.") - /\ pc' = "Lbl_7" - /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, - allArgs, curNode, subExprOf, result, mode, prevMode, - curContext, curName, opDefArityFound, opDefArgs, - firstFindingOpName, opNode, newName, newNode, - nodeArity, temp, tempArgs >> - -Lbl_7 == /\ pc = "Lbl_7" - /\ newNode' = (IF newName # null THEN LookUp(newName, curContext) ELSE null) - /\ IF newName = null - THEN /\ tempArgs' = tempArgs \o args[idx] - /\ idx' = idx + 1 - ELSE /\ TRUE - /\ UNCHANGED << idx, tempArgs >> - /\ pc' = "Lbl_3" - /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, - allArgs, curNode, subExprOf, result, mode, prevMode, - curContext, curName, opDefArityFound, opDefArgs, - firstFindingOpName, opNode, newName, nodeArity, temp >> - -Lbl_4 == /\ pc = "Lbl_4" - /\ idx' = idx - /\ Assert(FALSE, - "Failure of assertion at line line 1892, column 38 of macro called at line 1942, column 13.") - /\ pc' = "Lbl_5" - /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, - allArgs, curNode, subExprOf, result, mode, prevMode, - curContext, curName, opDefArityFound, opDefArgs, - firstFindingOpName, opNode, newName, newNode, - nodeArity, temp, tempArgs >> - -Lbl_10 == /\ pc = "Lbl_10" - /\ nodeArity' = Arity(curNode) - /\ tempArgs' = tempArgs \o args[idx] - /\ IF expectedArity = 0 - THEN /\ IF opDefArityFound + Len(tempArgs') # nodeArity' - THEN /\ PrintT("Wrong number of arguments") - /\ IF debug - THEN /\ idx' = idx - /\ pc' = "Lbl_11" - ELSE /\ pc' = "Done" - /\ UNCHANGED idx - ELSE /\ pc' = "Lbl_12" - /\ UNCHANGED idx - ELSE /\ IF expectedArity > 0 - THEN /\ IF \E i \in 1..Len(curNode.params) : - curNode.params[i].arity > 0 - THEN /\ PrintT("Higher-order operator selected " - \o "as operator argument") - /\ IF debug - THEN /\ idx' = idx - /\ pc' = "Lbl_14" - ELSE /\ pc' = "Done" - /\ UNCHANGED idx - ELSE /\ pc' = "Lbl_15" - /\ UNCHANGED idx - ELSE /\ pc' = "Lbl_15" - /\ UNCHANGED idx - /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, - allArgs, curNode, subExprOf, result, mode, prevMode, - curContext, curName, opDefArityFound, opDefArgs, - firstFindingOpName, opNode, newName, newNode, temp >> - -Lbl_12 == /\ pc = "Lbl_12" - /\ IF \E i \in 1..Len(tempArgs[idx]) : - Arity(Node[tempArgs[idx][i]]) # - ParamArity(curNode, i + opDefArityFound) - THEN /\ PrintT("Argument has wrong arity") - /\ IF debug - THEN /\ idx' = idx - /\ pc' = "Lbl_13" - ELSE /\ pc' = "Done" - /\ UNCHANGED idx - /\ UNCHANGED opDefArgs - ELSE /\ opDefArgs' = opDefArgs \o tempArgs - /\ pc' = "Lbl_15" - /\ UNCHANGED idx - /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, - allArgs, curNode, subExprOf, result, mode, prevMode, - curContext, curName, opDefArityFound, - firstFindingOpName, opNode, newName, newNode, - nodeArity, temp, tempArgs >> - -Lbl_13 == /\ pc = "Lbl_13" - /\ idx' = idx - /\ Assert(FALSE, - "Failure of assertion at line line 1892, column 38 of macro called at line 1990, column 26.") - /\ pc' = "Lbl_15" - /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, - allArgs, curNode, subExprOf, result, mode, prevMode, - curContext, curName, opDefArityFound, opDefArgs, - firstFindingOpName, opNode, newName, newNode, - nodeArity, temp, tempArgs >> - -Lbl_11 == /\ pc = "Lbl_11" - /\ idx' = idx - /\ Assert(FALSE, - "Failure of assertion at line line 1892, column 38 of macro called at line 1985, column 25.") - /\ pc' = "Lbl_12" - /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, - allArgs, curNode, subExprOf, result, mode, prevMode, - curContext, curName, opDefArityFound, opDefArgs, - firstFindingOpName, opNode, newName, newNode, - nodeArity, temp, tempArgs >> - -Lbl_14 == /\ pc = "Lbl_14" - /\ idx' = idx - /\ Assert(FALSE, - "Failure of assertion at line line 1892, column 38 of macro called at line 1996, column 32.") - /\ pc' = "Lbl_15" - /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, - allArgs, curNode, subExprOf, result, mode, prevMode, - curContext, curName, opDefArityFound, opDefArgs, - firstFindingOpName, opNode, newName, newNode, - nodeArity, temp, tempArgs >> - -Lbl_15 == /\ pc = "Lbl_15" - /\ opDefArityFound' = nodeArity - /\ IF curNode.kind \in {UserDefinedOpKind, ThmOrAssumpDefKind, - ConstantDeclKind, VariableDeclKind, - FormalParamKind, BoundSymbolKind} - THEN /\ IF /\ curNode.kind = UserDefinedOpKind - /\ ~ curNode.defined - /\ ~ Len(ops) = 1 - THEN /\ PrintT("Can't refer to subexpression of " - \o "operator inside its definition") - /\ IF debug - THEN /\ idx' = idx - /\ pc' = "Lbl_16" - ELSE /\ pc' = "Done" - /\ UNCHANGED idx - ELSE /\ pc' = "Lbl_17" - /\ UNCHANGED idx - ELSE /\ IF (idx = Len(ops)) - THEN /\ PrintT("Operator name incomplete") - /\ IF debug - THEN /\ idx' = idx - /\ pc' = "Lbl_19" - ELSE /\ pc' = "Done" - /\ UNCHANGED idx - ELSE /\ pc' = "Lbl_21" - /\ UNCHANGED idx - /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, - allArgs, curNode, subExprOf, result, mode, prevMode, - curContext, curName, opDefArgs, firstFindingOpName, - opNode, newName, newNode, nodeArity, temp, tempArgs >> - -Lbl_17 == /\ pc = "Lbl_17" - /\ IF /\ firstFindingOpName - /\ curNode.kind \in {UserDefinedOpKind, ThmOrAssumpDefKind} - THEN /\ subExprOf' = curNode - ELSE /\ TRUE - /\ UNCHANGED subExprOf - /\ IF idx # Len(ops) - THEN /\ params' = params \o curNode.params - /\ curName' = "" - /\ IF IsName(ops[idx+1]) - THEN /\ mode' = "FollowingLabels" - ELSE /\ mode' = "FindingSubExpr" - /\ allArgs' = allArgs \o opDefArgs - /\ opDefArityFound' = 0 - /\ opDefArgs' = << >> - /\ newNode' = Node[curNode.body] - /\ pc' = "Lbl_18" - ELSE /\ pc' = "Lbl_21" - /\ UNCHANGED << params, allArgs, mode, curName, - opDefArityFound, opDefArgs, newNode >> - /\ UNCHANGED << ops, args, expectedArity, substInPrefix, curNode, - result, idx, prevMode, curContext, - firstFindingOpName, opNode, newName, nodeArity, temp, - tempArgs >> - -Lbl_18 == /\ pc = "Lbl_18" - /\ IF newNode.kind = SubstInKind - THEN /\ substInPrefix' = substInPrefix \o <<newNode>> - /\ newNode' = Node[newNode.body] - /\ pc' = "Lbl_18" - /\ UNCHANGED curNode - ELSE /\ IF mode = "FindingSubExpr" - THEN /\ curNode' = newNode - ELSE /\ TRUE - /\ UNCHANGED curNode - /\ pc' = "Lbl_21" - /\ UNCHANGED << substInPrefix, newNode >> - /\ UNCHANGED << ops, args, expectedArity, params, allArgs, subExprOf, - result, idx, mode, prevMode, curContext, curName, - opDefArityFound, opDefArgs, firstFindingOpName, - opNode, newName, nodeArity, temp, tempArgs >> - -Lbl_16 == /\ pc = "Lbl_16" - /\ idx' = idx - /\ Assert(FALSE, - "Failure of assertion at line line 1892, column 38 of macro called at line 2010, column 25.") - /\ pc' = "Lbl_17" - /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, - allArgs, curNode, subExprOf, result, mode, prevMode, - curContext, curName, opDefArityFound, opDefArgs, - firstFindingOpName, opNode, newName, newNode, - nodeArity, temp, tempArgs >> - -Lbl_19 == /\ pc = "Lbl_19" - /\ idx' = idx - /\ Assert(FALSE, - "Failure of assertion at line line 1892, column 38 of macro called at line 2043, column 25.") - /\ pc' = "Lbl_21" - /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, - allArgs, curNode, subExprOf, result, mode, prevMode, - curContext, curName, opDefArityFound, opDefArgs, - firstFindingOpName, opNode, newName, newNode, - nodeArity, temp, tempArgs >> - -Lbl_8 == /\ pc = "Lbl_8" - /\ idx' = idx - /\ Assert(FALSE, - "Failure of assertion at line line 1892, column 38 of macro called at line 1974, column 26.") - /\ pc' = "Lbl_10" - /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, - allArgs, curNode, subExprOf, result, mode, prevMode, - curContext, curName, opDefArityFound, opDefArgs, - firstFindingOpName, opNode, newName, newNode, - nodeArity, temp, tempArgs >> - -Lbl_9 == /\ pc = "Lbl_9" - /\ idx' = idx - /\ Assert(FALSE, - "Failure of assertion at line line 1892, column 38 of macro called at line 1977, column 33.") - /\ pc' = "Lbl_10" - /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, - allArgs, curNode, subExprOf, result, mode, prevMode, - curContext, curName, opDefArityFound, opDefArgs, - firstFindingOpName, opNode, newName, newNode, - nodeArity, temp, tempArgs >> - -Lbl_20 == /\ pc = "Lbl_20" - /\ idx' = idx - /\ Assert(FALSE, - "Failure of assertion at line line 1892, column 38 of macro called at line 2046, column 9.") - /\ pc' = "Lbl_21" - /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, - allArgs, curNode, subExprOf, result, mode, prevMode, - curContext, curName, opDefArityFound, opDefArgs, - firstFindingOpName, opNode, newName, newNode, - nodeArity, temp, tempArgs >> - -Lbl_21 == /\ pc = "Lbl_21" - /\ prevMode' = "FindingOpName" - /\ pc' = "Lbl_69" - /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, - allArgs, curNode, subExprOf, result, idx, mode, - curContext, curName, opDefArityFound, opDefArgs, - firstFindingOpName, opNode, newName, newNode, - nodeArity, temp, tempArgs >> - -Lbl_23 == /\ pc = "Lbl_23" - /\ curNode' = newNode - /\ IF expectedArity = 0 - THEN /\ IF Len(args[idx]) # curNode'.arity - THEN /\ PrintT("bad arity") - /\ IF debug - THEN /\ idx' = idx - /\ pc' = "Lbl_24" - ELSE /\ pc' = "Done" - /\ UNCHANGED idx - ELSE /\ pc' = "Lbl_25" - /\ UNCHANGED idx - ELSE /\ pc' = "Lbl_28" - /\ UNCHANGED idx - /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, - allArgs, subExprOf, result, mode, prevMode, - curContext, curName, opDefArityFound, opDefArgs, - firstFindingOpName, opNode, newName, newNode, - nodeArity, temp, tempArgs >> - -Lbl_25 == /\ pc = "Lbl_25" - /\ IF \E i \in 1..Len(args[idx]) : - Arity(Node[args[idx][i]]) # 0 - THEN /\ PrintT("Operator argument given to label") - /\ IF debug - THEN /\ idx' = idx - /\ pc' = "Lbl_26" - ELSE /\ pc' = "Done" - /\ UNCHANGED idx - ELSE /\ pc' = "Lbl_27" - /\ UNCHANGED idx - /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, - allArgs, curNode, subExprOf, result, mode, prevMode, - curContext, curName, opDefArityFound, opDefArgs, - firstFindingOpName, opNode, newName, newNode, - nodeArity, temp, tempArgs >> - -Lbl_26 == /\ pc = "Lbl_26" - /\ idx' = idx - /\ Assert(FALSE, - "Failure of assertion at line line 1892, column 38 of macro called at line 2068, column 20.") - /\ pc' = "Lbl_27" - /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, - allArgs, curNode, subExprOf, result, mode, prevMode, - curContext, curName, opDefArityFound, opDefArgs, - firstFindingOpName, opNode, newName, newNode, - nodeArity, temp, tempArgs >> - -Lbl_27 == /\ pc = "Lbl_27" - /\ allArgs' = allArgs \o args[idx] - /\ pc' = "Lbl_28" - /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, - curNode, subExprOf, result, idx, mode, prevMode, - curContext, curName, opDefArityFound, opDefArgs, - firstFindingOpName, opNode, newName, newNode, - nodeArity, temp, tempArgs >> - -Lbl_24 == /\ pc = "Lbl_24" - /\ idx' = idx - /\ Assert(FALSE, - "Failure of assertion at line line 1892, column 38 of macro called at line 2064, column 20.") - /\ pc' = "Lbl_25" - /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, - allArgs, curNode, subExprOf, result, mode, prevMode, - curContext, curName, opDefArityFound, opDefArgs, - firstFindingOpName, opNode, newName, newNode, - nodeArity, temp, tempArgs >> - -Lbl_28 == /\ pc = "Lbl_28" - /\ params' = params \o curNode.params - /\ IF /\ idx < Len(ops) - /\ ~IsName(ops[idx+1]) - THEN /\ mode' = "FindingSubExpr" - ELSE /\ TRUE - /\ UNCHANGED mode - /\ IF \/ mode' = "FindingSubExpr" - \/ idx = Len(ops) - THEN /\ curNode' = Node[curNode.body] - ELSE /\ TRUE - /\ UNCHANGED curNode - /\ prevMode' = "FollowingLabels" - /\ pc' = "Lbl_69" - /\ UNCHANGED << ops, args, expectedArity, substInPrefix, allArgs, - subExprOf, result, idx, curContext, curName, - opDefArityFound, opDefArgs, firstFindingOpName, - opNode, newName, newNode, nodeArity, temp, tempArgs >> - -Lbl_22 == /\ pc = "Lbl_22" - /\ idx' = idx - /\ Assert(FALSE, - "Failure of assertion at line line 1892, column 38 of macro called at line 2059, column 13.") - /\ pc' = "Lbl_23" - /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, - allArgs, curNode, subExprOf, result, mode, prevMode, - curContext, curName, opDefArityFound, opDefArgs, - firstFindingOpName, opNode, newName, newNode, - nodeArity, temp, tempArgs >> - -Lbl_63 == /\ pc = "Lbl_63" - /\ IF idx # Len(ops) - THEN /\ IF IsName(ops[idx+1]) - THEN /\ pc' = "Lbl_64" - /\ UNCHANGED idx - ELSE /\ IF ops[idx+1] = ":" - THEN /\ PrintT("!: should not follow an operand selector") - /\ IF debug - THEN /\ idx' = idx - /\ pc' = "Lbl_66" - ELSE /\ pc' = "Done" - /\ UNCHANGED idx - ELSE /\ pc' = "Lbl_67" - /\ UNCHANGED idx - ELSE /\ pc' = "Lbl_67" - /\ UNCHANGED idx - /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, - allArgs, curNode, subExprOf, result, mode, prevMode, - curContext, curName, opDefArityFound, opDefArgs, - firstFindingOpName, opNode, newName, newNode, - nodeArity, temp, tempArgs >> - -Lbl_64 == /\ pc = "Lbl_64" - /\ IF curNode.kind = LabelKind - THEN /\ curNode' = Node[curNode.body] - /\ pc' = "Lbl_64" - /\ UNCHANGED << idx, mode, curContext, firstFindingOpName >> - ELSE /\ IF curNode.kind = LetInKind - THEN /\ curContext' = curNode.context - /\ mode' = "FindingOpName" - /\ firstFindingOpName' = FALSE - /\ pc' = "Lbl_67" - /\ UNCHANGED idx - ELSE /\ PrintT("A name not following the selector of a LET") - /\ IF debug - THEN /\ idx' = idx - /\ pc' = "Lbl_65" - ELSE /\ pc' = "Done" - /\ UNCHANGED idx - /\ UNCHANGED << mode, curContext, - firstFindingOpName >> - /\ UNCHANGED curNode - /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, - allArgs, subExprOf, result, prevMode, curName, - opDefArityFound, opDefArgs, opNode, newName, newNode, - nodeArity, temp, tempArgs >> - -Lbl_65 == /\ pc = "Lbl_65" - /\ idx' = idx - /\ Assert(FALSE, - "Failure of assertion at line line 1892, column 38 of macro called at line 2294, column 26.") - /\ pc' = "Lbl_67" - /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, - allArgs, curNode, subExprOf, result, mode, prevMode, - curContext, curName, opDefArityFound, opDefArgs, - firstFindingOpName, opNode, newName, newNode, - nodeArity, temp, tempArgs >> - -Lbl_66 == /\ pc = "Lbl_66" - /\ idx' = idx - /\ Assert(FALSE, - "Failure of assertion at line line 1892, column 38 of macro called at line 2297, column 26.") - /\ pc' = "Lbl_67" - /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, - allArgs, curNode, subExprOf, result, mode, prevMode, - curContext, curName, opDefArityFound, opDefArgs, - firstFindingOpName, opNode, newName, newNode, - nodeArity, temp, tempArgs >> - -Lbl_67 == /\ pc = "Lbl_67" - /\ prevMode' = "FindingSubExpr" - /\ pc' = "Lbl_69" - /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, - allArgs, curNode, subExprOf, result, idx, mode, - curContext, curName, opDefArityFound, opDefArgs, - firstFindingOpName, opNode, newName, newNode, - nodeArity, temp, tempArgs >> - -Lbl_29 == /\ pc = "Lbl_29" - /\ idx' = idx - /\ Assert(FALSE, - "Failure of assertion at line line 1892, column 38 of macro called at line 2091, column 14.") - /\ pc' = "Lbl_63" - /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, - allArgs, curNode, subExprOf, result, mode, prevMode, - curContext, curName, opDefArityFound, opDefArgs, - firstFindingOpName, opNode, newName, newNode, - nodeArity, temp, tempArgs >> - -Lbl_30 == /\ pc = "Lbl_30" - /\ idx' = idx - /\ Assert(FALSE, - "Failure of assertion at line line 1892, column 38 of macro called at line 2098, column 14.") - /\ pc' = "Lbl_63" - /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, - allArgs, curNode, subExprOf, result, mode, prevMode, - curContext, curName, opDefArityFound, opDefArgs, - firstFindingOpName, opNode, newName, newNode, - nodeArity, temp, tempArgs >> - -Lbl_31 == /\ pc = "Lbl_31" - /\ idx' = idx - /\ Assert(FALSE, - "Failure of assertion at line line 1892, column 38 of macro called at line 2110, column 17.") - /\ pc' = "Lbl_63" - /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, - allArgs, curNode, subExprOf, result, mode, prevMode, - curContext, curName, opDefArityFound, opDefArgs, - firstFindingOpName, opNode, newName, newNode, - nodeArity, temp, tempArgs >> - -Lbl_33 == /\ pc = "Lbl_33" - /\ curNode' = Node[curNode.operands[temp]] - /\ IF \/ curNode'.kind # OpApplKind - \/ Node[curNode'.operator].kind # BuiltInKind - \/ Node[curNode'.operator].name # "$Pair" - THEN /\ PrintT("Expecting $Pair(...)") - /\ IF debug - THEN /\ idx' = idx - /\ pc' = "Lbl_34" - ELSE /\ pc' = "Done" - /\ UNCHANGED idx - ELSE /\ pc' = "Lbl_35" - /\ UNCHANGED idx - /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, - allArgs, subExprOf, result, mode, prevMode, - curContext, curName, opDefArityFound, opDefArgs, - firstFindingOpName, opNode, newName, newNode, - nodeArity, temp, tempArgs >> - -Lbl_35 == /\ pc = "Lbl_35" - /\ curNode' = Node[curNode.operands[2]] - /\ pc' = "Lbl_63" - /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, - allArgs, subExprOf, result, idx, mode, prevMode, - curContext, curName, opDefArityFound, opDefArgs, - firstFindingOpName, opNode, newName, newNode, - nodeArity, temp, tempArgs >> - -Lbl_34 == /\ pc = "Lbl_34" - /\ idx' = idx - /\ Assert(FALSE, - "Failure of assertion at line line 1892, column 38 of macro called at line 2131, column 18.") - /\ pc' = "Lbl_63" - /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, - allArgs, curNode, subExprOf, result, mode, prevMode, - curContext, curName, opDefArityFound, opDefArgs, - firstFindingOpName, opNode, newName, newNode, - nodeArity, temp, tempArgs >> - -Lbl_32 == /\ pc = "Lbl_32" - /\ idx' = idx - /\ Assert(FALSE, - "Failure of assertion at line line 1892, column 38 of macro called at line 2125, column 18.") - /\ pc' = "Lbl_33" - /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, - allArgs, curNode, subExprOf, result, mode, prevMode, - curContext, curName, opDefArityFound, opDefArgs, - firstFindingOpName, opNode, newName, newNode, - nodeArity, temp, tempArgs >> - -Lbl_37 == /\ pc = "Lbl_37" - /\ temp' = ArgNum( ops[idx], Len(curNode.operands)) - /\ IF temp' = -1 - THEN /\ PrintT("Incorrect subexpression name") - /\ IF debug - THEN /\ idx' = idx - /\ pc' = "Lbl_38" - ELSE /\ pc' = "Done" - /\ UNCHANGED idx - ELSE /\ pc' = "Lbl_39" - /\ UNCHANGED idx - /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, - allArgs, curNode, subExprOf, result, mode, prevMode, - curContext, curName, opDefArityFound, opDefArgs, - firstFindingOpName, opNode, newName, newNode, - nodeArity, tempArgs >> - -Lbl_38 == /\ pc = "Lbl_38" - /\ idx' = idx - /\ Assert(FALSE, - "Failure of assertion at line line 1892, column 38 of macro called at line 2145, column 18.") - /\ pc' = "Lbl_39" - /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, - allArgs, curNode, subExprOf, result, mode, prevMode, - curContext, curName, opDefArityFound, opDefArgs, - firstFindingOpName, opNode, newName, newNode, - nodeArity, temp, tempArgs >> - -Lbl_39 == /\ pc = "Lbl_39" - /\ curNode' = Node[curNode.operands[temp]] - /\ IF \/ curNode'.kind # OpApplKind - \/ Node[curNode'.operator].kind # BuiltInKind - \/ Node[curNode'.operator].name # "$Pair" - THEN /\ PrintT("Expecting $Pair(...)") - /\ IF debug - THEN /\ idx' = idx - /\ pc' = "Lbl_40" - ELSE /\ pc' = "Done" - /\ UNCHANGED idx - ELSE /\ pc' = "Lbl_41" - /\ UNCHANGED idx - /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, - allArgs, subExprOf, result, mode, prevMode, - curContext, curName, opDefArityFound, opDefArgs, - firstFindingOpName, opNode, newName, newNode, - nodeArity, temp, tempArgs >> - -Lbl_40 == /\ pc = "Lbl_40" - /\ idx' = idx - /\ Assert(FALSE, - "Failure of assertion at line line 1892, column 38 of macro called at line 2151, column 18.") - /\ pc' = "Lbl_41" - /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, - allArgs, curNode, subExprOf, result, mode, prevMode, - curContext, curName, opDefArityFound, opDefArgs, - firstFindingOpName, opNode, newName, newNode, - nodeArity, temp, tempArgs >> - -Lbl_41 == /\ pc = "Lbl_41" - /\ idx' = idx+1 - /\ temp' = ArgNum(ops[idx'], 2) - /\ IF temp' = -1 - THEN /\ PrintT("Incorrect subexpression name") - /\ IF debug - THEN /\ pc' = "Lbl_42" - ELSE /\ pc' = "Done" - ELSE /\ pc' = "Lbl_44" - /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, - allArgs, curNode, subExprOf, result, mode, prevMode, - curContext, curName, opDefArityFound, opDefArgs, - firstFindingOpName, opNode, newName, newNode, - nodeArity, tempArgs >> - -Lbl_42 == /\ pc = "Lbl_42" - /\ idx' = idx - /\ pc' = "Lbl_43" - /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, - allArgs, curNode, subExprOf, result, mode, prevMode, - curContext, curName, opDefArityFound, opDefArgs, - firstFindingOpName, opNode, newName, newNode, - nodeArity, temp, tempArgs >> - -Lbl_43 == /\ pc = "Lbl_43" - /\ idx' = idx - /\ Assert(FALSE, - "Failure of assertion at line line 1892, column 38 of macro called at line 2156, column 18.") - /\ pc' = "Lbl_44" - /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, - allArgs, curNode, subExprOf, result, mode, prevMode, - curContext, curName, opDefArityFound, opDefArgs, - firstFindingOpName, opNode, newName, newNode, - nodeArity, temp, tempArgs >> - -Lbl_44 == /\ pc = "Lbl_44" - /\ curNode' = Node[curNode.operands[temp]] - /\ IF curNode' = null - THEN /\ PrintT("Selecting OTHER") - /\ IF debug - THEN /\ idx' = idx - /\ pc' = "Lbl_45" - ELSE /\ pc' = "Done" - /\ UNCHANGED idx - ELSE /\ pc' = "Lbl_63" - /\ UNCHANGED idx - /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, - allArgs, subExprOf, result, mode, prevMode, - curContext, curName, opDefArityFound, opDefArgs, - firstFindingOpName, opNode, newName, newNode, - nodeArity, temp, tempArgs >> - -Lbl_45 == /\ pc = "Lbl_45" - /\ idx' = idx - /\ Assert(FALSE, - "Failure of assertion at line line 1892, column 38 of macro called at line 2160, column 18.") - /\ pc' = "Lbl_63" - /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, - allArgs, curNode, subExprOf, result, mode, prevMode, - curContext, curName, opDefArityFound, opDefArgs, - firstFindingOpName, opNode, newName, newNode, - nodeArity, temp, tempArgs >> - -Lbl_36 == /\ pc = "Lbl_36" - /\ idx' = idx - /\ Assert(FALSE, - "Failure of assertion at line line 1892, column 38 of macro called at line 2141, column 18.") - /\ pc' = "Lbl_37" - /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, - allArgs, curNode, subExprOf, result, mode, prevMode, - curContext, curName, opDefArityFound, opDefArgs, - firstFindingOpName, opNode, newName, newNode, - nodeArity, temp, tempArgs >> - -Lbl_47 == /\ pc = "Lbl_47" - /\ curNode' = Node[curNode.operands[temp]] - /\ IF temp > 1 - THEN /\ IF \/ curNode'.kind # OpApplKind - \/ Node[curNode'.operator].kind # BuiltInKind - \/ Node[curNode'.operator].name # "$Pair" - THEN /\ PrintT("Unexpected expression node found.") - /\ IF debug - THEN /\ idx' = idx - /\ pc' = "Lbl_48" - ELSE /\ pc' = "Done" - /\ UNCHANGED idx - ELSE /\ pc' = "Lbl_49" - /\ UNCHANGED idx - ELSE /\ pc' = "Lbl_63" - /\ UNCHANGED idx - /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, - allArgs, subExprOf, result, mode, prevMode, - curContext, curName, opDefArityFound, opDefArgs, - firstFindingOpName, opNode, newName, newNode, - nodeArity, temp, tempArgs >> - -Lbl_49 == /\ pc = "Lbl_49" - /\ curNode' = Node[curNode.operands[2]] - /\ pc' = "Lbl_63" - /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, - allArgs, subExprOf, result, idx, mode, prevMode, - curContext, curName, opDefArityFound, opDefArgs, - firstFindingOpName, opNode, newName, newNode, - nodeArity, temp, tempArgs >> - -Lbl_48 == /\ pc = "Lbl_48" - /\ idx' = idx - /\ Assert(FALSE, - "Failure of assertion at line line 1892, column 38 of macro called at line 2179, column 26.") - /\ pc' = "Lbl_63" - /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, - allArgs, curNode, subExprOf, result, mode, prevMode, - curContext, curName, opDefArityFound, opDefArgs, - firstFindingOpName, opNode, newName, newNode, - nodeArity, temp, tempArgs >> - -Lbl_46 == /\ pc = "Lbl_46" - /\ idx' = idx - /\ Assert(FALSE, - "Failure of assertion at line line 1892, column 38 of macro called at line 2172, column 19.") - /\ pc' = "Lbl_47" - /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, - allArgs, curNode, subExprOf, result, mode, prevMode, - curContext, curName, opDefArityFound, opDefArgs, - firstFindingOpName, opNode, newName, newNode, - nodeArity, temp, tempArgs >> - -Lbl_51 == /\ pc = "Lbl_51" - /\ curNode' = Node[curNode.operands[temp]] - /\ pc' = "Lbl_63" - /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, - allArgs, subExprOf, result, idx, mode, prevMode, - curContext, curName, opDefArityFound, opDefArgs, - firstFindingOpName, opNode, newName, newNode, - nodeArity, temp, tempArgs >> - -Lbl_50 == /\ pc = "Lbl_50" - /\ idx' = idx - /\ Assert(FALSE, - "Failure of assertion at line line 1892, column 38 of macro called at line 2195, column 25.") - /\ pc' = "Lbl_51" - /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, - allArgs, curNode, subExprOf, result, mode, prevMode, - curContext, curName, opDefArityFound, opDefArgs, - firstFindingOpName, opNode, newName, newNode, - nodeArity, temp, tempArgs >> - -Lbl_53 == /\ pc = "Lbl_53" - /\ curNode' = Node[curNode.operands[1]] - /\ pc' = "Lbl_63" - /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, - allArgs, subExprOf, result, idx, mode, prevMode, - curContext, curName, opDefArityFound, opDefArgs, - firstFindingOpName, opNode, newName, newNode, - nodeArity, temp, tempArgs >> - -Lbl_52 == /\ pc = "Lbl_52" - /\ idx' = idx - /\ Assert(FALSE, - "Failure of assertion at line line 1892, column 38 of macro called at line 2220, column 32.") - /\ pc' = "Lbl_53" - /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, - allArgs, curNode, subExprOf, result, mode, prevMode, - curContext, curName, opDefArityFound, opDefArgs, - firstFindingOpName, opNode, newName, newNode, - nodeArity, temp, tempArgs >> - -Lbl_54 == /\ pc = "Lbl_54" - /\ idx' = idx - /\ Assert(FALSE, - "Failure of assertion at line line 1892, column 38 of macro called at line 2225, column 32.") - /\ pc' = "Lbl_63" - /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, - allArgs, curNode, subExprOf, result, mode, prevMode, - curContext, curName, opDefArityFound, opDefArgs, - firstFindingOpName, opNode, newName, newNode, - nodeArity, temp, tempArgs >> - -Lbl_55 == /\ pc = "Lbl_55" - /\ idx' = idx - /\ Assert(FALSE, - "Failure of assertion at line line 1892, column 38 of macro called at line 2232, column 12.") - /\ pc' = "Lbl_63" - /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, - allArgs, curNode, subExprOf, result, mode, prevMode, - curContext, curName, opDefArityFound, opDefArgs, - firstFindingOpName, opNode, newName, newNode, - nodeArity, temp, tempArgs >> - -Lbl_56 == /\ pc = "Lbl_56" - /\ idx' = idx - /\ Assert(FALSE, - "Failure of assertion at line line 1892, column 38 of macro called at line 2240, column 18.") - /\ pc' = "Lbl_63" - /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, - allArgs, curNode, subExprOf, result, mode, prevMode, - curContext, curName, opDefArityFound, opDefArgs, - firstFindingOpName, opNode, newName, newNode, - nodeArity, temp, tempArgs >> - -Lbl_57 == /\ pc = "Lbl_57" - /\ idx' = idx - /\ Assert(FALSE, - "Failure of assertion at line line 1892, column 38 of macro called at line 2255, column 9.") - /\ pc' = "Lbl_63" - /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, - allArgs, curNode, subExprOf, result, mode, prevMode, - curContext, curName, opDefArityFound, opDefArgs, - firstFindingOpName, opNode, newName, newNode, - nodeArity, temp, tempArgs >> - -Lbl_58 == /\ pc = "Lbl_58" - /\ idx' = idx - /\ Assert(FALSE, - "Failure of assertion at line line 1892, column 38 of macro called at line 2257, column 9.") - /\ pc' = "Lbl_63" - /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, - allArgs, curNode, subExprOf, result, mode, prevMode, - curContext, curName, opDefArityFound, opDefArgs, - firstFindingOpName, opNode, newName, newNode, - nodeArity, temp, tempArgs >> - -Lbl_59 == /\ pc = "Lbl_59" - /\ idx' = idx - /\ Assert(FALSE, - "Failure of assertion at line line 1892, column 38 of macro called at line 2260, column 9.") - /\ pc' = "Lbl_63" - /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, - allArgs, curNode, subExprOf, result, mode, prevMode, - curContext, curName, opDefArityFound, opDefArgs, - firstFindingOpName, opNode, newName, newNode, - nodeArity, temp, tempArgs >> - -Lbl_60 == /\ pc = "Lbl_60" - /\ idx' = idx - /\ Assert(FALSE, - "Failure of assertion at line line 1892, column 38 of macro called at line 2267, column 7.") - /\ pc' = "Lbl_63" - /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, - allArgs, curNode, subExprOf, result, mode, prevMode, - curContext, curName, opDefArityFound, opDefArgs, - firstFindingOpName, opNode, newName, newNode, - nodeArity, temp, tempArgs >> - -Lbl_61 == /\ pc = "Lbl_61" - /\ idx' = idx - /\ Assert(FALSE, - "Failure of assertion at line line 1892, column 38 of macro called at line 2272, column 10.") - /\ pc' = "Lbl_63" - /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, - allArgs, curNode, subExprOf, result, mode, prevMode, - curContext, curName, opDefArityFound, opDefArgs, - firstFindingOpName, opNode, newName, newNode, - nodeArity, temp, tempArgs >> - -Lbl_62 == /\ pc = "Lbl_62" - /\ idx' = idx - /\ Assert(FALSE, - "Failure of assertion at line line 1892, column 38 of macro called at line 2281, column 8.") - /\ pc' = "Lbl_63" - /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, - allArgs, curNode, subExprOf, result, mode, prevMode, - curContext, curName, opDefArityFound, opDefArgs, - firstFindingOpName, opNode, newName, newNode, - nodeArity, temp, tempArgs >> - -Lbl_68 == /\ pc = "Lbl_68" - /\ idx' = idx - /\ Assert(FALSE, - "Failure of assertion at line line 1892, column 38 of macro called at line 2303, column 8.") - /\ pc' = "Lbl_69" - /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, - allArgs, curNode, subExprOf, result, mode, prevMode, - curContext, curName, opDefArityFound, opDefArgs, - firstFindingOpName, opNode, newName, newNode, - nodeArity, temp, tempArgs >> - -Lbl_70 == /\ pc = "Lbl_70" - /\ idx' = idx - /\ Assert(FALSE, - "Failure of assertion at line line 1892, column 38 of macro called at line 2311, column 10.") - /\ pc' = "Lbl_71" - /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, - allArgs, curNode, subExprOf, result, mode, prevMode, - curContext, curName, opDefArityFound, opDefArgs, - firstFindingOpName, opNode, newName, newNode, - nodeArity, temp, tempArgs >> - -Lbl_71 == /\ pc = "Lbl_71" - /\ IF expectedArity < 0 - THEN /\ IF \/ prevMode # "FindingOpName" - \/ curNode.kind \notin {UserDefinedOpKind, ThmOrAssumpDefKind} - THEN /\ PrintT("Should have selected a definition, but didn't.") - /\ IF debug - THEN /\ idx' = idx - /\ pc' = "Lbl_72" - ELSE /\ pc' = "Done" - /\ UNCHANGED idx - ELSE /\ pc' = "Lbl_73" - /\ UNCHANGED idx - ELSE /\ pc' = "Lbl_74" - /\ UNCHANGED idx - /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, - allArgs, curNode, subExprOf, result, mode, prevMode, - curContext, curName, opDefArityFound, opDefArgs, - firstFindingOpName, opNode, newName, newNode, - nodeArity, temp, tempArgs >> - -Lbl_73 == /\ pc = "Lbl_73" - /\ result' = curNode - /\ pc' = "Finished" - /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, - allArgs, curNode, subExprOf, idx, mode, prevMode, - curContext, curName, opDefArityFound, opDefArgs, - firstFindingOpName, opNode, newName, newNode, - nodeArity, temp, tempArgs >> - -Lbl_72 == /\ pc = "Lbl_72" - /\ idx' = idx - /\ Assert(FALSE, - "Failure of assertion at line line 1892, column 38 of macro called at line 2317, column 17.") - /\ pc' = "Lbl_73" - /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, - allArgs, curNode, subExprOf, result, mode, prevMode, - curContext, curName, opDefArityFound, opDefArgs, - firstFindingOpName, opNode, newName, newNode, - nodeArity, temp, tempArgs >> - -Lbl_74 == /\ pc = "Lbl_74" - /\ IF expectedArity > 0 - THEN /\ temp' = (Len(params) + IF \/ prevMode = "FindingOpName" - \/ curNode.kind = OpArgKind - THEN Arity(curNode) - ELSE 0) - /\ IF expectedArity # temp' - THEN /\ PrintT("Expect arity = " \o ToString(expectedArity) - \o ", but found arity = " \o ToString(temp')) - /\ IF debug - THEN /\ idx' = idx - /\ pc' = "Lbl_75" - ELSE /\ pc' = "Done" - /\ UNCHANGED idx - ELSE /\ pc' = "Lbl_76" - /\ UNCHANGED idx - ELSE /\ pc' = "Lbl_76" - /\ UNCHANGED << idx, temp >> - /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, - allArgs, curNode, subExprOf, result, mode, prevMode, - curContext, curName, opDefArityFound, opDefArgs, - firstFindingOpName, opNode, newName, newNode, - nodeArity, tempArgs >> - -Lbl_75 == /\ pc = "Lbl_75" - /\ idx' = idx - /\ Assert(FALSE, - "Failure of assertion at line line 1892, column 38 of macro called at line 2329, column 18.") - /\ pc' = "Lbl_76" - /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, - allArgs, curNode, subExprOf, result, mode, prevMode, - curContext, curName, opDefArityFound, opDefArgs, - firstFindingOpName, opNode, newName, newNode, - nodeArity, temp, tempArgs >> - -Lbl_76 == /\ pc = "Lbl_76" - /\ IF /\ prevMode = "FindingOpName" - /\ Len(params) + Len(substInPrefix) > 0 - THEN /\ temp' = [i \in 1..Len(curNode.params) |-> - [curNode.params[i] EXCEPT !.name = "New " \o @]] - /\ curNode' = [kind |-> OpApplKind, - operands |-> temp', - operator |-> curNode, - unboundedBoundSymbols |-> <<>>, - boundedBoundSymbols |-> << >>, - ranges |-> << >>] - /\ params' = params \o temp' - /\ allArgs' = allArgs \o opDefArgs - ELSE /\ TRUE - /\ UNCHANGED << params, allArgs, curNode, temp >> - /\ IF curNode'.kind = OpArgKind - THEN /\ IF expectedArity = 0 - THEN /\ PrintT("Selected Operator Argument when expression expected.") - /\ IF debug - THEN /\ idx' = idx - /\ pc' = "Lbl_77" - ELSE /\ pc' = "Done" - /\ UNCHANGED idx - ELSE /\ IF expectedArity # Len(params') + curNode'.arity - THEN /\ PrintT("Selected operator has wrong arity.") - /\ IF debug - THEN /\ idx' = idx - /\ pc' = "Lbl_78" - ELSE /\ pc' = "Done" - /\ UNCHANGED idx - ELSE /\ IF Len(params') + Len(substInPrefix) > 0 - THEN /\ pc' = "Lbl_79" - ELSE /\ pc' = "Lbl_80" - /\ UNCHANGED idx - ELSE /\ pc' = "Lbl_80" - /\ UNCHANGED idx - /\ UNCHANGED << ops, args, expectedArity, substInPrefix, subExprOf, - result, mode, prevMode, curContext, curName, - opDefArityFound, opDefArgs, firstFindingOpName, - opNode, newName, newNode, nodeArity, tempArgs >> - -Lbl_77 == /\ pc = "Lbl_77" - /\ idx' = idx - /\ Assert(FALSE, - "Failure of assertion at line line 1892, column 38 of macro called at line 2363, column 12.") - /\ pc' = "Lbl_80" - /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, - allArgs, curNode, subExprOf, result, mode, prevMode, - curContext, curName, opDefArityFound, opDefArgs, - firstFindingOpName, opNode, newName, newNode, - nodeArity, temp, tempArgs >> - -Lbl_78 == /\ pc = "Lbl_78" - /\ idx' = idx - /\ Assert(FALSE, - "Failure of assertion at line line 1892, column 38 of macro called at line 2365, column 12.") - /\ pc' = "Lbl_80" - /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, - allArgs, curNode, subExprOf, result, mode, prevMode, - curContext, curName, opDefArityFound, opDefArgs, - firstFindingOpName, opNode, newName, newNode, - nodeArity, temp, tempArgs >> - -Lbl_79 == /\ pc = "Lbl_79" - /\ temp' = [i \in 1..curNode.arity |-> - [name |-> "NewParam" \o NumToString(i), - arity |-> 0]] - /\ curNode' = [kind |-> OpApplKind, - operands |-> temp', - operator |-> Node[curNode.op], - unboundedBoundSymbols |-> <<>>, - boundedBoundSymbols |-> << >>, - ranges |-> << >>] - /\ params' = params \o temp' - /\ pc' = "Lbl_80" - /\ UNCHANGED << ops, args, expectedArity, substInPrefix, allArgs, - subExprOf, result, idx, mode, prevMode, curContext, - curName, opDefArityFound, opDefArgs, - firstFindingOpName, opNode, newName, newNode, - nodeArity, tempArgs >> - -Lbl_80 == /\ pc = "Lbl_80" - /\ IF curNode.kind \in {UserDefinedOpKind, ConstantDeclKind, VariableDeclKind, - FormalParamKind, BuiltInKind, BoundSymbolKind, - ThmOrAssumpDefKind} - THEN /\ IF expectedArity > 0 - THEN /\ result' = [kind |-> OpArgKind, - name |-> curNode.name, - op |-> curNode, - arity |-> expectedArity] - ELSE /\ IF expectedArity = 0 - THEN /\ result' = [kind |-> OpApplKind, - operands |-> opDefArgs, - operator |-> curNode, - unboundedBoundSymbols |-> <<>>, - boundedBoundSymbols |-> << >>, - ranges |-> << >>, - subExprOf |-> subExprOf] - ELSE /\ TRUE - /\ UNCHANGED result - /\ pc' = "Finished" - /\ UNCHANGED temp - ELSE /\ IF curNode.kind = OpArgKind - THEN /\ result' = curNode - /\ pc' = "Finished" - /\ UNCHANGED temp - ELSE /\ temp' = Len(substInPrefix) - /\ pc' = "Lbl_81" - /\ UNCHANGED result - /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, - allArgs, curNode, subExprOf, idx, mode, prevMode, - curContext, curName, opDefArityFound, opDefArgs, - firstFindingOpName, opNode, newName, newNode, - nodeArity, tempArgs >> - -Lbl_81 == /\ pc = "Lbl_81" - /\ IF temp > 0 - THEN /\ curNode' = [kind |-> SubstInKind, - body |-> curNode , - subst |-> substInPrefix[temp].subst ] - /\ temp' = temp - 1 - /\ pc' = "Lbl_81" - /\ UNCHANGED idx - ELSE /\ IF expectedArity > 0 - THEN /\ IF Len(params) # expectedArity - THEN /\ PrintT("Selection has wrong arity") - /\ IF debug - THEN /\ idx' = idx - /\ pc' = "Lbl_82" - ELSE /\ pc' = "Done" - /\ UNCHANGED idx - ELSE /\ pc' = "Lbl_83" - /\ UNCHANGED idx - ELSE /\ IF Len(params) # Len(allArgs) - THEN /\ PrintT("Abort: number of params # num of args") - /\ IF debug - THEN /\ idx' = idx - /\ pc' = "Lbl_84" - ELSE /\ pc' = "Done" - /\ UNCHANGED idx - ELSE /\ pc' = "Lbl_85" - /\ UNCHANGED idx - /\ UNCHANGED << curNode, temp >> - /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, - allArgs, subExprOf, result, mode, prevMode, - curContext, curName, opDefArityFound, opDefArgs, - firstFindingOpName, opNode, newName, newNode, - nodeArity, tempArgs >> - -Lbl_83 == /\ pc = "Lbl_83" - /\ result' = [kind |-> OpArgKind, - op |-> [kind |-> UserDefinedOpKind, - name |-> "LAMBDA", - body |-> curNode, - params |-> params, - arity |-> Len(params), - defined |-> TRUE, - source |-> null], - name |-> "LAMBDA"] - /\ pc' = "Finished" - /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, - allArgs, curNode, subExprOf, idx, mode, prevMode, - curContext, curName, opDefArityFound, opDefArgs, - firstFindingOpName, opNode, newName, newNode, - nodeArity, temp, tempArgs >> - -Lbl_85 == /\ pc = "Lbl_85" - /\ IF Len(params) = 0 - THEN /\ result' = [kind |-> LabelKind, - name |-> "$Subexpression", - arity |-> 0, - params |-> << >>, - body |-> curNode, - subExprOf |-> subExprOf] - ELSE /\ result' = [kind |-> OpApplKind, - operator |-> [kind |-> UserDefinedOpKind, - name |-> "LAMBDA", - body |-> curNode, - params |-> params, - arity |-> Len(params), - defined |-> TRUE, - source |-> null], - operands |-> allArgs, - unboundedBoundSymbols |-> <<>>, - boundedBoundSymbols |-> << >>, - ranges |-> << >>, - subExprOf |-> subExprOf] - /\ pc' = "Finished" - /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, - allArgs, curNode, subExprOf, idx, mode, prevMode, - curContext, curName, opDefArityFound, opDefArgs, - firstFindingOpName, opNode, newName, newNode, - nodeArity, temp, tempArgs >> - -Lbl_82 == /\ pc = "Lbl_82" - /\ idx' = idx - /\ Assert(FALSE, - "Failure of assertion at line line 1892, column 38 of macro called at line 2431, column 22.") - /\ pc' = "Lbl_83" - /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, - allArgs, curNode, subExprOf, result, mode, prevMode, - curContext, curName, opDefArityFound, opDefArgs, - firstFindingOpName, opNode, newName, newNode, - nodeArity, temp, tempArgs >> - -Lbl_84 == /\ pc = "Lbl_84" - /\ idx' = idx - /\ Assert(FALSE, - "Failure of assertion at line line 1892, column 38 of macro called at line 2443, column 22.") - /\ pc' = "Lbl_85" - /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, - allArgs, curNode, subExprOf, result, mode, prevMode, - curContext, curName, opDefArityFound, opDefArgs, - firstFindingOpName, opNode, newName, newNode, - nodeArity, temp, tempArgs >> - -Finished == /\ pc = "Finished" - /\ PrintT("Result: ") - /\ PrintT(result) - /\ pc' = "Done" - /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, - allArgs, curNode, subExprOf, result, idx, mode, - prevMode, curContext, curName, opDefArityFound, - opDefArgs, firstFindingOpName, opNode, newName, - newNode, nodeArity, temp, tempArgs >> - -Next == Lbl_1 \/ Lbl_2 \/ Lbl_69 \/ Lbl_3 \/ Lbl_5 \/ Lbl_6 \/ Lbl_7 - \/ Lbl_4 \/ Lbl_10 \/ Lbl_12 \/ Lbl_13 \/ Lbl_11 \/ Lbl_14 \/ Lbl_15 - \/ Lbl_17 \/ Lbl_18 \/ Lbl_16 \/ Lbl_19 \/ Lbl_8 \/ Lbl_9 \/ Lbl_20 - \/ Lbl_21 \/ Lbl_23 \/ Lbl_25 \/ Lbl_26 \/ Lbl_27 \/ Lbl_24 \/ Lbl_28 - \/ Lbl_22 \/ Lbl_63 \/ Lbl_64 \/ Lbl_65 \/ Lbl_66 \/ Lbl_67 \/ Lbl_29 - \/ Lbl_30 \/ Lbl_31 \/ Lbl_33 \/ Lbl_35 \/ Lbl_34 \/ Lbl_32 \/ Lbl_37 - \/ Lbl_38 \/ Lbl_39 \/ Lbl_40 \/ Lbl_41 \/ Lbl_42 \/ Lbl_43 \/ Lbl_44 - \/ Lbl_45 \/ Lbl_36 \/ Lbl_47 \/ Lbl_49 \/ Lbl_48 \/ Lbl_46 \/ Lbl_51 - \/ Lbl_50 \/ Lbl_53 \/ Lbl_52 \/ Lbl_54 \/ Lbl_55 \/ Lbl_56 \/ Lbl_57 - \/ Lbl_58 \/ Lbl_59 \/ Lbl_60 \/ Lbl_61 \/ Lbl_62 \/ Lbl_68 \/ Lbl_70 - \/ Lbl_71 \/ Lbl_73 \/ Lbl_72 \/ Lbl_74 \/ Lbl_75 \/ Lbl_76 \/ Lbl_77 - \/ Lbl_78 \/ Lbl_79 \/ Lbl_80 \/ Lbl_81 \/ Lbl_83 \/ Lbl_85 \/ Lbl_82 - \/ Lbl_84 \/ Finished - \/ (* Disjunct to prevent deadlock on termination *) - (pc = "Done" /\ UNCHANGED vars) - -Spec == Init /\ [][Next]_vars - -Termination == <>(pc = "Done") - -\* END TRANSLATION - -NotDone == pc # "Done" - (*************************************************************************) - (* To print a trace of an execution that doesn't produce an error, add *) - (* "INVARIANT NotDone" to Subexpression.cfg . *) - (*************************************************************************) -============================================================================= - - -************************* end file Subexpression.tla ****************************/ - +} + +/************************ + * file Tarjan.tla *********************************** + * ------------------------------ MODULE Tarjan -------------------------------- + * (***************************************************************************) + * (* This version of Tarjan's algorithm for computing the strongly *) (* + * connected components of a directed graph was adapted from the version *) (* + * of 27 Mar 2007 on the Wikipedia page *) (* *) (* + * http://en.wikipedia.org/wiki/Tarjan's_strongly_connected_components_algorithm + * *) (* *) (* One modification is that the set of connected components it + * returns *) (* does not contain a singleton {n} unless n has an edge pointing + * to *) (* itself. *) (* *) (* This is an implementation of the algorithm in + * Tarjan.tla with data *) (* structures that correspond to the ones used in the + * algorithm for *) (* constructing dependency components in semantic/Generator. + * *) (* *) (* *) (* I have not rigorously verified this algorithm, but I think + * I understand *) (* why it works. I have tested it only on the 10 graphs at + * the end of *) (* this file. *) + * (***************************************************************************) + * + * EXTENDS Integers, Sequences, TLC + * + * CONSTANT N, \* The number of nodes Neighbors \* A sequence of edges. + * + * Node == 1..N + * + * ASSUME Neighbors \in Seq(Node \X Node) + * + * Min(a, b) == IF a < b THEN a ELSE b + * + * Front(s) == [i \in 1 .. (Len(s)-1) |-> s[i]] + * (*************************************************************************) + * (* The sequence obtained from a sequence s by deleting the last item. *) + * (*************************************************************************) + * + * Last(s) == s[Len(s)] + * (*************************************************************************) + * (* The last item in the sequence s. *) + * (*************************************************************************) + * + * (***************************************************************************) + * (* I believe the algorithm works because it maintains the following *) (* + * invariants (at reasonable points in the code). We say that node *) (* + * nstack[i] precedes nstack[j] iff i < j. *) (* *) (* - A node n is in nstack + * iff n.dfs \geq 0 *) (* *) (* - For all nodes n # m in nstack, if n precedes m + * then: *) (* /\ n.dfs < m.dfs. *) (* /\ there is a path from n to m in the + * graph. *) (* *) (* - For every node n in nstack, *) (* /\ n.lowlink \leq + * n.dfs and *) (* /\ there is a node m in nstack such that *) (* 1. n.lowlink = + * m.dfs. *) (* 2. if m # n then there is a path from n to m in the graph. *) (* + * /\ For every node m in the same connected component as n: *) (* m.examined = + * TRUE implies m is in nstack *) + * (***************************************************************************) + * + * (************************* --algorithm Tarjan2 variables nstack = << >> ; + * (***************************************************************) (* The + * stack of nodes, where nstack[1] is the bottom of the *) (* stack. *) + * (***************************************************************) max_dfs = 0 + * ; (***************************************************************) (* + * Incremented by 1 every time a new element is added to the *) (* stack. *) + * (***************************************************************) + * + * data = [nd \in Node |-> [lowlink |-> -1, dfs |-> -1, examined |-> FALSE, nbrs + * |-> << >>, (*****************************************) (* A sequence of nodes + * to which there *) (* are edges from this node. The *) (* same node may appear + * multiple *) (* times. *) (*****************************************) nxtDep + * |-> -1 ]] ; (***************************************************************) + * (* The information maintained for the nodes. Note that *) (* *) (* - Node nd + * is on nstack iff data[nd].dfs \geq 0, in *) (* which case nstack[data[nd].dfs + * + 1] = nd. *) (* *) (* - nbrs is set from Neighbors by the initialization + * code. *) (* *) (* - nxtDep represents the nextDependency field of the *) (* + * OpDefNode object, where a value of -1 represents *) (* null. Following nxtDep + * pointers leads in a cycle of *) (* the node's connected component. See the + * comments on *) (* the nextDependency field in semantic/OpDefNode. *) + * (***************************************************************) + * + * idx ; \* A counter variable. + * + * procedure tarjan(n) variable nxt ; \* A counter variable nxtnbr ; \* An + * abbreviation + * + * begin data[n].lowlink := max_dfs || data[n].dfs := max_dfs || + * data[n].examined := TRUE ; max_dfs := max_dfs + 1; nstack := Append(nstack, + * n) ; nxt := 1 ; while nxt \leq Len(data[n].nbrs) do nxtnbr := + * data[n].nbrs[nxt] ; if ~ data[nxtnbr].examined then + * (***************************************************************) (* nxtnbr + * is unexamined *) + * (***************************************************************) call + * tarjan(nxtnbr) ; data[n].lowlink := Min(data[n].lowlink, + * data[nxtnbr].lowlink) ; + * + * elsif data[nxtnbr].dfs \geq 0 then + * (**********************************************************) (* nxtnbr is in + * nstack *) (**********************************************************) + * data[n].lowlink := Min(data[n].lowlink, data[nxtnbr].dfs) ; end if ; nxt := + * nxt + 1; end while ; if data[n].lowlink = data[n].dfs then + * (*****************************************************************) (* The + * set of all nodes on nstack from n to the end form a *) (* connected + * component. *) + * (*****************************************************************) nxtnbr := + * Last(nstack) ; \* save last node on stack. while n # Last(nstack) do + * data[Last(nstack)].nxtDep := nstack[Len(nstack)-1] || data[Last(nstack)].dfs + * := -1 ; (*************************************************************) (* + * Make nxtDep field of last node on stack point to next to *) (* last node. *) + * (*************************************************************) nstack := + * Front(nstack) ; end while ; if \/ n # nxtnbr \/ \E i \in 1..Len(data[n].nbrs) + * : n = data[n].nbrs[i] then + * (**********************************************************) (* Form a + * connected component consisting of just n only *) (* if n is a neighbor of + * itself. *) (**********************************************************) + * data[n].nxtDep := nxtnbr; end if; data[n].dfs := -1 ; nstack := Front(nstack) + * ; end if ; data[n].nbrs := << >> ; + * (**********************************************************************) (* + * Can garbage collect the sequence of neighbors. *) + * (**********************************************************************) + * return ; end procedure + * + * begin + * (************************************************************************) (* + * Initialize the nbrs field of data elements from Neighbors. *) + * (************************************************************************) + * idx := 1 ; while idx \leq Len(Neighbors) do with edge = Neighbors[idx] do + * data[edge[1]].nbrs := Append(data[edge[1]].nbrs, edge[2]) ; end with ; idx := + * idx + 1; end while ; + * + * (************************************************************************) (* + * while there is an unexamined node, call tarjan(nd) *) (* for any unexamined + * node nd. *) + * (************************************************************************) + * idx := 1 ; while idx \leq N do if data[idx].lowlink < 0 then + * (***************************************************************) (* idx is + * unexamined *) + * (***************************************************************) call + * tarjan(idx) ; end if; idx := idx + 1 ; end while ; + * + * (************************************************************************) (* + * print result. *) + * (************************************************************************) + * idx := 1 ; while idx \leq N do if data[idx].nxtDep \geq 0 then print <<idx, + * "->", data[idx].nxtDep>> end if ; idx := idx+1; end while end algorithm ) + * + * \* BEGIN TRANSLATION \* END TRANSLATION + * + * \**** Test data + * + * Node1 == {1, 2, 3, 4} + * + * Nbrs1 == << <<1, 2>>, \* components 1 and 3,4 <<1, 2>>, \* components 1 and + * 3,4 <<1, 1>>, <<1, 1>>, <<3, 4>> , <<3, 4>> , <<4, 3>> , <<4, 3>>, <<4, 3>> + * >> + * + * Nbrs2 == << <<1, 2>>, \* components 1 and 3,4 <<1, 1>>, <<1, 3>>, <<3, 4>>, + * <<4, 3>> >> Nbrs3 == << <<1, 2>>, \* components 1 and 3,4 <<1, 1>>, <<3, 1>>, + * <<3, 4>>, <<4, 3>> >> Nbrs4 == << <<1, 3>>, \* <<1, 8>>, \* componetns: + * 2,3,4,5 and 7,8,9 <<1, 9>>, <<1, 9>>, <<1, 9>>, <<3, 2>>, <<3, 2>>, <<2, 4>>, + * <<4, 5>>, <<4, 10>>, <<5, 3>>, <<8, 7>>, <<8, 7>>, <<9, 8>>, <<7, 9>>, <<7, + * 9>>, <<9, 10>>, <<9, 10>>, <<9, 10>> >> + * + * Nbrs5 == << <<1, 8>>, \* components 7,8,9 <<1, 9>>, <<4, 10>>, <<8, 7>>, <<9, + * 8>>, <<7, 9>>, <<9, 10>> >> + * + * Nbrs6 == << <<6, 4>>, \* components 2,3,4,5 <<3, 4>>, <<4, 5>>, <<5, 2>>, + * <<2, 3>>, <<5, 1>>, <<2, 1>> >> + * + * Nbrs7 == << <<1, 7>>, \* 1 component with 1..9 \ {3} <<1, 9>>, <<2, 1>>, <<3, + * 2>>, <<3, 6>>, <<4, 6>>, <<5, 2>>, <<6, 5>>, <<6, 8>>, <<7, 5>>, <<7, 8>>, + * <<8, 4>>, <<8, 10>>, <<9, 7>>, <<9, 10>> >> Nbrs8 == << <<1, 7>>, \* 1 + * component with 1..8 \ {3} <<2, 1>>, <<3, 2>>, <<3, 2>>, <<3, 2>>, <<3, 6>>, + * <<4, 6>>, <<5, 2>>, <<6, 5>>, <<6, 8>>, <<6, 8>>, <<6, 8>>, <<7, 5>>, <<7, + * 8>>, <<8, 4>>, <<8, 10>>, <<9, 7>>, <<9, 10>> >> Nbrs9 == << \* 4,6, 7,8,9,10 + * <<2, 1>>, <<3, 2>>, <<3, 6>>, <<4, 6>>, <<4, 6>>, <<4, 6>>, <<5, 2>>, <<6, + * 5>>, <<6, 8>>, <<7, 5>>, <<7, 5>>, <<7, 5>>, <<7, 5>>, <<7, 8>>, <<8, 4>>, + * <<8, 10>>, <<9, 7>>, <<10, 9>> >> Nbrs == << \* 1, 3, 4, 5 and 6 <<1, 4>>, + * <<2, 1>>, <<2, 3>>, <<2, 3>>, <<2, 3>>, <<2, 5>>, <<3, 4>>, <<4, 1>>, <<4, + * 5>>, <<4, 5>>, <<4, 5>>, <<5, 3>>, <<6, 5>>, <<6, 6>> >> + * + * ============================================================================= + ************************** + * end file Tarjan.tla + ******************************/ + +/************************* + * file Subexpression.tla ********************** last modified on Fri 13 + * November 2009 at 14:11:05 PST by lamport ------------------------ MODULE + * Subexpression --------------------------- + * + * + * (***************************************************************************) + * (* NAMING *) (* *) (* There are four kinds of Nameable Objects: *) (* - + * Expressions *) (* - Operators that take an argument *) (* (and that can + * appear as operator arguments) *) (* - Operator definitions (named in + * BY/USE/HIDE statements) *) (* - ASSUME/PROVEs. *) (* *) (* There are three + * relevant kinds of primitive names that appear in a *) (* TLA+ module. *) (* + * *) (* Defined Module Name (ModuleName): *) (* It appears to the left of "== + * INSTANCE...". It may or may not *) (* contain arguments. *) (* Primitive + * Operator Name (PrimOpName): *) (* It appear to the left of the "==" in a + * definition, theorem *) (* or assumption. It may or may not contain arguments. + * *) (* Label: *) (* It appears before a "::" in an expression. *) (* *) (* + * Examples of possible primitive names are "Foo" and "Bar(x+1, 42)". *) (* *) + * (* The Built-In Operator names (like "SUBSET") will not be considered to *) + * (* be primitive names in the following. *) (* *) (* To these explicit names + * we add the following Operand Selector (OpSel) *) (* names: *) (* - numbers, + * *) (* - "<<", ">>", *) (* - Things of the form (exp_1, ... , exp_n) where the + * exp_i are *) (* expressions or operator arguments and n > 0. *) (* - "@", *) + * (* *) (* A Compound Name is a sequence of primitive names, operand selectors, + * *) (* and ":"s separated by "!"s. *) (* *) (* In TLA+1, the general way of + * naming a non-built-in operator is with *) (* what I will call an Elementary + * Operator Name (ElemOpName) which has *) (* the following syntax. For + * simplicity, I will ignore the "!"s in the *) (* `formal' syntax. *) (* *) (* + * ElemOpName ::= (ModuleName)* PrimOpName *) (* *) (* We consider this to name + * both the operator and its definition (the *) (* expression to the right of + * the "=="). How the two meanings are *) (* disambiguated will be explained + * below. *) (* *) (* If EON is an ElemOpName and Lab_1, ... , Lab_n are a + * sequence of *) (* label names, then EON ! Lab_1 ! ... ! Lab_N names the + * expression *) (* labeled by Lab_N, where each Lab_(i+1) must be the name of a + * labeled *) (* expression within the expression labeled by Lab_i with no + * labels *) (* between Lab_i and Lab_(i+1). Thus a name described by *) (* *) + * (* ElemOpName (Label)* *) (* *) (* describes an operator definition or a + * labeled expression. To such a *) (* name, we can append a sequence of + * OperandSelectors. By rules *) (* described later, the resulting name selects + * a subexpression (or *) (* ASSUME/PROVE node) or Operator (appearing as an + * operator argument) *) (* from the body of the operator definition or labeled + * expression. If *) (* the labeled expression is a LET/IN, then to this name + * can be appended *) (* an ElemOpName defined in the LET part, and the naming + * process can then *) (* be iterated. The full syntax of a General Name is: *) + * (* *) (* OperatorName ::= ElemOpName *) (* | ElemOpName (Label)* ((OpSel)+ | + * ":") ElemOpName *) (* *) (* GeneralName ::= OperatorName (":" | (Label)* + * (OpSeq)* ) *) (* *) (* Note that ":" is used in two ways: *) (* *) (* - An + * OperatorName not followed by ":" names the operator; one *) (* followed by + * ":" names the operator's definition. *) (* - Suppose a general name GN ending + * in a label names a LET/IN *) (* expression and EON is an ElemOpName. Then GN + * !: !EON names *) (* an operator defined in the LET. The general name GN ! Foo + * would *) (* name an expression labeled by Foo inside the IN clause. *) + * (***************************************************************************) + * + * + * (***************************************************************************) + * (* This +cal program describes an algorithm used in generating the *) (* + * semantic tree for a parse tree consisting of a GeneralId node or an *) (* + * OpApplication node whose first child is a GeneralId node. Two examples *) (* + * are *) (* *) (* Foo(x)!Bar!<<!(a,b)!G *) (* Foo(x)!Bar!<<!(a,b)!G(c) *) (* *) + * (* We assume that these are processed to yield two sequences of nodes: *) (* + * op and args. The sequences for the first are *) (* *) (* op = << "Foo", + * "Bar", "<<", "null", "G" >> *) (* args = << <<x>>, << >>, << >>, <<a, b>>, + * <<>> >> *) (* *) (* and for the second are *) (* *) (* op = << "Foo", "Bar", + * "<<", "null", "G" >> *) (* args = << <<x>>, << >>, << >>, <<a, b>>, <<c>> >> + * *) (* *) (* where each symbol in args represents the semantic node produced + * by that *) (* symbol. *) (* *) (* The other input to the algortithm is an + * integer expectedArity. There *) (* are three cases: *) (* *) (* expectedArity + * = 0 : *) (* The parser is expecting an expression. This is the case *) (* + * when the expression occurs in the first part of a USE *) (* or HIDE + * statement. *) (* *) (* expectedArity > 0 : The parser is expecting an + * operator *) (* argument of this arity. *) (* *) (* expectedArity = -1 : *) (* + * The parser is expecting the name of a definition. This is *) (* the case when + * the expression occurs in the DEF clause of a USE *) (* or HIDE or BY + * statement. *) (* *) (* If expectedArity # 0, then args equals a sequence each + * of whose *) (* element is the empty sequence. *) + * (***************************************************************************) + * + * (***************************************************************************) + * (* Note: This spec is assuming that proof-step numbers are treated like *) (* + * operator names. That is, a step *) (* *) (* <3>2. foo > bar *) (* *) (* will + * be represented by a ThmOrAssumpDefKind node just as if it were *) (* *) (* + * THEOREM <3>2 == foo > bar *) (* *) (* If this is not the case, then + * additional code needs to be added to deal *) (* with numbered steps. But + * however it is handled, the "<3>2" will be a *) (* name in the current symbol + * table. *) + * (***************************************************************************) + * + * EXTENDS Integers, Sequences, TLC + * + * (***************************************************************************) + * (* SeqSeqToSeq(ss) is defined to be the concatenation of the sequences *) (* + * that are the elements of the sequence ss. *) + * (***************************************************************************) + * RECURSIVE SeqSeqToSeq(_) SeqSeqToSeq(ss) == IF ss = << >> THEN << >> ELSE + * Head(ss) \o SeqSeqToSeq(Tail(ss)) + * + * (***************************************************************************) + * (* These are kinds of semantic nodes that can be encountered by the *) (* + * algorithm. See tlasany/sematic/ASTConstants.java. *) + * (***************************************************************************) + * ConstantDeclKind == "ConstantDecl" VariableDeclKind == "VariableDecl" + * BoundSymbolKind == "BoundSymbol" UserDefinedOpKind == "UserDefinedOp" + * ModuleInstanceKind == "ModuleInstance" BuiltInKind == "BuiltIn" OpArgKind == + * "OpArg" OpApplKind == "OpAppl" LetInKind == "LetIn" FormalParamKind == + * "FormalParam" TheoremKind == "Theorem" SubstInKind == "SubstIn" + * AssumeProveKind == "AssumeProve" ProofKind == "Proof" NumeralKind == + * "Numeral" DecimalKind == "Decimal" StringKind == "String" AtNodeKind == + * "AtNode" AssumeKind == "Assume" InstanceKind == "Instance" ThmOrAssumpDefKind + * == "ThmOrAssumpDef" LabelKind == "Label" APSubstInKind == "APSubstIn" + * + * CONSTANT NodeId, \* The set of all node identifiers. Node, \* A mapping from + * NodeId to nodes (which are records). GlobalContext, \* The initial context. + * null, \* A special value not equal to anything else. debug \* Setting to TRUE + * causes TLC to print a trace if the \* algorithm reports an error. + * + * (***************************************************************************) + * (* CONTEXTS *) (* *) (* A context is a mapping from names to NodeIds. There + * are actually three *) (* different kinds of mappings from names to nodes in + * the implementation *) (* that are all represented here as contexts: *) (* - + * The symbolTable of the Generator. *) (* - The context field of a LetInNode *) + * (* - The labels field of a LabelNode *) + * (***************************************************************************) + * IsContext(C) == + * (*************************************************************************) + * (* True iff C is a context. *) + * (*************************************************************************) + * /\ DOMAIN C \subseteq STRING /\ \A str \in DOMAIN C : C[str] \in NodeId + * + * LookUp(nm, ctxt) == + * (*************************************************************************) + * (* The value assigned to name nm by context cxt, or null if there is no *) (* + * value assigned. *) + * (*************************************************************************) + * IF nm \in DOMAIN ctxt THEN Node[ctxt[nm]] ELSE null + * + * Param == [name : STRING, arity : Nat] + * (*************************************************************************) + * (* The set of parameters, which can appear in operator definitions. *) + * (*************************************************************************) + * + * (***************************************************************************) + * (* For simplicity, we assume that argument numbers range from 1 to 9, and *) + * (* we define the following mappings from to and from argument numbers to *) + * (* their string representations. *) + * (***************************************************************************) + * NumberOp == {"1", "2", "3", "4", "5", "6", "7", "8", "9"} NumericVal(numOp) + * == CASE numOp = "1" -> 1 [] numOp = "2" -> 2 [] numOp = "3" -> 3 [] numOp = + * "4" -> 4 [] numOp = "5" -> 5 [] numOp = "6" -> 6 [] numOp = "7" -> 7 [] numOp + * = "8" -> 8 [] numOp = "9" -> 9 + * + * NumToString(num) == CASE num = 1 -> "1" [] num = 2 -> "2" [] num = 3 -> "3" + * [] num = 4 -> "4" [] num = 5 -> "5" [] num = 6 -> "6" [] num = 7 -> "7" [] + * num = 8 -> "8" [] num = 9 -> "9" + * + * IsName(op) == + * (*************************************************************************) + * (* A name is something other than an argument selector that can appear *) (* + * in a compound name. *) + * (*************************************************************************) + * op \in STRING \ ({"<<", ">>", "@", ":", "null"} \cup NumberOp) + * + * + * ArgNum(op, arity) == + * (*************************************************************************) + * (* The argument number chosen by argument selector op for an operator *) (* + * application with arity arguments. It equals -1 if op is not a legal *) (* + * argument selector for this arity. *) + * (*************************************************************************) + * CASE op \in NumberOp -> IF NumericVal(op) <= arity THEN NumericVal(op) ELSE + * -1 [] op = "<<" -> IF arity > 0 THEN 1 ELSE -1 [] op = ">>" -> CASE arity = 1 + * -> 1 [] arity = 2 -> 2 [] OTHER -> -1 [] OTHER -> -1 + * + * Arity(node) == + * (*************************************************************************) + * (* The arity of a node--that is, the number of arguments it takes. *) + * (*************************************************************************) + * IF node.kind \in {UserDefinedOpKind, BuiltInKind, ModuleInstanceKind, + * ThmOrAssumpDefKind, OpArgKind, LabelKind, ConstantDeclKind, FormalParamKind} + * THEN node.arity ELSE 0 + * + * ParamArity(node, i) == + * (*************************************************************************) + * (* The arity of the i-th parameter of the node of kind *) (* + * UserDefinedOpKind, ThmOrAssumpDefKind, ModuleInstanceKind, *) (* + * ConstantDeclKind, or FormalParamKind *) + * (*************************************************************************) + * IF node.kind \in {ConstantDeclKind, FormalParamKind} THEN 0 ELSE + * node.params[i].arity + * ----------------------------------------------------------------------------- + * + * (***************************************************************************) + * (* The following assumptions define the data types--in particular, the *) (* + * record components that each node type must contain. *) + * (***************************************************************************) + * ASSUME IsContext(GlobalContext) + * + * ASSUME DOMAIN Node = NodeId + * + * ASSUME /\ \A id \in NodeId : LET node == Node[id] IN /\ node.kind \in + * {ConstantDeclKind, VariableDeclKind, BoundSymbolKind, UserDefinedOpKind, + * ModuleInstanceKind, BuiltInKind, OpArgKind, OpApplKind, LetInKind, + * FormalParamKind, TheoremKind, SubstInKind, AssumeProveKind, ProofKind, + * NumeralKind, DecimalKind, StringKind, AtNodeKind, AssumeKind, InstanceKind, + * ThmOrAssumpDefKind, LabelKind, APSubstInKind} + * + * ASSUME /\ \A id \in NodeId : LET node == Node[id] IN /\ node.kind \in + * {UserDefinedOpKind, BuiltInKind, ModuleInstanceKind, ThmOrAssumpDefKind, + * LabelKind, ConstantDeclKind, FormalParamKind} => /\ node.name \in STRING /\ + * node.arity \in Nat \cup {-1} /\ (node.arity = -1) => (node.kind = + * BuiltInKind) + * + * ASSUME \A id \in NodeId : LET node == Node[id] IN node.kind \in + * {UserDefinedOpKind, ThmOrAssumpDefKind} => /\ node.body \in NodeId /\ + * IsContext(node.labels) /\ node.params \in Seq(Param) /\ Len(node.params) = + * node.arity /\ node.defined \in BOOLEAN /\ node.source \in {null} \cup NodeId + * \* The original definition (before instantiation) \* or null if this is it. + * + * ASSUME \A id \in NodeId : LET node == Node[id] IN /\ node.kind = OpApplKind + * => /\ node.operands \in Seq(NodeId) /\ node.operator \in NodeId /\ + * node.unboundedBoundSymbols \in Seq(STRING) /\ node.boundedBoundSymbols \in + * Seq(Seq(STRING)) + * (**************************************************************) (* These are + * actually arrays and arrays of arrays of *) (* FormalParamNode objects + * (formerly OpDeclNode objects). *) + * (**************************************************************) /\ + * node.ranges \in Seq(NodeId) /\ Len(node.ranges) = + * Len(node.boundedBoundSymbols) + * + * ASSUME \A id \in NodeId : LET node == Node[id] IN /\ node.kind = LetInKind => + * /\ node.body \in NodeId /\ IsContext(node.context) + * + * ASSUME \A id \in NodeId : LET node == Node[id] IN /\ node.kind = OpArgKind => + * /\ node.op \in NodeId /\ IsName(node.name) /\ (node.arity \in Nat) /\ + * (node.arity > 0) + * + * ASSUME \A id \in NodeId : LET node == Node[id] IN /\ node.kind = LabelKind => + * /\ node.body \in NodeId /\ IsContext(node.labels) /\ node.params \in + * Seq(Param) + * + * ASSUME \A id \in NodeId : LET node == Node[id] IN /\ node.kind = SubstInKind + * => /\ node.body \in NodeId /\ node.subst \in STRING \* Just to identify it. + * + * ASSUME \A id \in NodeId : LET node == Node[id] IN /\ node.kind = + * AssumeProveKind => /\ node.assumes\in Seq(NodeId) /\ node.prove \in NodeId + * + * + * RECURSIVE ExpandNode(_) ExpandNode(node) == CASE node.kind \in + * {UserDefinedOpKind, LetInKind} -> [node EXCEPT !.body = ExpandNode(Node[@])] + * [] node.kind = OpApplKind -> [node EXCEPT !.operands = [i \in DOMAIN @ |-> + * ExpandNode(Node[@[i]])], !.operator = ExpandNode(Node[@]), !.ranges = [i \in + * DOMAIN @ |-> ExpandNode(Node[@[i]])]] [] OTHER -> node + * + * + * ----------------------------------------------------------------------------- + * (***************************************************************************) + * (* Test Data *) (* *) (* The algorithm has been tested on the following sets + * of data, where each *) (* operator MCxxx is either substituted for constant + * parameter xxx or is *) (* used as initial value of the algorithm's input + * variable xxx. *) + * (***************************************************************************) + * + * + * (***************************** + * + * + * (***************************************************************************) + * (* ------------------------ MODULE M ------------------------ *) (* CONSTANT + * C *) (* Op(Arg(_), p) == \A x \in {1,2}, <<y, z>> \in {3} : *) (* lab(x,y,z) + * :: LET a + b == <<Arg(a), b>> *) (* IN 1 + label2 :: p + C *) (* Foo(u) == + * "FooBody(u)" *) (* + * ============================================================= *) (* *) (* + * Inst(d) == INSTANCE M WITH C <- "expr(d)" *) + * (***************************************************************************) + * + * \* MCNode == \* 1 :> [kind |-> UserDefinedOpKind, \* Op(Arg(_), p) == ... \* + * name |-> "Op", \* body |-> 3, \* labels |-> "lab" :> 4, \* arity |-> 2, \* + * params |-> <<[name |-> "Arg", arity |-> 1], \* [name |-> "p", arity |-> 0]>>, + * \* defined |-> TRUE, \* source |-> null ] \* @@ \* 2 :> [kind |-> + * BuiltInKind, \* name |-> "$BoundedForall", \* arity |-> -1 \* ] \* @@ \* 3 :> + * [kind |-> OpApplKind, \* \A x \in {1,2}, <<y, z>> \in {3} : ... \* operands + * |-> << 4 >>, \* operator |-> 2, \* unboundedBoundSymbols |-> <<>>, \* + * boundedBoundSymbols |-> << <<"x">>, <<"y", "z">> >>, \* ranges |-> << 19, 21 + * >>] \* @@ \* 4 :> [kind |-> LabelKind, \* lab(x,y,z) :: LET a + b == + * <<Arg(a), b>> \* name |-> "lab", \* IN 1 + label2 :: p + C \* body |-> 5, \* + * arity |-> 3, \* params |-> <<[name |-> "x", arity |->0], \* [name |-> "y", + * arity |->0], \* [name |-> "z", arity |->0] >>, \* labels |-> "label2" :> 15 ] + * \* @@ \* 5 :> [kind |-> LetInKind, \* LET a + b == <<Arg(a), b>> \* context + * |-> "+" :> 6, \* IN 1 + label2 :: p + C \* body |-> 13 ] \* @@ \* 6 :> [kind + * |-> UserDefinedOpKind, \* a + b == <<Arg(a), b>> \* name |-> "+", \* body |-> + * 7, \* labels |-> << >>, \* arity |-> 2, \* params |-> <<[name |-> "a", arity + * |-> 0], \* [name |-> "b", arity |-> 0]>>, \* defined |-> TRUE, \* source |-> + * null ] \* @@ \* 7 :> [kind |-> OpApplKind, \* <<Arg(a), b>> \* operands |-> + * << 9, 12 >>, \* operator |-> 8, \* unboundedBoundSymbols |-> <<>>, \* + * boundedBoundSymbols |-> << >>, \* ranges |-> << >>] \* @@ \* 8 :> [kind |-> + * BuiltInKind, \* name |-> "$Tuple", \* arity |-> -1 \* ] \* @@ \* 9 :> [kind + * |-> OpApplKind, \* Arg(a) \* operands |-> <<11 >>, \* operator |-> 10, \* + * unboundedBoundSymbols |-> <<>>, \* boundedBoundSymbols |-> << >>, \* ranges + * |-> << >>] \* @@ \* 10 :> [kind |-> FormalParamKind, \* name |-> "Arg", \* + * arity |-> 1] \* \* @@ \* 11 :> [kind |-> FormalParamKind, \* name |-> "a", \* + * arity |-> 0] \* @@ \* 12 :> [kind |-> FormalParamKind, \* name |-> "b", \* + * arity |-> 0] \* @@ \* 13 :> [kind |-> OpApplKind, \* 1 + label2 :: p + C \* + * operands |-> <<14, 15>>, \* operator |-> 6, \* unboundedBoundSymbols |-> + * <<>>, \* boundedBoundSymbols |-> << >>, \* ranges |-> << >>] \* @@ \* 14 :> + * [kind |-> NumeralKind, \* val |-> 1] \* @@ \* 15 :> [kind |-> LabelKind, \* + * label2 :: p + C \* name |-> "label2", \* body |-> 16, \* arity |-> 0, \* + * params |-> <<>>, \* labels |-> << >>] \* @@ \* 16 :> [kind |-> OpApplKind, \* + * p + C \* operands |-> <<17, 18>>, \* operator |-> 6, \* unboundedBoundSymbols + * |-> <<>>, \* boundedBoundSymbols |-> << >>, \* ranges |-> << >>] \* @@ \* 17 + * :> [kind |-> FormalParamKind, \* name |-> "p", \* arity |-> 0] \* @@ \* 18 :> + * [kind |-> ConstantDeclKind, \* name |-> "C", \* arity |-> 0 ] \* @@ \* 19 :> + * [kind |-> OpApplKind, \* {1,2} \* operands |-> << 14, 20 >>, \* operator |-> + * 8, \* Actually, specifying <<1, 2>> \* unboundedBoundSymbols |-> << >>, \* + * boundedBoundSymbols |-> << >>, \* ranges |-> << >>] \* @@ \* 20 :> [kind |-> + * NumeralKind, \* val |-> 2] \* @@ \* 21 :> [kind |-> OpApplKind, \* {1,2} \* + * operands |-> << 22>>, \* operator |-> 8, \* Actually, specifying <<1, 2>> \* + * unboundedBoundSymbols |-> << >>, \* boundedBoundSymbols |-> << >>, \* ranges + * |-> << >>] \* @@ \* 22 :> [kind |-> NumeralKind, \* val |-> 3] \* @@ \* 23 :> + * [kind |-> UserDefinedOpKind, \* Foo(u) == "FooBody(u)" \* name |-> "Foo", \* + * body |-> 24, \* labels |-> << >>, \* arity |-> 1, \* params |-> <<[name |-> + * "u", arity |-> 0]>>, \* defined |-> TRUE, \* source |-> null ] \* @@ \* 24 :> + * [kind |-> StringKind, \* val |-> "FooBody(u)"] \* @@ \* 25 :> [kind |-> + * StringKind, \* val |-> "string"] \* @@ \* 26 :> [kind |-> ModuleInstanceKind, + * \* Inst(d) == INSTANCE M WITH C <- "expr(d)" \* name |-> "Inst", \* arity |-> + * 1, \* params |-> <<[name |-> "d", arity |-> 0] >>, \* defined |-> TRUE ] + * \* @@ \* 27 :> [kind |-> SubstInKind, \* body |-> 24, \* subst |-> "expr(d)"] + * \* @@ \* 28 :> [kind |-> UserDefinedOpKind, \* Foo(u) == "FooBody(u)" \* name + * |-> "Inst!Foo", \* body |-> 27, \* labels |-> << >>, \* arity |-> 2, \* + * params |-> << [name |-> "d", arity |-> 0], \* [name |-> "u", arity |-> 0]>>, + * \* defined |-> TRUE, \* source |-> 23 ] \* @@ \* 29 :> [kind |-> + * UserDefinedOpKind, \* Op(Arg(_), p) == ... \* name |-> "Inst!Op", \* body |-> + * 30, \* labels |-> "lab" :> 4, \* arity |-> 3, \* params |-> <<[name |-> "d", + * arity |-> 0], \* [name |-> "Arg", arity |-> 1], \* [name |-> "p", arity |-> + * 0]>>, \* defined |-> TRUE, \* source |-> 1 ] \* @@ \* 30 :> [kind |-> + * SubstInKind, \* body |-> 3, \* subst |-> "expr(d)"] \* @@ \* 31 :> [kind |-> + * StringKind, \* val |-> "Argm"] \* \* MCNodeId == 1..31 \* \* MCGlobalContext + * == \* "Op" :> 1 \* @@ \* "Foo" :> 23 \* @@ \* "Inst" :> 26 \* @@ \* + * "Inst!Foo" :> 28 \* @@ \* "Inst!Op" :> 29 \* \* \* Foo("string") \* \* MCops + * == <<"Foo">> \* \* MCargs == << <<25>>>> \* \* MCexpectedArity == 0 \* \* \* + * Op(Foo, "string") \* \* MCops == <<"Op">> \* \* MCargs == << <<23, 25>> >> \* + * \* MCexpectedArity == 0 \* \* \* Op(Foo, "string")!lab(1,2,3) \* \* MCops == + * <<"Op", "lab">> \* \* MCargs == << <<23, 25>> , <<14, 20, 22 >> >> \* \* + * MCexpectedArity == 0 \* \* \* Op(Foo, "string")!lab(1,2,3)!<< \* \* MCops == + * <<"Op", "lab", "<<">> \* \* MCargs == << <<23, 25>> , <<14, 20, 22>> , << >> + * >> \* \* MCexpectedArity == 0 \* \* \* Op(Foo, "string")!lab(1,2,3)!<<!>> \* + * \* MCops == <<"Op", "lab", "<<", ">>">> \* \* MCargs == << <<23, 25>> , <<14, + * 20, 22>> , << >>, << >> >> \* \* MCexpectedArity == 0 \* \* \* Op(Foo, + * "string")!lab(1,2,3)!label2 \* \* MCops == <<"Op", "lab", "label2">> \* \* + * MCargs == << <<23, 25>> , <<14, 20, 22>> , << >> >> \* \* MCexpectedArity == + * 0 \* \* \* Op(Foo, "string")!lab(1,2,3)!label2!<< \* \* MCops == <<"Op", + * "lab", "label2", "<<">> \* \* MCargs == << <<23, 25>> , <<14, 20, 22>> , << + * >>, << >> >> \* \* MCexpectedArity == 0 \* \* \* Op(Foo, + * "string")!lab(1,2,3)!:!+(3,2) \* \* MCops == <<"Op", "lab", ":", "+" >> \* \* + * MCargs == << <<23, 25>> , <<14, 20, 22>> , << >> , <<22, 20 >> >> \* \* + * MCexpectedArity == 0 \* \* \* Op(Foo, "string")!lab(1,2,3)!:!+(3,2)!>> \* \* + * MCops == <<"Op", "lab", ":", "+", ">>" >> \* \* MCargs == << <<23, 25>> , + * <<14, 20, 22>> , << >> , <<22, 20 >>, << >> >> \* \* MCexpectedArity == 0 \* + * \* \* Op(Foo, "string")!(1,2,3) \* \* MCops == <<"Op", "null" >> \* \* MCargs + * == << <<23, 25>> , <<14, 20, 22>> >> \* \* MCexpectedArity == 0 \* \* \* + * Op(Foo, "string")!(1,2,3)!>>!>>!<< \* \* MCops == <<"Op", "null", ">>", ">>", + * "<<" >> \* \* MCargs == << <<23, 25>> , <<14, 20, 22>>, << >>, << >>, << >> + * >> \* \* MCexpectedArity == 0 \* \* \* Op(Foo, "string")!<<!>> \* \* MCops == + * <<"Op", "<<", ">>" >> \* \* MCargs == << <<23, 25>>, << >>, << >> >> \* \* + * MCexpectedArity == 0 \* \* \* Inst("Argm")!Foo("string") \* \* MCops == + * <<"Inst", "Foo">> \* \* MCargs == << <<31>>, <<25>>>> \* \* MCexpectedArity + * == 0 \* \* \* Inst("Argm")!Op(Foo, "string") \* \* MCops == <<"Inst", "Op">> + * \* \* MCargs == << <<31>>, <<23, 25>> >> \* \* MCexpectedArity == 0 \* \* \* + * Inst("Argm")!Op(Foo, "string")!lab(1,2,3) \* \* MCops == <<"Inst", "Op", + * "lab">> \* \* MCargs == << <<31>>, <<23, 25>> , <<14, 20, 22 >> >> \* \* + * MCexpectedArity == 0 \* \* \* Inst("Argm")!Op(Foo, "string")!lab(1,2,3)!<< \* + * \* MCops == <<"Inst", "Op", "lab", "<<">> \* \* MCargs == << <<31>>, <<23, + * 25>> , <<14, 20, 22>> , << >> >> \* \* MCexpectedArity == 0 \* \* \* + * Inst("Argm")!Op(Foo, "string")!lab(1,2,3)!<<!>> \* \* MCops == <<"Inst", + * "Op", "lab", "<<", ">>">> \* \* MCargs == << <<31>>, <<23, 25>> , <<14, 20, + * 22>> , << >>, << >> >> \* \* MCexpectedArity == 0 \* \* \* + * Inst("Argm")!Op(Foo, "string")!lab(1,2,3)!label2 \* \* MCops == <<"Inst", + * "Op", "lab", "label2">> \* \* MCargs == << <<31>>, <<23, 25>> , <<14, 20, + * 22>> , << >> >> \* \* MCexpectedArity == 0 \* \* \* Inst("Argm")!Op(Foo, + * "string")!lab(1,2,3)!label2!<< \* \* MCops == <<"Inst", "Op", "lab", + * "label2", "<<">> \* \* MCargs == << <<31>>, <<23, 25>> , <<14, 20, 22>> , << + * >>, << >> >> \* \* MCexpectedArity == 0 \* \* \* Inst("Argm")!Op(Foo, + * "string")!lab(1,2,3)!:!+(3,2) \* \* MCops == <<"Inst", "Op", "lab", ":", "+" + * >> \* \* MCargs == << <<31>>, <<23, 25>> , <<14, 20, 22>> , << >> , <<22, 20 + * >> >> \* \* MCexpectedArity == 0 \* \* \* Inst("Argm")!Op(Foo, + * "string")!lab(1,2,3)!:!+(3,2)!>> \* \* MCops == <<"Inst", "Op", "lab", ":", + * "+", ">>" >> \* \* MCargs == << <<31>>, <<23, 25>> , <<14, 20, 22>> , << >> , + * <<22, 20 >>, << >> >> \* \* MCexpectedArity == 0 \* \* \* + * Inst("Argm")!Op(Foo, "string")!(1,2,3) \* \* MCops == <<"Inst", "Op", "null" + * >> \* \* MCargs == << <<31>>, <<23, 25>> , <<14, 20, 22>> >> \* \* + * MCexpectedArity == 0 \* \* \* Inst("Argm")!Op(Foo, "string")!(1,2,3)!>>!>>!<< + * \* \* MCops == <<"Inst", "Op", "null", ">>", ">>", "<<" >> \* \* MCargs == << + * <<31>>, <<23, 25>> , <<14, 20, 22>>, << >>, << >>, << >> >> \* \* + * MCexpectedArity == 0 \* \* \* Inst("Argm")!Op(Foo, "string")!<<!>> \* \* + * MCops == <<"Inst", "Op", "<<", ">>" >> \* \* MCargs == << <<31>>, <<23, 25>>, + * << >>, << >> >> \* \* MCexpectedArity == 0 + ***************************************** + * ) + * + * (***************************************************************************) + * (* ------------------------ MODULE M ------------------------ *) (* CONSTANT + * C, Arg1(_) *) (* Op(Arg, p) == \A x \in {1,2}, <<y, z>> \in {3} : *) (* + * lab(x,y,z) :: LET a + b == <<Arg1(a), b>> *) (* IN 1 + label2 :: p + C *) (* + * Bar(AOp(_)) == AOp(1) *) (* Foo(u) == Bar(Arg1) *) (* Foo2 == Bar(LAMBDA m, n + * : <<m, n>>) *) (* THEOREM Thm == ASSUME Arg1(1) , Bar(Arg1) PROVE << C, + * Bar(Arg1)>> *) (* UU == lab:: C *) (* + * ============================================================= *) (* *) (* + * Foo3 == \A x : LET Bar3 == 1 IN 2 *) (* Inst(d) == INSTANCE M WITH C <- + * "expr(d)" *) + * (***************************************************************************) + * + * + * MCNode == 1 :> [kind |-> UserDefinedOpKind, \* Op(Arg, p) == ... name |-> + * "Op", body |-> 3, labels |-> "lab" :> 4, arity |-> 2, params |-> <<[name |-> + * "Arg", arity |-> 0], [name |-> "p", arity |-> 0]>>, defined |-> TRUE, source + * |-> null ] + * + * @@ 2 :> [kind |-> BuiltInKind, name |-> "$BoundedForall", arity |-> -1 ] @@ 3 + * :> [kind |-> OpApplKind, \* \A x \in {1,2}, <<y, z>> \in {3} : ... operands + * |-> << 4 >>, operator |-> 2, unboundedBoundSymbols |-> <<>>, + * boundedBoundSymbols |-> << <<"x">>, <<"y", "z">> >>, ranges |-> << 19, 21 + * >>] @@ 4 :> [kind |-> LabelKind, \* lab(x,y,z) :: LET a + b == <<Arg(a), b>> + * name |-> "lab", \* IN 1 + label2 :: p + C body |-> 5, arity |-> 3, params |-> + * <<[name |-> "x", arity |->0], [name |-> "y", arity |->0], [name |-> "z", + * arity |->0] >>, labels |-> "label2" :> 15 ] @@ 5 :> [kind |-> LetInKind, \* + * LET a + b == <<Arg(a), b>> context |-> "+" :> 6, \* IN 1 + label2 :: p + C + * body |-> 13 ] @@ 6 :> [kind |-> UserDefinedOpKind, \* a + b == <<Arg(a), b>> + * name |-> "+", body |-> 7, labels |-> << >>, arity |-> 2, params |-> <<[name + * |-> "a", arity |-> 0], [name |-> "b", arity |-> 0]>>, defined |-> TRUE, + * source |-> null ] @@ 7 :> [kind |-> OpApplKind, \* <<Arg(a), b>> operands |-> + * << 9, 12 >>, operator |-> 8, unboundedBoundSymbols |-> <<>>, + * boundedBoundSymbols |-> << >>, ranges |-> << >>] @@ 8 :> [kind |-> + * BuiltInKind, name |-> "$Tuple", arity |-> -1 ] @@ 9 :> [kind |-> OpApplKind, + * \* Arg1(a) operands |-> <<11 >>, operator |-> 10, unboundedBoundSymbols |-> + * <<>>, boundedBoundSymbols |-> << >>, ranges |-> << >>] @@ 10 :> [kind |-> + * ConstantDeclKind, name |-> "Arg1", arity |-> 1] + * + * @@ 11 :> [kind |-> FormalParamKind, name |-> "a", arity |-> 0] @@ 12 :> [kind + * |-> FormalParamKind, name |-> "b", arity |-> 0] @@ 13 :> [kind |-> + * OpApplKind, \* 1 + label2 :: p + C operands |-> <<14, 15>>, operator |-> 6, + * unboundedBoundSymbols |-> <<>>, boundedBoundSymbols |-> << >>, ranges |-> << + * >>] @@ 14 :> [kind |-> NumeralKind, val |-> 1] @@ 15 :> [kind |-> LabelKind, + * \* label2 :: p + C name |-> "label2", body |-> 16, arity |-> 0, params |-> + * <<>>, labels |-> << >>] @@ 16 :> [kind |-> OpApplKind, \* p + C operands |-> + * <<17, 18>>, operator |-> 6, unboundedBoundSymbols |-> <<>>, + * boundedBoundSymbols |-> << >>, ranges |-> << >>] @@ 17 :> [kind |-> + * FormalParamKind, name |-> "p", arity |-> 0] @@ 18 :> [kind |-> + * ConstantDeclKind, name |-> "C", arity |-> 0 ] @@ 19 :> [kind |-> OpApplKind, + * \* {1,2} operands |-> << 14, 20 >>, operator |-> 8, \* Actually, specifying + * <<1, 2>> unboundedBoundSymbols |-> << >>, boundedBoundSymbols |-> << >>, + * ranges |-> << >>] @@ 20 :> [kind |-> NumeralKind, val |-> 2] @@ 21 :> [kind + * |-> OpApplKind, \* {1,2} operands |-> << 22>>, operator |-> 8, \* Actually, + * specifying <<1, 2>> unboundedBoundSymbols |-> << >>, boundedBoundSymbols |-> + * << >>, ranges |-> << >>] @@ 22 :> [kind |-> NumeralKind, val |-> 3] @@ 23 :> + * [kind |-> UserDefinedOpKind, \* Foo(u) == Bar(Arg1) name |-> "Foo", body |-> + * 24, labels |-> << >>, arity |-> 1, params |-> <<[name |-> "u", arity |-> + * 0]>>, defined |-> TRUE, source |-> null ] @@ 24 :> [kind |-> OpApplKind, \* + * Bar(Arg1) operands |-> <<34>>, operator |-> 32, unboundedBoundSymbols |-> + * <<>>, boundedBoundSymbols |-> << >>, ranges |-> << >>] @@ 25 :> [kind |-> + * StringKind, val |-> "string"] @@ 26 :> [kind |-> ModuleInstanceKind, \* + * Inst(d) == INSTANCE M WITH C <- "expr(d)" name |-> "Inst", arity |-> 1, + * params |-> <<[name |-> "d", arity |-> 0] >>, defined |-> TRUE ] @@ 27 :> + * [kind |-> SubstInKind, body |-> 24, subst |-> "expr(d)"] @@ 28 :> [kind |-> + * UserDefinedOpKind, \* Foo(u) == "FooBody(u)" name |-> "Inst!Foo", body |-> + * 27, labels |-> << >>, arity |-> 2, params |-> << [name |-> "d", arity |-> 0], + * [name |-> "u", arity |-> 0]>>, defined |-> TRUE, source |-> 23 ] @@ 29 :> + * [kind |-> UserDefinedOpKind, \* Op(Arg, p) == ... name |-> "Inst!Op", body + * |-> 30, labels |-> "lab" :> 4, arity |-> 3, params |-> <<[name |-> "d", arity + * |-> 0], [name |-> "Arg", arity |-> 0], [name |-> "p", arity |-> 0]>>, defined + * |-> TRUE, source |-> 1 ] @@ 30 :> [kind |-> SubstInKind, body |-> 3, subst + * |-> "expr(d)"] @@ 31 :> [kind |-> StringKind, val |-> "Argm"] @@ 32 :> [kind + * |-> UserDefinedOpKind, \* Bar(AOp(_)) == AOp(1) name |-> "Bar", body |-> 33, + * labels |-> << >>, arity |-> 1, params |-> <<[name |-> "AOp", arity |-> 1]>>, + * defined |-> TRUE, source |-> null ] @@ 33 :> [kind |-> OpApplKind, \* Arg1(1) + * operands |-> <<14>>, operator |-> 10, unboundedBoundSymbols |-> <<>>, + * boundedBoundSymbols |-> << >>, ranges |-> << >>] @@ 34 :> [kind |-> + * OpArgKind, op |-> 10, arity |-> 1, name |-> "Arg1"] @@ 35 :> [kind |-> + * UserDefinedOpKind, \* Foo2 == Bar(LAMBDA m, n : <<m, n>>) name |-> "Foo2", + * body |-> 36, labels |-> << >>, arity |-> 0, params |-> <<>>, defined |-> + * TRUE, source |-> null ] @@ 36 :> [kind |-> OpApplKind, \* Bar(LAMBDA m, n : + * <<m, n>>) operands |-> <<41>>, operator |-> 32, unboundedBoundSymbols |-> + * <<>>, boundedBoundSymbols |-> << >>, ranges |-> << >>] @@ 37 :> [kind |-> + * UserDefinedOpKind, \* LAMBDA m, n : <<m, n>> name |-> "LAMBDA", body |-> 38, + * labels |-> << >>, arity |-> 2, params |-> <<[name |-> "m", arity |-> 0], + * [name |-> "n", arity |-> 0]>>, defined |-> TRUE, source |-> null ] @@ 38 :> + * [kind |-> OpApplKind, \* <<m, n>> operands |-> << 39, 40 >>, operator |-> 8, + * unboundedBoundSymbols |-> <<>>, boundedBoundSymbols |-> << >>, ranges |-> << + * >>] @@ 39 :> [kind |-> FormalParamKind, name |-> "m", arity |-> 0] @@ 40 :> + * [kind |-> FormalParamKind, name |-> "n", arity |-> 0] @@ 41 :> [kind |-> + * OpArgKind, \* LAMBDA m, n : <<m, n>> op |-> 37, arity |-> 2, name |-> + * "LAMBDA"] @@ 42 :> [kind |-> UserDefinedOpKind, \* Inst!Foo2 == Bar(LAMBDA m, + * n : <<m, n>>) name |-> "Inst!Foo2", body |-> 43, labels |-> << >>, arity |-> + * 1, params |-> <<[name |-> "d", arity |-> 0]>>, defined |-> TRUE, source |-> + * 35 ] @@ 43 :> [kind |-> SubstInKind, body |-> 36, subst |-> "expr(d)"] @@ 44 + * :> [kind |-> ThmOrAssumpDefKind, \* Thm == ASSUME Arg1(1) ... name |-> "Thm", + * body |-> 45, labels |-> << >>, arity |-> 0, params |-> << >> , defined |-> + * TRUE, source |-> null ] @@ 45 :> [kind |-> AssumeProveKind, assumes |-> <<33, + * 24>>, prove |-> 46 ] @@ 46 :> [kind |-> OpApplKind, \* <<c, Bar(Arg1)>> + * operands |-> << 59, 24 >>, operator |-> 8, unboundedBoundSymbols |-> <<>>, + * boundedBoundSymbols |-> << >>, ranges |-> << >>] @@ 47 :> [kind |-> + * ThmOrAssumpDefKind, \* Thm == ASSUME Arg1(1) ... name |-> "Inst!Thm", body + * |-> 48, labels |-> << >>, arity |-> 1, params |-> << [name |-> "d", arity |-> + * 0]>> , defined |-> TRUE, source |-> 44 ] @@ 48 :> [kind |-> SubstInKind, body + * |-> 45, subst |-> "expr(d)"] @@ 49 :> [kind |-> UserDefinedOpKind, \* Foo3 == + * \A x : LET ... name |-> "Foo3", body |-> 51, labels |-> << >>, arity |-> 0, + * params |-> <<>>, defined |-> TRUE, source |-> null ] @@ 50 :> [kind |-> + * LetInKind, \* Foo3 == \A x : LET Bar3 == 1 IN 2 context |-> "Bar3" :> 53, + * body |-> 13 ] @@ 51 :> [kind |-> OpApplKind, \* \A x : LET Bar3 == 1 IN 2 + * operands |-> << 50 >>, operator |-> 52, unboundedBoundSymbols |-> <<"x">>, + * boundedBoundSymbols |-> << >>, ranges |-> << >>] @@ 52 :> [kind |-> + * BuiltInKind, \* \A x : name |-> "$UnboundedForall", arity |-> -1] @@ 53 :> + * [kind |-> UserDefinedOpKind, \* Bar3 == 1 name |-> "Bar3", body |-> 14, + * labels |-> << >>, arity |-> 0, params |-> <<>>, defined |-> TRUE, source |-> + * null ] @@ 54 :> [kind |-> FormalParamKind, \* AOp name |-> "AOp", arity |-> + * 1] @@ 55 :> [kind |-> UserDefinedOpKind, \* Bar(AOp(_)) == AOp(1) name |-> + * "Inst!Bar", body |-> 56, labels |-> << >>, arity |-> 2, params |-> <<[name + * |-> "d", arity |-> 0], [name |-> "AOp", arity |-> 1]>>, defined |-> TRUE, + * source |-> null ] @@ 56 :> [kind |-> SubstInKind, body |-> 33, subst |-> + * "expr(d)"] @@ 57 :> [kind |-> UserDefinedOpKind, \* UU == lab:: C name |-> + * "UU", body |-> 58, labels |-> "lab":> 58, arity |-> 0, params |-> <<>>, + * defined |-> TRUE, source |-> null ] @@ 58 :> [kind |-> LabelKind, \* lab :: C + * name |-> "lab", body |-> 59, arity |-> 0, params |-> <<>>, labels |-> << >>] + * + * @@ 59 :> [kind |-> OpApplKind, \* C operands |-> <<>>, operator |-> 18, + * unboundedBoundSymbols |-> <<>>, boundedBoundSymbols |-> << >>, ranges |-> << + * >>] @@ 60 :> [kind |-> UserDefinedOpKind, \* UU == lab:: C name |-> + * "Inst!UU", body |-> 58, labels |-> "lab":> 58, arity |-> 1, params |-> + * <<[name |-> "d", arity |-> 0] >>, defined |-> TRUE, source |-> null ] + * + * + * MCNodeId == 1..60 + * + * MCGlobalContext == "Op" :> 1 @@ "C" :> 18 @@ "UU" :> 57 @@ "Foo" :> 23 @@ + * "Inst" :> 26 @@ "Inst!Foo" :> 28 @@ "Inst!Bar" :> 55 @@ "Inst!Op" :> 29 @@ + * "Inst!UU" :> 60 @@ "Foo2" :> 35 @@ "Inst!Foo2" :> 42 @@ "Thm" :> 44 @@ + * "Inst!Thm" :> 47 @@ "Foo3" :> 49 @@ "Bar" :> 32 @@ "Arg1" :> 10 @@ "AOp" :> + * 54 @@ "p" :> 17 + * + * \* Foo \* MCops == <<"Foo">> \* MCargs == << << >> >> \* MCexpectedArity == 1 + * + * \* Op() \* MCops == <<"Op">> \* MCargs == << <<>> >> \* MCexpectedArity == 2 + * + * \* Op!lab \* MCops == <<"Op", "lab">> \* MCargs == << <<>> , <<>> >> \* + * MCexpectedArity == 5 + * + * \* Op!lab!<< \* MCops == <<"Op", "lab", "<<">> \* MCargs == << <<>> , <<>> , + * << >> >> \* MCexpectedArity == 5 + * + * \* Op!lab!<<!>> \* MCops == <<"Op", "lab", "<<", ">>">> \* MCargs == << << >> + * , << >> , << >>, << >> >> \* MCexpectedArity == 5 + * + * \* Op!lab!label2 \* MCops == <<"Op", "lab", "label2">> \* MCargs == << << >> + * , << >> , << >> >> \* MCexpectedArity == 5 + * + * \* Op!lab!label2!<< \* MCops == <<"Op", "lab", "label2", "<<">> \* MCargs == + * << << >> , << >> , << >>, << >> >> \* MCexpectedArity == 5 + * + * \* Op!lab!:!+ \* MCops == <<"Op", "lab", ":", "+" >> \* MCargs == << << >> , + * << >> , << >> , << >> >> \* MCexpectedArity == 7 + * + * \* Op!lab!:!+ \* MCops == <<"Op", "lab", ":", "+", ">>" >> \* MCargs == << << + * >> , << >> , << >> , << >>, << >> >> \* MCexpectedArity == 7 + * + * \* Op!@ \* MCops == <<"Op", "@" >> \* MCargs == << << >> , << >> >> \* + * MCexpectedArity == 5 + * + * \* Op!@!>>!>>!<< \* MCops == <<"Op", "@", ">>", ">>", "<<" >> \* MCargs == << + * << >> , << >>, << >>, << >>, << >> >> \* MCexpectedArity == 5 + * + * \* Op!<<!>> \* MCops == <<"Op", "<<", ">>" >> \* MCargs == << << >>, << >>, + * << >> >> \* MCexpectedArity == 2 + * + * \* Inst!Foo \* MCops == <<"Inst", "Foo">> \* MCargs == << << >>, << >> >> \* + * MCexpectedArity == 2 + * + * \* Inst!Op \* MCops == <<"Inst", "Op">> \* MCargs == << << >>, << >> >> \* + * MCexpectedArity == 3 + * + * \* Inst!Op!lab \* MCops == <<"Inst", "Op", "lab">> \* MCargs == << << >>, << + * >> , << >> >> \* MCexpectedArity == 6 + * + * \* Inst!Op!lab!<< \* MCops == <<"Inst", "Op", "lab", "<<">> \* MCargs == << + * << >>, << >> , << >> , << >> >> \* MCexpectedArity == 6 + * + * \* Inst!Op!lab!<<!>> \* MCops == <<"Inst", "Op", "lab", "<<", ">>">> \* + * MCargs == << << >>, << >> , << >> , << >>, << >> >> \* MCexpectedArity == 6 + * + * \* Inst!Op!lab!label2 \* MCops == <<"Inst", "Op", "lab", "label2">> \* MCargs + * == << << >>, << >> , << >> , << >> >> \* MCexpectedArity == 6 + * + * \* Inst!Op!lab!label2!<< \* MCops == <<"Inst", "Op", "lab", "label2", "<<">> + * \* MCargs == << << >>, << >> , << >> , << >>, << >> >> \* MCexpectedArity == + * 6 + * + * \* Inst!Op!lab!:!+ \* MCops == <<"Inst", "Op", "lab", ":", "+" >> \* MCargs + * == << << >>, << >> , << >> , << >> , << >> >> \* MCexpectedArity == 8 + * + * \* Inst!Op!lab!:!+!>> \* MCops == <<"Inst", "Op", "lab", ":", "+", ">>" >> \* + * MCargs == << << >>, << >> , << >> , << >> , << >>, << >> >> \* + * MCexpectedArity == 8 + * + * \* Inst!Op!@ \* MCops == <<"Inst", "Op", "@" >> \* MCargs == << << >>, << >> + * , << >> >> \* MCexpectedArity == 6 + * + * \* Inst!Op!@!>>!>>!<< \* MCops == <<"Inst", "Op", "@", ">>", ">>", "<<" >> \* + * MCargs == << << >>, << >> , << >>, << >>, << >>, << >> >> \* MCexpectedArity + * == 6 + * + * \* Inst!Op!<<!>> \* MCops == <<"Inst", "Op", "<<", ">>" >> \* MCargs == << << + * >>, << >>, << >>, << >> >> \* MCexpectedArity == 3 + * + * \* Foo!1 \* MCops == <<"Foo", "1">> \* MCargs == << << >>, << >> >> \* + * MCexpectedArity == 2 + * + * \* Inst!Foo!1 \* MCops == <<"Inst", "Foo", "1">> \* MCargs == << << >>, << + * >>, << >> >> \* MCexpectedArity == 3 + * + * \* Foo2!1 \* MCops == <<"Foo2", "1">> \* MCargs == << << >>, << >> >> \* + * MCexpectedArity == 2 + * + * \* Foo2!1!(1, 2) \* MCops == <<"Foo2", "1", "null">> \* MCargs == << << >>, + * << >>, <<14, 20>> >> \* MCexpectedArity == 0 + * + * \* Foo2!1!(1, 2)!2 \* MCops == <<"Foo2", "1", "null", "2">> \* MCargs == << + * << >>, << >>, <<14, 20>>, << >> >> \* MCexpectedArity == 0 + * + * \* Inst!Foo2!1 \* MCops == <<"Inst", "Foo2", "1">> \* MCargs == << << >>, << + * >>, << >> >> \* MCexpectedArity == 3 + * + * \* Inst!Foo2!1 \* MCops == <<"Inst", "Foo2", "1", "@">> \* MCargs == << << + * >>, << >>, << >>, << >> >> \* MCexpectedArity == 3 + * + * \* Inst!Foo2!1!2 \* MCops == <<"Inst", "Foo2", "1", "@", "2">> \* MCargs == + * << << >>, << >>, << >>, << >>, << >> >> \* MCexpectedArity == 3 + * + * \* Inst("Argm")!Foo2!1!(1, 2) \* MCops == <<"Inst", "Foo2", "1", "null">> \* + * MCargs == << <<31>>, << >>, << >>, <<14, 20>> >> \* MCexpectedArity == 0 + * + * \* Inst("Argm")!Foo2!1!(1, 2)!2 \* MCops == <<"Inst", "Foo2", "1", "null", + * "2">> \* MCargs == << <<31>>, << >>, << >>, <<14, 20>>, << >> >> \* + * MCexpectedArity == 0 + * + * \* Thm!1 \* MCops == <<"Thm", "1" >> \* MCargs == << << >>, << >> >> \* + * MCexpectedArity == 0 + * + * \* Thm!2 \* MCops == <<"Thm", "2" >> \* MCargs == << << >>, << >> >> \* + * MCexpectedArity == 0 + * + * \* Thm!3!<< \* MCops == <<"Thm", "3", "<<" >> \* MCargs == << << >>, << >>, + * << >> >> \* MCexpectedArity == 0 + * + * \* Inst("Argm")!Thm!1 \* MCops == <<"Inst", "Thm", "1" >> \* MCargs == << + * <<31>>, << >>, << >> >> \* MCexpectedArity == 0 + * + * \* Inst("Argm")!Thm!2 \* MCops == <<"Inst", "Thm", "2" >> \* MCargs == << + * <<31>>, << >>, << >> >> \* MCexpectedArity == 0 + * + * \* Inst("Argm")!Thm!3!<< \* MCops == <<"Inst", "Thm", "3", "<<" >> \* MCargs + * == << <<31>>, << >>, << >>, << >> >> \* MCexpectedArity == 0 + * + * \* Inst("Argm")!UU \* MCops == <<"Inst", "UU">> \* MCargs == << <<31>>, << >> + * >> \* MCexpectedArity == 0 + * + * \* Inst("Argm")!Bar(Arg1)!1<< \* MCops == <<"Inst", "Bar", "1" >> \* MCargs + * == << <<31>>, <<10 >>, << >> >> \* MCexpectedArity == 0 + * + * \* Inst("Argm")!Bar(Arg1)!1<< \* MCops == <<"Inst", "Bar", "1" >> \* MCargs + * == << <<31>>, <<10 >>, << >> >> \* MCexpectedArity == 0 + * + * \* Inst("Argm")!Thm \* Note: doesn't report an error because \* MCops == + * <<"Inst", "Thm" >> \* Inst!Thm isn't expanded \* MCargs == << <<31>>, << >> + * >> \* MCexpectedArity == 0 + * + * \* Inst("Argm")!Thm!: ERROR! \* MCops == <<"Inst", "Thm", ":" >> \* MCargs == + * << <<31>>, << >>, << >> >> \* MCexpectedArity == 0 + * + * + * \* Foo2 \* MCops == <<"Foo2">> \* MCargs == <<<< >> >> \* MCexpectedArity == + * 0 + * + * \* Foo3!(3)!:Bar3 \* MCops == <<"Foo3", "null", "Bar3">> \* MCargs == << << + * >>, <<22>>, << >> >> \* MCexpectedArity == 0 + * + * \* Bar(Arg1) \* \* MCops == <<"Bar">> \* \* MCargs == << <<10>> >> \* \* + * MCexpectedArity == 0 + * + * \* Arg1(C) \* MCops == <<"Arg1">> \* MCargs == << <<18>> >> \* + * MCexpectedArity == 0 + * + * \* AOp \* MCops == <<"AOp">> \* MCargs == << <<18 >> >> \* MCexpectedArity == + * 0 + * + * \* C \* MCops == <<"C">> \* MCargs == << << >> >> \* MCexpectedArity == 0 + * + * \* p \* MCops == <<"p">> \* MCargs == << << >> >> \* MCexpectedArity == 0 + * + * \* Foo3 \* MCops == <<"Foo3">> \* MCargs == << << >> >> \* MCexpectedArity == + * 0 + * + * \***** THIS IS A BUG--NOT HANDLED CORRECTLY ****** \* UU!lab \* MCops == + * <<"UU", "lab">> \* MCargs == << << >>, << >> >> \* MCexpectedArity == 0 + * + * \* Op(2, 2)!(2, 2, 2) \* MCops == <<"Op", "null">> \* MCargs == << <<20, + * 20>>, <<20, 20, 20 >> >> \* MCexpectedArity == 0 + * + * \* Foo3!(2) \* MCops == <<"Foo3", "null">> \* MCargs == << << >>, <<20>> >> + * \* MCexpectedArity == 0 + * + * \* Op(2, 2)!(2,2,2)!+(2, 2) \* MCops == <<"Op", "null", "+">> \* MCargs == << + * <<20,20 >>, <<20,20,20>> , <<20,20>>>> \* MCexpectedArity == 0 + * + * \* MCops == <<"Op">> \* MCargs == << << >> >> \* MCexpectedArity == -1 + * + * \* MCops == <<"Inst", "Op">> \* MCargs == << << >> , << >> >> \* + * MCexpectedArity == -1 + * + * \* MCops == <<"Inst", "Thm">> \* MCargs == << << >> , << >> >> \* + * MCexpectedArity == -1 + * + * \* MCops == <<"Inst", "Op", "@", "+">> \* MCargs == << << >> , << >> , << >> + * , << >> >> \* MCexpectedArity == -1 + * + * \* MCops == <<"Op", "lab", ":", "+">> \* MCargs == << << >> , << >>, << >>, + * << >> >> \* MCexpectedArity == 7 + * + * \* MCops == <<"Op", "lab", ":", "+">> \* MCargs == << << >> , << >>, << >>, + * << >> >> \* MCexpectedArity == -1 + * + * \* MCops == <<"Foo2">> \* MCargs == << << >> >> \* MCexpectedArity == 0 + * + * \* MCops == <<"Foo3", ":">> \* MCargs == << << >>, << >> >> \* + * MCexpectedArity == 0 + * + * \* MCops == <<"Op", "lab", ":", "+">> \* MCargs == << << >>, << >>, << >>, << + * >> >> \* MCexpectedArity == -1 + * + * \* MCops == <<"Inst", "Thm">> \* MCargs == << <<14 >>, << >> >> \* + * MCexpectedArity == 0 + * + * MCops == <<"Inst", "Thm", ":">> MCargs == << <<14 >>, << >>, << >> >> + * MCexpectedArity == 0 MCdebug == FALSE \* TRUE + * + * (********************************* + * (***************************************************************************) + * (* Module M *) (* CONSTANT C *) (* Op(Arg(_), p) == \A x \in {1,2}, <<y, z>> + * \in {3} : *) (* lab(x,y,z) :: LET a + b == <<Arg(a), b>> *) (* IN 1 + label2 + * :: p + C *) (* *) (* Foo(u) == "FooBody(u)" *) (* Inst(w) == INSTANCE M WITH + * C <- <<w, CONST>> *) + * (***************************************************************************) + * + * \* MCNode == \* 1 :> [kind |-> NumeralKind, \* val |-> 1] \* @@ \* 2 :> [kind + * |-> BuiltInKind, \* \E \* name |-> "$UnboundedExists", \* arity |-> -1] \* + * \* @@ \* 3 :> [kind |-> OpApplKind, \* \E x : Node 1 \* operands |-> <<1>>, + * \* operator |-> 2, \* unboundedBoundSymbols |-> <<"x">>, \* + * boundedBoundSymbols |-> << >>, \* ranges |-> << >>] \* @@ \* 4 :> [kind |-> + * UserDefinedOpKind, \* Inst!UserOp(p1, OpP) == Node 8 \* name |-> + * "Inst!UserOp", \* body |-> 8, \* labels |-> "Label1" :> 7, \* arity |-> 2, \* + * params |-> <<[name |-> "p1", arity |-> 0], \* [name |-> "OpP", arity |-> + * 0]>>, \* defined |-> TRUE, \* source |-> 40 ] \* @@ \* 5 :> [kind |-> + * ModuleInstanceKind, \* Inst(p1) == INSTANCE \* name |-> "Inst", \* arity |-> + * 1, \* params |-> <<[name |-> "p1", arity |-> 0] >>, \* defined |-> TRUE ] + * \* @@ \* 6 :> [kind |-> UserDefinedOpKind, \* 1ArgOp(1OpP(_, _)) == Node 1 \* + * name |-> "1ArgOp", \* body |-> 41, \* labels |-> << >>, \* arity |-> 1, \* + * params |-> <<[name |-> "1OpP", arity |-> 0]>>, \* defined |-> TRUE, \* source + * |-> null ] \* @@ \* 7 :> [kind |-> LabelKind, \* name |-> "Label1", \* body + * |-> 14, \* arity |-> 1, \* params |-> <<[name |-> "labParam1", arity |-> + * 0]>>, \* labels |-> "Label2" :> 9 \* ] \* @@ \* 8 :> [kind |-> SubstInKind, + * \* body |-> 11, \* subst |-> "x <- something(p1)"] \* \* @@ \* 9 :> [kind |-> + * LabelKind, \* name |-> "Label2", \* body |-> 15, \* arity |-> 1, \* params + * |-> <<[name |-> "labParam2", arity |-> 0]>>, \* labels |-> << >> \* ] \* @@ + * \* 10 :> [kind |-> NumeralKind, \* val |-> 10] \* @@ \* 11 :> [kind |-> + * SubstInKind, \* body |-> 15, \* subst |-> "y <- something(p1)"] \* @@ \* 12 + * :> [kind |-> StringKind, \* val |-> "Body of UserOp (p1, OpP)"] \* @@ \* 13 + * :> [kind |-> StringKind, \* val |-> "Body of Label2(labParam2)"] \* @@ \* 14 + * :> [kind |-> StringKind, \* val |-> "Body of Label1(labParam1)"] \* @@ \* 15 + * :> [kind |-> LetInKind, \* context |-> "1ArgOp" :> 6, \* body |-> 16] \* @@ + * \* 16 :> [kind |-> StringKind, \* val |-> "Body of Let/In"] \* @@ \* 17 :> + * [kind |-> UserDefinedOpKind, \* 1ArgOp("1OpP") == Node 1 \* name |-> "Foo", + * \* body |-> 18, \* labels |-> << >>, \* arity |-> 0, \* params |-> << >> , \* + * <<[name |-> "FooPar1", arity |-> 0]>>, \* defined |-> TRUE, \* source |-> + * null] \* @@ \* 18 :> [kind |-> OpApplKind, \* operands |-> <<42 >>, \* + * operator |-> 21, \* unboundedBoundSymbols |-> <<>>, \* boundedBoundSymbols + * |-> << >>, \* ranges |-> << >>] \* @@ \* 19 :> [kind |-> StringKind, \* val + * |-> "Arg1"] \* @@ \* 20 :> [kind |-> StringKind, \* val |-> "Arg2"] \* @@ \* + * 21 :> [kind |-> UserDefinedOpKind, \* Foo2 == [rcd-lab1 |-> rcd-comp1, \* + * name |-> "Foo2", \* ... \* body |-> 34, \* rcd-lab3 |-> rcd-comp3] \* labels + * |-> << >>, \* arity |-> 1, \* params |-> <<[name |-> "p1", arity |-> 1]>>, \* + * defined |-> TRUE, \* source |-> null ] \* @@ \* 22 :> [kind |-> OpApplKind, + * \* operands |-> <<10 (* 25 *) , 28, 31>>, \* operator |-> 23, \* + * unboundedBoundSymbols |-> <<>>, \* boundedBoundSymbols |-> << >>, \* ranges + * |-> << >>] \* @@ \* 23 :> [kind |-> BuiltInKind, \* name |-> "$Except", \* + * "$Case", \* "$SetOfRcds", \* arity |-> -1 \* ] \* @@ \* 24 :> [kind |-> + * BuiltInKind, \* name |-> "$Pair", \* arity |-> 2 \* ] \* @@ \* 25 :> [kind + * |-> OpApplKind, \* operands |-> <<26,27>>, \* operator |-> 24, \* + * unboundedBoundSymbols |-> <<>>, \* boundedBoundSymbols |-> << >>, \* ranges + * |-> << >>] \* @@ \* 26 :> [kind |-> StringKind, \* val |-> "rcd-lab1"] \* @@ + * \* 27 :> [kind |-> StringKind, \* val |-> "rcd-comp1"] \* \* @@ \* 28 :> + * [kind |-> OpApplKind, \* operands |-> <<29,30>>, \* operator |-> 24, \* + * unboundedBoundSymbols |-> <<>>, \* boundedBoundSymbols |-> << >>, \* ranges + * |-> << >>] \* @@ \* 29 :> [kind |-> StringKind, \* val |-> "rcd-lab2"] \* @@ + * \* 30 :> [kind |-> StringKind, \* val |-> "rcd-comp2"] \* @@ \* 31 :> [kind + * |-> OpApplKind, \* operands |-> <<32,33>>, \* operator |-> 24, \* + * unboundedBoundSymbols |-> <<>>, \* boundedBoundSymbols |-> << >>, \* ranges + * |-> << >>] \* @@ \* 32 :> [kind |-> StringKind, \* val |-> "rcd-lab3"] \* @@ + * \* 33 :> [kind |-> StringKind, \* val |-> "rcd-comp3"] \* @@ \* 34 :> [kind + * |-> OpApplKind, \* operands |-> <<35>>, \* operator |-> 36, \* + * unboundedBoundSymbols |-> <<>>, \* boundedBoundSymbols |-> << <<"b1", "b2">>, + * <<"b3">> >>, \* ranges |-> <<37, 38 >>] \* @@ \* 35 :> [kind |-> StringKind, + * \* val |-> "Fcn of b1, b2, b3"] \* @@ \* 36 :> [kind |-> BuiltInKind, \* name + * |-> "$Pair", \* arity |-> 2 \* ] \* @@ \* 37 :> [kind |-> StringKind, \* val + * |-> "Range 1"] \* @@ \* 38 :> [kind |-> StringKind, \* val |-> "Range 2"] + * \* @@ \* 39 :> [kind |-> StringKind, \* val |-> "Arg3"] \* @@ \* 40 :> [kind + * |-> UserDefinedOpKind, \* Inst!UserOp(p1, OpP) == Node 8 \* name |-> + * "UserOp", \* body |-> 8, \* labels |-> "Label1" :> 7, \* arity |-> 1, \* + * params |-> <<[name |-> "OpP", arity |-> 1]>>, \* defined |-> TRUE, \* source + * |-> null ] \* @@ \* 41 :> [kind |-> StringKind, \* val |-> "Body of 1ArgOp + * may dep on (x, y)"] \* @@ \* 42 :> [kind |-> OpArgKind, \* op |-> 43, \* + * arity |-> 1, \* name |-> "Foo3"] \* @@ \* 43 :> [kind |-> UserDefinedOpKind, + * \* 1ArgOp("1OpP") == Node 1 \* name |-> "Foo3", \* body |-> 18, \* labels |-> + * << >>, \* arity |-> 0, \* params |-> << >>, \* <<[name |-> "FooPar1", arity + * |-> 0]>>, \* defined |-> TRUE, \* source |-> null] \* \* MCNodeId == 1..43 \* + * \* MCGlobalContext == \* "Inst!UserOp" :> 4 \* @@ \* "Inst" :> 5 \* @@ \* + * "Foo" :> 17 \* @@ \* "Foo2" :> 21 \* @@ \* "Foo3" :> 43 \* \* @@ \* \* + * "1ArgOp" :> 6 \* \* \* MCops == <<"Inst", "UserOp">> \* , "1">> \* MCargs == + * << <<1 >> , <<1>> >> + * + * \* \* << <<1>>, <<19, 20, 33 >> >> \* <<1>>, <<10>>, << >>, <<4>> >> \* \* + * MCexpectedArity == 0 \* -1 \* 5 ) + * + * ----------------------------------------------------------------------------- + * (***************************************************************************) + * (* THE ALGORITHM *) + * (***************************************************************************) + * (************************************** --algorithm Subexpression variables + * (*************************************************************************) + * (* The input variables. *) + * (*************************************************************************) + * ops = MCops, args = MCargs, + * (***********************************************************************) (* + * The sequences op and args described above. *) + * (***********************************************************************) + * expectedArity = MCexpectedArity, + * (***********************************************************************) (* + * The arity of the operator expected, or -1 if expecting a definition *) (* + * name. It is 0 iff an expression is expected. The result should be *) (* an + * OpArg node if expectedArity > 0, it should be an expression node *) (* if + * expectedArity = 0, and it should be a UserDefinedOpKind or a *) (* + * ThmOrAssumpDefKind if expectedArity < 0. *) + * (***********************************************************************) + * + * (*************************************************************************) + * (* The major variables of the algorithm. *) + * (*************************************************************************) + * substInPrefix = << >> , + * (***********************************************************************) (* + * The sequence of SubstInNode or APSubstInNode sequence that will be *) (* + * appended to body of the resulting OpApplNode *) + * (***********************************************************************) + * params = << >> , + * (***********************************************************************) (* + * The sequence of FormalParamNode objects that are the formal *) (* parameters + * if this produces a Lambda expression. A Lambda *) (* expression will be + * produced iff this is non-empty *) + * (***********************************************************************) + * allArgs = << >> , + * (***********************************************************************) (* + * The sequence of all arguments. *) + * (***********************************************************************) + * curNode = null, + * (***********************************************************************) (* + * If params = << >>, then the OpDefNode for the OpApplNode that is *) (* + * produced. If params # << >>, then the Expr node that will form the *) (* body + * of the Lambda expression. *) (* *) (* Note: in the Java implementation, this + * will actually be a ref to *) (* the node--in terms of this spec, a NodeId. *) + * (***********************************************************************) + * subExprOf = "null", + * (***********************************************************************) (* + * The node UserDefinedOpDefKind or ThmOrAssumpDefKind within which *) (* this + * subexpression is defined. *) + * (***********************************************************************) + * result = null, + * (***********************************************************************) (* + * The actual output node. *) + * (***********************************************************************) + * + * (*************************************************************************) + * (* The local variables. *) + * (*************************************************************************) + * idx = 1, + * (***********************************************************************) (* + * The element of the arrays op and args that the algorithm is *) (* currently + * examining. *) + * (***********************************************************************) + * mode = "FindingOpName", + * (***********************************************************************) (* + * The current mode describing what kind of selector it is expecting *) (* next. + * Its other possible values are "FollowingLabels" and *) (* "FindingSubExpr". + * *) (***********************************************************************) + * prevMode = "", + * (***********************************************************************) (* + * The mode for the previously examined selector. *) + * (***********************************************************************) + * curContext = GlobalContext, + * (***********************************************************************) (* + * The context for looking up operator or label names. *) + * (***********************************************************************) + * curName = "" , + * (***********************************************************************) (* + * When looking up an operator name, which may be something like *) (* + * "Foo!Bar!Baz", this is the part that has been found so far. *) + * (***********************************************************************) + * opDefArityFound = 0, opDefArgs = << >>, + * (***********************************************************************) (* + * The total arity and the sequence of arguments found so far for the *) (* + * current operator--for example, opDefArityFound will equal 2 if the *) (* + * algorithm has so far processed "Foo(a, b)!Bar" *) + * (***********************************************************************) + * firstFindingOpName = TRUE, + * (***********************************************************************) (* + * True iff have entered the FindingOpName only once (initially). *) + * (***********************************************************************) + * opNode, newName, newNode, nodeArity, temp, tempArgs + * + * + * (***************************************************************************) + * (* A macro used for reporting an error and terminating the execution. *) + * (***************************************************************************) + * macro Error(msg) begin print msg ; if debug then + * (**************************************) (* Force the +cal translator to *) + * (* insert a label so the error trace *) (* shows the final state. *) + * (**************************************) idx := idx ; idx := idx ; assert + * FALSE else goto Done end if end macro ; + * + * begin + * + * (*************************************************************************) + * (* An assertion that checks that ops and args are consistent with each *) (* + * other and with the value of expectedArity. In an implementation, *) (* some + * of these should be guaranteed by the context (e.g., Len(ops) = *) (* + * Len(args) while others will be checked as part of the processing for *) (* + * the particular value of idx. *) + * (*************************************************************************) + * assert /\ expectedArity \in Int /\ ops \in Seq(STRING) /\ args \in + * Seq(Seq(NodeId)) /\ Len(ops) = Len(args) /\ \A i \in 1..Len(ops) : /\ \/ + * ops[i] \in {"<<", ">>", "@", ":"} \cup NumberOp \/ expectedArity # 0 => + * args[i] = << >> /\ (ops[i] = "null") => (Len(args[i]) > 0) ; + * + * (*************************************************************************) + * (* The outer "for" loop on idx. *) + * (*************************************************************************) + * while idx <= Len(ops) do if mode = "FindingOpName" then + * + * \* The following code was modified by LL on 23 Sep 2009 to fix the following + * \* bug. The parser produced a bogus error on a reference to an identifier \* + * M!N that is imported by an unnamed INSTANCE of a module containing the \* + * statement M == INSTANCE ... . The original code essentially just \* contained + * the first iteration of the following while loop, and would \* report an error + * if it found newNode = null. Thus, it would report \* an error in the + * identifier M!N when it found M to be undefined. \* \* Corrected by LL on 9 + * Nov 2009 to handle arguments of those \* undefined operators. They are put + * into tempArgs when they are \* found. + * + * newNode := null ; tempArgs := << >> ; + * (**********************************************************************) (* + * The number of arguments found in the following loop for the *) (* undefined + * operators *) + * (**********************************************************************) + * while newNode = null do if idx \geq Len(ops) then Error ("Unknown operator"); + * end if; newName := IF IsName(ops[idx]) THEN IF curName = "" THEN ops[idx] + * ELSE curName \o "!" \o ops[idx] ELSE null ; if /\ curName = "" /\ ~ + * IsName(ops[idx]) then + * (***************************************************************) (* I think + * that this error can only happen for idx = 1, and *) (* should never happen in + * the implementation because the *) (* parser should not allow a compound name + * that doesn't begi *) (* with a name. *) + * (***************************************************************) Error("Need + * an operator or label name or step number here") end if; newNode := IF newName + * # null THEN LookUp(newName, curContext) ELSE null ; if newName = null then + * tempArgs := tempArgs \o args[idx]; idx := idx + 1; end if; end while ; + * curNode := newNode ; curName := newName ; if curNode.kind \in + * {UserDefinedOpKind, ThmOrAssumpDefKind, ModuleInstanceKind, ConstantDeclKind, + * VariableDeclKind, FormalParamKind, BuiltInKind, BoundSymbolKind} then if + * curNode.kind \in {ConstantDeclKind, VariableDeclKind, FormalParamKind, + * BuiltInKind, BoundSymbolKind} then if idx # 1 then Error("Abort: Impossible + * naming of declaration " \o "or built-in operator.") else if Len(ops) # 1 then + * Error("Can't take subexpression of this"); end if end if end if ; nodeArity + * := Arity(curNode) ; tempArgs := tempArgs \o args[idx]; if expectedArity = 0 + * then if opDefArityFound + Len(tempArgs) # nodeArity then Error("Wrong number + * of arguments") end if ; if \E i \in 1..Len(tempArgs[idx]) : + * Arity(Node[tempArgs[idx][i]]) # ParamArity(curNode, i + opDefArityFound) then + * Error("Argument has wrong arity") else opDefArgs := opDefArgs \o tempArgs; + * end if ; else if expectedArity > 0 then if \E i \in 1..Len(curNode.params) : + * curNode.params[i].arity > 0 then Error("Higher-order operator selected " \o + * "as operator argument") end if \* else Error( \* "Abort: Don't yet handle + * expectedArity < 0") end if end if ; opDefArityFound := nodeArity ; if + * curNode.kind \in {UserDefinedOpKind, ThmOrAssumpDefKind, ConstantDeclKind, + * VariableDeclKind, FormalParamKind, BoundSymbolKind} then if /\ curNode.kind = + * UserDefinedOpKind /\ ~ curNode.defined /\ ~ Len(ops) = 1 then Error("Can't + * refer to subexpression of " \o "operator inside its definition") end if; if + * /\ firstFindingOpName /\ curNode.kind \in {UserDefinedOpKind, + * ThmOrAssumpDefKind} then subExprOf := curNode end if; if idx # Len(ops) then + * params := params \o curNode.params ; curName := "" ; if IsName(ops[idx+1]) + * then mode := "FollowingLabels" else mode := "FindingSubExpr" ; end if ; + * allArgs := allArgs \o opDefArgs ; opDefArityFound := 0 ; opDefArgs := << >> ; + * newNode := Node[curNode.body] ; while newNode.kind = SubstInKind do + * substInPrefix := substInPrefix \o <<newNode>>; newNode := Node[newNode.body]; + * end while ; while newNode.kind \in {SubstInKind, APSubstInKind} + * (************************************************) (* Added APSubstInKind + * test on 13 Nov 2009. *) (************************************************) do + * substInPrefix := substInPrefix \o <<newNode>>; newNode := Node[newNode.body]; + * end while ; (********************************************) (* If the next op + * is an OpSel, then need to *) (* skip over SubstInNodes, which are *) (* + * invisible to the user. *) (********************************************) if + * mode = "FindingSubExpr" then curNode := newNode end if; end if ; else \* + * curNode.kind = ModuleInstanceKind if (idx = Len(ops)) then Error("Operator + * name incomplete") end if end if else Error("Unexpected node kind") ; end if ; + * prevMode := "FindingOpName" ; + * + * + * elsif mode = "FollowingLabels" then if prevMode = "FindingOpName" then assert + * curNode.kind \in {UserDefinedOpKind, ThmOrAssumpDefKind} ; newNode := + * LookUp(ops[idx], curNode.labels) ; else assert curNode.kind = LabelKind ; + * newNode := LookUp(ops[idx], curNode.labels) ; end if ; if newNode = null then + * Error("Label not found") end if; curNode := newNode ; if expectedArity = 0 + * then if Len(args[idx]) # curNode.arity then Error("bad arity") end if ; if \E + * i \in 1..Len(args[idx]) : Arity(Node[args[idx][i]]) # 0 then Error("Operator + * argument given to label") end if; allArgs := allArgs \o args[idx] ; end if; + * params := params \o curNode.params ; if /\ idx < Len(ops) /\ + * ~IsName(ops[idx+1]) then mode := "FindingSubExpr" end if ; if \/ mode = + * "FindingSubExpr" \/ idx = Len(ops) then curNode := Node[curNode.body] end if; + * prevMode := "FollowingLabels"; + * + * + * elsif mode = "FindingSubExpr" then if ops[idx] = ":" then if \/ prevMode + * \notin {"FindingOpName", "FollowingLabels"} \/ ~ \/ /\ idx = Len(ops) /\ + * prevMode = "FindingOpName" \/ /\ idx < Len(ops) /\ IsName(ops[idx+1]) then + * Error("`!:' can be used only after a name and either at the " \o "end after + * an operator name or before an operator name.") end if + * + * elsif curNode.kind = LetInKind then if ArgNum(ops[idx], 1) = 1 then curNode + * := Node[curNode.body] else Error("A Let/In has only a single operand") end if + * ; + * + * elsif curNode.kind = OpApplKind then opNode := Node[curNode.operator] ; if + * opNode.kind \in {FormalParamKind, ConstantDeclKind, UserDefinedOpKind} then + * (******************************************************************) (* + * Selecting an argument from something of the form Op(...). *) + * (******************************************************************) temp := + * ArgNum(ops[idx], opNode.arity) ; if temp = -1 then Error("Nonexistent operand + * specified") else curNode := Node[curNode.operands[temp]] end if elsif + * opNode.kind = BuiltInKind then \* See configuration/ConfigConstants for a + * list of all \* BuiltInKind operators. if opNode.name \in {"$RcdConstructor", + * "$SetOfRcds"} then + * (*****************************************************************) (* + * curNode represents an expression *) (* [a_1 |-> e_1, ... , a_n |-> e_n] or *) + * (* [a_1 : e_1, ... , a_n : e_n] *) (* Its i-th argument is the $Pair node + * (a_i, e_i) *) + * (*****************************************************************) temp := + * ArgNum(ops[idx], Len(curNode.operands)) ; if temp = -1 then Error("Incorrect + * subexpression number") end if ; curNode := Node[curNode.operands[temp]] ; if + * \/ curNode.kind # OpApplKind \/ Node[curNode.operator].kind # BuiltInKind \/ + * Node[curNode.operator].name # "$Pair" then Error ("Expecting $Pair(...)") + * else curNode := Node[curNode.operands[2]] end if + * + * elsif opNode.name \in {"$Case"} then + * (*****************************************************************) (* The + * i-th clause is a $Pair node, where for the OTHER clause *) (* the 1st element + * is null. *) + * (*****************************************************************) if idx = + * Len(ops) then Error( "CASE subexpression must be of form !i!j") end if ; temp + * := ArgNum( ops[idx], Len(curNode.operands)) ; if temp = -1 then + * Error("Incorrect subexpression name") end if ; curNode := + * Node[curNode.operands[temp]] ; if \/ curNode.kind # OpApplKind \/ + * Node[curNode.operator].kind # BuiltInKind \/ Node[curNode.operator].name # + * "$Pair" then Error ("Expecting $Pair(...)") end if ; idx := idx+1 ; temp := + * ArgNum(ops[idx], 2); if temp = -1 then Error("Incorrect subexpression name") + * end if ; curNode := Node[curNode.operands[temp]] ; if curNode = null then + * Error("Selecting OTHER") end if + * + * elsif opNode.name = "$Except" then + * (****************************************************************) (* For *) + * (* [exp_1 ELSE !... = exp_2, ... , !.. = exp_n] *) (* argument number i + * chooses exp_i. For i > 1, exp_i is the *) (* second argument of a $Pair + * operator. *) + * (****************************************************************) temp := + * ArgNum(ops[idx], Len(curNode.operands)); if temp = -1 then Error("Bad + * argument selector.") end if; curNode := Node[curNode.operands[temp]] ; if + * temp > 1 then if \/ curNode.kind # OpApplKind \/ Node[curNode.operator].kind + * # BuiltInKind \/ Node[curNode.operator].name # "$Pair" then Error("Unexpected + * expression node found.") else curNode := Node[curNode.operands[2]] end if end + * if + * + * else (*****************************************************************) (* + * Operator handled by standard procedure. *) + * (*****************************************************************) if /\ + * Len(curNode.unboundedBoundSymbols) = 0 /\ Len(curNode.boundedBoundSymbols) = + * 0 then (**********************************************************) (* + * Current subexpression has no bound variables. *) + * (**********************************************************) temp := + * ArgNum(ops[idx], Len(curNode.operands)) ; if temp = -1 then Error("Incorrect + * subexpression selector") end if; curNode := Node[curNode.operands[temp]] ; + * else (**********************************************************) (* Current + * subexpression has bound variables. If *) (* selector is "@" or null, then + * choosing body and adding *) (* parameters. Otherwise, must be selecting one + * of the *) (* bounds. *) + * (**********************************************************) if ops[idx] \in + * {"null", "@"} then (***************************************************) (* + * Set temp to the sequence of parameters. *) + * (***************************************************) temp := IF + * Len(curNode.unboundedBoundSymbols) > 0 THEN curNode.unboundedBoundSymbols + * ELSE SeqSeqToSeq( curNode.boundedBoundSymbols); + * + * params := params \o [i \in 1.. Len(temp) |-> [name |-> temp[i], arity |-> 0]] + * ; allArgs := allArgs \o args[idx] ; if /\ ops[idx] = "null" /\ Len(args[idx]) + * # Len(temp) then Error("Wrong number of selector arguments"); end if; curNode + * := Node[curNode.operands[1]]; else temp := ArgNum(ops[idx], + * Len(curNode.ranges)) ; if temp = -1 then Error ("Selecting non-existent + * range") ; else curNode := Node[curNode.ranges[temp]] end if end if end if end + * if \* opNode.name = ... + * + * else Error("Applying subexpression chooser to an expr" \o " with no choosable + * subexpressions") + * + * end if \* opNode.kind = ... + * + * elsif curNode.kind = AssumeProveKind then temp := ArgNum(ops[idx], 1 + + * Len(curNode.assumes)) ; if temp = -1 then Error("Illegal argument number") + * else if temp <= Len(curNode.assumes) then curNode := + * Node[curNode.assumes[temp]] else curNode := Node[curNode.prove] end if end if + * + * elsif curNode.kind = OpArgKind then + * (*********************************************************************) (* + * The only kind of OpArgNode that has a subpart is a Lambda *) (* expression. + * *) (*********************************************************************) + * opNode := Node[curNode.op] ; if \/ opNode.kind # UserDefinedOpKind \/ + * opNode.name # "LAMBDA" then Error("Selecting from operator argument that has + * no sub-part") elsif ops[idx] \notin {"null", "@"} then Error("Incorrect + * selection from LAMBDA") elsif /\ ops[idx] = "null" /\ Len(args[idx]) # + * Len(opNode.params) then Error("Incorrect number of arguments for LAMBDA") + * else params := params \o opNode.params ; allArgs := allArgs \o args[idx] ; + * curNode := Node[opNode.body] ; end if + * + * elsif curNode.kind \in {UserDefinedOpKind, BuiltInKind} then Error("Abort: + * should not have been able to choose this node.") + * + * elsif curNode.kind \in {AtNodeKind, DecimalKind, NumeralKind, StringKind, + * FormalParamKind, ConstantDeclKind, VariableDeclKind, BoundSymbolKind} then + * Error("Selected part has no subexpression") + * + * elsif curNode.kind = LabelKind then + * (*********************************************************************) (* + * Skip over label. *) + * (*********************************************************************) + * curNode := Node[curNode.body] ; idx := idx - 1 ; else Error("Unexpected node + * kind found in expression") + * + * end if; \* ops[idx] = ":" + * + * if idx # Len(ops) then if IsName(ops[idx+1]) then while curNode.kind = + * LabelKind do curNode := Node[curNode.body] end while ; if curNode.kind = + * LetInKind then curContext := curNode.context ; mode := "FindingOpName" ; + * firstFindingOpName := FALSE; else Error("A name not following the selector of + * a LET") end if else if ops[idx+1] = ":" then Error("!: should not follow an + * operand selector") end if ; end if ; end if ; \* ops[idx] = ":" prevMode := + * "FindingSubExpr"; + * + * else Error("Bad value of mode") + * + * end if ; \* mode = ... + * + * idx := idx + 1; end while ; + * + * if curNode.kind = AssumeProveKind then Error("Selecting ASSUME/PROVE instead + * of expression") end if; + * + * if expectedArity < 0 then if \/ prevMode # "FindingOpName" \/ curNode.kind + * \notin {UserDefinedOpKind, ThmOrAssumpDefKind} then Error("Should have + * selected a definition, but didn't.") end if; result := curNode ; goto + * Finished end if; + * + * if expectedArity > 0 then temp := Len(params) + IF \/ prevMode = + * "FindingOpName" \/ curNode.kind = OpArgKind THEN Arity(curNode) ELSE 0 ; if + * expectedArity # temp then Error("Expect arity = " \o ToString(expectedArity) + * \o ", but found arity = " \o ToString(temp)) end if end if; + * + * + * (*************************************************************************) + * (* If found an operator def and there are parameters or substitutions, *) (* + * then set curNode to the operator applied to new parameters, add those *) (* + * parameters to params and add the arguments to allArgs. Note: need to *) (* do + * this even if operator takes no parameters because we need to put *) (* the + * operator in a LAMBDA expression whose body is an expression *) + * (*************************************************************************) + * if /\ prevMode = "FindingOpName" /\ Len(params) + Len(substInPrefix) > 0 then + * temp := [i \in 1..Len(curNode.params) |-> [curNode.params[i] EXCEPT !.name = + * "New " \o @]] ; + * (****************************************************************) (* temp := + * new formal parameters for the operator, with the *) (* same arities as the + * original parameters. *) + * (****************************************************************) + * + * curNode := [kind |-> OpApplKind, operands |-> temp, operator |-> curNode, + * unboundedBoundSymbols |-> <<>>, boundedBoundSymbols |-> << >>, ranges |-> << + * >>] ; params := params \o temp ; allArgs := allArgs \o opDefArgs ; end if ; + * + * if curNode.kind = OpArgKind then if expectedArity = 0 then Error("Selected + * Operator Argument when expression expected.") elsif expectedArity # + * Len(params) + curNode.arity then Error("Selected operator has wrong arity.") + * else if Len(params) + Len(substInPrefix) > 0 then + * (********************************************************) (* If curNode is a + * LAMBDA, then this will eventually *) (* produce an OpArg node whose operator + * is a LAMBDA *) (* whose body is an OpApplNode that applies the *) (* + * curNode's LAMBDA to parameters of the outer LAMBDA. *) (* This result can be + * simplified to a LAMBDA whose body *) (* is the body of curNode. However, that + * *) (* simplification is what one will get by selecting the *) (* body of the + * LAMBDA, so we keep this complicated *) (* expression in this case. *) + * (********************************************************) temp := [i \in + * 1..curNode.arity |-> [name |-> "NewParam" \o NumToString(i), arity |-> 0]] ; + * curNode := [kind |-> OpApplKind, operands |-> temp, operator |-> + * Node[curNode.op], unboundedBoundSymbols |-> <<>>, boundedBoundSymbols |-> << + * >>, ranges |-> << >>] ; params := params \o temp end if end if end if ; + * + * if curNode.kind \in {UserDefinedOpKind, ConstantDeclKind, VariableDeclKind, + * FormalParamKind, BuiltInKind, BoundSymbolKind, ThmOrAssumpDefKind} then + * (***********************************************************************) (* + * There are no params or substitutions, so this is an easy case. *) + * (***********************************************************************) if + * expectedArity > 0 then result := [kind |-> OpArgKind, name |-> curNode.name, + * op |-> curNode, arity |-> expectedArity] elsif expectedArity = 0 then result + * := [kind |-> OpApplKind, operands |-> opDefArgs, operator |-> curNode, + * unboundedBoundSymbols |-> <<>>, boundedBoundSymbols |-> << >>, ranges |-> << + * >>, subExprOf |-> subExprOf] end if elsif curNode.kind = OpArgKind then + * (***********************************************************************) (* + * There are no params or substitutions, so this is an easy case. *) + * (***********************************************************************) + * result := curNode ; else + * (******************************************************************) (* + * curNode should be an expression node. *) + * (******************************************************************) temp := + * Len(substInPrefix) ; while temp > 0 do curNode := [kind |-> + * substInPrefix[temp].kind, (********************************************) (* + * Changed on 13 Nov 2009 from SubstInKind. *) + * (********************************************) body |-> curNode , subst |-> + * substInPrefix[temp].subst ] ; temp := temp - 1; end while; + * + * if expectedArity > 0 then if Len(params) # expectedArity then Error + * ("Selection has wrong arity") end if ; result := [kind |-> OpArgKind, op |-> + * [kind |-> UserDefinedOpKind, name |-> "LAMBDA", body |-> curNode, params |-> + * params, arity |-> Len(params), defined |-> TRUE, source |-> null], name |-> + * "LAMBDA"] else if Len(params) # Len(allArgs) then Error("Abort: number of + * params # num of args") end if ; if Len(params) = 0 then + * (******************************************************) (* This is the one + * case with expectedArity = 0 in *) (* which the result is not a + * newly-constructed node. *) (* In this case, we construct a dummy label node + * so *) (* we have a node to which the implementation can *) (* attach the + * syntax node. *) (******************************************************) + * result := [kind |-> LabelKind, name |-> "$Subexpression", arity |-> 0, params + * |-> << >>, body |-> curNode, subExprOf |-> subExprOf] + * + * else result := [kind |-> OpApplKind, operator |-> [kind |-> + * UserDefinedOpKind, name |-> "LAMBDA", body |-> curNode, params |-> params, + * arity |-> Len(params), defined |-> TRUE, source |-> null], operands |-> + * allArgs, unboundedBoundSymbols |-> <<>>, boundedBoundSymbols |-> << >>, + * ranges |-> << >>, subExprOf |-> subExprOf] end if end if + * + * end if; + * + * Finished: print "Result: " ; print result ; + * + * end algorithm ) \* BEGIN TRANSLATION CONSTANT defaultInitValue VARIABLES ops, + * args, expectedArity, substInPrefix, params, allArgs, curNode, subExprOf, + * result, idx, mode, prevMode, curContext, curName, opDefArityFound, opDefArgs, + * firstFindingOpName, opNode, newName, newNode, nodeArity, temp, tempArgs, pc + * + * vars == << ops, args, expectedArity, substInPrefix, params, allArgs, curNode, + * subExprOf, result, idx, mode, prevMode, curContext, curName, opDefArityFound, + * opDefArgs, firstFindingOpName, opNode, newName, newNode, nodeArity, temp, + * tempArgs, pc >> + * + * Init == (* Global variables *) /\ ops = MCops /\ args = MCargs /\ + * expectedArity = MCexpectedArity /\ substInPrefix = << >> /\ params = << >> /\ + * allArgs = << >> /\ curNode = null /\ subExprOf = "null" /\ result = null /\ + * idx = 1 /\ mode = "FindingOpName" /\ prevMode = "" /\ curContext = + * GlobalContext /\ curName = "" /\ opDefArityFound = 0 /\ opDefArgs = << >> /\ + * firstFindingOpName = TRUE /\ opNode = defaultInitValue /\ newName = + * defaultInitValue /\ newNode = defaultInitValue /\ nodeArity = + * defaultInitValue /\ temp = defaultInitValue /\ tempArgs = defaultInitValue /\ + * pc = "Lbl_1" + * + * Lbl_1 == /\ pc = "Lbl_1" /\ Assert(/\ expectedArity \in Int /\ ops \in + * Seq(STRING) /\ args \in Seq(Seq(NodeId)) /\ Len(ops) = Len(args) /\ \A i \in + * 1..Len(ops) : /\ \/ ops[i] \in {"<<", ">>", "@", ":"} \cup NumberOp \/ + * expectedArity # 0 => args[i] = << >> /\ (ops[i] = "null") => (Len(args[i]) > + * 0), "Failure of assertion at line line 1906, column 3.") /\ pc' = "Lbl_2" /\ + * UNCHANGED << ops, args, expectedArity, substInPrefix, params, allArgs, + * curNode, subExprOf, result, idx, mode, prevMode, curContext, curName, + * opDefArityFound, opDefArgs, firstFindingOpName, opNode, newName, newNode, + * nodeArity, temp, tempArgs >> + * + * Lbl_2 == /\ pc = "Lbl_2" /\ IF idx <= Len(ops) THEN /\ IF mode = + * "FindingOpName" THEN /\ newNode' = null /\ tempArgs' = << >> /\ pc' = "Lbl_3" + * /\ UNCHANGED << params, allArgs, curNode, idx, opNode, temp >> ELSE /\ IF + * mode = "FollowingLabels" THEN /\ IF prevMode = "FindingOpName" THEN /\ + * Assert(curNode.kind \in {UserDefinedOpKind, ThmOrAssumpDefKind}, "Failure of + * assertion at line line 2053, column 13.") /\ newNode' = LookUp(ops[idx], + * curNode.labels) ELSE /\ Assert(curNode.kind = LabelKind, "Failure of + * assertion at line line 2055, column 13.") /\ newNode' = LookUp(ops[idx], + * curNode.labels) /\ IF newNode' = null THEN /\ PrintT("Label not found") /\ IF + * debug THEN /\ idx' = idx /\ pc' = "Lbl_22" ELSE /\ pc' = "Done" /\ UNCHANGED + * idx ELSE /\ pc' = "Lbl_23" /\ UNCHANGED idx /\ UNCHANGED << params, allArgs, + * curNode, opNode, temp >> ELSE /\ IF mode = "FindingSubExpr" THEN /\ IF + * ops[idx] = ":" THEN /\ IF \/ prevMode \notin {"FindingOpName", + * "FollowingLabels"} \/ ~ \/ /\ idx = Len(ops) /\ prevMode = "FindingOpName" \/ + * /\ idx < Len(ops) /\ IsName(ops[idx+1]) THEN /\ PrintT("`!:' can be used only + * after a name and either at the " \o "end after an operator name or before an + * operator name.") /\ IF debug THEN /\ idx' = idx /\ pc' = "Lbl_29" ELSE /\ pc' + * = "Done" /\ UNCHANGED idx ELSE /\ pc' = "Lbl_63" /\ UNCHANGED idx /\ + * UNCHANGED << params, allArgs, curNode, opNode, temp >> ELSE /\ IF + * curNode.kind = LetInKind THEN /\ IF ArgNum(ops[idx], 1) = 1 THEN /\ curNode' + * = Node[curNode.body] /\ pc' = "Lbl_63" /\ UNCHANGED idx ELSE /\ PrintT("A + * Let/In has only a single operand") /\ IF debug THEN /\ idx' = idx /\ pc' = + * "Lbl_30" ELSE /\ pc' = "Done" /\ UNCHANGED idx /\ UNCHANGED curNode /\ + * UNCHANGED << params, allArgs, opNode, temp >> ELSE /\ IF curNode.kind = + * OpApplKind THEN /\ opNode' = Node[curNode.operator] /\ IF opNode'.kind \in + * {FormalParamKind, ConstantDeclKind, UserDefinedOpKind} THEN /\ temp' = + * ArgNum(ops[idx], opNode'.arity) /\ IF temp' = -1 THEN /\ PrintT("Nonexistent + * operand specified") /\ IF debug THEN /\ idx' = idx /\ pc' = "Lbl_31" ELSE /\ + * pc' = "Done" /\ UNCHANGED idx /\ UNCHANGED curNode ELSE /\ curNode' = + * Node[curNode.operands[temp']] /\ pc' = "Lbl_63" /\ UNCHANGED idx /\ UNCHANGED + * << params, allArgs >> ELSE /\ IF opNode'.kind = BuiltInKind THEN /\ IF + * opNode'.name \in {"$RcdConstructor", "$SetOfRcds"} THEN /\ temp' = + * ArgNum(ops[idx], Len(curNode.operands)) /\ IF temp' = -1 THEN /\ + * PrintT("Incorrect subexpression number") /\ IF debug THEN /\ idx' = idx /\ + * pc' = "Lbl_32" ELSE /\ pc' = "Done" /\ UNCHANGED idx ELSE /\ pc' = "Lbl_33" + * /\ UNCHANGED idx /\ UNCHANGED << params, allArgs, curNode >> ELSE /\ IF + * opNode'.name \in {"$Case"} THEN /\ IF idx = Len(ops) THEN /\ PrintT("CASE + * subexpression must be of form !i!j") /\ IF debug THEN /\ idx' = idx /\ pc' = + * "Lbl_36" ELSE /\ pc' = "Done" /\ UNCHANGED idx ELSE /\ pc' = "Lbl_37" /\ + * UNCHANGED idx /\ UNCHANGED << params, allArgs, curNode, temp >> ELSE /\ IF + * opNode'.name = "$Except" THEN /\ temp' = ArgNum(ops[idx], + * Len(curNode.operands)) /\ IF temp' = -1 THEN /\ PrintT("Bad argument + * selector.") /\ IF debug THEN /\ idx' = idx /\ pc' = "Lbl_46" ELSE /\ pc' = + * "Done" /\ UNCHANGED idx ELSE /\ pc' = "Lbl_47" /\ UNCHANGED idx /\ UNCHANGED + * << params, allArgs, curNode >> ELSE /\ IF /\ + * Len(curNode.unboundedBoundSymbols) = 0 /\ Len(curNode.boundedBoundSymbols) = + * 0 THEN /\ temp' = ArgNum(ops[idx], Len(curNode.operands)) /\ IF temp' = -1 + * THEN /\ PrintT("Incorrect subexpression selector") /\ IF debug THEN /\ idx' = + * idx /\ pc' = "Lbl_50" ELSE /\ pc' = "Done" /\ UNCHANGED idx ELSE /\ pc' = + * "Lbl_51" /\ UNCHANGED idx /\ UNCHANGED << params, allArgs, curNode >> ELSE /\ + * IF ops[idx] \in {"null", "@"} THEN /\ temp' = (IF + * Len(curNode.unboundedBoundSymbols) > 0 THEN curNode.unboundedBoundSymbols + * ELSE SeqSeqToSeq( curNode.boundedBoundSymbols)) /\ params' = params \o [i \in + * 1.. Len(temp') |-> [name |-> temp'[i], arity |-> 0]] /\ allArgs' = allArgs \o + * args[idx] /\ IF /\ ops[idx] = "null" /\ Len(args[idx]) # Len(temp') THEN /\ + * PrintT("Wrong number of selector arguments") /\ IF debug THEN /\ idx' = idx + * /\ pc' = "Lbl_52" ELSE /\ pc' = "Done" /\ UNCHANGED idx ELSE /\ pc' = + * "Lbl_53" /\ UNCHANGED idx /\ UNCHANGED curNode ELSE /\ temp' = + * ArgNum(ops[idx], Len(curNode.ranges)) /\ IF temp' = -1 THEN /\ + * PrintT("Selecting non-existent range") /\ IF debug THEN /\ idx' = idx /\ pc' + * = "Lbl_54" ELSE /\ pc' = "Done" /\ UNCHANGED idx /\ UNCHANGED curNode ELSE /\ + * curNode' = Node[curNode.ranges[temp']] /\ pc' = "Lbl_63" /\ UNCHANGED idx /\ + * UNCHANGED << params, allArgs >> ELSE /\ PrintT("Applying subexpression + * chooser to an expr" \o " with no choosable subexpressions") /\ IF debug THEN + * /\ idx' = idx /\ pc' = "Lbl_55" ELSE /\ pc' = "Done" /\ UNCHANGED idx /\ + * UNCHANGED << params, allArgs, curNode, temp >> ELSE /\ IF curNode.kind = + * AssumeProveKind THEN /\ temp' = ArgNum(ops[idx], 1 + Len(curNode.assumes)) /\ + * IF temp' = -1 THEN /\ PrintT("Illegal argument number") /\ IF debug THEN /\ + * idx' = idx /\ pc' = "Lbl_56" ELSE /\ pc' = "Done" /\ UNCHANGED idx /\ + * UNCHANGED curNode ELSE /\ IF temp' <= Len(curNode.assumes) THEN /\ curNode' = + * Node[curNode.assumes[temp']] ELSE /\ curNode' = Node[curNode.prove] /\ pc' = + * "Lbl_63" /\ UNCHANGED idx /\ UNCHANGED << params, allArgs, opNode >> ELSE /\ + * IF curNode.kind = OpArgKind THEN /\ opNode' = Node[curNode.op] /\ IF \/ + * opNode'.kind # UserDefinedOpKind \/ opNode'.name # "LAMBDA" THEN /\ + * PrintT("Selecting from operator argument that has no sub-part") /\ IF debug + * THEN /\ idx' = idx /\ pc' = "Lbl_57" ELSE /\ pc' = "Done" /\ UNCHANGED idx /\ + * UNCHANGED << params, allArgs, curNode >> ELSE /\ IF ops[idx] \notin {"null", + * "@"} THEN /\ PrintT("Incorrect selection from LAMBDA") /\ IF debug THEN /\ + * idx' = idx /\ pc' = "Lbl_58" ELSE /\ pc' = "Done" /\ UNCHANGED idx /\ + * UNCHANGED << params, allArgs, curNode >> ELSE /\ IF /\ ops[idx] = "null" /\ + * Len(args[idx]) # Len(opNode'.params) THEN /\ PrintT("Incorrect number of + * arguments for LAMBDA") /\ IF debug THEN /\ idx' = idx /\ pc' = "Lbl_59" ELSE + * /\ pc' = "Done" /\ UNCHANGED idx /\ UNCHANGED << params, allArgs, curNode >> + * ELSE /\ params' = params \o opNode'.params /\ allArgs' = allArgs \o args[idx] + * /\ curNode' = Node[opNode'.body] /\ pc' = "Lbl_63" /\ UNCHANGED idx ELSE /\ + * IF curNode.kind \in {UserDefinedOpKind, BuiltInKind} THEN /\ PrintT("Abort: + * should not have been able to choose this node.") /\ IF debug THEN /\ idx' = + * idx /\ pc' = "Lbl_60" ELSE /\ pc' = "Done" /\ UNCHANGED idx /\ UNCHANGED + * curNode ELSE /\ IF curNode.kind \in {AtNodeKind, DecimalKind, NumeralKind, + * StringKind, FormalParamKind, ConstantDeclKind, VariableDeclKind, + * BoundSymbolKind} THEN /\ PrintT("Selected part has no subexpression") /\ IF + * debug THEN /\ idx' = idx /\ pc' = "Lbl_61" ELSE /\ pc' = "Done" /\ UNCHANGED + * idx /\ UNCHANGED curNode ELSE /\ IF curNode.kind = LabelKind THEN /\ curNode' + * = Node[curNode.body] /\ idx' = idx - 1 /\ pc' = "Lbl_63" ELSE /\ + * PrintT("Unexpected node kind found in expression") /\ IF debug THEN /\ idx' = + * idx /\ pc' = "Lbl_62" ELSE /\ pc' = "Done" /\ UNCHANGED idx /\ UNCHANGED + * curNode /\ UNCHANGED << params, allArgs, opNode >> /\ UNCHANGED temp ELSE /\ + * PrintT("Bad value of mode") /\ IF debug THEN /\ idx' = idx /\ pc' = "Lbl_68" + * ELSE /\ pc' = "Done" /\ UNCHANGED idx /\ UNCHANGED << params, allArgs, + * curNode, opNode, temp >> /\ UNCHANGED newNode /\ UNCHANGED tempArgs ELSE /\ + * IF curNode.kind = AssumeProveKind THEN /\ PrintT("Selecting ASSUME/PROVE + * instead of expression") /\ IF debug THEN /\ idx' = idx /\ pc' = "Lbl_70" ELSE + * /\ pc' = "Done" /\ UNCHANGED idx ELSE /\ pc' = "Lbl_71" /\ UNCHANGED idx /\ + * UNCHANGED << params, allArgs, curNode, opNode, newNode, temp, tempArgs >> /\ + * UNCHANGED << ops, args, expectedArity, substInPrefix, subExprOf, result, + * mode, prevMode, curContext, curName, opDefArityFound, opDefArgs, + * firstFindingOpName, newName, nodeArity >> + * + * Lbl_69 == /\ pc = "Lbl_69" /\ idx' = idx + 1 /\ pc' = "Lbl_2" /\ UNCHANGED << + * ops, args, expectedArity, substInPrefix, params, allArgs, curNode, subExprOf, + * result, mode, prevMode, curContext, curName, opDefArityFound, opDefArgs, + * firstFindingOpName, opNode, newName, newNode, nodeArity, temp, tempArgs >> + * + * Lbl_3 == /\ pc = "Lbl_3" /\ IF newNode = null THEN /\ IF idx \geq Len(ops) + * THEN /\ PrintT("Unknown operator") /\ IF debug THEN /\ idx' = idx /\ pc' = + * "Lbl_4" ELSE /\ pc' = "Done" /\ UNCHANGED idx ELSE /\ pc' = "Lbl_5" /\ + * UNCHANGED idx /\ UNCHANGED << curNode, curName >> ELSE /\ curNode' = newNode + * /\ curName' = newName /\ IF curNode'.kind \in {UserDefinedOpKind, + * ThmOrAssumpDefKind, ModuleInstanceKind, ConstantDeclKind, VariableDeclKind, + * FormalParamKind, BuiltInKind, BoundSymbolKind} THEN /\ IF curNode'.kind \in + * {ConstantDeclKind, VariableDeclKind, FormalParamKind, BuiltInKind, + * BoundSymbolKind} THEN /\ IF idx # 1 THEN /\ PrintT("Abort: Impossible naming + * of declaration " \o "or built-in operator.") /\ IF debug THEN /\ idx' = idx + * /\ pc' = "Lbl_8" ELSE /\ pc' = "Done" /\ UNCHANGED idx ELSE /\ IF Len(ops) # + * 1 THEN /\ PrintT("Can't take subexpression of this") /\ IF debug THEN /\ idx' + * = idx /\ pc' = "Lbl_9" ELSE /\ pc' = "Done" /\ UNCHANGED idx ELSE /\ pc' = + * "Lbl_10" /\ UNCHANGED idx ELSE /\ pc' = "Lbl_10" /\ UNCHANGED idx ELSE /\ + * PrintT("Unexpected node kind") /\ IF debug THEN /\ idx' = idx /\ pc' = + * "Lbl_20" ELSE /\ pc' = "Done" /\ UNCHANGED idx /\ UNCHANGED << ops, args, + * expectedArity, substInPrefix, params, allArgs, subExprOf, result, mode, + * prevMode, curContext, opDefArityFound, opDefArgs, firstFindingOpName, opNode, + * newName, newNode, nodeArity, temp, tempArgs >> + * + * Lbl_5 == /\ pc = "Lbl_5" /\ newName' = IF IsName(ops[idx]) THEN IF curName = + * "" THEN ops[idx] ELSE curName \o "!" \o ops[idx] ELSE null /\ IF /\ curName = + * "" /\ ~ IsName(ops[idx]) THEN /\ PrintT("Need an operator or label name or + * step number here") /\ IF debug THEN /\ idx' = idx /\ pc' = "Lbl_6" ELSE /\ + * pc' = "Done" /\ UNCHANGED idx ELSE /\ pc' = "Lbl_7" /\ UNCHANGED idx /\ + * UNCHANGED << ops, args, expectedArity, substInPrefix, params, allArgs, + * curNode, subExprOf, result, mode, prevMode, curContext, curName, + * opDefArityFound, opDefArgs, firstFindingOpName, opNode, newNode, nodeArity, + * temp, tempArgs >> + * + * Lbl_6 == /\ pc = "Lbl_6" /\ idx' = idx /\ Assert(FALSE, "Failure of assertion + * at line line 1892, column 38 of macro called at line 1956, column 13.") /\ + * pc' = "Lbl_7" /\ UNCHANGED << ops, args, expectedArity, substInPrefix, + * params, allArgs, curNode, subExprOf, result, mode, prevMode, curContext, + * curName, opDefArityFound, opDefArgs, firstFindingOpName, opNode, newName, + * newNode, nodeArity, temp, tempArgs >> + * + * Lbl_7 == /\ pc = "Lbl_7" /\ newNode' = (IF newName # null THEN + * LookUp(newName, curContext) ELSE null) /\ IF newName = null THEN /\ tempArgs' + * = tempArgs \o args[idx] /\ idx' = idx + 1 ELSE /\ TRUE /\ UNCHANGED << idx, + * tempArgs >> /\ pc' = "Lbl_3" /\ UNCHANGED << ops, args, expectedArity, + * substInPrefix, params, allArgs, curNode, subExprOf, result, mode, prevMode, + * curContext, curName, opDefArityFound, opDefArgs, firstFindingOpName, opNode, + * newName, nodeArity, temp >> + * + * Lbl_4 == /\ pc = "Lbl_4" /\ idx' = idx /\ Assert(FALSE, "Failure of assertion + * at line line 1892, column 38 of macro called at line 1942, column 13.") /\ + * pc' = "Lbl_5" /\ UNCHANGED << ops, args, expectedArity, substInPrefix, + * params, allArgs, curNode, subExprOf, result, mode, prevMode, curContext, + * curName, opDefArityFound, opDefArgs, firstFindingOpName, opNode, newName, + * newNode, nodeArity, temp, tempArgs >> + * + * Lbl_10 == /\ pc = "Lbl_10" /\ nodeArity' = Arity(curNode) /\ tempArgs' = + * tempArgs \o args[idx] /\ IF expectedArity = 0 THEN /\ IF opDefArityFound + + * Len(tempArgs') # nodeArity' THEN /\ PrintT("Wrong number of arguments") /\ IF + * debug THEN /\ idx' = idx /\ pc' = "Lbl_11" ELSE /\ pc' = "Done" /\ UNCHANGED + * idx ELSE /\ pc' = "Lbl_12" /\ UNCHANGED idx ELSE /\ IF expectedArity > 0 THEN + * /\ IF \E i \in 1..Len(curNode.params) : curNode.params[i].arity > 0 THEN /\ + * PrintT("Higher-order operator selected " \o "as operator argument") /\ IF + * debug THEN /\ idx' = idx /\ pc' = "Lbl_14" ELSE /\ pc' = "Done" /\ UNCHANGED + * idx ELSE /\ pc' = "Lbl_15" /\ UNCHANGED idx ELSE /\ pc' = "Lbl_15" /\ + * UNCHANGED idx /\ UNCHANGED << ops, args, expectedArity, substInPrefix, + * params, allArgs, curNode, subExprOf, result, mode, prevMode, curContext, + * curName, opDefArityFound, opDefArgs, firstFindingOpName, opNode, newName, + * newNode, temp >> + * + * Lbl_12 == /\ pc = "Lbl_12" /\ IF \E i \in 1..Len(tempArgs[idx]) : + * Arity(Node[tempArgs[idx][i]]) # ParamArity(curNode, i + opDefArityFound) THEN + * /\ PrintT("Argument has wrong arity") /\ IF debug THEN /\ idx' = idx /\ pc' = + * "Lbl_13" ELSE /\ pc' = "Done" /\ UNCHANGED idx /\ UNCHANGED opDefArgs ELSE /\ + * opDefArgs' = opDefArgs \o tempArgs /\ pc' = "Lbl_15" /\ UNCHANGED idx /\ + * UNCHANGED << ops, args, expectedArity, substInPrefix, params, allArgs, + * curNode, subExprOf, result, mode, prevMode, curContext, curName, + * opDefArityFound, firstFindingOpName, opNode, newName, newNode, nodeArity, + * temp, tempArgs >> + * + * Lbl_13 == /\ pc = "Lbl_13" /\ idx' = idx /\ Assert(FALSE, "Failure of + * assertion at line line 1892, column 38 of macro called at line 1990, column + * 26.") /\ pc' = "Lbl_15" /\ UNCHANGED << ops, args, expectedArity, + * substInPrefix, params, allArgs, curNode, subExprOf, result, mode, prevMode, + * curContext, curName, opDefArityFound, opDefArgs, firstFindingOpName, opNode, + * newName, newNode, nodeArity, temp, tempArgs >> + * + * Lbl_11 == /\ pc = "Lbl_11" /\ idx' = idx /\ Assert(FALSE, "Failure of + * assertion at line line 1892, column 38 of macro called at line 1985, column + * 25.") /\ pc' = "Lbl_12" /\ UNCHANGED << ops, args, expectedArity, + * substInPrefix, params, allArgs, curNode, subExprOf, result, mode, prevMode, + * curContext, curName, opDefArityFound, opDefArgs, firstFindingOpName, opNode, + * newName, newNode, nodeArity, temp, tempArgs >> + * + * Lbl_14 == /\ pc = "Lbl_14" /\ idx' = idx /\ Assert(FALSE, "Failure of + * assertion at line line 1892, column 38 of macro called at line 1996, column + * 32.") /\ pc' = "Lbl_15" /\ UNCHANGED << ops, args, expectedArity, + * substInPrefix, params, allArgs, curNode, subExprOf, result, mode, prevMode, + * curContext, curName, opDefArityFound, opDefArgs, firstFindingOpName, opNode, + * newName, newNode, nodeArity, temp, tempArgs >> + * + * Lbl_15 == /\ pc = "Lbl_15" /\ opDefArityFound' = nodeArity /\ IF curNode.kind + * \in {UserDefinedOpKind, ThmOrAssumpDefKind, ConstantDeclKind, + * VariableDeclKind, FormalParamKind, BoundSymbolKind} THEN /\ IF /\ + * curNode.kind = UserDefinedOpKind /\ ~ curNode.defined /\ ~ Len(ops) = 1 THEN + * /\ PrintT("Can't refer to subexpression of " \o "operator inside its + * definition") /\ IF debug THEN /\ idx' = idx /\ pc' = "Lbl_16" ELSE /\ pc' = + * "Done" /\ UNCHANGED idx ELSE /\ pc' = "Lbl_17" /\ UNCHANGED idx ELSE /\ IF + * (idx = Len(ops)) THEN /\ PrintT("Operator name incomplete") /\ IF debug THEN + * /\ idx' = idx /\ pc' = "Lbl_19" ELSE /\ pc' = "Done" /\ UNCHANGED idx ELSE /\ + * pc' = "Lbl_21" /\ UNCHANGED idx /\ UNCHANGED << ops, args, expectedArity, + * substInPrefix, params, allArgs, curNode, subExprOf, result, mode, prevMode, + * curContext, curName, opDefArgs, firstFindingOpName, opNode, newName, newNode, + * nodeArity, temp, tempArgs >> + * + * Lbl_17 == /\ pc = "Lbl_17" /\ IF /\ firstFindingOpName /\ curNode.kind \in + * {UserDefinedOpKind, ThmOrAssumpDefKind} THEN /\ subExprOf' = curNode ELSE /\ + * TRUE /\ UNCHANGED subExprOf /\ IF idx # Len(ops) THEN /\ params' = params \o + * curNode.params /\ curName' = "" /\ IF IsName(ops[idx+1]) THEN /\ mode' = + * "FollowingLabels" ELSE /\ mode' = "FindingSubExpr" /\ allArgs' = allArgs \o + * opDefArgs /\ opDefArityFound' = 0 /\ opDefArgs' = << >> /\ newNode' = + * Node[curNode.body] /\ pc' = "Lbl_18" ELSE /\ pc' = "Lbl_21" /\ UNCHANGED << + * params, allArgs, mode, curName, opDefArityFound, opDefArgs, newNode >> /\ + * UNCHANGED << ops, args, expectedArity, substInPrefix, curNode, result, idx, + * prevMode, curContext, firstFindingOpName, opNode, newName, nodeArity, temp, + * tempArgs >> + * + * Lbl_18 == /\ pc = "Lbl_18" /\ IF newNode.kind = SubstInKind THEN /\ + * substInPrefix' = substInPrefix \o <<newNode>> /\ newNode' = + * Node[newNode.body] /\ pc' = "Lbl_18" /\ UNCHANGED curNode ELSE /\ IF mode = + * "FindingSubExpr" THEN /\ curNode' = newNode ELSE /\ TRUE /\ UNCHANGED curNode + * /\ pc' = "Lbl_21" /\ UNCHANGED << substInPrefix, newNode >> /\ UNCHANGED << + * ops, args, expectedArity, params, allArgs, subExprOf, result, idx, mode, + * prevMode, curContext, curName, opDefArityFound, opDefArgs, + * firstFindingOpName, opNode, newName, nodeArity, temp, tempArgs >> + * + * Lbl_16 == /\ pc = "Lbl_16" /\ idx' = idx /\ Assert(FALSE, "Failure of + * assertion at line line 1892, column 38 of macro called at line 2010, column + * 25.") /\ pc' = "Lbl_17" /\ UNCHANGED << ops, args, expectedArity, + * substInPrefix, params, allArgs, curNode, subExprOf, result, mode, prevMode, + * curContext, curName, opDefArityFound, opDefArgs, firstFindingOpName, opNode, + * newName, newNode, nodeArity, temp, tempArgs >> + * + * Lbl_19 == /\ pc = "Lbl_19" /\ idx' = idx /\ Assert(FALSE, "Failure of + * assertion at line line 1892, column 38 of macro called at line 2043, column + * 25.") /\ pc' = "Lbl_21" /\ UNCHANGED << ops, args, expectedArity, + * substInPrefix, params, allArgs, curNode, subExprOf, result, mode, prevMode, + * curContext, curName, opDefArityFound, opDefArgs, firstFindingOpName, opNode, + * newName, newNode, nodeArity, temp, tempArgs >> + * + * Lbl_8 == /\ pc = "Lbl_8" /\ idx' = idx /\ Assert(FALSE, "Failure of assertion + * at line line 1892, column 38 of macro called at line 1974, column 26.") /\ + * pc' = "Lbl_10" /\ UNCHANGED << ops, args, expectedArity, substInPrefix, + * params, allArgs, curNode, subExprOf, result, mode, prevMode, curContext, + * curName, opDefArityFound, opDefArgs, firstFindingOpName, opNode, newName, + * newNode, nodeArity, temp, tempArgs >> + * + * Lbl_9 == /\ pc = "Lbl_9" /\ idx' = idx /\ Assert(FALSE, "Failure of assertion + * at line line 1892, column 38 of macro called at line 1977, column 33.") /\ + * pc' = "Lbl_10" /\ UNCHANGED << ops, args, expectedArity, substInPrefix, + * params, allArgs, curNode, subExprOf, result, mode, prevMode, curContext, + * curName, opDefArityFound, opDefArgs, firstFindingOpName, opNode, newName, + * newNode, nodeArity, temp, tempArgs >> + * + * Lbl_20 == /\ pc = "Lbl_20" /\ idx' = idx /\ Assert(FALSE, "Failure of + * assertion at line line 1892, column 38 of macro called at line 2046, column + * 9.") /\ pc' = "Lbl_21" /\ UNCHANGED << ops, args, expectedArity, + * substInPrefix, params, allArgs, curNode, subExprOf, result, mode, prevMode, + * curContext, curName, opDefArityFound, opDefArgs, firstFindingOpName, opNode, + * newName, newNode, nodeArity, temp, tempArgs >> + * + * Lbl_21 == /\ pc = "Lbl_21" /\ prevMode' = "FindingOpName" /\ pc' = "Lbl_69" + * /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, allArgs, + * curNode, subExprOf, result, idx, mode, curContext, curName, opDefArityFound, + * opDefArgs, firstFindingOpName, opNode, newName, newNode, nodeArity, temp, + * tempArgs >> + * + * Lbl_23 == /\ pc = "Lbl_23" /\ curNode' = newNode /\ IF expectedArity = 0 THEN + * /\ IF Len(args[idx]) # curNode'.arity THEN /\ PrintT("bad arity") /\ IF debug + * THEN /\ idx' = idx /\ pc' = "Lbl_24" ELSE /\ pc' = "Done" /\ UNCHANGED idx + * ELSE /\ pc' = "Lbl_25" /\ UNCHANGED idx ELSE /\ pc' = "Lbl_28" /\ UNCHANGED + * idx /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, allArgs, + * subExprOf, result, mode, prevMode, curContext, curName, opDefArityFound, + * opDefArgs, firstFindingOpName, opNode, newName, newNode, nodeArity, temp, + * tempArgs >> + * + * Lbl_25 == /\ pc = "Lbl_25" /\ IF \E i \in 1..Len(args[idx]) : + * Arity(Node[args[idx][i]]) # 0 THEN /\ PrintT("Operator argument given to + * label") /\ IF debug THEN /\ idx' = idx /\ pc' = "Lbl_26" ELSE /\ pc' = "Done" + * /\ UNCHANGED idx ELSE /\ pc' = "Lbl_27" /\ UNCHANGED idx /\ UNCHANGED << ops, + * args, expectedArity, substInPrefix, params, allArgs, curNode, subExprOf, + * result, mode, prevMode, curContext, curName, opDefArityFound, opDefArgs, + * firstFindingOpName, opNode, newName, newNode, nodeArity, temp, tempArgs >> + * + * Lbl_26 == /\ pc = "Lbl_26" /\ idx' = idx /\ Assert(FALSE, "Failure of + * assertion at line line 1892, column 38 of macro called at line 2068, column + * 20.") /\ pc' = "Lbl_27" /\ UNCHANGED << ops, args, expectedArity, + * substInPrefix, params, allArgs, curNode, subExprOf, result, mode, prevMode, + * curContext, curName, opDefArityFound, opDefArgs, firstFindingOpName, opNode, + * newName, newNode, nodeArity, temp, tempArgs >> + * + * Lbl_27 == /\ pc = "Lbl_27" /\ allArgs' = allArgs \o args[idx] /\ pc' = + * "Lbl_28" /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, + * curNode, subExprOf, result, idx, mode, prevMode, curContext, curName, + * opDefArityFound, opDefArgs, firstFindingOpName, opNode, newName, newNode, + * nodeArity, temp, tempArgs >> + * + * Lbl_24 == /\ pc = "Lbl_24" /\ idx' = idx /\ Assert(FALSE, "Failure of + * assertion at line line 1892, column 38 of macro called at line 2064, column + * 20.") /\ pc' = "Lbl_25" /\ UNCHANGED << ops, args, expectedArity, + * substInPrefix, params, allArgs, curNode, subExprOf, result, mode, prevMode, + * curContext, curName, opDefArityFound, opDefArgs, firstFindingOpName, opNode, + * newName, newNode, nodeArity, temp, tempArgs >> + * + * Lbl_28 == /\ pc = "Lbl_28" /\ params' = params \o curNode.params /\ IF /\ idx + * < Len(ops) /\ ~IsName(ops[idx+1]) THEN /\ mode' = "FindingSubExpr" ELSE /\ + * TRUE /\ UNCHANGED mode /\ IF \/ mode' = "FindingSubExpr" \/ idx = Len(ops) + * THEN /\ curNode' = Node[curNode.body] ELSE /\ TRUE /\ UNCHANGED curNode /\ + * prevMode' = "FollowingLabels" /\ pc' = "Lbl_69" /\ UNCHANGED << ops, args, + * expectedArity, substInPrefix, allArgs, subExprOf, result, idx, curContext, + * curName, opDefArityFound, opDefArgs, firstFindingOpName, opNode, newName, + * newNode, nodeArity, temp, tempArgs >> + * + * Lbl_22 == /\ pc = "Lbl_22" /\ idx' = idx /\ Assert(FALSE, "Failure of + * assertion at line line 1892, column 38 of macro called at line 2059, column + * 13.") /\ pc' = "Lbl_23" /\ UNCHANGED << ops, args, expectedArity, + * substInPrefix, params, allArgs, curNode, subExprOf, result, mode, prevMode, + * curContext, curName, opDefArityFound, opDefArgs, firstFindingOpName, opNode, + * newName, newNode, nodeArity, temp, tempArgs >> + * + * Lbl_63 == /\ pc = "Lbl_63" /\ IF idx # Len(ops) THEN /\ IF IsName(ops[idx+1]) + * THEN /\ pc' = "Lbl_64" /\ UNCHANGED idx ELSE /\ IF ops[idx+1] = ":" THEN /\ + * PrintT("!: should not follow an operand selector") /\ IF debug THEN /\ idx' = + * idx /\ pc' = "Lbl_66" ELSE /\ pc' = "Done" /\ UNCHANGED idx ELSE /\ pc' = + * "Lbl_67" /\ UNCHANGED idx ELSE /\ pc' = "Lbl_67" /\ UNCHANGED idx /\ + * UNCHANGED << ops, args, expectedArity, substInPrefix, params, allArgs, + * curNode, subExprOf, result, mode, prevMode, curContext, curName, + * opDefArityFound, opDefArgs, firstFindingOpName, opNode, newName, newNode, + * nodeArity, temp, tempArgs >> + * + * Lbl_64 == /\ pc = "Lbl_64" /\ IF curNode.kind = LabelKind THEN /\ curNode' = + * Node[curNode.body] /\ pc' = "Lbl_64" /\ UNCHANGED << idx, mode, curContext, + * firstFindingOpName >> ELSE /\ IF curNode.kind = LetInKind THEN /\ curContext' + * = curNode.context /\ mode' = "FindingOpName" /\ firstFindingOpName' = FALSE + * /\ pc' = "Lbl_67" /\ UNCHANGED idx ELSE /\ PrintT("A name not following the + * selector of a LET") /\ IF debug THEN /\ idx' = idx /\ pc' = "Lbl_65" ELSE /\ + * pc' = "Done" /\ UNCHANGED idx /\ UNCHANGED << mode, curContext, + * firstFindingOpName >> /\ UNCHANGED curNode /\ UNCHANGED << ops, args, + * expectedArity, substInPrefix, params, allArgs, subExprOf, result, prevMode, + * curName, opDefArityFound, opDefArgs, opNode, newName, newNode, nodeArity, + * temp, tempArgs >> + * + * Lbl_65 == /\ pc = "Lbl_65" /\ idx' = idx /\ Assert(FALSE, "Failure of + * assertion at line line 1892, column 38 of macro called at line 2294, column + * 26.") /\ pc' = "Lbl_67" /\ UNCHANGED << ops, args, expectedArity, + * substInPrefix, params, allArgs, curNode, subExprOf, result, mode, prevMode, + * curContext, curName, opDefArityFound, opDefArgs, firstFindingOpName, opNode, + * newName, newNode, nodeArity, temp, tempArgs >> + * + * Lbl_66 == /\ pc = "Lbl_66" /\ idx' = idx /\ Assert(FALSE, "Failure of + * assertion at line line 1892, column 38 of macro called at line 2297, column + * 26.") /\ pc' = "Lbl_67" /\ UNCHANGED << ops, args, expectedArity, + * substInPrefix, params, allArgs, curNode, subExprOf, result, mode, prevMode, + * curContext, curName, opDefArityFound, opDefArgs, firstFindingOpName, opNode, + * newName, newNode, nodeArity, temp, tempArgs >> + * + * Lbl_67 == /\ pc = "Lbl_67" /\ prevMode' = "FindingSubExpr" /\ pc' = "Lbl_69" + * /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, allArgs, + * curNode, subExprOf, result, idx, mode, curContext, curName, opDefArityFound, + * opDefArgs, firstFindingOpName, opNode, newName, newNode, nodeArity, temp, + * tempArgs >> + * + * Lbl_29 == /\ pc = "Lbl_29" /\ idx' = idx /\ Assert(FALSE, "Failure of + * assertion at line line 1892, column 38 of macro called at line 2091, column + * 14.") /\ pc' = "Lbl_63" /\ UNCHANGED << ops, args, expectedArity, + * substInPrefix, params, allArgs, curNode, subExprOf, result, mode, prevMode, + * curContext, curName, opDefArityFound, opDefArgs, firstFindingOpName, opNode, + * newName, newNode, nodeArity, temp, tempArgs >> + * + * Lbl_30 == /\ pc = "Lbl_30" /\ idx' = idx /\ Assert(FALSE, "Failure of + * assertion at line line 1892, column 38 of macro called at line 2098, column + * 14.") /\ pc' = "Lbl_63" /\ UNCHANGED << ops, args, expectedArity, + * substInPrefix, params, allArgs, curNode, subExprOf, result, mode, prevMode, + * curContext, curName, opDefArityFound, opDefArgs, firstFindingOpName, opNode, + * newName, newNode, nodeArity, temp, tempArgs >> + * + * Lbl_31 == /\ pc = "Lbl_31" /\ idx' = idx /\ Assert(FALSE, "Failure of + * assertion at line line 1892, column 38 of macro called at line 2110, column + * 17.") /\ pc' = "Lbl_63" /\ UNCHANGED << ops, args, expectedArity, + * substInPrefix, params, allArgs, curNode, subExprOf, result, mode, prevMode, + * curContext, curName, opDefArityFound, opDefArgs, firstFindingOpName, opNode, + * newName, newNode, nodeArity, temp, tempArgs >> + * + * Lbl_33 == /\ pc = "Lbl_33" /\ curNode' = Node[curNode.operands[temp]] /\ IF + * \/ curNode'.kind # OpApplKind \/ Node[curNode'.operator].kind # BuiltInKind + * \/ Node[curNode'.operator].name # "$Pair" THEN /\ PrintT("Expecting + * $Pair(...)") /\ IF debug THEN /\ idx' = idx /\ pc' = "Lbl_34" ELSE /\ pc' = + * "Done" /\ UNCHANGED idx ELSE /\ pc' = "Lbl_35" /\ UNCHANGED idx /\ UNCHANGED + * << ops, args, expectedArity, substInPrefix, params, allArgs, subExprOf, + * result, mode, prevMode, curContext, curName, opDefArityFound, opDefArgs, + * firstFindingOpName, opNode, newName, newNode, nodeArity, temp, tempArgs >> + * + * Lbl_35 == /\ pc = "Lbl_35" /\ curNode' = Node[curNode.operands[2]] /\ pc' = + * "Lbl_63" /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, + * allArgs, subExprOf, result, idx, mode, prevMode, curContext, curName, + * opDefArityFound, opDefArgs, firstFindingOpName, opNode, newName, newNode, + * nodeArity, temp, tempArgs >> + * + * Lbl_34 == /\ pc = "Lbl_34" /\ idx' = idx /\ Assert(FALSE, "Failure of + * assertion at line line 1892, column 38 of macro called at line 2131, column + * 18.") /\ pc' = "Lbl_63" /\ UNCHANGED << ops, args, expectedArity, + * substInPrefix, params, allArgs, curNode, subExprOf, result, mode, prevMode, + * curContext, curName, opDefArityFound, opDefArgs, firstFindingOpName, opNode, + * newName, newNode, nodeArity, temp, tempArgs >> + * + * Lbl_32 == /\ pc = "Lbl_32" /\ idx' = idx /\ Assert(FALSE, "Failure of + * assertion at line line 1892, column 38 of macro called at line 2125, column + * 18.") /\ pc' = "Lbl_33" /\ UNCHANGED << ops, args, expectedArity, + * substInPrefix, params, allArgs, curNode, subExprOf, result, mode, prevMode, + * curContext, curName, opDefArityFound, opDefArgs, firstFindingOpName, opNode, + * newName, newNode, nodeArity, temp, tempArgs >> + * + * Lbl_37 == /\ pc = "Lbl_37" /\ temp' = ArgNum( ops[idx], + * Len(curNode.operands)) /\ IF temp' = -1 THEN /\ PrintT("Incorrect + * subexpression name") /\ IF debug THEN /\ idx' = idx /\ pc' = "Lbl_38" ELSE /\ + * pc' = "Done" /\ UNCHANGED idx ELSE /\ pc' = "Lbl_39" /\ UNCHANGED idx /\ + * UNCHANGED << ops, args, expectedArity, substInPrefix, params, allArgs, + * curNode, subExprOf, result, mode, prevMode, curContext, curName, + * opDefArityFound, opDefArgs, firstFindingOpName, opNode, newName, newNode, + * nodeArity, tempArgs >> + * + * Lbl_38 == /\ pc = "Lbl_38" /\ idx' = idx /\ Assert(FALSE, "Failure of + * assertion at line line 1892, column 38 of macro called at line 2145, column + * 18.") /\ pc' = "Lbl_39" /\ UNCHANGED << ops, args, expectedArity, + * substInPrefix, params, allArgs, curNode, subExprOf, result, mode, prevMode, + * curContext, curName, opDefArityFound, opDefArgs, firstFindingOpName, opNode, + * newName, newNode, nodeArity, temp, tempArgs >> + * + * Lbl_39 == /\ pc = "Lbl_39" /\ curNode' = Node[curNode.operands[temp]] /\ IF + * \/ curNode'.kind # OpApplKind \/ Node[curNode'.operator].kind # BuiltInKind + * \/ Node[curNode'.operator].name # "$Pair" THEN /\ PrintT("Expecting + * $Pair(...)") /\ IF debug THEN /\ idx' = idx /\ pc' = "Lbl_40" ELSE /\ pc' = + * "Done" /\ UNCHANGED idx ELSE /\ pc' = "Lbl_41" /\ UNCHANGED idx /\ UNCHANGED + * << ops, args, expectedArity, substInPrefix, params, allArgs, subExprOf, + * result, mode, prevMode, curContext, curName, opDefArityFound, opDefArgs, + * firstFindingOpName, opNode, newName, newNode, nodeArity, temp, tempArgs >> + * + * Lbl_40 == /\ pc = "Lbl_40" /\ idx' = idx /\ Assert(FALSE, "Failure of + * assertion at line line 1892, column 38 of macro called at line 2151, column + * 18.") /\ pc' = "Lbl_41" /\ UNCHANGED << ops, args, expectedArity, + * substInPrefix, params, allArgs, curNode, subExprOf, result, mode, prevMode, + * curContext, curName, opDefArityFound, opDefArgs, firstFindingOpName, opNode, + * newName, newNode, nodeArity, temp, tempArgs >> + * + * Lbl_41 == /\ pc = "Lbl_41" /\ idx' = idx+1 /\ temp' = ArgNum(ops[idx'], 2) /\ + * IF temp' = -1 THEN /\ PrintT("Incorrect subexpression name") /\ IF debug THEN + * /\ pc' = "Lbl_42" ELSE /\ pc' = "Done" ELSE /\ pc' = "Lbl_44" /\ UNCHANGED << + * ops, args, expectedArity, substInPrefix, params, allArgs, curNode, subExprOf, + * result, mode, prevMode, curContext, curName, opDefArityFound, opDefArgs, + * firstFindingOpName, opNode, newName, newNode, nodeArity, tempArgs >> + * + * Lbl_42 == /\ pc = "Lbl_42" /\ idx' = idx /\ pc' = "Lbl_43" /\ UNCHANGED << + * ops, args, expectedArity, substInPrefix, params, allArgs, curNode, subExprOf, + * result, mode, prevMode, curContext, curName, opDefArityFound, opDefArgs, + * firstFindingOpName, opNode, newName, newNode, nodeArity, temp, tempArgs >> + * + * Lbl_43 == /\ pc = "Lbl_43" /\ idx' = idx /\ Assert(FALSE, "Failure of + * assertion at line line 1892, column 38 of macro called at line 2156, column + * 18.") /\ pc' = "Lbl_44" /\ UNCHANGED << ops, args, expectedArity, + * substInPrefix, params, allArgs, curNode, subExprOf, result, mode, prevMode, + * curContext, curName, opDefArityFound, opDefArgs, firstFindingOpName, opNode, + * newName, newNode, nodeArity, temp, tempArgs >> + * + * Lbl_44 == /\ pc = "Lbl_44" /\ curNode' = Node[curNode.operands[temp]] /\ IF + * curNode' = null THEN /\ PrintT("Selecting OTHER") /\ IF debug THEN /\ idx' = + * idx /\ pc' = "Lbl_45" ELSE /\ pc' = "Done" /\ UNCHANGED idx ELSE /\ pc' = + * "Lbl_63" /\ UNCHANGED idx /\ UNCHANGED << ops, args, expectedArity, + * substInPrefix, params, allArgs, subExprOf, result, mode, prevMode, + * curContext, curName, opDefArityFound, opDefArgs, firstFindingOpName, opNode, + * newName, newNode, nodeArity, temp, tempArgs >> + * + * Lbl_45 == /\ pc = "Lbl_45" /\ idx' = idx /\ Assert(FALSE, "Failure of + * assertion at line line 1892, column 38 of macro called at line 2160, column + * 18.") /\ pc' = "Lbl_63" /\ UNCHANGED << ops, args, expectedArity, + * substInPrefix, params, allArgs, curNode, subExprOf, result, mode, prevMode, + * curContext, curName, opDefArityFound, opDefArgs, firstFindingOpName, opNode, + * newName, newNode, nodeArity, temp, tempArgs >> + * + * Lbl_36 == /\ pc = "Lbl_36" /\ idx' = idx /\ Assert(FALSE, "Failure of + * assertion at line line 1892, column 38 of macro called at line 2141, column + * 18.") /\ pc' = "Lbl_37" /\ UNCHANGED << ops, args, expectedArity, + * substInPrefix, params, allArgs, curNode, subExprOf, result, mode, prevMode, + * curContext, curName, opDefArityFound, opDefArgs, firstFindingOpName, opNode, + * newName, newNode, nodeArity, temp, tempArgs >> + * + * Lbl_47 == /\ pc = "Lbl_47" /\ curNode' = Node[curNode.operands[temp]] /\ IF + * temp > 1 THEN /\ IF \/ curNode'.kind # OpApplKind \/ + * Node[curNode'.operator].kind # BuiltInKind \/ Node[curNode'.operator].name # + * "$Pair" THEN /\ PrintT("Unexpected expression node found.") /\ IF debug THEN + * /\ idx' = idx /\ pc' = "Lbl_48" ELSE /\ pc' = "Done" /\ UNCHANGED idx ELSE /\ + * pc' = "Lbl_49" /\ UNCHANGED idx ELSE /\ pc' = "Lbl_63" /\ UNCHANGED idx /\ + * UNCHANGED << ops, args, expectedArity, substInPrefix, params, allArgs, + * subExprOf, result, mode, prevMode, curContext, curName, opDefArityFound, + * opDefArgs, firstFindingOpName, opNode, newName, newNode, nodeArity, temp, + * tempArgs >> + * + * Lbl_49 == /\ pc = "Lbl_49" /\ curNode' = Node[curNode.operands[2]] /\ pc' = + * "Lbl_63" /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, + * allArgs, subExprOf, result, idx, mode, prevMode, curContext, curName, + * opDefArityFound, opDefArgs, firstFindingOpName, opNode, newName, newNode, + * nodeArity, temp, tempArgs >> + * + * Lbl_48 == /\ pc = "Lbl_48" /\ idx' = idx /\ Assert(FALSE, "Failure of + * assertion at line line 1892, column 38 of macro called at line 2179, column + * 26.") /\ pc' = "Lbl_63" /\ UNCHANGED << ops, args, expectedArity, + * substInPrefix, params, allArgs, curNode, subExprOf, result, mode, prevMode, + * curContext, curName, opDefArityFound, opDefArgs, firstFindingOpName, opNode, + * newName, newNode, nodeArity, temp, tempArgs >> + * + * Lbl_46 == /\ pc = "Lbl_46" /\ idx' = idx /\ Assert(FALSE, "Failure of + * assertion at line line 1892, column 38 of macro called at line 2172, column + * 19.") /\ pc' = "Lbl_47" /\ UNCHANGED << ops, args, expectedArity, + * substInPrefix, params, allArgs, curNode, subExprOf, result, mode, prevMode, + * curContext, curName, opDefArityFound, opDefArgs, firstFindingOpName, opNode, + * newName, newNode, nodeArity, temp, tempArgs >> + * + * Lbl_51 == /\ pc = "Lbl_51" /\ curNode' = Node[curNode.operands[temp]] /\ pc' + * = "Lbl_63" /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, + * allArgs, subExprOf, result, idx, mode, prevMode, curContext, curName, + * opDefArityFound, opDefArgs, firstFindingOpName, opNode, newName, newNode, + * nodeArity, temp, tempArgs >> + * + * Lbl_50 == /\ pc = "Lbl_50" /\ idx' = idx /\ Assert(FALSE, "Failure of + * assertion at line line 1892, column 38 of macro called at line 2195, column + * 25.") /\ pc' = "Lbl_51" /\ UNCHANGED << ops, args, expectedArity, + * substInPrefix, params, allArgs, curNode, subExprOf, result, mode, prevMode, + * curContext, curName, opDefArityFound, opDefArgs, firstFindingOpName, opNode, + * newName, newNode, nodeArity, temp, tempArgs >> + * + * Lbl_53 == /\ pc = "Lbl_53" /\ curNode' = Node[curNode.operands[1]] /\ pc' = + * "Lbl_63" /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, + * allArgs, subExprOf, result, idx, mode, prevMode, curContext, curName, + * opDefArityFound, opDefArgs, firstFindingOpName, opNode, newName, newNode, + * nodeArity, temp, tempArgs >> + * + * Lbl_52 == /\ pc = "Lbl_52" /\ idx' = idx /\ Assert(FALSE, "Failure of + * assertion at line line 1892, column 38 of macro called at line 2220, column + * 32.") /\ pc' = "Lbl_53" /\ UNCHANGED << ops, args, expectedArity, + * substInPrefix, params, allArgs, curNode, subExprOf, result, mode, prevMode, + * curContext, curName, opDefArityFound, opDefArgs, firstFindingOpName, opNode, + * newName, newNode, nodeArity, temp, tempArgs >> + * + * Lbl_54 == /\ pc = "Lbl_54" /\ idx' = idx /\ Assert(FALSE, "Failure of + * assertion at line line 1892, column 38 of macro called at line 2225, column + * 32.") /\ pc' = "Lbl_63" /\ UNCHANGED << ops, args, expectedArity, + * substInPrefix, params, allArgs, curNode, subExprOf, result, mode, prevMode, + * curContext, curName, opDefArityFound, opDefArgs, firstFindingOpName, opNode, + * newName, newNode, nodeArity, temp, tempArgs >> + * + * Lbl_55 == /\ pc = "Lbl_55" /\ idx' = idx /\ Assert(FALSE, "Failure of + * assertion at line line 1892, column 38 of macro called at line 2232, column + * 12.") /\ pc' = "Lbl_63" /\ UNCHANGED << ops, args, expectedArity, + * substInPrefix, params, allArgs, curNode, subExprOf, result, mode, prevMode, + * curContext, curName, opDefArityFound, opDefArgs, firstFindingOpName, opNode, + * newName, newNode, nodeArity, temp, tempArgs >> + * + * Lbl_56 == /\ pc = "Lbl_56" /\ idx' = idx /\ Assert(FALSE, "Failure of + * assertion at line line 1892, column 38 of macro called at line 2240, column + * 18.") /\ pc' = "Lbl_63" /\ UNCHANGED << ops, args, expectedArity, + * substInPrefix, params, allArgs, curNode, subExprOf, result, mode, prevMode, + * curContext, curName, opDefArityFound, opDefArgs, firstFindingOpName, opNode, + * newName, newNode, nodeArity, temp, tempArgs >> + * + * Lbl_57 == /\ pc = "Lbl_57" /\ idx' = idx /\ Assert(FALSE, "Failure of + * assertion at line line 1892, column 38 of macro called at line 2255, column + * 9.") /\ pc' = "Lbl_63" /\ UNCHANGED << ops, args, expectedArity, + * substInPrefix, params, allArgs, curNode, subExprOf, result, mode, prevMode, + * curContext, curName, opDefArityFound, opDefArgs, firstFindingOpName, opNode, + * newName, newNode, nodeArity, temp, tempArgs >> + * + * Lbl_58 == /\ pc = "Lbl_58" /\ idx' = idx /\ Assert(FALSE, "Failure of + * assertion at line line 1892, column 38 of macro called at line 2257, column + * 9.") /\ pc' = "Lbl_63" /\ UNCHANGED << ops, args, expectedArity, + * substInPrefix, params, allArgs, curNode, subExprOf, result, mode, prevMode, + * curContext, curName, opDefArityFound, opDefArgs, firstFindingOpName, opNode, + * newName, newNode, nodeArity, temp, tempArgs >> + * + * Lbl_59 == /\ pc = "Lbl_59" /\ idx' = idx /\ Assert(FALSE, "Failure of + * assertion at line line 1892, column 38 of macro called at line 2260, column + * 9.") /\ pc' = "Lbl_63" /\ UNCHANGED << ops, args, expectedArity, + * substInPrefix, params, allArgs, curNode, subExprOf, result, mode, prevMode, + * curContext, curName, opDefArityFound, opDefArgs, firstFindingOpName, opNode, + * newName, newNode, nodeArity, temp, tempArgs >> + * + * Lbl_60 == /\ pc = "Lbl_60" /\ idx' = idx /\ Assert(FALSE, "Failure of + * assertion at line line 1892, column 38 of macro called at line 2267, column + * 7.") /\ pc' = "Lbl_63" /\ UNCHANGED << ops, args, expectedArity, + * substInPrefix, params, allArgs, curNode, subExprOf, result, mode, prevMode, + * curContext, curName, opDefArityFound, opDefArgs, firstFindingOpName, opNode, + * newName, newNode, nodeArity, temp, tempArgs >> + * + * Lbl_61 == /\ pc = "Lbl_61" /\ idx' = idx /\ Assert(FALSE, "Failure of + * assertion at line line 1892, column 38 of macro called at line 2272, column + * 10.") /\ pc' = "Lbl_63" /\ UNCHANGED << ops, args, expectedArity, + * substInPrefix, params, allArgs, curNode, subExprOf, result, mode, prevMode, + * curContext, curName, opDefArityFound, opDefArgs, firstFindingOpName, opNode, + * newName, newNode, nodeArity, temp, tempArgs >> + * + * Lbl_62 == /\ pc = "Lbl_62" /\ idx' = idx /\ Assert(FALSE, "Failure of + * assertion at line line 1892, column 38 of macro called at line 2281, column + * 8.") /\ pc' = "Lbl_63" /\ UNCHANGED << ops, args, expectedArity, + * substInPrefix, params, allArgs, curNode, subExprOf, result, mode, prevMode, + * curContext, curName, opDefArityFound, opDefArgs, firstFindingOpName, opNode, + * newName, newNode, nodeArity, temp, tempArgs >> + * + * Lbl_68 == /\ pc = "Lbl_68" /\ idx' = idx /\ Assert(FALSE, "Failure of + * assertion at line line 1892, column 38 of macro called at line 2303, column + * 8.") /\ pc' = "Lbl_69" /\ UNCHANGED << ops, args, expectedArity, + * substInPrefix, params, allArgs, curNode, subExprOf, result, mode, prevMode, + * curContext, curName, opDefArityFound, opDefArgs, firstFindingOpName, opNode, + * newName, newNode, nodeArity, temp, tempArgs >> + * + * Lbl_70 == /\ pc = "Lbl_70" /\ idx' = idx /\ Assert(FALSE, "Failure of + * assertion at line line 1892, column 38 of macro called at line 2311, column + * 10.") /\ pc' = "Lbl_71" /\ UNCHANGED << ops, args, expectedArity, + * substInPrefix, params, allArgs, curNode, subExprOf, result, mode, prevMode, + * curContext, curName, opDefArityFound, opDefArgs, firstFindingOpName, opNode, + * newName, newNode, nodeArity, temp, tempArgs >> + * + * Lbl_71 == /\ pc = "Lbl_71" /\ IF expectedArity < 0 THEN /\ IF \/ prevMode # + * "FindingOpName" \/ curNode.kind \notin {UserDefinedOpKind, + * ThmOrAssumpDefKind} THEN /\ PrintT("Should have selected a definition, but + * didn't.") /\ IF debug THEN /\ idx' = idx /\ pc' = "Lbl_72" ELSE /\ pc' = + * "Done" /\ UNCHANGED idx ELSE /\ pc' = "Lbl_73" /\ UNCHANGED idx ELSE /\ pc' = + * "Lbl_74" /\ UNCHANGED idx /\ UNCHANGED << ops, args, expectedArity, + * substInPrefix, params, allArgs, curNode, subExprOf, result, mode, prevMode, + * curContext, curName, opDefArityFound, opDefArgs, firstFindingOpName, opNode, + * newName, newNode, nodeArity, temp, tempArgs >> + * + * Lbl_73 == /\ pc = "Lbl_73" /\ result' = curNode /\ pc' = "Finished" /\ + * UNCHANGED << ops, args, expectedArity, substInPrefix, params, allArgs, + * curNode, subExprOf, idx, mode, prevMode, curContext, curName, + * opDefArityFound, opDefArgs, firstFindingOpName, opNode, newName, newNode, + * nodeArity, temp, tempArgs >> + * + * Lbl_72 == /\ pc = "Lbl_72" /\ idx' = idx /\ Assert(FALSE, "Failure of + * assertion at line line 1892, column 38 of macro called at line 2317, column + * 17.") /\ pc' = "Lbl_73" /\ UNCHANGED << ops, args, expectedArity, + * substInPrefix, params, allArgs, curNode, subExprOf, result, mode, prevMode, + * curContext, curName, opDefArityFound, opDefArgs, firstFindingOpName, opNode, + * newName, newNode, nodeArity, temp, tempArgs >> + * + * Lbl_74 == /\ pc = "Lbl_74" /\ IF expectedArity > 0 THEN /\ temp' = + * (Len(params) + IF \/ prevMode = "FindingOpName" \/ curNode.kind = OpArgKind + * THEN Arity(curNode) ELSE 0) /\ IF expectedArity # temp' THEN /\ + * PrintT("Expect arity = " \o ToString(expectedArity) \o ", but found arity = " + * \o ToString(temp')) /\ IF debug THEN /\ idx' = idx /\ pc' = "Lbl_75" ELSE /\ + * pc' = "Done" /\ UNCHANGED idx ELSE /\ pc' = "Lbl_76" /\ UNCHANGED idx ELSE /\ + * pc' = "Lbl_76" /\ UNCHANGED << idx, temp >> /\ UNCHANGED << ops, args, + * expectedArity, substInPrefix, params, allArgs, curNode, subExprOf, result, + * mode, prevMode, curContext, curName, opDefArityFound, opDefArgs, + * firstFindingOpName, opNode, newName, newNode, nodeArity, tempArgs >> + * + * Lbl_75 == /\ pc = "Lbl_75" /\ idx' = idx /\ Assert(FALSE, "Failure of + * assertion at line line 1892, column 38 of macro called at line 2329, column + * 18.") /\ pc' = "Lbl_76" /\ UNCHANGED << ops, args, expectedArity, + * substInPrefix, params, allArgs, curNode, subExprOf, result, mode, prevMode, + * curContext, curName, opDefArityFound, opDefArgs, firstFindingOpName, opNode, + * newName, newNode, nodeArity, temp, tempArgs >> + * + * Lbl_76 == /\ pc = "Lbl_76" /\ IF /\ prevMode = "FindingOpName" /\ Len(params) + * + Len(substInPrefix) > 0 THEN /\ temp' = [i \in 1..Len(curNode.params) |-> + * [curNode.params[i] EXCEPT !.name = "New " \o @]] /\ curNode' = [kind |-> + * OpApplKind, operands |-> temp', operator |-> curNode, unboundedBoundSymbols + * |-> <<>>, boundedBoundSymbols |-> << >>, ranges |-> << >>] /\ params' = + * params \o temp' /\ allArgs' = allArgs \o opDefArgs ELSE /\ TRUE /\ UNCHANGED + * << params, allArgs, curNode, temp >> /\ IF curNode'.kind = OpArgKind THEN /\ + * IF expectedArity = 0 THEN /\ PrintT("Selected Operator Argument when + * expression expected.") /\ IF debug THEN /\ idx' = idx /\ pc' = "Lbl_77" ELSE + * /\ pc' = "Done" /\ UNCHANGED idx ELSE /\ IF expectedArity # Len(params') + + * curNode'.arity THEN /\ PrintT("Selected operator has wrong arity.") /\ IF + * debug THEN /\ idx' = idx /\ pc' = "Lbl_78" ELSE /\ pc' = "Done" /\ UNCHANGED + * idx ELSE /\ IF Len(params') + Len(substInPrefix) > 0 THEN /\ pc' = "Lbl_79" + * ELSE /\ pc' = "Lbl_80" /\ UNCHANGED idx ELSE /\ pc' = "Lbl_80" /\ UNCHANGED + * idx /\ UNCHANGED << ops, args, expectedArity, substInPrefix, subExprOf, + * result, mode, prevMode, curContext, curName, opDefArityFound, opDefArgs, + * firstFindingOpName, opNode, newName, newNode, nodeArity, tempArgs >> + * + * Lbl_77 == /\ pc = "Lbl_77" /\ idx' = idx /\ Assert(FALSE, "Failure of + * assertion at line line 1892, column 38 of macro called at line 2363, column + * 12.") /\ pc' = "Lbl_80" /\ UNCHANGED << ops, args, expectedArity, + * substInPrefix, params, allArgs, curNode, subExprOf, result, mode, prevMode, + * curContext, curName, opDefArityFound, opDefArgs, firstFindingOpName, opNode, + * newName, newNode, nodeArity, temp, tempArgs >> + * + * Lbl_78 == /\ pc = "Lbl_78" /\ idx' = idx /\ Assert(FALSE, "Failure of + * assertion at line line 1892, column 38 of macro called at line 2365, column + * 12.") /\ pc' = "Lbl_80" /\ UNCHANGED << ops, args, expectedArity, + * substInPrefix, params, allArgs, curNode, subExprOf, result, mode, prevMode, + * curContext, curName, opDefArityFound, opDefArgs, firstFindingOpName, opNode, + * newName, newNode, nodeArity, temp, tempArgs >> + * + * Lbl_79 == /\ pc = "Lbl_79" /\ temp' = [i \in 1..curNode.arity |-> [name |-> + * "NewParam" \o NumToString(i), arity |-> 0]] /\ curNode' = [kind |-> + * OpApplKind, operands |-> temp', operator |-> Node[curNode.op], + * unboundedBoundSymbols |-> <<>>, boundedBoundSymbols |-> << >>, ranges |-> << + * >>] /\ params' = params \o temp' /\ pc' = "Lbl_80" /\ UNCHANGED << ops, args, + * expectedArity, substInPrefix, allArgs, subExprOf, result, idx, mode, + * prevMode, curContext, curName, opDefArityFound, opDefArgs, + * firstFindingOpName, opNode, newName, newNode, nodeArity, tempArgs >> + * + * Lbl_80 == /\ pc = "Lbl_80" /\ IF curNode.kind \in {UserDefinedOpKind, + * ConstantDeclKind, VariableDeclKind, FormalParamKind, BuiltInKind, + * BoundSymbolKind, ThmOrAssumpDefKind} THEN /\ IF expectedArity > 0 THEN /\ + * result' = [kind |-> OpArgKind, name |-> curNode.name, op |-> curNode, arity + * |-> expectedArity] ELSE /\ IF expectedArity = 0 THEN /\ result' = [kind |-> + * OpApplKind, operands |-> opDefArgs, operator |-> curNode, + * unboundedBoundSymbols |-> <<>>, boundedBoundSymbols |-> << >>, ranges |-> << + * >>, subExprOf |-> subExprOf] ELSE /\ TRUE /\ UNCHANGED result /\ pc' = + * "Finished" /\ UNCHANGED temp ELSE /\ IF curNode.kind = OpArgKind THEN /\ + * result' = curNode /\ pc' = "Finished" /\ UNCHANGED temp ELSE /\ temp' = + * Len(substInPrefix) /\ pc' = "Lbl_81" /\ UNCHANGED result /\ UNCHANGED << ops, + * args, expectedArity, substInPrefix, params, allArgs, curNode, subExprOf, idx, + * mode, prevMode, curContext, curName, opDefArityFound, opDefArgs, + * firstFindingOpName, opNode, newName, newNode, nodeArity, tempArgs >> + * + * Lbl_81 == /\ pc = "Lbl_81" /\ IF temp > 0 THEN /\ curNode' = [kind |-> + * SubstInKind, body |-> curNode , subst |-> substInPrefix[temp].subst ] /\ + * temp' = temp - 1 /\ pc' = "Lbl_81" /\ UNCHANGED idx ELSE /\ IF expectedArity + * > 0 THEN /\ IF Len(params) # expectedArity THEN /\ PrintT("Selection has + * wrong arity") /\ IF debug THEN /\ idx' = idx /\ pc' = "Lbl_82" ELSE /\ pc' = + * "Done" /\ UNCHANGED idx ELSE /\ pc' = "Lbl_83" /\ UNCHANGED idx ELSE /\ IF + * Len(params) # Len(allArgs) THEN /\ PrintT("Abort: number of params # num of + * args") /\ IF debug THEN /\ idx' = idx /\ pc' = "Lbl_84" ELSE /\ pc' = "Done" + * /\ UNCHANGED idx ELSE /\ pc' = "Lbl_85" /\ UNCHANGED idx /\ UNCHANGED << + * curNode, temp >> /\ UNCHANGED << ops, args, expectedArity, substInPrefix, + * params, allArgs, subExprOf, result, mode, prevMode, curContext, curName, + * opDefArityFound, opDefArgs, firstFindingOpName, opNode, newName, newNode, + * nodeArity, tempArgs >> + * + * Lbl_83 == /\ pc = "Lbl_83" /\ result' = [kind |-> OpArgKind, op |-> [kind |-> + * UserDefinedOpKind, name |-> "LAMBDA", body |-> curNode, params |-> params, + * arity |-> Len(params), defined |-> TRUE, source |-> null], name |-> "LAMBDA"] + * /\ pc' = "Finished" /\ UNCHANGED << ops, args, expectedArity, substInPrefix, + * params, allArgs, curNode, subExprOf, idx, mode, prevMode, curContext, + * curName, opDefArityFound, opDefArgs, firstFindingOpName, opNode, newName, + * newNode, nodeArity, temp, tempArgs >> + * + * Lbl_85 == /\ pc = "Lbl_85" /\ IF Len(params) = 0 THEN /\ result' = [kind |-> + * LabelKind, name |-> "$Subexpression", arity |-> 0, params |-> << >>, body |-> + * curNode, subExprOf |-> subExprOf] ELSE /\ result' = [kind |-> OpApplKind, + * operator |-> [kind |-> UserDefinedOpKind, name |-> "LAMBDA", body |-> + * curNode, params |-> params, arity |-> Len(params), defined |-> TRUE, source + * |-> null], operands |-> allArgs, unboundedBoundSymbols |-> <<>>, + * boundedBoundSymbols |-> << >>, ranges |-> << >>, subExprOf |-> subExprOf] /\ + * pc' = "Finished" /\ UNCHANGED << ops, args, expectedArity, substInPrefix, + * params, allArgs, curNode, subExprOf, idx, mode, prevMode, curContext, + * curName, opDefArityFound, opDefArgs, firstFindingOpName, opNode, newName, + * newNode, nodeArity, temp, tempArgs >> + * + * Lbl_82 == /\ pc = "Lbl_82" /\ idx' = idx /\ Assert(FALSE, "Failure of + * assertion at line line 1892, column 38 of macro called at line 2431, column + * 22.") /\ pc' = "Lbl_83" /\ UNCHANGED << ops, args, expectedArity, + * substInPrefix, params, allArgs, curNode, subExprOf, result, mode, prevMode, + * curContext, curName, opDefArityFound, opDefArgs, firstFindingOpName, opNode, + * newName, newNode, nodeArity, temp, tempArgs >> + * + * Lbl_84 == /\ pc = "Lbl_84" /\ idx' = idx /\ Assert(FALSE, "Failure of + * assertion at line line 1892, column 38 of macro called at line 2443, column + * 22.") /\ pc' = "Lbl_85" /\ UNCHANGED << ops, args, expectedArity, + * substInPrefix, params, allArgs, curNode, subExprOf, result, mode, prevMode, + * curContext, curName, opDefArityFound, opDefArgs, firstFindingOpName, opNode, + * newName, newNode, nodeArity, temp, tempArgs >> + * + * Finished == /\ pc = "Finished" /\ PrintT("Result: ") /\ PrintT(result) /\ pc' + * = "Done" /\ UNCHANGED << ops, args, expectedArity, substInPrefix, params, + * allArgs, curNode, subExprOf, result, idx, mode, prevMode, curContext, + * curName, opDefArityFound, opDefArgs, firstFindingOpName, opNode, newName, + * newNode, nodeArity, temp, tempArgs >> + * + * Next == Lbl_1 \/ Lbl_2 \/ Lbl_69 \/ Lbl_3 \/ Lbl_5 \/ Lbl_6 \/ Lbl_7 \/ Lbl_4 + * \/ Lbl_10 \/ Lbl_12 \/ Lbl_13 \/ Lbl_11 \/ Lbl_14 \/ Lbl_15 \/ Lbl_17 \/ + * Lbl_18 \/ Lbl_16 \/ Lbl_19 \/ Lbl_8 \/ Lbl_9 \/ Lbl_20 \/ Lbl_21 \/ Lbl_23 \/ + * Lbl_25 \/ Lbl_26 \/ Lbl_27 \/ Lbl_24 \/ Lbl_28 \/ Lbl_22 \/ Lbl_63 \/ Lbl_64 + * \/ Lbl_65 \/ Lbl_66 \/ Lbl_67 \/ Lbl_29 \/ Lbl_30 \/ Lbl_31 \/ Lbl_33 \/ + * Lbl_35 \/ Lbl_34 \/ Lbl_32 \/ Lbl_37 \/ Lbl_38 \/ Lbl_39 \/ Lbl_40 \/ Lbl_41 + * \/ Lbl_42 \/ Lbl_43 \/ Lbl_44 \/ Lbl_45 \/ Lbl_36 \/ Lbl_47 \/ Lbl_49 \/ + * Lbl_48 \/ Lbl_46 \/ Lbl_51 \/ Lbl_50 \/ Lbl_53 \/ Lbl_52 \/ Lbl_54 \/ Lbl_55 + * \/ Lbl_56 \/ Lbl_57 \/ Lbl_58 \/ Lbl_59 \/ Lbl_60 \/ Lbl_61 \/ Lbl_62 \/ + * Lbl_68 \/ Lbl_70 \/ Lbl_71 \/ Lbl_73 \/ Lbl_72 \/ Lbl_74 \/ Lbl_75 \/ Lbl_76 + * \/ Lbl_77 \/ Lbl_78 \/ Lbl_79 \/ Lbl_80 \/ Lbl_81 \/ Lbl_83 \/ Lbl_85 \/ + * Lbl_82 \/ Lbl_84 \/ Finished \/ (* Disjunct to prevent deadlock on + * termination *) (pc = "Done" /\ UNCHANGED vars) + * + * Spec == Init /\ [][Next]_vars + * + * Termination == <>(pc = "Done") + * + * \* END TRANSLATION + * + * NotDone == pc # "Done" + * (*************************************************************************) + * (* To print a trace of an execution that doesn't produce an error, add *) (* + * "INVARIANT NotDone" to Subexpression.cfg . *) + * (*************************************************************************) + * ============================================================================= + ************************* + * + * end file Subexpression.tla + ****************************/ diff --git a/tlatools/src/tla2sany/semantic/InstanceNode.java b/tlatools/src/tla2sany/semantic/InstanceNode.java index e65037bd15c82e0a57074a78e74822dba31618a9..c667f07a84470193596e75174f1a4438198a2b10 100644 --- a/tlatools/src/tla2sany/semantic/InstanceNode.java +++ b/tlatools/src/tla2sany/semantic/InstanceNode.java @@ -6,13 +6,15 @@ import java.util.HashSet; import java.util.Hashtable; import java.util.Iterator; +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +import tla2sany.explorer.ExploreNode; +import tla2sany.explorer.ExplorerVisitor; import tla2sany.st.TreeNode; import tla2sany.utilities.Strings; import util.UniqueString; -import org.w3c.dom.Document; -import org.w3c.dom.Element; - public class InstanceNode extends LevelNode { /** @@ -397,16 +399,18 @@ public class InstanceNode extends LevelNode { return res; } - public final void walkGaph(Hashtable semNodesTable) { - Integer uid = new Integer(myUID); + public final void walkGaph(Hashtable<Integer, ExploreNode> semNodesTable, final ExplorerVisitor visitor) { + Integer uid = Integer.valueOf(myUID); if (semNodesTable.get(uid) != null) return; - semNodesTable.put(new Integer(myUID), this); + semNodesTable.put(myUID, this); + visitor.preVisit(this); for (int i = 0; i < params.length; i++) { - params[i].walkGraph(semNodesTable); + params[i].walkGraph(semNodesTable, visitor); } - module.walkGraph(semNodesTable); + module.walkGraph(semNodesTable, visitor); + visitor.postVisit(this); } public final String toString(int depth) { @@ -449,7 +453,8 @@ public class InstanceNode extends LevelNode { } Element ret = doc.createElement("InstanceNode"); - ret.appendChild(appendText(doc,"uniquename",name.toString())); + if (name != null) ret.appendChild(appendText(doc,"uniquename",name.toString())); + ret.appendChild(appendText(doc, "module", module.getName().toString() )); ret.appendChild(sbts); ret.appendChild(prms); return ret; diff --git a/tlatools/src/tla2sany/semantic/LabelNode.java b/tlatools/src/tla2sany/semantic/LabelNode.java index 38c6c0db179f194bca9206f6d248a7237def82b4..e3b60bbc063e151b89633b81538c961a1c3abb0e 100644 --- a/tlatools/src/tla2sany/semantic/LabelNode.java +++ b/tlatools/src/tla2sany/semantic/LabelNode.java @@ -41,17 +41,19 @@ import java.util.Enumeration; import java.util.HashSet; import java.util.Hashtable; +import org.w3c.dom.Document; +import org.w3c.dom.Element; + import tla2sany.explorer.ExploreNode; +import tla2sany.explorer.ExplorerVisitor; import tla2sany.parser.SyntaxTreeNode; import tla2sany.st.TreeNode; import tla2sany.utilities.Strings; import tla2sany.utilities.Vector; +import tla2sany.xml.SymbolContext; import util.UniqueString; import util.WrongInvocationException; -import org.w3c.dom.Document; -import org.w3c.dom.Element; - public class LabelNode extends ExprNode implements ExploreNode, OpDefOrLabelNode { @@ -214,6 +216,7 @@ public class LabelNode extends ExprNode /************************************************************************* * Level-Checking. * *************************************************************************/ + @Override public final boolean levelCheck(int iter) { if (levelChecked >= iter) {return true ;} ; levelChecked = iter; @@ -224,37 +227,43 @@ public class LabelNode extends ExprNode return this.body.levelCheck(iter) && retVal ; } + @Override public final int getLevel() { if (levelChecked == 0) {throw new WrongInvocationException("getLevel called for TheoremNode before levelCheck");}; return this.body.getLevel(); } - public final HashSet getLevelParams() { + @Override + public final HashSet<SymbolNode> getLevelParams() { if (levelChecked == 0) {throw new WrongInvocationException("getLevelParams called for ThmNode before levelCheck");}; return this.body.getLevelParams(); } - public final HashSet getAllParams() { + @Override + public final HashSet<SymbolNode> getAllParams() { if (levelChecked == 0) {throw new WrongInvocationException("getAllParams called for ThmNode before levelCheck");}; return this.body.getAllParams(); } + @Override public final SetOfLevelConstraints getLevelConstraints() { if (levelChecked == 0) {throw new WrongInvocationException("getLevelConstraints called for ThmNode before levelCheck");}; return this.body.getLevelConstraints(); } + @Override public final SetOfArgLevelConstraints getArgLevelConstraints() { if (levelChecked == 0) {throw new WrongInvocationException("getArgLevelConstraints called for ThmNode before levelCheck");}; return this.body.getArgLevelConstraints(); } - public final HashSet getArgLevelParams() { + @Override + public final HashSet<ArgLevelParam> getArgLevelParams() { if (levelChecked == 0) {throw new WrongInvocationException("getArgLevelParams called for ThmNode before levelCheck");}; return this.body.getArgLevelParams(); @@ -266,22 +275,27 @@ public class LabelNode extends ExprNode * * @see tla2sany.semantic.SemanticNode#getChildren() */ + @Override public SemanticNode[] getChildren() { return new SemanticNode[] { this.body }; } /************************************************************************* * The methods for implementing the ExploreNode interface. * *************************************************************************/ - public final void walkGraph(Hashtable semNodesTable) { - Integer uid = new Integer(myUID); + @Override + public final void walkGraph(Hashtable<Integer, ExploreNode> semNodesTable, ExplorerVisitor visitor) { + Integer uid = Integer.valueOf(myUID); if (semNodesTable.get(uid) != null) return; - semNodesTable.put(new Integer(myUID), this); - if (body != null) body.walkGraph(semNodesTable); + semNodesTable.put(uid, this); + visitor.preVisit(this); + if (body != null) body.walkGraph(semNodesTable, visitor); for (int i = 0 ; i < params.length; i++) { - params[i].walkGraph(semNodesTable); + params[i].walkGraph(semNodesTable, visitor); } ; + visitor.postVisit(this); } + @Override public final String toString(int depth) { if (depth <= 0) return ""; String ret = "\n*LabelNode: " + super.toString(depth); @@ -319,7 +333,8 @@ public class LabelNode extends ExprNode return ret; } - protected Element getLevelElement(Document doc, tla2sany.xml.SymbolContext context) { + @Override + protected Element getLevelElement(Document doc, SymbolContext context) { Element ret = doc.createElement("LabelNode"); ret.appendChild(appendText(doc,"uniquename",getName().toString())); ret.appendChild(appendText(doc,"arity",Integer.toString(getArity()))); diff --git a/tlatools/src/tla2sany/semantic/LeafProofNode.java b/tlatools/src/tla2sany/semantic/LeafProofNode.java index 289a17ae259cc8a03ff85e84094c1bd8777dcec9..c4054b40cc00400109bb5d388ec62e17e0bfc7b6 100644 --- a/tlatools/src/tla2sany/semantic/LeafProofNode.java +++ b/tlatools/src/tla2sany/semantic/LeafProofNode.java @@ -1,157 +1,167 @@ -// Copyright (c) 2007 Microsoft Corporation. All rights reserved. -package tla2sany.semantic; - -import java.util.Hashtable; - -import tla2sany.st.TreeNode; -import tla2sany.utilities.Strings; - -import org.w3c.dom.Document; -import org.w3c.dom.Element; - -/*************************************************************************** -* This class represents a leaf proof. It is of kind LeafProffKind * -***************************************************************************/ -public class LeafProofNode extends ProofNode { - - /************************************************************************* - * The fields. * - * * - * A leaf proof has the syntax * - * * - * [PROOF] BY [ONLY] [facts] [DEF[S] defs]. * - * | OBVIOUS * - * | OMITTED * - * * - * The following two fields are the semantic nodes for the facts and * - * defs. * - *************************************************************************/ - LevelNode[] facts = null ; - /*********************************************************************** - * For each i, facts[i] will be either an ExprNode, a ModuleNode, or * - * an OpDefNode of type ModuleInstanceKind (with no parameters). A * - * proof management tool will probably put restrictions on the class * - * of expressions that can be used as facts. * - ***********************************************************************/ - SymbolNode[] defs = null ; - /*********************************************************************** - * For each i, defs[i] should be a UserDefinedOpDefKind or * - * ModuleInstanceKind OpDefNode or a ThmOrAssumpDefNode * - ***********************************************************************/ - boolean omitted ; - /*********************************************************************** - * True iff this is a "[PROOF] OMITTED" statement. In this case, the * - * facts and defs field will be null. But that is also the case for * - * an "OBVIOUS" proof. * - ***********************************************************************/ - boolean isOnly ; - /*********************************************************************** - * True iff this is a "BY ONLY" proof. * - ***********************************************************************/ - - /************************************************************************* - * The constructor. * - *************************************************************************/ - public LeafProofNode(TreeNode stn, LevelNode[] theFacts, - SymbolNode[] theDefs, boolean omit, boolean only) { - super(LeafProofKind, stn) ; - this.facts = theFacts ; - this.defs = theDefs ; - this.omitted = omit ; - this.isOnly = only ; - } ; - - - /************************************************************************* - * Methods that return the values of the fields. * - *************************************************************************/ - public LevelNode[] getFacts() {return facts ; } ; - public SymbolNode[] getDefs() {return defs ;} ; - public boolean getOmitted() {return omitted ;} ; - public boolean getOnlyFlag() {return isOnly ;} ; - - public boolean levelCheck(int iter) { - /*********************************************************************** - * Level checking is performed by level-checking the facts. Since the * - * defs should be defined operators, they have already been level * - * checked. * - ***********************************************************************/ - if (this.levelChecked >= iter) return this.levelCorrect; - return this.levelCheckSubnodes(iter, facts) ; - } - - /* - * The children are the facts. - * @see tla2sany.semantic.SemanticNode#getChildren() - */ - public SemanticNode[] getChildren() { - if (this.facts == null || this.facts.length == 0) { - return null; - } - SemanticNode[] res = new SemanticNode[this.facts.length]; - for (int i = 0; i < facts.length; i++) { - res[i] = facts[i]; - } - return res; - } - - public void walkGraph(Hashtable semNodesTable) { - Integer uid = new Integer(myUID); - if (semNodesTable.get(uid) != null) return; - semNodesTable.put(new Integer(myUID), this); - for (int i = 0; i < facts.length; i++) { - facts[i].walkGraph(semNodesTable); - } ; - /*********************************************************************** - * Note: there's no need to walk the defs array because all the nodes * - * on it are walked from the nodes under which they appear. * - ***********************************************************************/ - } - - public String toString(int depth) { - if (depth <= 0) return ""; - String ret = "\n*LeafProofNode:\n" - + super.toString(depth) - + Strings.indent(2, "\nfacts:") ; - for (int i = 0 ; i < this.facts.length; i++) { - ret += Strings.indent(4, this.facts[i].toString(depth-1)) ; - } ; - ret += Strings.indent(2, "\ndefs:") ; - for (int i = 0 ; i < this.defs.length; i++) { - ret += Strings.indent(4, this.defs[i].toString(depth-1)) ; - } ; - ret += Strings.indent(2, "\nomitted: " + this.omitted) - + Strings.indent(2, "\nonlyFlag: " + this.isOnly); - return ret; - } - - protected Element getLevelElement(Document doc, tla2sany.xml.SymbolContext context) { - Element e; - - if (getOmitted()) { - e = doc.createElement("omitted"); - } - else if (getFacts().length == 0 && getDefs().length == 0) { - e = doc.createElement("obvious"); - } - else { - //SemanticNode.SymbolContext context = new SemanticNode.SymbolContext(context2); - e = doc.createElement("by"); - - Element factse = doc.createElement("facts"); - Element definitions = doc.createElement("defs"); - - for (int i=0; i<facts.length; i++) factse.appendChild(facts[i].export(doc,context)); - for (int i=0; i<defs.length; i++) definitions.appendChild(defs[i].export(doc,context)); - - e.appendChild(factse); - e.appendChild(definitions); - if(getOnlyFlag()) e.appendChild(doc.createElement("only")); - // at the end, we append the context of the symbols used in this node - //e.appendChild(context.getContextElement(doc)); - } - - return e; - } - -} +// Copyright (c) 2007 Microsoft Corporation. All rights reserved. +package tla2sany.semantic; + +import java.util.Hashtable; + +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +import tla2sany.explorer.ExploreNode; +import tla2sany.explorer.ExplorerVisitor; +import tla2sany.st.TreeNode; +import tla2sany.utilities.Strings; +import tla2sany.xml.SymbolContext; + +/*************************************************************************** +* This class represents a leaf proof. It is of kind LeafProffKind * +***************************************************************************/ +public class LeafProofNode extends ProofNode { + + /************************************************************************* + * The fields. * + * * + * A leaf proof has the syntax * + * * + * [PROOF] BY [ONLY] [facts] [DEF[S] defs]. * + * | OBVIOUS * + * | OMITTED * + * * + * The following two fields are the semantic nodes for the facts and * + * defs. * + *************************************************************************/ + LevelNode[] facts = null ; + /*********************************************************************** + * For each i, facts[i] will be either an ExprNode, a ModuleNode, or * + * an OpDefNode of type ModuleInstanceKind (with no parameters). A * + * proof management tool will probably put restrictions on the class * + * of expressions that can be used as facts. * + ***********************************************************************/ + SymbolNode[] defs = null ; + /*********************************************************************** + * For each i, defs[i] should be a UserDefinedOpDefKind or * + * ModuleInstanceKind OpDefNode or a ThmOrAssumpDefNode * + ***********************************************************************/ + boolean omitted ; + /*********************************************************************** + * True iff this is a "[PROOF] OMITTED" statement. In this case, the * + * facts and defs field will be null. But that is also the case for * + * an "OBVIOUS" proof. * + ***********************************************************************/ + boolean isOnly ; + /*********************************************************************** + * True iff this is a "BY ONLY" proof. * + ***********************************************************************/ + + /************************************************************************* + * The constructor. * + *************************************************************************/ + public LeafProofNode(TreeNode stn, LevelNode[] theFacts, + SymbolNode[] theDefs, boolean omit, boolean only) { + super(LeafProofKind, stn) ; + this.facts = theFacts ; + this.defs = theDefs ; + this.omitted = omit ; + this.isOnly = only ; + } ; + + + /************************************************************************* + * Methods that return the values of the fields. * + *************************************************************************/ + public LevelNode[] getFacts() {return facts ; } ; + public SymbolNode[] getDefs() {return defs ;} ; + public boolean getOmitted() {return omitted ;} ; + public boolean getOnlyFlag() {return isOnly ;} ; + + @Override + public boolean levelCheck(int iter) { + /*********************************************************************** + * Level checking is performed by level-checking the facts. Since the * + * defs should be defined operators, they have already been level * + * checked. * + ***********************************************************************/ + if (this.levelChecked >= iter) return this.levelCorrect; + return this.levelCheckSubnodes(iter, facts) ; + } + + /* + * The children are the facts. + * @see tla2sany.semantic.SemanticNode#getChildren() + */ + @Override + public SemanticNode[] getChildren() { + if (this.facts == null || this.facts.length == 0) { + return null; + } + SemanticNode[] res = new SemanticNode[this.facts.length]; + for (int i = 0; i < facts.length; i++) { + res[i] = facts[i]; + } + return res; + } + + @Override + public void walkGraph(Hashtable<Integer, ExploreNode> semNodesTable, ExplorerVisitor visitor) { + Integer uid = Integer.valueOf(myUID); + if (semNodesTable.get(uid) != null) return; + semNodesTable.put(uid, this); + visitor.preVisit(this); + for (int i = 0; i < facts.length; i++) { + facts[i].walkGraph(semNodesTable, visitor); + } ; + /*********************************************************************** + * Note: there's no need to walk the defs array because all the nodes * + * on it are walked from the nodes under which they appear. * + ***********************************************************************/ + visitor.postVisit(this); + } + + @Override + public String toString(int depth) { + if (depth <= 0) return ""; + String ret = "\n*LeafProofNode:\n" + + super.toString(depth) + + Strings.indent(2, "\nfacts:") ; + for (int i = 0 ; i < this.facts.length; i++) { + ret += Strings.indent(4, this.facts[i].toString(depth-1)) ; + } ; + ret += Strings.indent(2, "\ndefs:") ; + for (int i = 0 ; i < this.defs.length; i++) { + ret += Strings.indent(4, this.defs[i].toString(depth-1)) ; + } ; + ret += Strings.indent(2, "\nomitted: " + this.omitted) + + Strings.indent(2, "\nonlyFlag: " + this.isOnly); + return ret; + } + + @Override + protected Element getLevelElement(Document doc, SymbolContext context) { + Element e; + + if (getOmitted()) { + e = doc.createElement("omitted"); + } + else if (getFacts().length == 0 && getDefs().length == 0) { + e = doc.createElement("obvious"); + } + else { + //SemanticNode.SymbolContext context = new SemanticNode.SymbolContext(context2); + e = doc.createElement("by"); + + Element factse = doc.createElement("facts"); + Element definitions = doc.createElement("defs"); + + for (int i=0; i<facts.length; i++) factse.appendChild(facts[i].export(doc,context)); + for (int i=0; i<defs.length; i++) definitions.appendChild(defs[i].export(doc,context)); + + e.appendChild(factse); + e.appendChild(definitions); + if(getOnlyFlag()) e.appendChild(doc.createElement("only")); + // at the end, we append the context of the symbols used in this node + //e.appendChild(context.getContextElement(doc)); + } + + return e; + } + +} diff --git a/tlatools/src/tla2sany/semantic/LetInNode.java b/tlatools/src/tla2sany/semantic/LetInNode.java index 22345cbc902cab6d0ba450d74e4373fde1b772ae..2e94d23d39dd9a9bf514f7071c7196026b3abb75 100644 --- a/tlatools/src/tla2sany/semantic/LetInNode.java +++ b/tlatools/src/tla2sany/semantic/LetInNode.java @@ -6,13 +6,15 @@ import java.util.HashSet; import java.util.Hashtable; import java.util.Iterator; +import org.w3c.dom.Document; +import org.w3c.dom.Element; + import tla2sany.explorer.ExploreNode; +import tla2sany.explorer.ExplorerVisitor; import tla2sany.st.TreeNode; import tla2sany.utilities.Strings; import tla2sany.utilities.Vector; - -import org.w3c.dom.Document; -import org.w3c.dom.Element; +import tla2sany.xml.SymbolContext; public class LetInNode extends ExprNode implements ExploreNode, LevelConstants { @@ -106,6 +108,8 @@ implements ExploreNode, LevelConstants { // private SetOfArgLevelConstraints argLevelConstraints; // private HashSet argLevelParams; + @Override + @SuppressWarnings("unchecked") public final boolean levelCheck(int itr) { if (this.levelChecked >= itr) return this.levelCorrect; levelChecked = itr ; @@ -141,8 +145,8 @@ implements ExploreNode, LevelConstants { * the aliasing of the levelParams and allParams fields of this node * * and its body to the same HashSets, but it doesn't hurt to be safe. * ***********************************************************************/ - this.levelParams = (HashSet) this.body.getLevelParams().clone(); - this.allParams = (HashSet) this.body.getAllParams().clone(); + this.levelParams = (HashSet<SymbolNode>)this.body.getLevelParams().clone(); + this.allParams = (HashSet<SymbolNode>) this.body.getAllParams().clone(); // this.levelConstraints = new SetOfLevelConstraints(); this.levelConstraints.putAll(this.body.getLevelConstraints()); @@ -173,9 +177,9 @@ implements ExploreNode, LevelConstants { if (this.opDefs[i].getKind() != ThmOrAssumpDefKind){ params = ((OpDefNode) this.opDefs[i]).getParams(); } ; - Iterator iter = this.opDefs[i].getArgLevelParams().iterator(); + Iterator<ArgLevelParam> iter = this.opDefs[i].getArgLevelParams().iterator(); while (iter.hasNext()) { - ArgLevelParam alp = (ArgLevelParam)iter.next(); + ArgLevelParam alp = iter.next(); if (!alp.occur(params)) { this.argLevelParams.add(alp); } @@ -219,6 +223,7 @@ implements ExploreNode, LevelConstants { // "ArgLevelParams: " + this.argLevelParams + "\n" ; // } + @Override public SemanticNode[] getChildren() { SemanticNode[] res = new SemanticNode[this.opDefs.length + this.insts.length + 1]; @@ -233,26 +238,30 @@ implements ExploreNode, LevelConstants { return res; } - public final void walkGraph(Hashtable semNodesTable) { - Integer uid = new Integer(myUID); + @Override + public final void walkGraph(Hashtable<Integer, ExploreNode> semNodesTable, ExplorerVisitor visitor) { + Integer uid = Integer.valueOf(myUID); if (semNodesTable.get(uid) != null) return; - semNodesTable.put(new Integer(myUID), this); + semNodesTable.put(uid, this); + visitor.preVisit(this); /*********************************************************************** * Can now walk LET nodes from context, don't need to use opDefs * * (which is incomplete). * ***********************************************************************/ - if (context != null){context.walkGraph(semNodesTable);} ; + if (context != null){context.walkGraph(semNodesTable, visitor);} ; // if (opDefs != null) { // for (int i = 0; i < opDefs.length; i++) { // if (opDefs[i] != null) opDefs[i].walkGraph(semNodesTable); // } // } - if (body != null) body.walkGraph(semNodesTable); + if (body != null) body.walkGraph(semNodesTable, visitor); + visitor.postVisit(this); } + @Override public final String toString(int depth) { if (depth <= 0) return ""; @@ -286,8 +295,8 @@ implements ExploreNode, LevelConstants { return ret; } - - protected Element getLevelElement(Document doc, tla2sany.xml.SymbolContext context) { + @Override + protected Element getLevelElement(Document doc, SymbolContext context) { Element ret = doc.createElement("LetInNode"); ret.appendChild(appendElement(doc,"body",body.export(doc,context))); Element arguments = doc.createElement("opDefs"); diff --git a/tlatools/src/tla2sany/semantic/LevelNode.java b/tlatools/src/tla2sany/semantic/LevelNode.java index ccd88bc3f782aa6de39290056fb3e4149fc9bdac..189c4139e33df9c44de9f53d5f73814fef0d2977 100644 --- a/tlatools/src/tla2sany/semantic/LevelNode.java +++ b/tlatools/src/tla2sany/semantic/LevelNode.java @@ -4,12 +4,13 @@ package tla2sany.semantic; import java.util.HashSet; import java.util.Iterator; -import tla2sany.st.TreeNode; -import util.WrongInvocationException; - import org.w3c.dom.Document; import org.w3c.dom.Element; +import tla2sany.st.TreeNode; +import tla2sany.xml.SymbolContext; +import util.WrongInvocationException; + /*************************************************************************** * Note: The SANY1 level checking algorithm is specified in the file * * LevelSpec.tla. The handling of recursive operators is explained in the * @@ -63,10 +64,10 @@ public class LevelNode extends SemanticNode { ***************************************************************************/ public boolean levelCorrect = true ; public int level = ConstantLevel ; -public HashSet levelParams = new HashSet() ; +public HashSet<SymbolNode> levelParams = new HashSet<>() ; public SetOfLevelConstraints levelConstraints = new SetOfLevelConstraints(); public SetOfArgLevelConstraints argLevelConstraints = new SetOfArgLevelConstraints(); -public HashSet argLevelParams = new HashSet() ; +public HashSet<ArgLevelParam> argLevelParams = new HashSet<>() ; /*************************************************************************** * The following HashSets are used in computing Leibnizity. * @@ -80,8 +81,8 @@ public HashSet argLevelParams = new HashSet() ; * See the file leibniz-checking.txt, appended below, for an explanation * * of Leibnizity and Leibniz checking. * ***************************************************************************/ -public HashSet allParams = new HashSet() ; -public HashSet nonLeibnizParams = new HashSet() ; +public HashSet<SymbolNode> allParams = new HashSet<>() ; +public HashSet<SymbolNode> nonLeibnizParams = new HashSet<>() ; public int levelChecked = 0 ; /************************************************************************* @@ -178,11 +179,11 @@ public int levelChecked = 0 ; * instantiated by a temporal formula. Added by LL on 1 Mar 2009 * *************************************************************************/ static void addTemporalLevelConstraintToConstants( - HashSet params, + HashSet<SymbolNode> params, SetOfLevelConstraints constrs ) { - Iterator iter = params.iterator(); + Iterator<SymbolNode> iter = params.iterator(); while (iter.hasNext()) { - LevelNode node = (LevelNode) iter.next() ; + SymbolNode node = iter.next() ; if (node.getKind() == ConstantDeclKind) { constrs.put(node, Levels[ActionLevel]); }; @@ -191,6 +192,10 @@ public int levelChecked = 0 ; /*************************************************************************** * The checks in the following methods should probably be eliminated after * * SANY2 is debugged. * +* <ul><li>0 for constant * +* <li>1 for non-primed variable * +* <li>2 for primed variable * +* <li>3 for temporal formula</ul> * ***************************************************************************/ public int getLevel(){ if (this.levelChecked == 0) @@ -198,7 +203,7 @@ public int levelChecked = 0 ; return this.level; } - public HashSet getLevelParams(){ + public HashSet<SymbolNode> getLevelParams(){ /*********************************************************************** * Seems to return a HashSet of OpDeclNode objects. Presumably, these * * are the parameters from the local context that contribute to the * @@ -209,7 +214,7 @@ public int levelChecked = 0 ; return this.levelParams; } - public HashSet getAllParams(){ + public HashSet<SymbolNode> getAllParams(){ /*********************************************************************** * Returns a HashSet of OpDeclNode objects, which are the parameters * * from the local context that appear within the object. * @@ -219,7 +224,7 @@ public int levelChecked = 0 ; return this.allParams; } - public HashSet getNonLeibnizParams(){ + public HashSet<SymbolNode> getNonLeibnizParams(){ /*********************************************************************** * Returns a HashSet of OpDeclNode objects, which is the subset of * * parameters returned by getAllParams() that appear within a * @@ -254,7 +259,7 @@ public int levelChecked = 0 ; return this.argLevelConstraints; } - public HashSet getArgLevelParams(){ + public HashSet<ArgLevelParam> getArgLevelParams(){ /*********************************************************************** * Seems to return a HashSet of ArgLevelParam objects. (See * * ArgLevelParam.java for an explanation of those objects.) * @@ -278,32 +283,32 @@ public int levelChecked = 0 ; "NonLeibnizParams: " + HashSetToString(this.getNonLeibnizParams()) ; } - public static String HashSetToString(HashSet hs) { + public static String HashSetToString(HashSet<? extends SymbolNode> hs) { /*********************************************************************** * Converts a HashSet of SymbolNodes to a printable string. * ***********************************************************************/ String rval = "{" ; boolean first = true ; - Iterator iter = hs.iterator(); + final Iterator<? extends SymbolNode> iter = hs.iterator(); while (iter.hasNext()) { if (! first) {rval = rval + ", ";} ; - rval = rval + ((SymbolNode) iter.next()).getName() ; + rval = rval + iter.next().getName() ; first = false ; } ; rval = rval + "}" ; return rval ; } - public static String ALPHashSetToString(HashSet hs) { + public static String ALPHashSetToString(HashSet<ArgLevelParam> hs) { /*********************************************************************** * Converts a HashSet of ArgLevelParam objects to a printable string. * ***********************************************************************/ String rval = "{" ; boolean first = true ; - Iterator iter = hs.iterator(); + Iterator<ArgLevelParam> iter = hs.iterator(); while (iter.hasNext()) { if (! first) {rval = rval + ", ";} ; - ArgLevelParam alp = (ArgLevelParam) iter.next(); + ArgLevelParam alp = iter.next(); rval = rval + "<" + alp.op.getName() + ", " + alp.i + ", " + alp.param.getName() + ">" ; first = false; @@ -322,9 +327,10 @@ public int levelChecked = 0 ; return this.defaultLevelDataToString() ;} - protected Element getSemanticElement(Document doc, tla2sany.xml.SymbolContext context) { + @Override + protected Element getSemanticElement(Document doc, SymbolContext context) { // T.L. abstract method used to add data from subclasses - Element e = getLevelElement(doc, context); + Element e = getLevelElement(doc, context); //SymbolElement.getLevelElement is not supposed to be called try { Element l = appendText(doc,"level",Integer.toString(getLevel())); e.insertBefore(l,e.getFirstChild()); @@ -338,7 +344,7 @@ public int levelChecked = 0 ; * T.L. October 2014 * Abstract method for subclasses of LevelNode to add their information * */ - protected Element getLevelElement(Document doc, tla2sany.xml.SymbolContext context) { + protected Element getLevelElement(Document doc, SymbolContext context) { throw new UnsupportedOperationException("xml export is not yet supported for: " + getClass() + " with toString: " + toString(100)); } @@ -607,6 +613,28 @@ public int levelChecked = 0 ; // (* any reasonable case where this will disallow a level-correct expression *) // (* that a user is likely to write. *) // (* *) +// (* The decision to simplify the bookkeeping results in the following, *) +// (* somewhat less unlikely problem. With the definitions *) +// (* *) +// (* ApplyToPrime(Op(_)) == Op(x') *) +// (* EqualsNoPrime(a) == x *) +// (* *) +// (* the expression ApplyToPrime(EqualsNoPrime)' , which equals x', is *) +// (* considered to be illegal. This is because the algorithm to compute the *) +// (* level makes the assumption that ApplyToPrime will always be applied to *) +// (* operators Op for which the level of Op(exp) depends on the level of *) +// (* exp. Hence, SANY's algorithm gives ApplyToPrime(Op) a level of at *) +// (* least 2 (primed expression) for any operator Op. A slightly more *) +// (* realistic example can be constructed by modifying ApplyToPrime a bit *) +// (* and applying it to ENABLED. *) +// (* TLC warns users about this bug if it reports an invariant to be *) +// (* level-incorrect in tlc2.tool.Spec.processConfigInvariants() with error *) +// (* code tlc2.output.EC.TLC_INVARIANT_VIOLATED_LEVEL. *) +// (* A corresponding test can be found in test52. Its invariant "Invariant" *) +// (* covers the ENABLED scenario. However, the invariant remains disabled *) +// (* for as long as this bug is open. The invariant Invariant can be *) +// (* re-enabled in test52.cfg once this bug is closed. *) +// (* *) // (* To compute the values of op.level, op.weights, and op.minMaxLevel for *) // (* an OpDefNode op corresponding to a definition, a level-checking *) // (* algorithm needs to keep track of the constraints on its formal *) @@ -1834,7 +1862,7 @@ public int levelChecked = 0 ; // (* I believe this shold be OpDeclNode. (LL, Mar 2007) *) // (*******************************************************************) // param == op.params -// IN /\ n.level = Max(op.level, +// IN /\ n.level = NumMax(op.level, // SetMax({arg[i].level : i \in 1..p}) // (****************************************************************) // (* For an operator parameter, we assume that the weights of *) diff --git a/tlatools/src/tla2sany/semantic/ModuleNode.java b/tlatools/src/tla2sany/semantic/ModuleNode.java index 32173854f8d7de3e902961b30d34014652e700b0..739024b73caea39d5f050969d14ae49e37ddd820 100644 --- a/tlatools/src/tla2sany/semantic/ModuleNode.java +++ b/tlatools/src/tla2sany/semantic/ModuleNode.java @@ -12,19 +12,30 @@ package tla2sany.semantic; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Enumeration; +import java.util.HashMap; import java.util.HashSet; import java.util.Hashtable; import java.util.Iterator; +import java.util.List; +import java.util.stream.Stream; +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +import tla2sany.explorer.ExploreNode; +import tla2sany.explorer.ExplorerVisitor; +import tla2sany.semantic.Context.Pair; import tla2sany.st.TreeNode; import tla2sany.utilities.Strings; import tla2sany.utilities.Vector; +import tla2sany.xml.SymbolContext; import util.UniqueString; import util.WrongInvocationException; -import org.w3c.dom.Document; -import org.w3c.dom.Element; - public class ModuleNode extends SymbolNode { /*************************************************************************** @@ -174,10 +185,12 @@ public class ModuleNode extends SymbolNode { * ModuleNode class. * ***********************************************************************/ - private HashSet allExtendees = null ; + private HashMap<Boolean, HashSet<ModuleNode>> depthAllExtendeesMap = new HashMap<>(); /*********************************************************************** * The set of all modules that are extended by this module--either * - * directly or indirectly. Returned by getExtendModules * + * directly or indirectly, keyed a Boolean representing whether the * + * extendees are gathered recursively of not. * + * Returned by getExtendModules * ***********************************************************************/ private OpDeclNode[] constantDecls = null; @@ -187,7 +200,7 @@ public class ModuleNode extends SymbolNode { // VARIABLEs declared in this module - private Vector definitions = new Vector(8); + private ArrayList<SemanticNode> definitions = new ArrayList<>(); // AssumeNodes, internal ModuleNodes, OpDefNodes, and TheoremNodes, in // the exact order they were defined in this module /*********************************************************************** @@ -208,7 +221,8 @@ public class ModuleNode extends SymbolNode { * It also contains ModuleNodes for inner modules, but not for modules * * nested within them. * * * - * It appears that this field is never used in SANY1. * + * It appears that this field is never used in SANY1; it is used by the * + * MCParser though. * ***********************************************************************/ Vector recursiveDecls = new Vector(8); @@ -217,7 +231,7 @@ public class ModuleNode extends SymbolNode { * RECURSIVE statements, in the order in which they were created. * ***********************************************************************/ - Vector opDefsInRecursiveSection = new Vector(16); + Vector<OpDefNode> opDefsInRecursiveSection = new Vector<>(16); /*********************************************************************** * The list of all OpDefNode objects opd in this module, and in any * * inner modules, with opd.recursiveSection >= 0. (See the comments * @@ -336,8 +350,12 @@ public class ModuleNode extends SymbolNode { return (getConstantDecls().length == 0 && getVariableDecls().length == 0); } + + public List<SemanticNode> getDefinitions() { + return definitions; + } - public final void createExtendeeArray(Vector extendeeVec) { + public final void createExtendeeArray(Vector<ModuleNode> extendeeVec) { /*********************************************************************** * This is called by Generator.processExtendsList to set the * * ModuleNode's extendees field, which never seems to be used. * @@ -345,7 +363,7 @@ public class ModuleNode extends SymbolNode { extendees = new ModuleNode[extendeeVec.size()]; for ( int i = 0; i < extendees.length; i++ ) { - extendees[i] = (ModuleNode)extendeeVec.elementAt(i); + extendees[i] = extendeeVec.elementAt(i); } } @@ -358,7 +376,7 @@ public class ModuleNode extends SymbolNode { public final OpDeclNode[] getConstantDecls() { if (constantDecls != null) return constantDecls; - Vector contextVec = ctxt.getConstantDecls(); + Vector<SemanticNode> contextVec = ctxt.getConstantDecls(); constantDecls = new OpDeclNode[contextVec.size()]; for (int i = 0, j = constantDecls.length - 1; i < constantDecls.length; i++) { constantDecls[j--] = (OpDeclNode)contextVec.elementAt(i); @@ -374,7 +392,7 @@ public class ModuleNode extends SymbolNode { public final OpDeclNode[] getVariableDecls() { if (variableDecls != null) return variableDecls; - Vector contextVec = ctxt.getVariableDecls(); + Vector<SemanticNode> contextVec = ctxt.getVariableDecls(); variableDecls = new OpDeclNode[contextVec.size()]; for (int i = 0, j = variableDecls.length - 1; i < variableDecls.length; i++) { variableDecls[j--] = (OpDeclNode)contextVec.elementAt(i); @@ -402,6 +420,14 @@ public class ModuleNode extends SymbolNode { return opDefs; } + public final OpDefNode getOpDef(final String name) { + return getOpDef(UniqueString.uniqueStringOf(name)); + } + + public final OpDefNode getOpDef(final UniqueString name) { + return Stream.of(getOpDefs()).filter(o -> o.getName() == name).findFirst().orElse(null); + } + /************************************************************************* * Returns an array of all ThmOrAssumpDefNode objects created in the * * current module (but not in inner modules). They should appear in the * @@ -419,14 +445,13 @@ public class ModuleNode extends SymbolNode { return thmOrAssDefs; } - /** - * Appends to vector of definitions in this module; should only be - * called with AssumeNodes, ModuleNodes, OpDefNodes and TheoremNodes - * as arguments. - */ - public final void appendDef(SemanticNode s) { - definitions.addElement(s); - } + /** + * Appends to vector of definitions in this module; should only be called with + * AssumeNodes, ModuleNodes, OpDefNodes and TheoremNodes as arguments. + */ + public final void appendDef(SemanticNode s) { + definitions.add(s); + } /** * Returns array of the InstanceNode's representing module @@ -529,6 +554,7 @@ public void setInstantiated(boolean isInstantiated) { /** * @return the isStandard + * @see tla2sany.modanalyzer.ParseUnit.isLibraryModule() */ public boolean isStandard() { return isStandard; @@ -536,6 +562,7 @@ public boolean isStandard() { /** * @param isStandard the isStandard to set + * @see tla2sany.modanalyzer.ParseUnit.isLibraryModule() */ public void setStandard(boolean isStandard) { this.isStandard = isStandard; @@ -592,20 +619,37 @@ final void addAssumption(TreeNode stn, ExprNode ass, SymbolTable st, } - public final HashSet getExtendedModuleSet() { - /*********************************************************************** - * Returns a Hashset whose elements are ModuleNode objects representing * - * all modules that are extended by this module--either directly or * - * indirectly. * - ***********************************************************************/ - if (this.allExtendees == null) { - this.allExtendees = new HashSet() ; - for (int i = 0; i < this.extendees.length; i++) { - this.allExtendees.add(this.extendees[i]) ; - this.allExtendees.addAll(this.extendees[i].getExtendedModuleSet()) ; - } // for - }; //if - return this.allExtendees ; + public final HashSet<ModuleNode> getExtendedModuleSet() { + return getExtendedModuleSet(true); + } + + /** + * @param recursively if true, the extendees of extendees of extendees of ... + * will be included; if false, only the direct extendees + * of this instance will be returned. + * @return + */ + public final HashSet<ModuleNode> getExtendedModuleSet(final boolean recursively) { + /*********************************************************************** + * Returns a Hashset whose elements are ModuleNode objects representing * + * all modules that are extended by this module--either directly or * + * indirectly. * + ***********************************************************************/ + final Boolean key = Boolean.valueOf(recursively); + HashSet<ModuleNode> extendeesSet = depthAllExtendeesMap.get(key); + if (extendeesSet == null) { + extendeesSet = new HashSet<>(); + for (int i = 0; i < this.extendees.length; i++) { + extendeesSet.add(extendees[i]); + if (recursively) { + extendeesSet.addAll(extendees[i].getExtendedModuleSet(true)); + } + } + + depthAllExtendeesMap.put(key, extendeesSet); + } + + return extendeesSet; } public boolean extendsModule(ModuleNode mod) { @@ -680,6 +724,7 @@ final void addAssumption(TreeNode stn, ExprNode ass, SymbolTable st, // private SetOfArgLevelConstraints argLevelConstraints; // private HashSet argLevelParams; + @Override public final boolean levelCheck(int itr) { if (levelChecked >= itr) return this.levelCorrect; @@ -733,8 +778,7 @@ final void addAssumption(TreeNode stn, ExprNode ass, SymbolTable st, * opDefsInRecursiveSection vector. * *********************************************************************/ int curNodeIdx = firstInSectIdx ; - OpDefNode curNode = - (OpDefNode) opDefsInRecursiveSection.elementAt(curNodeIdx); + OpDefNode curNode = opDefsInRecursiveSection.elementAt(curNodeIdx); int curSection = curNode.recursiveSection ; boolean notDone = true ; while (notDone) { @@ -758,8 +802,7 @@ final void addAssumption(TreeNode stn, ExprNode ass, SymbolTable st, else {curNode.levelChecked = 0 ;}; curNodeIdx++ ; if (curNodeIdx < opDefsInRecursiveSection.size()) { - curNode = (OpDefNode) - opDefsInRecursiveSection.elementAt(curNodeIdx); + curNode = opDefsInRecursiveSection.elementAt(curNodeIdx); notDone = (curNode.recursiveSection == curSection) ; } else {notDone = false ;} ; @@ -777,7 +820,7 @@ final void addAssumption(TreeNode stn, ExprNode ass, SymbolTable st, HashSet recursiveLevelParams = new HashSet() ; HashSet recursiveAllParams = new HashSet() ; for (int i = firstInSectIdx ; i < curNodeIdx ; i++) { - curNode = (OpDefNode) opDefsInRecursiveSection.elementAt(i) ; + curNode = opDefsInRecursiveSection.elementAt(i) ; if (curNode.inRecursive) {curNode.levelChecked = 0 ;} ; curNode.levelCheck(1) ; @@ -805,7 +848,7 @@ final void addAssumption(TreeNode stn, ExprNode ass, SymbolTable st, * for every operator in the recursive section. * *********************************************************************/ for (int i = firstInSectIdx ; i < curNodeIdx ; i++) { - curNode = (OpDefNode) opDefsInRecursiveSection.elementAt(i) ; + curNode = opDefsInRecursiveSection.elementAt(i) ; if (curNode.inRecursive) {curNode.levelChecked = 2;} ; curNode.level = Math.max(curNode.level, maxRecursiveLevel) ; curNode.levelParams.addAll(recursiveLevelParams) ; @@ -817,7 +860,7 @@ final void addAssumption(TreeNode stn, ExprNode ass, SymbolTable st, * recursive section. * *********************************************************************/ for (int i = firstInSectIdx ; i < curNodeIdx ; i++) { - curNode = (OpDefNode) opDefsInRecursiveSection.elementAt(i) ; + curNode = opDefsInRecursiveSection.elementAt(i) ; if (curNode.inRecursive) {curNode.levelChecked = 1;} ; curNode.levelCheck(2) ; }; // for i @@ -948,6 +991,7 @@ final void addAssumption(TreeNode stn, ExprNode ass, SymbolTable st, return this.levelCorrect; } + @Override public final int getLevel() { throw new WrongInvocationException("Internal Error: Should never call ModuleNode.getLevel()"); } @@ -1018,11 +1062,29 @@ final void addAssumption(TreeNode stn, ExprNode ass, SymbolTable st, // Otherwise this module is a constant module return true; } + + // TODO Change to take an action/operation that is to be executed on matching + // symbols. That way, clients don't have to iterate the result set again. + public Collection<SymbolNode> getSymbols(final SymbolMatcher symbolMatcher) { + final List<SymbolNode> result = new ArrayList<SymbolNode>(); // TreeSet to order result. + + final Enumeration<Pair> content = this.ctxt.content(); + while (content.hasMoreElements()) { + final SymbolNode aSymbol = content.nextElement().getSymbol(); + if (symbolMatcher.matches(aSymbol)) { + result.add(aSymbol); + } + } + + Collections.sort(result); + return result; + } /** * walkGraph, levelDataToString, and toString methods to implement * ExploreNode interface */ + @Override public final String levelDataToString() { return "LevelParams: " + getLevelParams() + "\n" + "LevelConstraints: " + getLevelConstraints() + "\n" + @@ -1031,6 +1093,7 @@ final void addAssumption(TreeNode stn, ExprNode ass, SymbolTable st, } private SemanticNode[] children = null; + @Override public SemanticNode[] getChildren() { if (children != null) { return children; @@ -1047,17 +1110,19 @@ final void addAssumption(TreeNode stn, ExprNode ass, SymbolTable st, return children; } - public final void walkGraph (Hashtable semNodesTable) { - Integer uid = new Integer(myUID); + @Override + public final void walkGraph (Hashtable<Integer, ExploreNode> semNodesTable, ExplorerVisitor visitor) { + Integer uid = Integer.valueOf(myUID); if (semNodesTable.get(uid) != null) return; semNodesTable.put(uid, this); + visitor.preVisit(this); if (ctxt != null) { - ctxt.walkGraph(semNodesTable); + ctxt.walkGraph(semNodesTable, visitor); } for (int i = 0; i < topLevelVec.size(); i++) { - ((LevelNode)(topLevelVec.elementAt(i))).walkGraph(semNodesTable); + ((LevelNode)(topLevelVec.elementAt(i))).walkGraph(semNodesTable, visitor); } // for (int i = 0; i < instanceVec.size(); i++) { // ((InstanceNode)(instanceVec.elementAt(i))).walkGraph(semNodesTable); @@ -1068,6 +1133,7 @@ final void addAssumption(TreeNode stn, ExprNode ass, SymbolTable st, // for (int i = 0; i < assumptionVec.size(); i++) { // ((AssumeNode)(assumptionVec.elementAt(i))).walkGraph(semNodesTable); // } + visitor.postVisit(this); } public final void print(int indent, int depth, boolean b) { @@ -1087,6 +1153,7 @@ final void addAssumption(TreeNode stn, ExprNode ass, SymbolTable st, } } + @Override public final String toString(int depth) { if (depth <= 0) return ""; @@ -1149,35 +1216,36 @@ final void addAssumption(TreeNode stn, ExprNode ass, SymbolTable st, return "ModuleNodeRef"; } - protected Element getSymbolElement(Document doc, tla2sany.xml.SymbolContext context) { + protected Element getSymbolElement(Document doc, SymbolContext context) { Element ret = doc.createElement("ModuleNode"); ret.appendChild(appendText(doc, "uniquename", getName().toString())); SemanticNode[] nodes = null; // constants - Element constants = doc.createElement("constants"); + //Element constants = doc.createElement("constants"); nodes = getConstantDecls(); for (int i=0; i<nodes.length; i++) { - constants.appendChild(nodes[i].export(doc,context)); + ret.appendChild(nodes[i].export(doc,context)); } - ret.appendChild(constants); + //ret.appendChild(constants); // variables - Element variables = doc.createElement("variables"); + //Element variables = doc.createElement("variables"); nodes = getVariableDecls(); for (int i=0; i<nodes.length; i++) { - variables.appendChild(nodes[i].export(doc,context)); + ret.appendChild(nodes[i].export(doc,context)); } - ret.appendChild(variables); + //ret.appendChild(variables); //operators - Element operators = doc.createElement("definitions"); + //Element operators = doc.createElement("definitions"); nodes = getOpDefs(); for (int i=0; i<nodes.length; i++) { - operators.appendChild(nodes[i].export(doc,context)); //was with true to expand operators + ret.appendChild(nodes[i].export(doc,context)); //was with true to expand operators } - ret.appendChild(operators); + //ret.appendChild(operators); + /* //assumptions Element assums = doc.createElement("assumptions"); nodes = getAssumptions(); @@ -1193,6 +1261,12 @@ final void addAssumption(TreeNode stn, ExprNode ass, SymbolTable st, thms.appendChild(nodes[i].export(doc,context)); } ret.appendChild(thms); + */ + + nodes = getTopLevel(); + for (int i=0; i<nodes.length; i++) { + ret.appendChild(nodes[i].export(doc,context)); + } return ret; } diff --git a/tlatools/src/tla2sany/semantic/NewSymbNode.java b/tlatools/src/tla2sany/semantic/NewSymbNode.java index 81e3d33a002aa8adef04a6d636eafb33b8dbb5c4..f5a5897829c704cc9a7ca54dbb6399596f238c9a 100644 --- a/tlatools/src/tla2sany/semantic/NewSymbNode.java +++ b/tlatools/src/tla2sany/semantic/NewSymbNode.java @@ -1,198 +1,208 @@ -// Copyright (c) 2007 Microsoft Corporation. All rights reserved. - -// last modified on Fri 23 Nov 2007 at 17:21:52 PST by lamport -/*************************************************************************** -* The NewSymbNode constructor is invoked in semantic/Generator.java to * -* construct the nodes corresponding to a NewSymb syntactic node in an * -* AssumeProve. The NewSymb node represents a declaration in an ASSUME, * -* such as "ASSUME CONSTANT x \in S ". * -* * -* The node has two descendants in the semantic tree, returned * -* by these methods: * -* OpDeclNode getOpDeclNode() * -* An OpDeclNode for the declaration. * -* * -* ExprNode getSet() * -* If the node represents a declaration such as "CONSTANT x \in S", * -* then this returns the ExprNode for S. Otherwise, it returns null. * -***************************************************************************/ - -package tla2sany.semantic; - -import java.util.Hashtable; - -import tla2sany.st.TreeNode; -import tla2sany.utilities.Strings; - -import org.w3c.dom.Document; -import org.w3c.dom.Element; - -public class NewSymbNode extends LevelNode { - - /************************************************************************* - * Fields. * - *************************************************************************/ - private OpDeclNode opDeclNode = null; - /*********************************************************************** - * The OpDeclNode for the declaration represented by the NewSymbNode * - * object. * - ***********************************************************************/ - private ExprNode set = null; - /*********************************************************************** - * The ExprNode for expression S in "CONSTANT id \in S". * - ***********************************************************************/ -// There's now a level field for all LevelNode subclasses, and this.levelChecked -// indicates if levelCheck has been callsed -// private int level = -1; -// /*********************************************************************** -// * The level. The value of -1 indicates that levelCheck has not yet * -// * been called. * -// ***********************************************************************/ - -// Role subsumed by this.levelCorrect -// private boolean theLevelCheck = true ; -// /*********************************************************************** -// * Set by this.levelCheck to the value to be returned on subsequent * -// * calls. * -// ***********************************************************************/ - - /************************************************************************* - * The Constructor. * - *************************************************************************/ - public NewSymbNode( - OpDeclNode opDeclNode, // The OpDeclNode for the declaration. - ExprNode set, // Expression S in "CONSTANT x \in S", - // null for other kinds of declaration. - TreeNode stn // The syntax node. - ) { - super(NewSymbKind, stn) ; - this.set = set; - this.opDeclNode = opDeclNode ; - } - - - /************************************************************************* - * Methods particular to the NewSymb node. * - *************************************************************************/ - public final ExprNode getSet() {return set;} - public final OpDeclNode getOpDeclNode() {return opDeclNode;} - - /************************************************************************* - * The implementation of the LevelNode abstract methods. * - * * - * The level of the node is the maximum of opDeclNode's level and the * - * level of the `set' expression, if it's non-null. Any other level * - * information comes from the `set' expression. * - *************************************************************************/ - public boolean levelCheck(int iter) { - - if (levelChecked < iter) { - /********************************************************************* - * This is the first call of levelCheck, so the level information * - * must be computed. Actually, with the current implementation, * - * there's no need to call opDeclNode.levelCheck, since that just * - * returns true. However, we do it anyway in case the OpDeclNode * - * class is changed to make levelCheck do something. * - *********************************************************************/ - levelChecked = iter ; - boolean opDeclLevelCheck = opDeclNode.levelCheck(iter) ; - level = opDeclNode.getLevel() ; - if (set != null) { - levelCorrect = set.levelCheck(iter) ; - level = Math.max(set.getLevel(), level); - if (level == TemporalLevel) { - levelCorrect = false; - errors.addError(this.stn.getLocation(), - "Level error:\n" + - "Temporal formula used as set."); - } - } ; - levelCorrect = levelCorrect && opDeclLevelCheck; - if (set != null) { - levelParams = set.getLevelParams(); - allParams = set.getAllParams(); - levelConstraints = set.getLevelConstraints(); - argLevelConstraints = set.getArgLevelConstraints(); - argLevelParams = set.getArgLevelParams(); - }; // if (set != null) - }; // if (levelChecked < iter) - return levelCorrect; - } - -// public int getLevel() {return this.level; } -// -// public HashSet getLevelParams() { -// if (set == null) {return EmptySet;} -// else return set.getLevelParams(); -// } -// -// public SetOfLevelConstraints getLevelConstraints() { -// if (set == null) {return EmptyLC;} -// else return set.getLevelConstraints(); -// } -// -// public SetOfArgLevelConstraints getArgLevelConstraints() { -// if (set == null) {return EmptyALC;} -// else return set.getArgLevelConstraints(); -// } -// -// public HashSet getArgLevelParams() { -// if (set == null) {return EmptySet;} -// else return set.getArgLevelParams(); -// } - - /** - * toString, levelDataToString and walkGraph methods to implement - * ExploreNode interface - */ -// public final String levelDataToString() { -// return "Level: " + this.level + "\n" + -// "LevelParameters: " + this.getLevelParams() + "\n" + -// "LevelConstraints: " + this.getLevelConstraints() + "\n" + -// "ArgLevelConstraints: " + this.getArgLevelConstraints() + "\n" + -// "ArgLevelParams: " + this.getArgLevelParams() + "\n"; -// } - - /** - * The body is the node's only child. - */ - - public SemanticNode[] getChildren() { - if (this.set == null) { - return null; - } else { - return new SemanticNode[] {this.set}; - } - } - - public final void walkGraph(Hashtable semNodesTable) { - Integer uid = new Integer(myUID); - if (semNodesTable.get(uid) != null) return; - semNodesTable.put(new Integer(myUID), this); - if (set != null) { set.walkGraph(semNodesTable); } ; - } - - public final String toString(int depth) { - if (depth <= 0) return ""; - String setString = "" ; - if (this.set != null) { - setString = Strings.indent(2, - "\nSet:" + Strings.indent(2, this.set.toString(depth-1))); - } - return "\n*NewSymbNode: " + - " " + super.toString(depth) + - Strings.indent(2, "\nKind: " + this.getKind() + - "\nopDeclNode:" + Strings.indent(2, - this.opDeclNode.toString(depth-1)) + - setString); - } - - protected Element getLevelElement(Document doc, tla2sany.xml.SymbolContext context) { - Element e = doc.createElement("NewSymbNode"); - e.appendChild(getOpDeclNode().export(doc,context)); - if (getSet() != null) { - e.appendChild(getSet().export(doc,context)); - } - return e; - } -} +// Copyright (c) 2007 Microsoft Corporation. All rights reserved. + +// last modified on Fri 23 Nov 2007 at 17:21:52 PST by lamport +/*************************************************************************** +* The NewSymbNode constructor is invoked in semantic/Generator.java to * +* construct the nodes corresponding to a NewSymb syntactic node in an * +* AssumeProve. The NewSymb node represents a declaration in an ASSUME, * +* such as "ASSUME CONSTANT x \in S ". * +* * +* The node has two descendants in the semantic tree, returned * +* by these methods: * +* OpDeclNode getOpDeclNode() * +* An OpDeclNode for the declaration. * +* * +* ExprNode getSet() * +* If the node represents a declaration such as "CONSTANT x \in S", * +* then this returns the ExprNode for S. Otherwise, it returns null. * +***************************************************************************/ + +package tla2sany.semantic; + +import java.util.Hashtable; + +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +import tla2sany.explorer.ExploreNode; +import tla2sany.explorer.ExplorerVisitor; +import tla2sany.st.TreeNode; +import tla2sany.utilities.Strings; +import tla2sany.xml.SymbolContext; + +public class NewSymbNode extends LevelNode { + + /************************************************************************* + * Fields. * + *************************************************************************/ + private OpDeclNode opDeclNode = null; + /*********************************************************************** + * The OpDeclNode for the declaration represented by the NewSymbNode * + * object. * + ***********************************************************************/ + private ExprNode set = null; + /*********************************************************************** + * The ExprNode for expression S in "CONSTANT id \in S". * + ***********************************************************************/ +// There's now a level field for all LevelNode subclasses, and this.levelChecked +// indicates if levelCheck has been callsed +// private int level = -1; +// /*********************************************************************** +// * The level. The value of -1 indicates that levelCheck has not yet * +// * been called. * +// ***********************************************************************/ + +// Role subsumed by this.levelCorrect +// private boolean theLevelCheck = true ; +// /*********************************************************************** +// * Set by this.levelCheck to the value to be returned on subsequent * +// * calls. * +// ***********************************************************************/ + + /************************************************************************* + * The Constructor. * + *************************************************************************/ + public NewSymbNode( + OpDeclNode opDeclNode, // The OpDeclNode for the declaration. + ExprNode set, // Expression S in "CONSTANT x \in S", + // null for other kinds of declaration. + TreeNode stn // The syntax node. + ) { + super(NewSymbKind, stn) ; + this.set = set; + this.opDeclNode = opDeclNode ; + } + + + /************************************************************************* + * Methods particular to the NewSymb node. * + *************************************************************************/ + public final ExprNode getSet() {return set;} + public final OpDeclNode getOpDeclNode() {return opDeclNode;} + + /************************************************************************* + * The implementation of the LevelNode abstract methods. * + * * + * The level of the node is the maximum of opDeclNode's level and the * + * level of the `set' expression, if it's non-null. Any other level * + * information comes from the `set' expression. * + *************************************************************************/ + @Override + public boolean levelCheck(int iter) { + + if (levelChecked < iter) { + /********************************************************************* + * This is the first call of levelCheck, so the level information * + * must be computed. Actually, with the current implementation, * + * there's no need to call opDeclNode.levelCheck, since that just * + * returns true. However, we do it anyway in case the OpDeclNode * + * class is changed to make levelCheck do something. * + *********************************************************************/ + levelChecked = iter ; + boolean opDeclLevelCheck = opDeclNode.levelCheck(iter) ; + level = opDeclNode.getLevel() ; + if (set != null) { + levelCorrect = set.levelCheck(iter) ; + level = Math.max(set.getLevel(), level); + if (level == TemporalLevel) { + levelCorrect = false; + errors.addError(this.stn.getLocation(), + "Level error:\n" + + "Temporal formula used as set."); + } + } ; + levelCorrect = levelCorrect && opDeclLevelCheck; + if (set != null) { + levelParams = set.getLevelParams(); + allParams = set.getAllParams(); + levelConstraints = set.getLevelConstraints(); + argLevelConstraints = set.getArgLevelConstraints(); + argLevelParams = set.getArgLevelParams(); + }; // if (set != null) + }; // if (levelChecked < iter) + return levelCorrect; + } + +// public int getLevel() {return this.level; } +// +// public HashSet getLevelParams() { +// if (set == null) {return EmptySet;} +// else return set.getLevelParams(); +// } +// +// public SetOfLevelConstraints getLevelConstraints() { +// if (set == null) {return EmptyLC;} +// else return set.getLevelConstraints(); +// } +// +// public SetOfArgLevelConstraints getArgLevelConstraints() { +// if (set == null) {return EmptyALC;} +// else return set.getArgLevelConstraints(); +// } +// +// public HashSet getArgLevelParams() { +// if (set == null) {return EmptySet;} +// else return set.getArgLevelParams(); +// } + + /** + * toString, levelDataToString and walkGraph methods to implement + * ExploreNode interface + */ +// public final String levelDataToString() { +// return "Level: " + this.level + "\n" + +// "LevelParameters: " + this.getLevelParams() + "\n" + +// "LevelConstraints: " + this.getLevelConstraints() + "\n" + +// "ArgLevelConstraints: " + this.getArgLevelConstraints() + "\n" + +// "ArgLevelParams: " + this.getArgLevelParams() + "\n"; +// } + + /** + * The body is the node's only child. + */ + + @Override + public SemanticNode[] getChildren() { + if (this.set == null) { + return null; + } else { + return new SemanticNode[] {this.set}; + } + } + + @Override + public final void walkGraph(Hashtable<Integer, ExploreNode> semNodesTable, ExplorerVisitor visitor) { + Integer uid = Integer.valueOf(myUID); + if (semNodesTable.get(uid) != null) return; + semNodesTable.put(uid, this); + visitor.preVisit(this); + if (set != null) { set.walkGraph(semNodesTable, visitor); } ; + visitor.postVisit(this); + } + + @Override + public final String toString(int depth) { + if (depth <= 0) return ""; + String setString = "" ; + if (this.set != null) { + setString = Strings.indent(2, + "\nSet:" + Strings.indent(2, this.set.toString(depth-1))); + } + return "\n*NewSymbNode: " + + " " + super.toString(depth) + + Strings.indent(2, "\nKind: " + this.getKind() + + "\nopDeclNode:" + Strings.indent(2, + this.opDeclNode.toString(depth-1)) + + setString); + } + + @Override + protected Element getLevelElement(Document doc, SymbolContext context) { + Element e = doc.createElement("NewSymbNode"); + e.appendChild(getOpDeclNode().export(doc,context)); + if (getSet() != null) { + e.appendChild(getSet().export(doc,context)); + } + return e; + } +} diff --git a/tlatools/src/tla2sany/semantic/NonLeafProofNode.java b/tlatools/src/tla2sany/semantic/NonLeafProofNode.java index 67544148b12faed67054ed5fb8718396971a97c1..b9819c21642c6ad63950799f8357d76380cd3f81 100644 --- a/tlatools/src/tla2sany/semantic/NonLeafProofNode.java +++ b/tlatools/src/tla2sany/semantic/NonLeafProofNode.java @@ -3,12 +3,15 @@ package tla2sany.semantic; import java.util.Hashtable; +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +import tla2sany.explorer.ExploreNode; +import tla2sany.explorer.ExplorerVisitor; import tla2sany.st.TreeNode; import tla2sany.utilities.Strings; import tla2sany.utilities.Vector; - -import org.w3c.dom.Document; -import org.w3c.dom.Element; +import tla2sany.xml.SymbolContext; /*************************************************************************** * This class represents a non-leaf node in the tree representing a * @@ -141,6 +144,7 @@ public class NonLeafProofNode extends ProofNode { public LevelNode[] getSteps() {return steps ;} public Context getContext() {return context ;} + @Override public boolean levelCheck(int iter) { /*********************************************************************** * Level check the steps and the instantiated modules coming from * @@ -157,6 +161,7 @@ public class NonLeafProofNode extends ProofNode { * The children are the steps. * @see tla2sany.semantic.SemanticNode#getChildren() */ + @Override public SemanticNode[] getChildren() { if (this.steps == null || this.steps.length == 0) { return null; @@ -168,24 +173,28 @@ public class NonLeafProofNode extends ProofNode { return res; } - public void walkGraph(Hashtable semNodesTable) { - Integer uid = new Integer(myUID); + @Override + public void walkGraph(Hashtable<Integer, ExploreNode> semNodesTable, ExplorerVisitor visitor) { + Integer uid = Integer.valueOf(myUID); if (semNodesTable.get(uid) != null) return; - semNodesTable.put(new Integer(myUID), this); + semNodesTable.put(uid, this); + visitor.preVisit(this); for (int i = 0; i < steps.length; i++) { - steps[i].walkGraph(semNodesTable); + steps[i].walkGraph(semNodesTable, visitor); } ; /*********************************************************************** * Note: there's no need to walk the defs array because all the nodes * * on it are walked from the nodes under which they appear. * ***********************************************************************/ - context.walkGraph(semNodesTable) ; + context.walkGraph(semNodesTable, visitor) ; /********************************************************************* * Walk the ThmOrOpApplDef NumberedProofStepKind nodes. * *********************************************************************/ + visitor.postVisit(this); } + @Override public String toString(int depth) { if (depth <= 0) return ""; String ret = "\n*ProofNode:\n" @@ -211,7 +220,8 @@ public class NonLeafProofNode extends ProofNode { return ret; } - protected Element getLevelElement(Document doc, tla2sany.xml.SymbolContext context) { + @Override + protected Element getLevelElement(Document doc, SymbolContext context) { Element e = doc.createElement("steps"); for (int i=0; i< steps.length; i++) { diff --git a/tlatools/src/tla2sany/semantic/NumeralNode.java b/tlatools/src/tla2sany/semantic/NumeralNode.java index 89bce398f95dcef2a5b4dffb8f054873c8f05f31..ff1f369a9c26b20ba5326c6636a252ab69001da3 100644 --- a/tlatools/src/tla2sany/semantic/NumeralNode.java +++ b/tlatools/src/tla2sany/semantic/NumeralNode.java @@ -5,12 +5,15 @@ package tla2sany.semantic; import java.math.BigInteger; import java.util.Hashtable; -import tla2sany.st.TreeNode; - import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; +import tla2sany.explorer.ExploreNode; +import tla2sany.explorer.ExplorerVisitor; +import tla2sany.st.TreeNode; +import tla2sany.xml.SymbolContext; + /** * Describes a numeral like 4095. This number is represented by the * values @@ -64,15 +67,26 @@ public class NumeralNode extends ExprNode { public final int val() { return this.value; } public final BigInteger bigVal() { return this.bigValue; } + + /** + * @return true if the numerical value of this instance should be referenced via + * {@link #val()}, false if it should be referenced via + * {@link #bigVal()} + */ + public final boolean useVal() { + return (bigValue == null); + } /** * Returns the value as a string--for example, "4095". This string * reflects how the value appeared in the input, so it should be * "\O7777" if that's what appears in the source. */ + @Override public final String toString() { return this.image; } /* Level Checking */ + @Override public final boolean levelCheck(int iter) { levelChecked = iter; /********************************************************************* @@ -107,13 +121,17 @@ public class NumeralNode extends ExprNode { // "ArgLevelParams: " + this.getArgLevelParams() + "\n" ; // } - public final void walkGraph(Hashtable semNodesTable) { - Integer uid = new Integer(myUID); + @Override + public final void walkGraph(Hashtable<Integer, ExploreNode> semNodesTable, ExplorerVisitor visitor) { + Integer uid = Integer.valueOf(myUID); if (semNodesTable.get(uid) != null) return; - semNodesTable.put(new Integer(myUID), this); + semNodesTable.put(uid, this); + visitor.preVisit(this); + visitor.postVisit(this); } + @Override public final String toString(int depth) { if (depth <= 0) return ""; @@ -122,7 +140,8 @@ public class NumeralNode extends ExprNode { "; image: " + image); } - protected Element getLevelElement(Document doc, tla2sany.xml.SymbolContext context) { + @Override + protected Element getLevelElement(Document doc, SymbolContext context) { String v = (bigValue != null) ? bigValue.toString() : (Integer.toString(value)); Element e = doc.createElement("IntValue"); Node n = doc.createTextNode(v); diff --git a/tlatools/src/tla2sany/semantic/OpApplNode.java b/tlatools/src/tla2sany/semantic/OpApplNode.java index 6454ce55d2d31f5fd0c066f7822dbe5989765759..474c05c481283ce2bf7207d3d3b962ea830f6b45 100644 --- a/tlatools/src/tla2sany/semantic/OpApplNode.java +++ b/tlatools/src/tla2sany/semantic/OpApplNode.java @@ -19,19 +19,27 @@ package tla2sany.semantic; import java.util.HashSet; import java.util.Hashtable; import java.util.Iterator; +import java.util.TreeSet; + +import org.w3c.dom.Document; +import org.w3c.dom.Element; import tla2sany.explorer.ExploreNode; +import tla2sany.explorer.ExplorerVisitor; import tla2sany.parser.SyntaxTreeNode; import tla2sany.st.TreeNode; import tla2sany.utilities.Strings; +import tla2sany.xml.SymbolContext; +import tlc2.tool.BuiltInOPs; +import tlc2.value.ITupleValue; +import tlc2.value.IValue; +import tlc2.value.Values; import util.UniqueString; -import org.w3c.dom.Document; -import org.w3c.dom.Element; - /** * OpApplNodes represent all kinds of operator applications in TLA+, - * including the application of builtin operators and user-defined + * including the application of builtin + * ors and user-defined * operators, operators with a variable number of arguments or a fixed * number (including, of course, 0), and including quantifiers and * choose, which involve bound variables with or without bounding sets. @@ -262,6 +270,15 @@ public class OpApplNode extends ExprNode implements ExploreNode { */ public final void setArgs(ExprOrOpArgNode[] args) { this.operands = args; } + public final boolean argsContainOpArgNodes() { + for (ExprOrOpArgNode o : operands) { + if (o instanceof OpArgNode) { + return true; + } + } + return false; + } + /************************************************************************* * Despite what you might gather from its 79 character name, this does * * not return the number of bounded bound symbols. It returns a weird * @@ -397,6 +414,7 @@ public class OpApplNode extends ExprNode implements ExploreNode { // } // } + @Override public final boolean levelCheck(int itr) { if (this.levelChecked >= itr) return this.levelCorrect; this.levelChecked = itr ; @@ -491,6 +509,7 @@ public class OpApplNode extends ExprNode implements ExploreNode { if (opd instanceof OpArgNode && ((OpArgNode)opd).getOp() instanceof AnyDefNode) { AnyDefNode opdDef = (AnyDefNode)((OpArgNode)opd).getOp(); + @SuppressWarnings("unused") // See below comment block boolean opdDefLevelCheck = opdDef.levelCheck(itr) ; /************************************************************* * Need to call opdDef.levelCheck before using its level * @@ -598,7 +617,7 @@ public class OpApplNode extends ExprNode implements ExploreNode { * Set allBoundSymbols to a hashset containing all the * * FormalParamNodes of the bound symbols. * *********************************************************************/ - HashSet allBoundSymbols = new HashSet() ; + HashSet<FormalParamNode> allBoundSymbols = new HashSet<>() ; if (this.unboundedBoundSymbols != null) { for (int i = 0 ; i < this.unboundedBoundSymbols.length; i++){ allBoundSymbols.add(this.unboundedBoundSymbols[i]); @@ -618,9 +637,9 @@ public class OpApplNode extends ExprNode implements ExploreNode { * Remove bound identifiers from levelParams, allParams, and * * nonLeibnizParams. * *********************************************************************/ - Iterator absIter = allBoundSymbols.iterator() ; + Iterator<FormalParamNode> absIter = allBoundSymbols.iterator() ; while (absIter.hasNext()) { - Object nextBoundSymbol = absIter.next() ; + FormalParamNode nextBoundSymbol = absIter.next() ; this.levelParams.remove(nextBoundSymbol) ; this.allParams.remove(nextBoundSymbol) ; this.nonLeibnizParams.remove(nextBoundSymbol) ; @@ -649,9 +668,9 @@ public class OpApplNode extends ExprNode implements ExploreNode { ***************************************************************/ SetOfLevelConstraints lcons = this.operands[i].getLevelConstraints() ; - Iterator iter = lcons.keySet().iterator(); + Iterator<SymbolNode> iter = lcons.keySet().iterator(); while (iter.hasNext()) { - SymbolNode param = (SymbolNode) iter.next() ; + SymbolNode param = iter.next() ; if (! allBoundSymbols.contains(param)) { this.levelConstraints.put(param, lcons.get(param)) ; } @@ -663,9 +682,9 @@ public class OpApplNode extends ExprNode implements ExploreNode { this.levelConstraints.putAll(this.ranges[i].getLevelConstraints()); } for (int i = 0; i < this.operands.length; i++) { - Integer mlevel = new Integer(opDef.getMaxLevel(i)); + Integer mlevel = Integer.valueOf(opDef.getMaxLevel(i)); if (this.operands[i] != null) { - Iterator iter = this.operands[i].getLevelParams().iterator(); + Iterator<SymbolNode> iter = this.operands[i].getLevelParams().iterator(); while (iter.hasNext()) { this.levelConstraints.put(iter.next(), mlevel); } @@ -694,8 +713,8 @@ public class OpApplNode extends ExprNode implements ExploreNode { for (int j = 0; j < this.operands.length; j++) { for (int k = 0; k < alen; k++) { if (opDef.getOpLevelCond(i, j, k)) { - Integer mlevel = new Integer(argDef.getMaxLevel(k)); - Iterator iter = this.operands[j].getLevelParams().iterator(); + Integer mlevel = Integer.valueOf(argDef.getMaxLevel(k)); + Iterator<SymbolNode> iter = this.operands[j].getLevelParams().iterator(); while (iter.hasNext()) { this.levelConstraints.put(iter.next(), mlevel); } @@ -722,10 +741,10 @@ public class OpApplNode extends ExprNode implements ExploreNode { } ; // if (! argDef.isLeibniz) } // if (opdi != null && ...) } // for i - HashSet alpSet = opDef.getArgLevelParams(); - Iterator iter = alpSet.iterator(); + HashSet<ArgLevelParam> alpSet = opDef.getArgLevelParams(); + Iterator<ArgLevelParam> iter = alpSet.iterator(); while (iter.hasNext()) { - ArgLevelParam alp = (ArgLevelParam)iter.next(); + ArgLevelParam alp = iter.next(); ExprOrOpArgNode arg = this.getArg(alp.op); // LL changed OpDefNode -> AnyDefNode in the following. // See comments in AnyDefNode.java. (It is only in the @@ -739,7 +758,7 @@ public class OpApplNode extends ExprNode implements ExploreNode { /*************************************************************** * Need to invoke levelCheck before invoking getMaxLevel. * ***************************************************************/ - Integer mlevel = new Integer(argDef.getMaxLevel(alp.i)); + Integer mlevel = Integer.valueOf(argDef.getMaxLevel(alp.i)); this.levelConstraints.put(alp.param, mlevel); } } // while @@ -767,14 +786,14 @@ public class OpApplNode extends ExprNode implements ExploreNode { int alen = opArg.getArity(); for (int j = 0; j < alen; j++) { ParamAndPosition pap = new ParamAndPosition(opArg, j); - Integer mlevel = new Integer(opDef.getMinMaxLevel(i, j)); + Integer mlevel = Integer.valueOf(opDef.getMinMaxLevel(i, j)); this.argLevelConstraints.put(pap, mlevel); } for (int j = 0; j < this.operands.length; j++) { for (int k = 0; k < alen; k++) { if (opDef.getOpLevelCond(i, j, k)) { ParamAndPosition pap = new ParamAndPosition(opArg, k); - Integer mlevel = new Integer(this.operands[j].getLevel()); + Integer mlevel = Integer.valueOf(this.operands[j].getLevel()); this.argLevelConstraints.put(pap, mlevel); } } @@ -783,7 +802,7 @@ public class OpApplNode extends ExprNode implements ExploreNode { } // for i iter = alpSet.iterator(); while (iter.hasNext()) { - ArgLevelParam alp = (ArgLevelParam)iter.next(); + ArgLevelParam alp = iter.next(); ExprOrOpArgNode arg = this.getArg(alp.op); if (arg != null) { arg.levelCheck(itr) ; @@ -791,14 +810,14 @@ public class OpApplNode extends ExprNode implements ExploreNode { * Have to invoke levelCheck before invoking getLevel. * ***************************************************************/ ParamAndPosition pap = new ParamAndPosition(alp.op, alp.i); - this.argLevelConstraints.put(pap, new Integer(arg.getLevel())); + this.argLevelConstraints.put(pap, Integer.valueOf(arg.getLevel())); } } /********************************************************************* * Compute this.argLevelParams. * *********************************************************************/ - this.argLevelParams = new HashSet(); + this.argLevelParams = new HashSet<>(); for (int i = 0; i < this.operands.length; i++) { if (this.operands[i] != null) { if (allBoundSymbols.size() == 0) { @@ -812,9 +831,9 @@ public class OpApplNode extends ExprNode implements ExploreNode { * argLevelConstraints the element implied as described above * * if alp.param IS a bound identifier. * ***************************************************************/ - Iterator alpiter = this.operands[i].getArgLevelParams().iterator(); - while (alpiter.hasNext()) { - ArgLevelParam alp = (ArgLevelParam) alpiter.next() ; + Iterator<ArgLevelParam> alpIter = this.operands[i].getArgLevelParams().iterator(); + while (alpIter.hasNext()) { + ArgLevelParam alp = alpIter.next() ; if (!allBoundSymbols.contains(alp.param)) { this.argLevelParams.add(alp) ; } @@ -839,9 +858,9 @@ public class OpApplNode extends ExprNode implements ExploreNode { /************************************************************* * Need to invoke levelCheck before invoking getLevelParams. * *************************************************************/ - Iterator iter1 = arg.getLevelParams().iterator(); + Iterator<SymbolNode> iter1 = arg.getLevelParams().iterator(); while (iter1.hasNext()) { - SymbolNode param = (SymbolNode)iter1.next(); + SymbolNode param = iter1.next(); this.argLevelParams.add(new ArgLevelParam(alp.op, alp.i, param)); } } @@ -868,9 +887,9 @@ public class OpApplNode extends ExprNode implements ExploreNode { for (int j = 0; j < this.operands.length; j++) { for (int k = 0; k < alen; k++) { if (opDef.getOpLevelCond(i, j, k)) { - Iterator iter1 = this.operands[j].getLevelParams().iterator(); + Iterator<SymbolNode> iter1 = this.operands[j].getLevelParams().iterator(); while (iter1.hasNext()) { - SymbolNode param = (SymbolNode)iter1.next(); + SymbolNode param = iter1.next(); this.argLevelParams.add(new ArgLevelParam(opArg, k, param)); } } @@ -891,7 +910,7 @@ public class OpApplNode extends ExprNode implements ExploreNode { this.level = Math.max(this.level, this.operands[i].getLevel()); } // for - this.levelParams = new HashSet(); + this.levelParams = new HashSet<>(); /********************************************************************* * We only add this.operator to the levelParams and allParams. * *********************************************************************/ @@ -935,17 +954,17 @@ public class OpApplNode extends ExprNode implements ExploreNode { this.operands[i].getArgLevelConstraints()); } - this.argLevelParams = new HashSet(); + this.argLevelParams = new HashSet<>(); for (int i = 0; i < this.operands.length; i++) { /******************************************************************* * For every levelParam p of the i-th operand, add to * * argLevelParams an entry asserting that p appears within an i-th * * operand of this.operator. * *******************************************************************/ - HashSet lpSet = this.operands[i].getLevelParams(); - Iterator iter = lpSet.iterator(); + HashSet<SymbolNode> lpSet = this.operands[i].getLevelParams(); + Iterator<SymbolNode> iter = lpSet.iterator(); while (iter.hasNext()) { - SymbolNode param = (SymbolNode)iter.next(); + SymbolNode param = iter.next(); this.argLevelParams.add( new ArgLevelParam(this.operator, i, param)); }; // end while @@ -1096,6 +1115,12 @@ public class OpApplNode extends ExprNode implements ExploreNode { // "ArgLevelParams: " + this.argLevelParams + "\n" ; // } + + public boolean hasOpcode(final int opCode) { + return opCode == BuiltInOPs.getOpCode(getOperator().getName()); + } + + @Override public SemanticNode[] getChildren() { SemanticNode[] res = new SemanticNode[this.ranges.length + this.operands.length]; @@ -1113,30 +1138,32 @@ public class OpApplNode extends ExprNode implements ExploreNode { * walkGraph finds all reachable nodes in the semantic graph * and inserts them in the Hashtable semNodesTable for use by the Explorer tool. */ - public void walkGraph(Hashtable semNodesTable) { - Integer uid = new Integer(myUID); + @Override + public void walkGraph(Hashtable<Integer, ExploreNode> semNodesTable, ExplorerVisitor visitor) { + Integer uid = Integer.valueOf(myUID); if (semNodesTable.get(uid) != null) return; semNodesTable.put(uid, this); + visitor.preVisit(this); if (operator != null) { - operator.walkGraph(semNodesTable); + operator.walkGraph(semNodesTable, visitor); } if (unboundedBoundSymbols != null && unboundedBoundSymbols.length > 0) { for (int i = 0; i < unboundedBoundSymbols.length; i++) if (unboundedBoundSymbols[i] != null) - unboundedBoundSymbols[i].walkGraph(semNodesTable); + unboundedBoundSymbols[i].walkGraph(semNodesTable, visitor); } if (operands != null && operands.length > 0) { for (int i = 0; i < operands.length; i++) - if (operands[i] != null) operands[i].walkGraph(semNodesTable); + if (operands[i] != null) operands[i].walkGraph(semNodesTable, visitor); } if (ranges.length > 0) { for (int i = 0; i < ranges.length; i++) - if (ranges[i] != null) ranges[i].walkGraph(semNodesTable); + if (ranges[i] != null) ranges[i].walkGraph(semNodesTable, visitor); } if (boundedBoundSymbols != null && boundedBoundSymbols.length > 0) { @@ -1144,11 +1171,12 @@ public class OpApplNode extends ExprNode implements ExploreNode { if (boundedBoundSymbols[i] != null && boundedBoundSymbols[i].length > 0) { for (int j = 0; j < boundedBoundSymbols[i].length; j++) { if (boundedBoundSymbols[i][j] != null) - boundedBoundSymbols[i][j].walkGraph(semNodesTable); + boundedBoundSymbols[i][j].walkGraph(semNodesTable, visitor); } } } } + visitor.postVisit(this); } // Used in implementation of toString() below @@ -1216,6 +1244,7 @@ public class OpApplNode extends ExprNode implements ExploreNode { * Displays this node as a String, implementing ExploreNode interface; depth * parameter is a bound on the depth of the portion of the tree that is displayed. */ + @Override public String toString(int depth) { if (depth <= 0) return ""; String sEO = "" ; @@ -1228,7 +1257,39 @@ public class OpApplNode extends ExprNode implements ExploreNode { + toStringBody(depth) + sEO ; } - protected Element getLevelElement(Document doc, tla2sany.xml.SymbolContext context) { + @Override + public String toString(final IValue aValue) { + if (aValue instanceof ITupleValue && allParams.size() == ((ITupleValue) aValue).size()) { + final StringBuffer result = new StringBuffer(); + + // The values in aValue are ordered by the varloc of the variable names (see + // tlc2.tool.TLCStateMut.bind(UniqueString, Value, SemanticNode). Thus, sort + // allParams - which are unordered - under same varloc order. + final TreeSet<SymbolNode> s = new TreeSet<SymbolNode>(new java.util.Comparator<SymbolNode>() { + @Override + public int compare(SymbolNode o1, SymbolNode o2) { + return Integer.compare(o1.getName().getVarLoc(), o2.getName().getVarLoc()); + } + }); + s.addAll(allParams); + + int idx = 0; + for (final SymbolNode sn : s) { + result.append("/\\ "); + result.append(sn.getName().toString()); + + final IValue value = ((ITupleValue) aValue).getElem(idx++); + result.append(" = "); + result.append(Values.ppr(value)); + result.append("\n"); + } + return result.toString(); + } + return super.toString(aValue); + } + + @Override + protected Element getLevelElement(Document doc, SymbolContext context) { Element e = doc.createElement("OpApplNode"); // TL 2014 - A fix for detecting null representing OTHER inside a case (please refrain from using null as a semantical object), @@ -1242,8 +1303,8 @@ public class OpApplNode extends ExprNode implements ExploreNode { // indeed the OTHER case if (other.getOperator().getName().toString().equals("$Pair") && other.getArgs()[0] == null) { // we pass a flag that tells any future OpApplNode that a null operand in 0 position should be replaced by the string $Other - context = new tla2sany.xml.SymbolContext(context); - context.setFlag(tla2sany.xml.SymbolContext.OTHER_BUG); + context = new SymbolContext(context); + context.setFlag(SymbolContext.OTHER_BUG); } } @@ -1258,7 +1319,7 @@ public class OpApplNode extends ExprNode implements ExploreNode { Element ope = doc.createElement("operands"); for (int i=0; i< operands.length; i++) { // dealing with the $Case OTHER null bug - if (i == 0 && operands[0] == null && context.hasFlag(tla2sany.xml.SymbolContext.OTHER_BUG)) { + if (i == 0 && operands[0] == null && context.hasFlag(SymbolContext.OTHER_BUG)) { ope.appendChild(appendText(doc,"StringNode","$Other")); } else { diff --git a/tlatools/src/tla2sany/semantic/OpArgNode.java b/tlatools/src/tla2sany/semantic/OpArgNode.java index 8f7f32f38025ff7cfc6194913377178534e5871c..1f50c9b21241874ce42cbfc34a78960a7e97a38c 100644 --- a/tlatools/src/tla2sany/semantic/OpArgNode.java +++ b/tlatools/src/tla2sany/semantic/OpArgNode.java @@ -7,13 +7,16 @@ package tla2sany.semantic; import java.util.Hashtable; +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +import tla2sany.explorer.ExploreNode; +import tla2sany.explorer.ExplorerVisitor; import tla2sany.parser.SyntaxTreeNode; import tla2sany.st.TreeNode; +import tla2sany.xml.SymbolContext; import util.UniqueString; -import org.w3c.dom.Document; -import org.w3c.dom.Element; - /** * This class represents operators of arity > 0 used as arguments to * other operators. Such operator instances are used in syntactic @@ -65,6 +68,7 @@ public class OpArgNode extends ExprOrOpArgNode { public final ModuleNode getModule() { return this.mn; } /* Level check */ + @Override public final boolean levelCheck(int iter) { if (levelChecked >= iter) {return this.levelCorrect; } ; levelChecked = iter ; @@ -108,11 +112,13 @@ public class OpArgNode extends ExprOrOpArgNode { // "ArgLevelParams: " + this.getArgLevelParams() + "\n" ; // } - public final void walkGraph(Hashtable semNodesTable) { - Integer uid = new Integer(myUID); + @Override + public final void walkGraph(Hashtable<Integer, ExploreNode> semNodesTable, ExplorerVisitor visitor) { + Integer uid = Integer.valueOf(myUID); if (semNodesTable.get(uid) != null) return; - semNodesTable.put(new Integer(myUID), this); + semNodesTable.put(uid, this); + visitor.preVisit(this); /*********************************************************************** * Modified on 28 Mar 2007 by LL to walk the operator node of the * @@ -122,9 +128,11 @@ public class OpArgNode extends ExprOrOpArgNode { * walking the node representing the declaration or definition of the * * operator. * ***********************************************************************/ - if (op != null) {op.walkGraph(semNodesTable) ;} ; + if (op != null) {op.walkGraph(semNodesTable, visitor) ;} ; + visitor.postVisit(this); } + @Override public final String toString(int depth) { if (depth <= 0) return ""; @@ -134,11 +142,17 @@ public class OpArgNode extends ExprOrOpArgNode { " op: " + (op != null ? "" + ((SemanticNode)op).getUid() : "null" ); } - protected Element getLevelElement(Document doc, tla2sany.xml.SymbolContext context) { + @Override + protected Element getLevelElement(Document doc, SymbolContext context) { Element e = doc.createElement("OpArgNode"); - - e.appendChild(appendText(doc,"uniquename",getName().toString())); - e.appendChild(appendText(doc,"arity", Integer.toString(getArity()))); + Element n = doc.createElement("argument"); + //Element ope = op.getSymbolElement(doc, context); + Element ope = op.export(doc, context); + n.appendChild(ope); + e.appendChild(n); + + //e.appendChild(appendText(doc,"uniquename",getName().toString())); + //e.appendChild(appendText(doc,"arity", Integer.toString(getArity()))); return e; } diff --git a/tlatools/src/tla2sany/semantic/OpDeclNode.java b/tlatools/src/tla2sany/semantic/OpDeclNode.java index a0cb9c0c9b7c2262fd5eb9d3f9a2505c303bde24..7475d966ad8ccf21952c38afc7093be71e616b70 100644 --- a/tlatools/src/tla2sany/semantic/OpDeclNode.java +++ b/tlatools/src/tla2sany/semantic/OpDeclNode.java @@ -7,12 +7,15 @@ package tla2sany.semantic; import java.util.Hashtable; -import tla2sany.st.TreeNode; -import util.UniqueString; - import org.w3c.dom.Document; import org.w3c.dom.Element; +import tla2sany.explorer.ExploreNode; +import tla2sany.explorer.ExplorerVisitor; +import tla2sany.st.TreeNode; +import tla2sany.xml.SymbolContext; +import util.UniqueString; + /** * An OpDeclNode can have one of the following kinds: * @@ -85,6 +88,7 @@ public class OpDeclNode extends OpDefOrDeclNode { // private HashSet levelParams; + @Override public final boolean levelCheck(int iter) { /*********************************************************************** * Level information set by constructor. * @@ -136,12 +140,26 @@ public class OpDeclNode extends OpDefOrDeclNode { // "ArgLevelParams: " + this.getArgLevelParams() + "\n"; // } - public final void walkGraph(Hashtable semNodesTable) { - Integer uid = new Integer(myUID); + @Override + public final void walkGraph(Hashtable<Integer, ExploreNode> semNodesTable, ExplorerVisitor visitor) { + Integer uid = Integer.valueOf(myUID); if (semNodesTable.get(uid) != null) return; - semNodesTable.put(new Integer(myUID), this); + semNodesTable.put(uid, this); + visitor.preVisit(this); + visitor.postVisit(this); } + @Override + public String getHumanReadableImage() { + if (getKind() == 2) { + return super.getName().toString() + " CONSTANT"; + } else if (getKind() == 3) { + return super.getName().toString() + " VARIABLE"; + } + return super.getHumanReadableImage(); + } + + @Override public final String toString (int depth) { if (depth <= 0) return ""; return "\n*OpDeclNode: " + this.getName() + " " + super.toString(depth) @@ -156,7 +174,7 @@ public class OpDeclNode extends OpDefOrDeclNode { return "OpDeclNodeRef"; } - protected Element getSymbolElement(Document doc, tla2sany.xml.SymbolContext context) { + protected Element getSymbolElement(Document doc, SymbolContext context) { Element e = doc.createElement("OpDeclNode"); e.appendChild(appendText(doc,"uniquename",getName().toString())); e.appendChild(appendText(doc,"arity",Integer.toString(getArity()))); diff --git a/tlatools/src/tla2sany/semantic/OpDefNode.java b/tlatools/src/tla2sany/semantic/OpDefNode.java index 59ee96d97099dc368e031025ba53c81b40ea34f2..3b5c9f958fc89d8caada229ac2d81b1107ba8eec 100644 --- a/tlatools/src/tla2sany/semantic/OpDefNode.java +++ b/tlatools/src/tla2sany/semantic/OpDefNode.java @@ -35,17 +35,21 @@ import java.util.HashSet; import java.util.Hashtable; import java.util.Iterator; +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +import tla2sany.explorer.ExploreNode; +import tla2sany.explorer.ExplorerVisitor; import tla2sany.parser.SyntaxTreeNode; import tla2sany.st.Location; import tla2sany.st.TreeNode; import tla2sany.utilities.Strings; import tla2sany.utilities.Vector; +import tla2sany.xml.SymbolContext; +import tlc2.tool.BuiltInOPs; import util.UniqueString; import util.WrongInvocationException; -import org.w3c.dom.Document; -import org.w3c.dom.Element; - /** * An OpDefNode can have one of the following kinds: * @@ -620,11 +624,52 @@ public class OpDefNode extends OpDefOrDeclNode /** * This method tests whether an operand is a legal instance of an - * operator being passed as argument to another operator + * operator being passed as argument to another operator. + * + * Bug discovered in July 2017: This method was testing if the + * arg is an operator argument that may be used as parameter + * number i of the operator described by this OpDefNode. For example, + * if + * + * D(Op(_)) == Op(42) + * + * then the argument of D must be an operator that takes a single + * expression as an argument. However, this method returned true + * if the argument is an operator like D, that takes a single + * OPERATOR as an argument. LL rewrote the code on 22 July 2017 + * to catch this case. + * + * @see IllegalOperatorTest */ private boolean matchingOpArgOperand (ExprOrOpArgNode arg, int i) { - return ((arg instanceof OpArgNode) && - params[i].getArity() == ((OpArgNode)arg).getArity()); + // Set result to true iff arg is an operator argument of the + // correct arity. + boolean result = (arg instanceof OpArgNode) && + (params[i].getArity() == ((OpArgNode)arg).getArity()) ; + + // We now must check that arg does not expect an operator as any + // of its arguments. The comments in OpArgNode.java state that + // OpArgNode.op is an OpDefNode, OpDeclNode, or FormalParamNode. Of + // those, only an OpDefNode represents an operator that can have + // an argument that is an operator. Note that an argument j of an + // OpDefNode is an operator iff its arity is 0. + if (result) { + OpArgNode argOpArg = (OpArgNode) arg ; + if (argOpArg.getOp() instanceof OpDefNode) { + OpDefNode opdefArg = (OpDefNode) argOpArg.getOp() ; + for (int j = 0; j < opdefArg.getArity() ; j++) { + if (opdefArg.getParams()[j].getArity() > 0) { + result = false ; + } + } + } + } + + return result ; + + // Code before 22 July 2017 change. + // return ((arg instanceof OpArgNode) && + // params[i].getArity() == ((OpArgNode)arg).getArity()); } /* This method shortens the match() method right after it */ @@ -904,6 +949,7 @@ public class OpDefNode extends OpDefOrDeclNode // this.argLevelParams = EmptySet; } + @Override public final boolean levelCheck(int itr) { if ( (this.levelChecked >= itr) || ( (! inRecursiveSection) @@ -1103,6 +1149,7 @@ public class OpDefNode extends OpDefOrDeclNode * toString, levelDataToString, and walkGraph methods to implement * ExploreNode interface */ + @Override public final String levelDataToString() { if ( (this.getKind() == ModuleInstanceKind) || (this.getKind() == NumberedProofStepKind)) {return "";} ; @@ -1165,10 +1212,15 @@ public class OpDefNode extends OpDefOrDeclNode } + public boolean hasOpcode(final int opCode) { + return opCode == BuiltInOPs.getOpCode(getName()); + } + /** * The body is the node's only child. */ + @Override public SemanticNode[] getChildren() { return new SemanticNode[] {this.body}; } @@ -1178,24 +1230,53 @@ public class OpDefNode extends OpDefOrDeclNode * and inserts them in the Hashtable semNodesTable for use by * the Explorer tool. */ - public final void walkGraph(Hashtable semNodesTable) { - Integer uid = new Integer(myUID); + @Override + public final void walkGraph(Hashtable<Integer, ExploreNode> semNodesTable, ExplorerVisitor visitor) { + Integer uid = Integer.valueOf(myUID); if (semNodesTable.get(uid) != null) return; semNodesTable.put(uid, this); + visitor.preVisit(this); if (params != null && params.length > 0) { for (int i = 0; i < params.length; i++) { - if (params[i] != null) params[i].walkGraph(semNodesTable); + if (params[i] != null) params[i].walkGraph(semNodesTable, visitor); } } - if (body != null) body.walkGraph(semNodesTable); - if (stepNode != null) stepNode.walkGraph(semNodesTable); + if (body != null) body.walkGraph(semNodesTable, visitor); + if (stepNode != null) stepNode.walkGraph(semNodesTable, visitor); + visitor.postVisit(this); } + @Override + public String getSignature() { + final StringBuffer buf = new StringBuffer(); + buf.append(getName().toString()); + if (getArity() > 0 && getKind() != ASTConstants.BuiltInKind) { + buf.append("("); + //TODO This hack doesn't work for infix operators + final FormalParamNode[] params = getParams(); + for (int i = 0; i < params.length - 1; i++) { + final FormalParamNode formalParamNode = params[i]; + if (formalParamNode.getTreeNode() != null) { + final SyntaxTreeNode treeNode = (SyntaxTreeNode) formalParamNode.getTreeNode(); + buf.append(treeNode.getHumanReadableImage()); + buf.append(", "); + } + } + if (params[params.length - 1].getTreeNode() != null) { + final TreeNode treeNode = params[params.length - 1].getTreeNode(); + buf.append(treeNode.getHumanReadableImage()); + } + buf.append(")"); + } + return buf.toString(); + } + /** * Displays this node as a String, implementing ExploreNode * interface; depth parameter is a bound on the depth of the portion * of the tree that is displayed. */ + @Override public final String toString(int depth) { if (depth <= 0) return ""; @@ -1270,6 +1351,25 @@ public class OpDefNode extends OpDefOrDeclNode return ret; } + @Override + public String getHumanReadableImage() { + // This ony gets pre-comments (directly in front of the operator definition). + // In-line comments - e.g. found in the standard modules - are not pre-comments. + final String comment = getComment(); + + final StringBuffer buf = new StringBuffer(comment); + buf.append("\n"); + + TreeNode[] ones = getTreeNode().one(); + for (TreeNode treeNode : ones) { + // TODO This omits all whitespaces from the definition and is thus hard to read. + // I couldn't figure out a better way to obtain the original image though. + buf.append(treeNode.getHumanReadableImage()); + buf.append(" "); + } + return buf.toString().trim(); + } + protected String getNodeRef() { switch (getKind()) { case UserDefinedOpKind: @@ -1282,7 +1382,7 @@ public class OpDefNode extends OpDefOrDeclNode } } - protected Element getSymbolElement(Document doc, tla2sany.xml.SymbolContext context) { + protected Element getSymbolElement(Document doc, SymbolContext context) { Element ret = null; switch (getKind()) { case UserDefinedOpKind: @@ -1325,4 +1425,4 @@ public class OpDefNode extends OpDefOrDeclNode } return ret; } -} +} \ No newline at end of file diff --git a/tlatools/src/tla2sany/semantic/OpDefOrDeclNode.java b/tlatools/src/tla2sany/semantic/OpDefOrDeclNode.java index d174b3fb54c84c53fd893b410d87893319374437..adb581946e9dc2a60ff1dbd6ddca1892568c7132 100644 --- a/tlatools/src/tla2sany/semantic/OpDefOrDeclNode.java +++ b/tlatools/src/tla2sany/semantic/OpDefOrDeclNode.java @@ -81,5 +81,17 @@ public abstract class OpDefOrDeclNode extends SymbolNode { ? originallyDefinedInModule.getName().toString() : "<null>" ); } - + + public String getComment() { + final StringBuffer buf = new StringBuffer(); + + //TODO Support in-line comments found in the standard modules. + final String[] preComments = getPreComments(); + for (String string : preComments) { + buf.append(string); + buf.append("\n"); + } + + return buf.toString().replace("\n$", "").trim(); + } } diff --git a/tlatools/src/tla2sany/semantic/OpDefOrLabelNode.java b/tlatools/src/tla2sany/semantic/OpDefOrLabelNode.java index d7eb889c0253dba0b5f92e8358d972e24dc07157..fe64c21a815c930ee0c6f34705b675da139afb6b 100644 --- a/tlatools/src/tla2sany/semantic/OpDefOrLabelNode.java +++ b/tlatools/src/tla2sany/semantic/OpDefOrLabelNode.java @@ -1,66 +1,66 @@ -// Last modified on Sat 28 Apr 2007 at 14:37:16 PST by lamport - -/*************************************************************************** -* This interface is implemented by LabelNode, OpDefNode, and * -* ThmOrAssumpDefNode. It contains methods for accessing the set of * -* LabelNode objects for labels that occur immediately within this * -* node--that is, with no intervening LabelNode or OpDefNode in the * -* semantic tree. This set is represented by a Hashtable, and by the null * -* object if the set is empty. The Hashtable key of a LabelNode ln is * -* ln.getName(), which is a UniqueString. * -* * -* WARNING: If an OpDefNode odn represents an operator obtained by * -* instanting a module M, then each LabelNode object lab returned by * -* getLabel() and getLabels() is the same one as in the OpDefNode * -* from module M. Therefore, lab.body is an expression in module M, not in * -* the current module. The meaning of this label must be interpreted as * -* lab.body under the substitutions of the instantiation. This * -* substitution is described by the SubstInNode odn.body. Hence, to * -* interpret a label in the label set of an OpDefNode odn, one must check * -* if odn.body is a SubstInNode, in which case that substitution must be * -* applied to the body of the label. In general, odn is the result of a * -* series of k instantiations iff odn.body.body. ... .body is a SubstIn * -* node, for up to k ".body"s. * -* * -* A similar warning applies to ThmOrAssumpDefNode objects, except they * -* use APSubstInNode objects instead of SubstInNode objects for the * -* instantiation. * -***************************************************************************/ - -package tla2sany.semantic; - -import java.util.Hashtable; - -import util.UniqueString; - -interface OpDefOrLabelNode { - -public abstract void setLabels(Hashtable ht) ; - /************************************************************************* - * Set the set of labels to ht. * - *************************************************************************/ - -public abstract LabelNode getLabel(UniqueString us) ; - /************************************************************************* - * If the set contains an OpDefNode with name `us', then that OpDefNode * - * is returned; otherwise null is returned. * - *************************************************************************/ - -public abstract boolean addLabel(LabelNode odn) ; - /************************************************************************* - * If the set contains no LabelNode with the same name as odn, then odn * - * is added to the set and true is return; else the set is unchanged and * - * false is returned. * - *************************************************************************/ - -public abstract LabelNode[] getLabels() ; - /************************************************************************* - * Returns an array containing the Label objects in the set. * - *************************************************************************/ - -public abstract int getArity(); - /************************************************************************* - * Returns the arity of the label or operator. * - *************************************************************************/ - +// Last modified on Sat 28 Apr 2007 at 14:37:16 PST by lamport + +/*************************************************************************** +* This interface is implemented by LabelNode, OpDefNode, and * +* ThmOrAssumpDefNode. It contains methods for accessing the set of * +* LabelNode objects for labels that occur immediately within this * +* node--that is, with no intervening LabelNode or OpDefNode in the * +* semantic tree. This set is represented by a Hashtable, and by the null * +* object if the set is empty. The Hashtable key of a LabelNode ln is * +* ln.getName(), which is a UniqueString. * +* * +* WARNING: If an OpDefNode odn represents an operator obtained by * +* instanting a module M, then each LabelNode object lab returned by * +* getLabel() and getLabels() is the same one as in the OpDefNode * +* from module M. Therefore, lab.body is an expression in module M, not in * +* the current module. The meaning of this label must be interpreted as * +* lab.body under the substitutions of the instantiation. This * +* substitution is described by the SubstInNode odn.body. Hence, to * +* interpret a label in the label set of an OpDefNode odn, one must check * +* if odn.body is a SubstInNode, in which case that substitution must be * +* applied to the body of the label. In general, odn is the result of a * +* series of k instantiations iff odn.body.body. ... .body is a SubstIn * +* node, for up to k ".body"s. * +* * +* A similar warning applies to ThmOrAssumpDefNode objects, except they * +* use APSubstInNode objects instead of SubstInNode objects for the * +* instantiation. * +***************************************************************************/ + +package tla2sany.semantic; + +import java.util.Hashtable; + +import util.UniqueString; + +interface OpDefOrLabelNode { + +public abstract void setLabels(Hashtable<UniqueString, LabelNode> ht) ; + /************************************************************************* + * Set the set of labels to ht. * + *************************************************************************/ + +public abstract LabelNode getLabel(UniqueString us) ; + /************************************************************************* + * If the set contains an OpDefNode with name `us', then that OpDefNode * + * is returned; otherwise null is returned. * + *************************************************************************/ + +public abstract boolean addLabel(LabelNode odn) ; + /************************************************************************* + * If the set contains no LabelNode with the same name as odn, then odn * + * is added to the set and true is return; else the set is unchanged and * + * false is returned. * + *************************************************************************/ + +public abstract LabelNode[] getLabels() ; + /************************************************************************* + * Returns an array containing the Label objects in the set. * + *************************************************************************/ + +public abstract int getArity(); + /************************************************************************* + * Returns the arity of the label or operator. * + *************************************************************************/ + } \ No newline at end of file diff --git a/tlatools/src/tla2sany/semantic/SemanticNode.java b/tlatools/src/tla2sany/semantic/SemanticNode.java index 78b1334ca4511e79c085247bb3d8fecc1813c630..fc265c0b64c071eab9e836ab7dccc20b95ce787d 100644 --- a/tlatools/src/tla2sany/semantic/SemanticNode.java +++ b/tlatools/src/tla2sany/semantic/SemanticNode.java @@ -4,19 +4,21 @@ // last modified on Fri 16 Mar 2007 at 17:22:54 PST by lamport package tla2sany.semantic; -import java.util.Comparator; import java.util.Hashtable; +import java.util.concurrent.atomic.AtomicInteger; + +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; import tla2sany.explorer.ExploreNode; +import tla2sany.explorer.ExplorerVisitor; import tla2sany.parser.SyntaxTreeNode; import tla2sany.st.Location; import tla2sany.st.TreeNode; -import util.ToolIO; - import tla2sany.xml.XMLExportable; -import org.w3c.dom.Document; -import org.w3c.dom.Element; -import org.w3c.dom.Node; +import tlc2.value.IValue; +import tlc2.value.Values; /** * SemanticNode is the (abstract) superclass of all nodes in the @@ -26,15 +28,15 @@ import org.w3c.dom.Node; * used to check for equality. */ public abstract class SemanticNode - implements ASTConstants, ExploreNode, LevelConstants, Comparable, XMLExportable /* interface for exporting into XML */ { + implements ASTConstants, ExploreNode, LevelConstants, Comparable<SemanticNode>, XMLExportable /* interface for exporting into XML */ { private static final Object[] EmptyArr = new Object[0]; - private static int uid = 0; // the next unique ID for any semantic node + private static final AtomicInteger uid = new AtomicInteger(); // the next unique ID for any semantic node protected static Errors errors; - public int myUID; // the unique ID of THIS semantic node + public final int myUID; // the unique ID of THIS semantic node public TreeNode stn; // the concrete syntax tree node associated with THIS semantic node private Object[] tools; // each tool has a location in this array where // it may store an object for its own purposes @@ -42,7 +44,7 @@ public abstract class SemanticNode // strongly correlated with the Java type of the node public SemanticNode(int kind, TreeNode stn) { - myUID = uid++; + myUID = uid.getAndIncrement(); this.kind = kind; this.stn = stn; this.tools = EmptyArr; @@ -88,7 +90,6 @@ public abstract class SemanticNode this.tools = newTools; } this.tools[toolId] = obj; - ToolIO.registerSemanticNode(this, toolId); } /** @@ -177,10 +178,12 @@ public abstract class SemanticNode * of walkgraph is to find all reachable nodes in the semantic graph * and insert them in a Hashtable for use by the Explorer tool. */ - public void walkGraph(Hashtable semNodesTable) { - Integer uid = new Integer(myUID); + public void walkGraph(Hashtable<Integer, ExploreNode> semNodesTable, ExplorerVisitor visitor) { + Integer uid = Integer.valueOf(myUID); if (semNodesTable.get(uid) != null) return; - semNodesTable.put(new Integer(myUID), this); + semNodesTable.put(uid, this); + visitor.preVisit(this); + visitor.postVisit(this); } /** @@ -194,10 +197,25 @@ public abstract class SemanticNode " kind: " + (kind == -1 ? "<none>" : kinds[kind]) + getPreCommentsAsString()); } + + public boolean isBuiltIn() { + return Context.isBuiltIn(this); + } + /** + * @see tla2sany.modanalyzer.ParseUnit.isLibraryModule() + * @see StandardModules.isDefinedInStandardModule() + */ + public boolean isStandardModule() { + return StandardModules.isDefinedInStandardModule(this); + } + // YY's code public final Location getLocation() { - return this.stn.getLocation(); + if (this.stn != null) { + return this.stn.getLocation(); + } + return Location.nullLoc; } /** @@ -212,9 +230,9 @@ public abstract class SemanticNode * @param s2 * @return */ - public int compareTo(Object s) { + public int compareTo(SemanticNode s) { Location loc1 = this.stn.getLocation(); - Location loc2 = ((SemanticNode) s).stn.getLocation(); + Location loc2 = s.stn.getLocation(); if (loc1.beginLine() < loc2.beginLine()) { return -1; @@ -228,18 +246,70 @@ public abstract class SemanticNode return (loc1.beginColumn() < loc2.beginColumn())?-1:1; } + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + kind; + result = prime * result + myUID; + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + SemanticNode other = (SemanticNode) obj; + if (kind != other.kind) { + return false; + } + if (myUID != other.myUID) { + return false; + } + return true; + } + /*************************************************************************** * XXXXX A test for getLocation() returning null should be added * * to the following two toString methods. * ***************************************************************************/ public final void toString(StringBuffer sb, String padding) { - sb.append(this.getLocation()); + TreeNode treeNode = getTreeNode(); + if (treeNode instanceof SyntaxTreeNode + && System.getProperty(SemanticNode.class.getName() + ".showPlainFormulae") != null) { + SyntaxTreeNode stn = (SyntaxTreeNode) treeNode; + sb.append(stn.getHumanReadableImage()); + } else { + sb.append(this.getLocation()); + } } + @Override public String toString() { + TreeNode treeNode = getTreeNode(); + if (treeNode instanceof SyntaxTreeNode + && System.getProperty(SemanticNode.class.getName() + ".showPlainFormulae") != null) { + SyntaxTreeNode stn = (SyntaxTreeNode) treeNode; + return stn.getHumanReadableImage(); + } return this.getLocation().toString(); } + public String getHumanReadableImage() { + return getLocation().toString(); + } + + public String toString(final IValue aValue) { + return Values.ppr(aValue.toString()); + } + /** * August 2014 - TL * All nodes inherit from semantic node, which just attach location to the returned node diff --git a/tlatools/src/tla2sany/semantic/SetOfArgLevelConstraints.java b/tlatools/src/tla2sany/semantic/SetOfArgLevelConstraints.java index 08d295e3f3a10f7c4586626d3806c51f969bec08..edafe574a290a855dbd488667f1fdcc38fe32c7e 100644 --- a/tlatools/src/tla2sany/semantic/SetOfArgLevelConstraints.java +++ b/tlatools/src/tla2sany/semantic/SetOfArgLevelConstraints.java @@ -6,7 +6,7 @@ import java.util.HashMap; import java.util.Iterator; import java.util.Map; -class SetOfArgLevelConstraints extends HashMap implements LevelConstants { +class SetOfArgLevelConstraints extends HashMap<ParamAndPosition, Integer> implements LevelConstants { // Implements a map mapping arg-level parameters (ParamAndPosition) // to levels (Integer). An entry <pap,x> means that the argument // pap.position of the operator pap.param must have a level >= x. @@ -23,16 +23,17 @@ class SetOfArgLevelConstraints extends HashMap implements LevelConstants { * it with another one for the same parameter if one is there, or * ignoring the constraint if it is vacuous. */ - public final Object put(Object pap, Object level) { - int newLevel = ((Integer)level).intValue(); - Object old = this.get(pap); + @Override + public final Integer put(ParamAndPosition pap, Integer level) { + int newLevel = level.intValue(); + Integer old = this.get(pap); - int oldLevel = (old == null) ? MinLevel : ((Integer)old).intValue(); + int oldLevel = (old == null) ? MinLevel : old.intValue(); super.put(pap, new Integer(Math.max(newLevel, oldLevel))); return old; } - public final Object put(SymbolNode param, int position, int level) { + public final Integer put(SymbolNode param, int position, int level) { ParamAndPosition pap = new ParamAndPosition(param, position); return this.put(pap, new Integer(level)); } @@ -42,17 +43,19 @@ class SetOfArgLevelConstraints extends HashMap implements LevelConstants { * map, in each case "subsuming" it with any constraint already * present for the same parameter if one is there. */ - public final void putAll(Map s) { - for (Iterator iter = s.keySet().iterator(); iter.hasNext(); ) { - Object key = iter.next(); - put(key, s.get(key)); + @Override + public final void putAll(Map<? extends ParamAndPosition, ? extends Integer> s) { + for (Iterator<? extends ParamAndPosition> iter = s.keySet().iterator(); iter.hasNext(); ) { + ParamAndPosition key = iter.next(); + this.put(key, s.get(key)); } } + @Override public final String toString() { StringBuffer sb = new StringBuffer("{ "); - for (Iterator iter = this.keySet().iterator(); iter.hasNext(); ) { - Object pap = iter.next(); + for (Iterator<ParamAndPosition> iter = this.keySet().iterator(); iter.hasNext(); ) { + ParamAndPosition pap = iter.next(); sb.append(pap.toString() + " -> " + this.get(pap)); if (iter.hasNext()) sb.append(", "); } diff --git a/tlatools/src/tla2sany/semantic/SetOfLevelConstraints.java b/tlatools/src/tla2sany/semantic/SetOfLevelConstraints.java index 8f3c4dfec2ce722c8523218d25068c643d7a0265..676bb196cb14fb49f9aea86788468a4939f8e261 100644 --- a/tlatools/src/tla2sany/semantic/SetOfLevelConstraints.java +++ b/tlatools/src/tla2sany/semantic/SetOfLevelConstraints.java @@ -6,7 +6,7 @@ import java.util.HashMap; import java.util.Iterator; import java.util.Map; -class SetOfLevelConstraints extends HashMap implements LevelConstants { +class SetOfLevelConstraints extends HashMap<SymbolNode, Integer> implements LevelConstants { // Implements a map mapping parameters to levels. An entry <p,x> in // the set means that p must have a level <= x. /************************************************************************* @@ -15,17 +15,16 @@ class SetOfLevelConstraints extends HashMap implements LevelConstants { * that the key/parameter must have a level <= the value/int. * *************************************************************************/ - - /** * This method adds <param, level> into this map. It subsumes * any existing one. */ - public final Object put(Object param, Object level) { - int newLevel = ((Integer)level).intValue(); - Object old = this.get(param); + @Override + public final Integer put(SymbolNode param, Integer level) { + int newLevel = level.intValue(); + Integer old = this.get(param); - int oldLevel = (old == null) ? MaxLevel : ((Integer)old).intValue(); + int oldLevel = (old == null) ? MaxLevel : old.intValue(); super.put(param, new Integer(Math.min(newLevel, oldLevel))); return old; } @@ -35,17 +34,19 @@ class SetOfLevelConstraints extends HashMap implements LevelConstants { * map, in each case "subsuming" it with any constraint already * present for the same parameter if one is there. */ - public final void putAll(Map s) { - for (Iterator iter = s.keySet().iterator(); iter.hasNext(); ) { - Object key = iter.next(); + @Override + public final void putAll(Map<? extends SymbolNode, ? extends Integer> s) { + for (Iterator<? extends SymbolNode> iter = s.keySet().iterator(); iter.hasNext(); ) { + SymbolNode key = iter.next(); this.put(key, s.get(key)); } } + @Override public final String toString() { StringBuffer sb = new StringBuffer("{ "); - for (Iterator iter = this.keySet().iterator(); iter.hasNext(); ) { - SymbolNode param = (SymbolNode)iter.next(); + for (Iterator<SymbolNode> iter = this.keySet().iterator(); iter.hasNext(); ) { + SymbolNode param = iter.next(); sb.append(param.getName() + " -> " + this.get(param)); if (iter.hasNext()) sb.append(", "); } diff --git a/tlatools/src/tla2sany/semantic/StandardModules.java b/tlatools/src/tla2sany/semantic/StandardModules.java new file mode 100644 index 0000000000000000000000000000000000000000..7c8634a2685a5294c61850ab2bb9b76ef503def8 --- /dev/null +++ b/tlatools/src/tla2sany/semantic/StandardModules.java @@ -0,0 +1,67 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tla2sany.semantic; + +import java.util.HashSet; +import java.util.Set; + +public class StandardModules { + private static final Set<String> STANDARD_MODULES = new HashSet<>(); + + static { + // Quick'n'Dirty hack! This breaks if any one of the operators is override by + // the model or a complete standard module has been replaced by a user-defined + // module with the same name. + STANDARD_MODULES.add("FiniteSets"); + STANDARD_MODULES.add("Sequences"); + STANDARD_MODULES.add("Bags"); + STANDARD_MODULES.add("Naturals"); + STANDARD_MODULES.add("Integers"); + STANDARD_MODULES.add("Reals"); + STANDARD_MODULES.add("RealTime"); + STANDARD_MODULES.add("Randomization"); + STANDARD_MODULES.add("TLC"); + } + + private StandardModules() { + // no instances + } + + public static boolean isDefinedInStandardModule(SemanticNode sn) { + if ((sn != null) && (sn.getLocation() != null)) { + return isDefinedInStandardModule(sn.getLocation().source()); // source might be null + } + return false; + } + + public static boolean isDefinedInStandardModule(final String moduleName) { + return STANDARD_MODULES.contains(moduleName); + } + + public static void filterNonStandardModulesFromSet(final Set<String> listOfModules) { + listOfModules.retainAll(STANDARD_MODULES); + } +} diff --git a/tlatools/src/tla2sany/semantic/StringNode.java b/tlatools/src/tla2sany/semantic/StringNode.java index 054c5077b397d17aebf4683d9b247cc0f49f9c50..80746864ee8e21279d624291cd9c6c005d855d1c 100644 --- a/tlatools/src/tla2sany/semantic/StringNode.java +++ b/tlatools/src/tla2sany/semantic/StringNode.java @@ -4,14 +4,16 @@ package tla2sany.semantic; import java.util.Hashtable; -import tla2sany.explorer.ExploreNode; -import tla2sany.st.TreeNode; -import util.UniqueString; - import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; +import tla2sany.explorer.ExploreNode; +import tla2sany.explorer.ExplorerVisitor; +import tla2sany.st.TreeNode; +import tla2sany.xml.SymbolContext; +import util.UniqueString; + /** @@ -47,6 +49,7 @@ public class StringNode extends ExprNode implements ExploreNode { public final UniqueString getRep() { return this.value; } /* Level Checking */ + @Override public final boolean levelCheck(int iter) { levelChecked = iter; /********************************************************************* @@ -81,11 +84,14 @@ public class StringNode extends ExprNode implements ExploreNode { // "ArgLevelParams: " + this.getArgLevelParams() + "\n" ; // } - public final void walkGraph(Hashtable semNodesTable) { - Integer uid = new Integer(myUID); + @Override + public final void walkGraph(Hashtable<Integer, ExploreNode> semNodesTable, ExplorerVisitor visitor) { + Integer uid = Integer.valueOf(myUID); if (semNodesTable.get(uid) != null) return; - semNodesTable.put(new Integer(myUID), this); + semNodesTable.put(uid, this); + visitor.preVisit(this); + visitor.postVisit(this); } final String PrintVersion(String str) { @@ -118,6 +124,7 @@ public class StringNode extends ExprNode implements ExploreNode { return buf.toString(); } + @Override public final String toString(int depth) { if (depth <= 0) return ""; return "\n*StringNode: " + super.toString(depth) @@ -125,7 +132,8 @@ public class StringNode extends ExprNode implements ExploreNode { "'" + " Length: " + value.length(); } - protected Element getLevelElement(Document doc, tla2sany.xml.SymbolContext context) { + @Override + protected Element getLevelElement(Document doc, SymbolContext context) { Element e = doc.createElement("StringValue"); Node n = doc.createTextNode(value.toString()); e.appendChild(n); diff --git a/tlatools/src/tla2sany/semantic/Subst.java b/tlatools/src/tla2sany/semantic/Subst.java index 4e91afb6866dbb8c81e954b5644973b6999e83e8..8f3bd4de55010c74d99707e149c317534cb5a859 100644 --- a/tlatools/src/tla2sany/semantic/Subst.java +++ b/tlatools/src/tla2sany/semantic/Subst.java @@ -6,13 +6,16 @@ import java.util.HashSet; import java.util.Hashtable; import java.util.Iterator; +import org.w3c.dom.Document; +import org.w3c.dom.Element; + import tla2sany.explorer.ExploreNode; +import tla2sany.explorer.ExplorerVisitor; import tla2sany.st.TreeNode; import tla2sany.utilities.Strings; - +import tla2sany.xml.SymbolContext; import tla2sany.xml.XMLExportable; -import org.w3c.dom.Document; -import org.w3c.dom.Element; +import tlc2.tool.coverage.CostModel; public class Subst implements LevelConstants, ASTConstants, ExploreNode, XMLExportable /* interface for exporting into XML */ { @@ -24,6 +27,7 @@ public class Subst implements LevelConstants, ASTConstants, ExploreNode, XMLExpo private ExprOrOpArgNode expr; private TreeNode exprSTN; private boolean implicit; + private CostModel cm = CostModel.DO_NOT_RECORD; /* Constructors */ public Subst(OpDeclNode odn, ExprOrOpArgNode exp, TreeNode exSTN, boolean imp) { @@ -49,6 +53,13 @@ public class Subst implements LevelConstants, ASTConstants, ExploreNode, XMLExpo public final void setExprSTN(TreeNode stn) { this.exprSTN = stn; } public final boolean isImplicit() { return this.implicit; } + + public final CostModel getCM() { return this.cm; } + + public final Subst setCM(final CostModel cm) { + this.cm = cm; + return this; + } public static ExprOrOpArgNode getSub(Object param, Subst[] subs) { for (int i = 0; i < subs.length; i++) { @@ -59,7 +70,7 @@ public class Subst implements LevelConstants, ASTConstants, ExploreNode, XMLExpo return null; } - public static HashSet paramSet(Object param, Subst[] subs) { + public static HashSet<SymbolNode> paramSet(SymbolNode param, Subst[] subs) { /*********************************************************************** * If subs[i] is of the form `parm <- expr', then it returns the * * expr.levelParams. Otherwise, it returns the HashSet containing * @@ -76,12 +87,12 @@ public class Subst implements LevelConstants, ASTConstants, ExploreNode, XMLExpo return subs[idx].getExpr().getLevelParams(); } - HashSet res = new HashSet(); + HashSet<SymbolNode> res = new HashSet<>(); res.add(param); return res; } - public static HashSet allParamSet(Object param, Subst[] subs) { + public static HashSet<SymbolNode> allParamSet(SymbolNode param, Subst[] subs) { /*********************************************************************** * This is exactly like paramSet, except it returns the allParams * * HashSet instead of levelParams. * @@ -94,7 +105,7 @@ public class Subst implements LevelConstants, ASTConstants, ExploreNode, XMLExpo return subs[idx].getExpr().getAllParams(); } - HashSet res = new HashSet(); + HashSet<SymbolNode> res = new HashSet<>(); res.add(param); return res; } @@ -110,10 +121,10 @@ public class Subst implements LevelConstants, ASTConstants, ExploreNode, XMLExpo ***********************************************************************/ SetOfLevelConstraints res = new SetOfLevelConstraints(); SetOfLevelConstraints lcSet = body.getLevelConstraints(); - Iterator iter = lcSet.keySet().iterator(); + Iterator<SymbolNode> iter = lcSet.keySet().iterator(); while (iter.hasNext()) { - SymbolNode param = (SymbolNode)iter.next(); - Object plevel = lcSet.get(param); + SymbolNode param = iter.next(); + Integer plevel = lcSet.get(param); if (!isConstant) { if (param.getKind() == ConstantDeclKind) { plevel = Levels[ConstantLevel]; @@ -122,15 +133,15 @@ public class Subst implements LevelConstants, ASTConstants, ExploreNode, XMLExpo plevel = Levels[VariableLevel]; } } - Iterator iter1 = paramSet(param, subs).iterator(); + Iterator<SymbolNode> iter1 = paramSet(param, subs).iterator(); while (iter1.hasNext()) { res.put(iter1.next(), plevel); } } - HashSet alpSet = body.getArgLevelParams(); - iter = alpSet.iterator(); - while (iter.hasNext()) { - ArgLevelParam alp = (ArgLevelParam)iter.next(); + HashSet<ArgLevelParam> alpSet = body.getArgLevelParams(); + Iterator<ArgLevelParam> alpIter = alpSet.iterator(); + while (alpIter.hasNext()) { + ArgLevelParam alp = alpIter.next(); OpArgNode sub = (OpArgNode)getSub(alp.op, subs); if (sub != null && sub.getOp() instanceof OpDefNode) { @@ -145,7 +156,7 @@ public class Subst implements LevelConstants, ASTConstants, ExploreNode, XMLExpo * argument of this method. * *****************************************************************/ Integer mlevel = new Integer(subDef.getMaxLevel(alp.i)); - Iterator iter1 = paramSet(alp.param, subs).iterator(); + Iterator<SymbolNode> iter1 = paramSet(alp.param, subs).iterator(); while (iter1.hasNext()) { res.put(iter1.next(), mlevel); } @@ -162,10 +173,10 @@ public class Subst implements LevelConstants, ASTConstants, ExploreNode, XMLExpo ***********************************************************************/ SetOfArgLevelConstraints res = new SetOfArgLevelConstraints(); SetOfArgLevelConstraints alcSet = body.getArgLevelConstraints(); - Iterator iter = alcSet.keySet().iterator(); + Iterator<ParamAndPosition> iter = alcSet.keySet().iterator(); while (iter.hasNext()) { - ParamAndPosition pap = (ParamAndPosition)iter.next(); - Object plevel = alcSet.get(pap); + ParamAndPosition pap = iter.next(); + Integer plevel = alcSet.get(pap); ExprOrOpArgNode sub = getSub(pap.param, subs); if (sub == null) { res.put(pap, plevel); @@ -178,10 +189,10 @@ public class Subst implements LevelConstants, ASTConstants, ExploreNode, XMLExpo } } } - HashSet alpSet = body.getArgLevelParams(); - iter = alpSet.iterator(); - while (iter.hasNext()) { - ArgLevelParam alp = (ArgLevelParam)iter.next(); + HashSet<ArgLevelParam> alpSet = body.getArgLevelParams(); + Iterator<ArgLevelParam> alpIter = alpSet.iterator(); + while (alpIter.hasNext()) { + ArgLevelParam alp = alpIter.next(); ExprOrOpArgNode subParam = getSub(alp.param, subs); if (subParam != null) { ExprOrOpArgNode subOp = getSub(alp.op, subs); @@ -200,16 +211,16 @@ public class Subst implements LevelConstants, ASTConstants, ExploreNode, XMLExpo return res; } - public static HashSet getSubALPSet(LevelNode body, Subst[] subs) { + public static HashSet<ArgLevelParam> getSubALPSet(LevelNode body, Subst[] subs) { /*********************************************************************** * This should only be called after level checking has been called on * * body and on all subs[i].getExpr(). * ***********************************************************************/ - HashSet res = new HashSet(); - HashSet alpSet = body.getArgLevelParams(); - Iterator iter = alpSet.iterator(); + HashSet<ArgLevelParam> res = new HashSet<>(); + HashSet<ArgLevelParam> alpSet = body.getArgLevelParams(); + Iterator<ArgLevelParam> iter = alpSet.iterator(); while (iter.hasNext()) { - ArgLevelParam alp = (ArgLevelParam)iter.next(); + ArgLevelParam alp = iter.next(); ExprOrOpArgNode sub = getSub(alp.op, subs); if (sub == null) { res.add(alp); @@ -217,9 +228,9 @@ public class Subst implements LevelConstants, ASTConstants, ExploreNode, XMLExpo else { SymbolNode subOp = ((OpArgNode)sub).getOp(); if (subOp.isParam()) { - Iterator iter1 = paramSet(alp.param, subs).iterator(); + Iterator<SymbolNode> iter1 = paramSet(alp.param, subs).iterator(); while (iter1.hasNext()) { - res.add(new ArgLevelParam(subOp, alp.i, (SymbolNode)iter1.next())); + res.add(new ArgLevelParam(subOp, alp.i, iter1.next())); } } } @@ -229,9 +240,11 @@ public class Subst implements LevelConstants, ASTConstants, ExploreNode, XMLExpo public final String levelDataToString() { return "Dummy level string"; } - public final void walkGraph(Hashtable semNodesTable) { - if (op != null) op.walkGraph(semNodesTable); - if (expr != null) expr.walkGraph(semNodesTable); + public final void walkGraph(Hashtable<Integer, ExploreNode> semNodesTable, ExplorerVisitor visitor) { + visitor.preVisit(this); + if (op != null) op.walkGraph(semNodesTable, visitor); + if (expr != null) expr.walkGraph(semNodesTable, visitor); + visitor.postVisit(this); } public final String toString(int depth) { @@ -240,7 +253,7 @@ public class Subst implements LevelConstants, ASTConstants, ExploreNode, XMLExpo "\nExpr: " + Strings.indent(2,(expr!=null ? expr.toString(depth-1) : "<null>")); } - public Element export(Document doc, tla2sany.xml.SymbolContext context) { + public Element export(Document doc, SymbolContext context) { Element ret = doc.createElement("Subst"); ret.appendChild(op.export(doc,context)); ret.appendChild(expr.export(doc,context)); diff --git a/tlatools/src/tla2sany/semantic/SubstInNode.java b/tlatools/src/tla2sany/semantic/SubstInNode.java index 07230da85a00f4a1062a90e26390665170f0c78f..e1de6219a3995def4cf6e237be98cfe8144804c0 100644 --- a/tlatools/src/tla2sany/semantic/SubstInNode.java +++ b/tlatools/src/tla2sany/semantic/SubstInNode.java @@ -24,14 +24,17 @@ import java.util.HashSet; import java.util.Hashtable; import java.util.Iterator; +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +import tla2sany.explorer.ExploreNode; +import tla2sany.explorer.ExplorerVisitor; import tla2sany.st.TreeNode; import tla2sany.utilities.Strings; import tla2sany.utilities.Vector; +import tla2sany.xml.SymbolContext; import util.UniqueString; -import org.w3c.dom.Document; -import org.w3c.dom.Element; - public class SubstInNode extends ExprNode { /** * For a SubstInNode object s that has the WITH clause @@ -80,7 +83,7 @@ public class SubstInNode extends ExprNode { * substitutions is to be produced. */ public SubstInNode(TreeNode treeNode, SymbolTable instancerST, - Vector instanceeDecls, ModuleNode ingmn, ModuleNode edmn) + Vector<OpDeclNode> instanceeDecls, ModuleNode ingmn, ModuleNode edmn) throws AbortException { super(SubstInKind, treeNode); this.instantiatingModule = ingmn; @@ -130,17 +133,17 @@ public class SubstInNode extends ExprNode { * OpApplNode or an OpArgNode substituted for each CONSTANT of * VARIABLE OpDeclNode in vector v. */ - final void constructSubst(Vector instanceeDecls, SymbolTable instancerST, + final void constructSubst(Vector<OpDeclNode> instanceeDecls, SymbolTable instancerST, TreeNode treeNode) throws AbortException { - Vector vtemp = new Vector(); + Vector<Subst> vtemp = new Vector<>(); // for each CONSTANT or VARIABLE declared in module being // instantiated (the instancee) for ( int i = 0; i < instanceeDecls.size(); i++ ) { // Get the OpDeclNode for the CONSTANT or VARIABLE being // substituted for, i.e. "c" in" c <- e" - OpDeclNode decl = (OpDeclNode)instanceeDecls.elementAt(i); + OpDeclNode decl = instanceeDecls.elementAt(i); // Try to resolve the name in the instancer module so we can see // if it is recognized as an operator, and if so, what kind of @@ -196,7 +199,8 @@ public class SubstInNode extends ExprNode { * to this method can contain a mixture of explicit and implicit * substitutions */ - final void addExplicitSubstitute(Context instanceeCtxt, UniqueString lhs, + @SuppressWarnings("unused") // TODO final else block is dead code + final void addExplicitSubstitute(Context instanceCtxt, UniqueString lhs, TreeNode stn, ExprOrOpArgNode sub) { int index; for (index = 0; index < this.substs.length; index++) { @@ -224,7 +228,7 @@ public class SubstInNode extends ExprNode { // the instancee context // look up the lhs symbol in the instancee context - SymbolNode lhsSymbol = instanceeCtxt.getSymbol(lhs); + SymbolNode lhsSymbol = instanceCtxt.getSymbol(lhs); // lhs must be an OpDeclNode; if not just return, as this error // will have been earlier, though semantic analysis was allowed @@ -261,10 +265,10 @@ public class SubstInNode extends ExprNode { * possible, because X is not defined in the instantiating module, * then we have an error. */ - final void matchAll(Vector decls) { + final void matchAll(Vector<OpDeclNode> decls) { for (int i = 0; i < decls.size(); i++) { // Get the name of the i'th operator that must be substituted for - UniqueString opName = ((OpDeclNode)decls.elementAt(i)).getName(); + UniqueString opName = decls.elementAt(i).getName(); // See if it is represented in the substitutions array int j; @@ -276,7 +280,7 @@ public class SubstInNode extends ExprNode { if ( j >= this.substs.length ) { errors.addError(stn.getLocation(), "Substitution missing for symbol " + opName + " declared at " + - ((OpDeclNode)(decls.elementAt(i))).getTreeNode().getLocation() + + decls.elementAt(i).getTreeNode().getLocation() + " \nand instantiated in module " + instantiatingModule.getName() + "." ); } } @@ -291,6 +295,8 @@ public class SubstInNode extends ExprNode { // private SetOfArgLevelConstraints argLevelConstraints; // private HashSet argLevelParams; + @Override + @SuppressWarnings("unchecked") public final boolean levelCheck(int itr) { if (this.levelChecked >= itr) return this.levelCorrect; this.levelChecked = itr ; @@ -311,7 +317,7 @@ public class SubstInNode extends ExprNode { // Calculate the level information this.level = this.body.getLevel(); - HashSet lpSet = this.body.getLevelParams(); + HashSet<SymbolNode> lpSet = this.body.getLevelParams(); for (int i = 0; i < this.substs.length; i++) { if (lpSet.contains(this.getSubFor(i))) { this.level = Math.max(level, this.getSubWith(i).getLevel()); @@ -319,10 +325,9 @@ public class SubstInNode extends ExprNode { } // this.levelParams = new HashSet(); - Iterator iter = lpSet.iterator(); + Iterator<SymbolNode> iter = lpSet.iterator(); while (iter.hasNext()) { - Object param = iter.next(); - this.levelParams.addAll(Subst.paramSet(param, this.substs)); + this.levelParams.addAll(Subst.paramSet(iter.next(), this.substs)); /******************************************************************* * At this point, levelCheck(itr) has been invoked on * * this.substs[i].getExpr() (which equals this.getSubWith(i)). * @@ -353,8 +358,8 @@ public class SubstInNode extends ExprNode { * 23 February 2009: Added ".clone" to the following statements to fix * * bug. * ***********************************************************************/ - this.allParams = (HashSet) this.body.getAllParams().clone() ; - this.nonLeibnizParams = (HashSet) this.body.getNonLeibnizParams().clone() ; + this.allParams = (HashSet<SymbolNode>)this.body.getAllParams().clone() ; + this.nonLeibnizParams = (HashSet<SymbolNode>)this.body.getNonLeibnizParams().clone() ; for (int i = 0 ; i < this.substs.length ; i++) { OpDeclNode param = substs[i].getOp() ; if (this.allParams.contains(param)) { @@ -460,6 +465,7 @@ public class SubstInNode extends ExprNode { // "ArgLevelParams: " + this.argLevelParams + "\n" ; // } + @Override public final String toString(int depth) { if (depth <= 0) return ""; @@ -489,6 +495,7 @@ public class SubstInNode extends ExprNode { * The children of this node are the body and the expressions * being substituted for symbols. */ + @Override public SemanticNode[] getChildren() { SemanticNode[] res = new SemanticNode[this.substs.length + 1]; res[0] = this.body; @@ -498,31 +505,46 @@ public class SubstInNode extends ExprNode { return res; } - public final void walkGraph(Hashtable semNodesTable) { - Integer uid = new Integer(myUID); + @Override + public final void walkGraph(Hashtable<Integer, ExploreNode> semNodesTable, ExplorerVisitor visitor) { + Integer uid = Integer.valueOf(myUID); if (semNodesTable.get(uid) != null) return; - semNodesTable.put(new Integer(myUID), this); + semNodesTable.put(uid, this); + visitor.preVisit(this); if (this.substs != null) { for (int i = 0; i < this.substs.length; i++) { - if (this.substs[i] != null) this.substs[i].walkGraph(semNodesTable); + if (this.substs[i] != null) this.substs[i].walkGraph(semNodesTable, visitor); } } - if (this.body != null) this.body.walkGraph(semNodesTable); + if (this.body != null) this.body.walkGraph(semNodesTable, visitor); + visitor.postVisit(this); return; } - protected Element getLevelElement(Document doc, tla2sany.xml.SymbolContext context) { + @Override + protected Element getLevelElement(Document doc, SymbolContext context) { Element sbts = doc.createElement("substs"); for (int i=0; i<substs.length; i++) { sbts.appendChild(substs[i].export(doc,context)); } Element bdy = doc.createElement("body"); bdy.appendChild(body.export(doc,context)); + + Element from = doc.createElement("instFrom"); + Element fromchild = this.instantiatingModule.export(doc, context); + from.appendChild(fromchild); + + Element to = doc.createElement("instTo"); + Element tochild = instantiatedModule.export(doc,context); + to.appendChild(tochild); + Element ret = doc.createElement("SubstInNode"); ret.appendChild(sbts); ret.appendChild(bdy); + ret.appendChild(from); + ret.appendChild(to); // at the end, we append the context of the symbols used in this node //ret.appendChild(instanceeCtxt.export(doc)); diff --git a/tlatools/src/tla2sany/semantic/SymbolMatcher.java b/tlatools/src/tla2sany/semantic/SymbolMatcher.java new file mode 100644 index 0000000000000000000000000000000000000000..2866761da704b34b4371e7acd8e0a1ce77c6e0f9 --- /dev/null +++ b/tlatools/src/tla2sany/semantic/SymbolMatcher.java @@ -0,0 +1,97 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tla2sany.semantic; + +import java.util.HashSet; +import java.util.Set; + +public interface SymbolMatcher { + + /** + * @return true if the given {@link SymbolNode} matches this predicate. + */ + boolean matches(final SymbolNode aSymbol); + + public static class NameAndTypeMatcher implements SymbolMatcher { + + private String prefix; + + //** Invoked by clients of ModuleNode#getSymbols **// + + public NameAndTypeMatcher setPrefix(final String aPrefix) { + this.prefix = aPrefix; + return this; + } + + //** Invoked by ModuleNode, overridden by clients **// + + /* (non-Javadoc) + * @see tla2sany.semantic.SymbolMatcher#matches(tla2sany.semantic.SymbolNode) + */ + @Override + public boolean matches(final SymbolNode aSymbol) { + if (!matchesAnyType() && !matchTypes().contains(aSymbol.getClass())) { + // TODO Better test for isAssignableFrom(aSymbol.getClass()), but would require + // looping over matchTypes. + return false; + } + if (aSymbol.getKind() == ASTConstants.BuiltInKind && aSymbol.getName().toString().startsWith("$")) { + // Do not match internal built-in operators. + return false; + } + + final String symbolName = aSymbol.getName().toString(); + if (matchCaseSensitive() && !symbolName.startsWith(getPrefix())) { + return false; + } else if (!symbolName.toLowerCase().startsWith(getPrefix().toLowerCase())){ + return false; + } + + return true; + } + + //** Invoked by matches, subclasses may override **// + + /** + * @return A Set of SymbolNodes to match. + */ + protected Set<Class<? extends SymbolNode>> matchTypes() { + return new HashSet<>(); + } + + protected boolean matchesAnyType() { + return matchTypes().isEmpty(); + } + + protected boolean matchCaseSensitive() { + return false; + } + + protected String getPrefix() { + return prefix; + } + } +} diff --git a/tlatools/src/tla2sany/semantic/SymbolNode.java b/tlatools/src/tla2sany/semantic/SymbolNode.java index fd5a442d8fc79f78c1834cdc64fb0524cec9c51c..b5ca60576480ad4a822342723d10a1171422bf52 100644 --- a/tlatools/src/tla2sany/semantic/SymbolNode.java +++ b/tlatools/src/tla2sany/semantic/SymbolNode.java @@ -2,12 +2,12 @@ // Portions Copyright (c) 2003 Microsoft Corporation. All rights reserved. package tla2sany.semantic; -import tla2sany.st.TreeNode; -import util.UniqueString; - import org.w3c.dom.Document; import org.w3c.dom.Element; +import tla2sany.st.TreeNode; +import util.UniqueString; + /** * Abstract class extended by classes that represent the meaning of an * identifier, including generalized IDs. @@ -73,6 +73,10 @@ public abstract class SymbolNode extends LevelNode { return (this instanceof OpDeclNode || this instanceof FormalParamNode); } + + public String getSignature() { + return getName().toString(); + } /** * Returns true iff this node and otherNode are both OpDefOrDeclNode objects or @@ -127,6 +131,9 @@ public abstract class SymbolNode extends LevelNode { * we need to add location and level information here. */ public Element exportDefinition(Document doc, tla2sany.xml.SymbolContext context) { + if (!context.isTop_level_entry()) + throw new IllegalArgumentException("Exporting definition "+getName()+" ref "+getNodeRef()+" twice!"); + context.resetTop_level_entry(); try { Element e = getSymbolElement(doc, context); // level @@ -158,7 +165,7 @@ public abstract class SymbolNode extends LevelNode { * we also override getLevelElement as it should never be called */ protected Element getLevelElement(Document doc, tla2sany.xml.SymbolContext context) { - throw new UnsupportedOperationException("This should not be possible and therefore a bug"); + throw new UnsupportedOperationException("implementation Error: A symbol node may not be called for its level element."); } /** TL diff --git a/tlatools/src/tla2sany/semantic/SymbolTable.java b/tlatools/src/tla2sany/semantic/SymbolTable.java index 167d1e75a82ec6284e7f6d56ae959449e6f7f6c7..864320ad87eac4f6fc269b5ff57c610ac7fb5344 100644 --- a/tlatools/src/tla2sany/semantic/SymbolTable.java +++ b/tlatools/src/tla2sany/semantic/SymbolTable.java @@ -88,14 +88,15 @@ public class SymbolTable implements ASTConstants { * Looks up `name' in the symbol table and returns the node it finds, or * * null if there is no entry for `name'. * *************************************************************************/ - public final SymbolNode resolveSymbol(UniqueString name) { - for (int c = contextStack.size()-1; c >= 0; c-- ) { - Context ct = (Context)contextStack.elementAt(c); - SymbolNode r = ct.getSymbol( name ); - if (r != null) return r; - } - return null; - } + public final SymbolNode resolveSymbol(UniqueString name) { + for (int c = contextStack.size() - 1; c >= 0; c--) { + Context ct = (Context) contextStack.elementAt(c); + SymbolNode r = ct.getSymbol(name); + if (r != null) + return r; + } + return null; + } public final ModuleNode resolveModule(UniqueString name) { ModuleName modName = new ModuleName(name); diff --git a/tlatools/src/tla2sany/semantic/TheoremNode.java b/tlatools/src/tla2sany/semantic/TheoremNode.java index ded540740327eb836f937973f7adbfc3c4ae27eb..7b07e72372603313e677ad6f34bac6b7e69c2c92 100644 --- a/tlatools/src/tla2sany/semantic/TheoremNode.java +++ b/tlatools/src/tla2sany/semantic/TheoremNode.java @@ -15,13 +15,17 @@ package tla2sany.semantic; import java.util.Hashtable; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; + +import tla2sany.explorer.ExploreNode; +import tla2sany.explorer.ExplorerVisitor; import tla2sany.st.TreeNode; import tla2sany.utilities.Strings; +import tla2sany.xml.SymbolContext; import util.UniqueString; -import org.w3c.dom.Document; -import org.w3c.dom.Element; - /** * This class represents a theorem */ @@ -70,6 +74,10 @@ public class TheoremNode extends LevelNode { this.def = opd; this.proof = pf; if (opd != null) opd.thmOrAssump = this; + + // make sure that definition and statemtent agree + if (def != null) + assert(def.getBody() == theoremExprOrAssumeProve); } /* Returns the statement of the theorem */ @@ -107,7 +115,8 @@ public class TheoremNode extends LevelNode { /* (non-Javadoc) * @see tla2sany.semantic.LevelNode#levelCheck(int) */ -public final boolean levelCheck(int iter) { + @Override + public final boolean levelCheck(int iter) { if (levelChecked >= iter) {return true ;} ; levelChecked = iter; LevelNode sub[] ; @@ -313,6 +322,7 @@ public final boolean levelCheck(int iter) { * toString, levelDataToString, and walkGraph methods to implement * ExploreNode interface */ + @Override public final String levelDataToString() { return "Level: " + this.getLevel() + "\n" + "LevelParameters: " + this.getLevelParams() + "\n" + @@ -321,6 +331,7 @@ public final boolean levelCheck(int iter) { "ArgLevelParams: " + this.getArgLevelParams() + "\n"; } + @Override public final String toString(int depth) { if (depth <= 0) return ""; String res = @@ -353,6 +364,7 @@ public final boolean levelCheck(int iter) { * The children are the statement and the proof (if there is one). */ + @Override public SemanticNode[] getChildren() { if (this.proof == null) { return new SemanticNode[] {this.theoremExprOrAssumeProve}; @@ -361,30 +373,90 @@ public final boolean levelCheck(int iter) { this.proof}; } - public final void walkGraph(Hashtable semNodesTable) { - Integer uid = new Integer(myUID); + @Override + public final void walkGraph(Hashtable<Integer, ExploreNode> semNodesTable, ExplorerVisitor visitor) { + Integer uid = Integer.valueOf(myUID); if (semNodesTable.get(uid) != null) return; semNodesTable.put(uid, this); + visitor.preVisit(this); if (theoremExprOrAssumeProve != null) - {theoremExprOrAssumeProve.walkGraph(semNodesTable);} ; - if (proof != null) {proof.walkGraph(semNodesTable);} ; + {theoremExprOrAssumeProve.walkGraph(semNodesTable, visitor);} ; + if (proof != null) {proof.walkGraph(semNodesTable, visitor);} ; + visitor.postVisit(this); } + /* MR: this does not do anything public Element export(Document doc, tla2sany.xml.SymbolContext context) { - if (getDef() == null) - // we export the definition of the theorem - return super.export(doc,context); - else - // we export its name only, named theorem will be exported through the ThmOrAss.. - return getDef().export(doc,context); + Element e = super.export(doc, context); + return e; + } + */ + + /* MR: This is the same as SymbolNode.exportDefinition. Exports the actual theorem content, not only a reference. + */ + public Element exportDefinition(Document doc, SymbolContext context) { + //makes sure that the we are creating an entry in the database + if (!context.isTop_level_entry()) + throw new IllegalArgumentException("Exporting theorem ref "+getNodeRef()+" twice!"); + context.resetTop_level_entry(); + + try { + Element e = getLevelElement(doc, context); + // level + try { + Element l = appendText(doc,"level",Integer.toString(getLevel())); + e.insertBefore(l,e.getFirstChild()); + } catch (RuntimeException ee) { + // not sure it is legal for a LevelNode not to have level, debug it! + } + //location + try { + Element loc = getLocationElement(doc); + e.insertBefore(loc,e.getFirstChild()); + } catch (RuntimeException ee) { + // do nothing if no location + } + return e; + } catch (RuntimeException ee) { + System.err.println("failed for node.toString(): " + toString() + "\n with error "); + ee.printStackTrace(); + throw ee; + } + } + + protected String getNodeRef() { + return "TheoremNodeRef"; } protected Element getLevelElement(Document doc, tla2sany.xml.SymbolContext context) { Element e = doc.createElement("TheoremNode"); - e.appendChild(getTheorem().export(doc,context)); + + //the theorem name is now contained in the definition, if it exists + Node n = doc.createElement("body"); + if (def != null) { + //if there is a definition, export it too + Node d = doc.createElement("definition"); + d.appendChild(def.export(doc, context)); + e.appendChild(d); + assert( def.getBody() == getTheorem() ); //make sure theorem and definition body agree before export + } + + n.appendChild(getTheorem().export(doc,context)); + e.appendChild( n ); + if (getProof() != null) e.appendChild(getProof().export(doc,context)); if (isSuffices()) e.appendChild(doc.createElement("suffices")); return e; } + + /* overrides LevelNode.export and exports a UID reference instad of the full version*/ + @Override + public Element export(Document doc, SymbolContext context) { + // first add symbol to context + context.put(this, doc); + Element e = doc.createElement(getNodeRef()); + e.appendChild(appendText(doc,"UID",Integer.toString(myUID))); + return e; + } } diff --git a/tlatools/src/tla2sany/semantic/ThmOrAssumpDefNode.java b/tlatools/src/tla2sany/semantic/ThmOrAssumpDefNode.java index 1079a1216b60aba137083fd0ff2b73daa60b6ab4..3e64c84fc28e76e9371ddc7ff9b9e6a8fa8e0bd0 100644 --- a/tlatools/src/tla2sany/semantic/ThmOrAssumpDefNode.java +++ b/tlatools/src/tla2sany/semantic/ThmOrAssumpDefNode.java @@ -1,647 +1,676 @@ -// Copyright (c) 2007 Microsoft Corporation. All rights reserved. - -// last modified on Thu 2 July 2009 at 15:44:33 PST by lamport -/*************************************************************************** -* The ThmOrAssumpDefNode constructor is invoked in * -* semantic/Generator.java to construct the nodes corresponding to the * -* definition in something like * -* * -* THEOREM Foo == ... * -* * -* The following are the only public methods that a tool is likely to * -* call. (Search for the method to find an explanation of what it does.) * -* * -* LevelNode getBody() * -* ModuleNode getOriginallyDefinedInModuleNode() * -* boolean isTheorem() * -* boolean isSuffices() * -* ProofNode getProof() * -* FormalParamNode[] getParams() * -* ModuleNode getInstantiatedFrom() * -***************************************************************************/ - -package tla2sany.semantic; - -import java.util.Enumeration; -import java.util.HashSet; -import java.util.Hashtable; -import java.util.Iterator; - -import tla2sany.st.TreeNode; -import tla2sany.utilities.Strings; -import tla2sany.utilities.Vector; -import util.UniqueString; -import util.WrongInvocationException; - -import org.w3c.dom.Document; -import org.w3c.dom.Element; - -/*************************************************************************** -* This node represents the definition of Foo in * -* * -* THEOREM Foo == ... * -* ASSUME Foo == ... * -***************************************************************************/ - -public class ThmOrAssumpDefNode extends SymbolNode - implements OpDefOrLabelNode, AnyDefNode { - - // T.L. 2014 - the actual theorem or assumption associated with this def - // this is required in order to simplify presentation. After all normalizations - // we dont really need this def any more - // this field is being set by TheoremNode or AssumeNode - protected LevelNode thmOrAssump = null; - private LevelNode body = null; - private ModuleNode originallyDefinedInModule = null; - private boolean theorem = true; - /*********************************************************************** - * True if a theorem, false if an assumption. * - ***********************************************************************/ - private boolean suffices = false ; - /*********************************************************************** - * If this represents a theorem, then this is the value of the * - * TheoremNode's suffices field (which is false unless the TheoremNode * - * represents a SUFFICES proof step). This is a kludge added for the * - * following reason. To check if a reference to a subexpression of an * - * ASSUME/PROVE is legal, we need to know whether or not the * - * ASSUME/PROVE lies within a SUFFICES step. However, the reference * - * accesses the ThmOrAssumpDefNode, while it's the TheoremNode that * - * contains the suffices field. It might seem sensible to have the * - * ThmOrAssumpDefNode contain a pointer to the TheoremNode. However, * - * that's problematic. For one thing, instantiation creates a new * - * ThmOrAssumpDefNode for the imported definition, but does not create * - * a new TheoremNode. So, we copy just the suffices field (which will * - * be false for an instantiated node because proof steps don't get * - * instantiated). * - * * - * This field is set by setSuffices() and read by isSuffices() ; * - ***********************************************************************/ - - - /************************************************************************* - * The following field is used to implement the OpDefOrLabel interface. * - * It is a hashtable of OpDefNode objects representing labels within * - * the body that are not within the scope of an inner label or LET * - * definition. * - *************************************************************************/ - private Hashtable labels = null ; - public Hashtable getLabelsHT() { - /*********************************************************************** - * Return the labels field. Used to "clone" an OpDefNode for module * - * instantiation. * - ***********************************************************************/ - return labels ; - } - - public int arity = 0 ; - private FormalParamNode[] params = new FormalParamNode[0]; - /*********************************************************************** - * A theorem or assumption definition like * - * * - * THEOREM foo == ... * - * * - * has no parameters. However, it can acquire parameters when * - * the module it's in is instantiated. * - ***********************************************************************/ - ProofNode proof; - /********************************************************************** - * The proof, if there is one; else null. Obviously, only a theorem * - * can have a proof. * - **********************************************************************/ - - private ModuleNode instantiatedFrom = null ; - /*********************************************************************** - * If the theorem or assumption was obtained by instantiation, then * - * this is the module from which it was instantiated. * - ***********************************************************************/ - - /** - * The source field and its methods was added by LL on 15 June 2010. - * I don't know why it wasn't added to this class when it was added - * to OpDefNode. However, it's needed by the Toolbox, so it's being - * added now by trying to duplicate what was done for OpDefNode - */ - private ThmOrAssumpDefNode source = null; - - public ThmOrAssumpDefNode getSource() { - if (source == null) { - return this; - } - return source; - } - - /************************************************************************* - * The Constructor. * - *************************************************************************/ - public ThmOrAssumpDefNode( - UniqueString name, // The thm/assump name. - boolean thm, // true if a theorem, false if an assump. - LevelNode exp, // The body - ModuleNode oModNode, // Name of module. - SymbolTable st, // The current value of - // Generator.symbolTable - TreeNode stn, // The syntax node. - FormalParamNode[] parms, // The parameters, or null if none. - ModuleNode iFrom, // The instantiatedFrom field. - ThmOrAssumpDefNode src // The source field. - ) { - super(ThmOrAssumpDefKind, stn, name) ; - this.theorem = thm; - this.body = exp; - this.originallyDefinedInModule = oModNode; - /********************************************************************* - * Note that when this theorem or assumption is instantiated with a * - * parameter substitution, then the newly created ThmOrAssumpDefNode * - * has this field set to the instantiating module. Thus, this field * - * is not useful to track down the module of origin of a theorem. * - *********************************************************************/ - // On 1 Nov 2012,, LL moved the following two statements to here from - // the end of the constructor. It's necessary for the source field to be - // set before addSymbol is called. - this.instantiatedFrom = iFrom ; - this.source = src; - - if (st != null) {st.addSymbol(name, this);} ; - /********************************************************************* - * By some magic, this eventually puts the name into the current * - * module's context. * - *********************************************************************/ - if (parms != null) { - this.params = parms; - this.arity = parms.length; - } ; - } - - /************************************************************************* - * A place-holder constructor and a method for setting the remaining * - * fields. It is used in Generator.processTheorem, where the object * - * needs to be created before its body is generated. This method is * - * not used when created a ThmOrAssumpDefNode by instantiation, so * - * getInstantitedFrom will be null for this object. * - *************************************************************************/ - public ThmOrAssumpDefNode(UniqueString name, TreeNode stn) { - super(ThmOrAssumpDefKind, stn, name) ; - } - - public void construct( - boolean thm, // true if a theorem, false if an assump. - LevelNode exp, // The body - ModuleNode oModNode, // Name of module. - SymbolTable st, // The current value of - // Generator.symbolTable - FormalParamNode[] parms // The parameters, or null if none. - ) { - this.theorem = thm; - this.body = exp; - this.originallyDefinedInModule = oModNode; - if (st != null) {st.addSymbol(name, this);} ; - /********************************************************************* - * By some magic, this eventually puts the name into the current * - * module's context. * - *********************************************************************/ - if (parms != null) { - this.params = parms; - this.arity = parms.length; - } ; - } - - /************************************************************************* - * The methods. * - *************************************************************************/ - public LevelNode getBody() {return this.body; } ; - /*********************************************************************** - * Note: this is a LevelNode rather than an ExprNode because it could * - * be either an ExprNode, AssumeProve node, or an APSubstInNode whose * - * body is either an ExprNode or an AssumeProve node. To check if a * - * node N is an ExprNode, use the test * - * * - * N instanceof ExprNode * - ***********************************************************************/ - - public ModuleNode getOriginallyDefinedInModuleNode() - /*********************************************************************** - * This is the module for which the node was created, which for a * - * theorem or assumption instantiated with substitution is not the * - * module from which the theorem or assumption came. * - ***********************************************************************/ - { return originallyDefinedInModule; } - - public ModuleNode getInstantiatedFrom() { return this.instantiatedFrom ; } - /*********************************************************************** - * If the theorem or assumption was obtained by instantiation, then * - * this is the module from which it was instantiated. Otherwise, it * - * is null. * - ***********************************************************************/ - - public boolean isTheorem() {return this.theorem ;} - public boolean isSuffices() {return this.suffices ;}; - void setSuffices() {this.suffices = true;}; - - /************************************************************************* - * Return the proof of the theorem, which is null unless this is a * - * theorem and it has a proof. * - *************************************************************************/ - public final ProofNode getProof() {return this.proof;} - - public final FormalParamNode[] getParams() { return this.params; } - - public final boolean isExpr() { return this.body instanceof ExprNode; } - /*********************************************************************** - * Returns true iff the body is an ExprNode (rather than an * - * AssumeProveNode or APSubstInNode). * - ***********************************************************************/ - - - /************************************************************************* - * Implementations of the abstract methods of the SymbolNode superclass. * - *************************************************************************/ - public final int getArity() { return this.arity;} - /*********************************************************************** - * The name of a theorem or assumption has no parameters. * - ***********************************************************************/ - - // Modified by LL on 30 Oct 2012 to handle locally instantiated theorems - // and assumptions. - private boolean local = false ; - public final boolean isLocal() { return local; } - /*********************************************************************** - * Theorem and assumption definitions are local iff imported with a * - * LOCAL instance. * - ***********************************************************************/ - public final void setLocal(boolean localness) { - local = localness ; - } - -// public final ModuleNode getModuleNode() { return this.moduleNode; } - - public final boolean match( OpApplNode test, ModuleNode mn ) { - /*********************************************************************** - * True iff the current object has the same arity as the node operator * - * of the OpApplNode test. * - ***********************************************************************/ - SymbolNode odn = test.getOperator(); - return odn.getArity() == 0; - } - - - /************************************************************************* - * The following methods implement the OpDefOrLabel interface. * - * * - * These are the same as the other classes that implement the interface. * - * There doesn't seem to be any easy way to write these methods only * - * once. * - *************************************************************************/ - public void setLabels(Hashtable ht) {labels = ht; } - /*********************************************************************** - * Sets the set of labels. * - ***********************************************************************/ - - public LabelNode getLabel(UniqueString us) { - /*********************************************************************** - * If the hashtable `labels' contains a LabelNode with name `us', * - * then that LabelNode is returned; otherwise null is returned. * - ***********************************************************************/ - if (labels == null) {return null;} ; - return (LabelNode) labels.get(us) ; - } - - public boolean addLabel(LabelNode odn) { - /*********************************************************************** - * If the hashtable `labels' contains no OpDefNode with the same name * - * as odn, then odn is added to the set and true is return; else the * - * set is unchanged and false is returned. * - ***********************************************************************/ - if (labels == null) {labels = new Hashtable(); } ; - if (labels.containsKey(odn)) {return false ;} ; - labels.put(odn.getName(), odn) ; - return true; - } - - public LabelNode[] getLabels() { - /*********************************************************************** - * Returns an array containing the Label objects in the hashtable * - * `labels'. * - ***********************************************************************/ - if (labels == null) {return new LabelNode[0];} ; - Vector v = new Vector() ; - Enumeration e = labels.elements() ; - while (e.hasMoreElements()) { v.addElement(e.nextElement()); } ; - LabelNode[] retVal = new LabelNode[v.size()] ; - for (int i = 0 ; i < v.size() ; i++) - {retVal[i] = (LabelNode) v.elementAt(i); } ; - return retVal ; - } - -// On 24 Oct 2012 replaced the following levelCheck method with the current -// one, which was obtained by cloning the method from OpDefNode with some -// simplifications that were possible because a theorem or assumption, unlike -// and ordinary definition, cannot appear in the scope of a RECURSIVE declaration. -// He also made the ThmOrAssumpDefNode implement the AnyNode interface. -// See the comments in AnyDefNode.java for an explanation of why. - -// /************************************************************************* -// * Level checking. * -// *************************************************************************/ -// public boolean levelCheck(int iter) { -// if (levelChecked >= iter) {return levelCorrect;} ; -// levelChecked = iter ; -// levelCorrect = this.body.levelCheck(iter) ; -// level = this.body.level ; -// levelParams = this.body.levelParams ; -// allParams = this.body.allParams ; -// levelConstraints = this.body.levelConstraints ; -// argLevelConstraints = this.body.argLevelConstraints ; -// argLevelParams = this.body.argLevelParams ; -// return levelCorrect ; -// } -// -//// /************************************************************************* -//// * The implementation of the LevelNode abstract methods. They simply * -//// * return the corresponding values for the body. * -//// *************************************************************************/ -//// public final LevelNode getBody() {return this.body;} -//// public boolean levelCheck() {return this.body.levelCheck();} -//// public int getLevel() {return this.body.getLevel();} -//// public HashSet getLevelParams() {return this.body.getLevelParams();} -//// public SetOfLevelConstraints getLevelConstraints() -//// {return this.body.getLevelConstraints() ;} -//// public SetOfArgLevelConstraints getArgLevelConstraints() -//// {return this.body.getArgLevelConstraints() ; } -//// public HashSet getArgLevelParams() -//// {return this.body.getArgLevelParams() ; } - - /************************************************************************* - * The fields used for level checking. * - *************************************************************************/ -// These fields are now present in all LevelNode subclasses -// private boolean levelCorrect; -// private int level; -// private HashSet levelParams; -// private SetOfLevelConstraints levelConstraints; -// private SetOfArgLevelConstraints argLevelConstraints; -// private HashSet argLevelParams; - - - int[] maxLevels; - int[] weights; - int[][] minMaxLevel; - /*********************************************************************** - * According to LevelSpec.tla, if this is the OpDefNode for the * - * definition of op, then op.minMaxLevel[i][k] is the minimum value of * - * oparg.maxLevels[k] for the i-th argument of Op. Thus, * - * op.minMaxLevels[i] is a sequence whose length is the number of * - * arguments taken by the i-th argument of Op. (It equals 0 if the * - * i-th argument of Op is not an operator argument.) * - ***********************************************************************/ - - boolean[] isLeibnizArg; - boolean isLeibniz; - /*********************************************************************** - * If this is the OpDefNode for the definition of op, then * - * isLeibnizArg[i] is true iff the i-th argument of op is Leibniz, and * - * isLeibniz = \A i : isLeibnizArg[i] * - ***********************************************************************/ - public boolean[] getIsLeibnizArg() { - return isLeibnizArg; - } - public boolean getIsLeibniz() { - return isLeibniz; - } - private boolean[][][] opLevelCond; - /*********************************************************************** - * According to LevelSpec.tla, if thisnode defines op, * - * then op.opLevelCond[i][j][k] is true iff * - * the i-th argument of op is an operator argument opArg, and the * - * definition of op contains an expression in which the j-th formal * - * parameter of the definition of op appears within the k-th argument * - * of opArg. * - ***********************************************************************/ - public final boolean levelCheck(int itr) { - if (this.levelChecked >= itr) { return this.levelCorrect; } - this.levelChecked = itr ; - - /*********************************************************************** - * Initialize maxLevels to the least restrictive value and weights to 0.* - * Initialize isLeibniz and all isLeibniz[i] to true. * - ***********************************************************************/ - this.maxLevels = new int[this.params.length]; - this.weights = new int[this.params.length]; - for (int i = 0 ; i < this.params.length ; i++) { - this.maxLevels[i] = MaxLevel ; - this.weights[i] = 0 ; - this.isLeibniz = true; - this.isLeibnizArg = new boolean[this.params.length]; - this.isLeibnizArg[i] = true ; - } ; - - - // Level check the body: - this.levelCorrect = this.body.levelCheck(itr); - this.level = this.body.getLevel(); - - SetOfLevelConstraints lcSet = this.body.getLevelConstraints(); - for (int i = 0; i < this.params.length; i++) { - Object plevel = lcSet.get(params[i]); - if (plevel != null) { - this.maxLevels[i] = ((Integer)plevel).intValue(); - } - } - - for (int i = 0; i < this.params.length; i++) { - if (this.body.getLevelParams().contains(this.params[i])) { - this.weights[i] = this.weights[i]; - } - } - - this.minMaxLevel = new int[this.params.length][]; - SetOfArgLevelConstraints alcSet = this.body.getArgLevelConstraints(); - for (int i = 0; i < this.params.length; i++) { - int alen = this.params[i].getArity(); - this.minMaxLevel[i] = new int[alen]; - for (int j = 0; j < alen; j++) { - Object alevel = alcSet.get(new ParamAndPosition(this.params[i], j)); - if (alevel == null) { - this.minMaxLevel[i][j] = MinLevel; - } - else { - this.minMaxLevel[i][j] = ((Integer)alevel).intValue(); - } - } - } - - this.opLevelCond = new boolean[this.params.length][this.params.length][]; - HashSet alpSet = this.body.getArgLevelParams(); - for (int i = 0; i < this.params.length; i++) { - for (int j = 0; j < this.params.length; j++) { - this.opLevelCond[i][j] = new boolean[this.params[i].getArity()]; - for (int k = 0; k < this.params[i].getArity(); k++) { - ArgLevelParam alp = new ArgLevelParam(this.params[i], k, this.params[j]); - this.opLevelCond[i][j][k] = alpSet.contains(alp); - } - } - } - - this.levelParams.addAll(this.body.getLevelParams()); - this.allParams.addAll(this.body.getAllParams()); - this.nonLeibnizParams.addAll(this.body.getNonLeibnizParams()); - for (int i = 0; i < this.params.length; i++) { - this.levelParams.remove(this.params[i]); - this.allParams.remove(this.params[i]); - if (this.nonLeibnizParams.contains(this.params[i])) { - /******************************************************************* - * The i-th argument is non-Leibniz if this.params[i] is in the * - * body's nonLeibnizParams hashset (and hence now in this node's * - * nonLeibnizParams hashset. * - *******************************************************************/ - this.nonLeibnizParams.remove(this.params[i]) ; - this.isLeibnizArg[i] = false ; - this.isLeibniz = false ; - } ; - } - - this.levelConstraints = (SetOfLevelConstraints)lcSet.clone(); - for (int i = 0; i < this.params.length; i++) { - this.levelConstraints.remove(this.params[i]); - } - - this.argLevelConstraints = (SetOfArgLevelConstraints)alcSet.clone(); - for (int i = 0; i < this.params.length; i++) { - int alen = this.params[i].getArity(); - for (int j = 0; j < alen; j++) { - this.argLevelConstraints.remove(new ParamAndPosition(this.params[i], j)); - } - } - - Iterator iter = alpSet.iterator(); - while (iter.hasNext()) { - ArgLevelParam alp = (ArgLevelParam)iter.next(); - if (!alp.op.occur(this.params) || - !alp.param.occur(this.params)) { - this.argLevelParams.add(alp); - } - } - - return levelCorrect ; - } - - /*************************************************************************** - * The following Asserts can be removed after debugging. * - ***************************************************************************/ - public final int getMaxLevel(int i) { - if (this.levelChecked == 0) - {throw new WrongInvocationException("getMaxLevel called before levelCheck");}; - int idx = (this.getArity() == -1) ? 0 : i; - return this.maxLevels[idx]; - } - - public final int getWeight(int i) { - if (this.levelChecked == 0) - {throw new WrongInvocationException("getWeight called before levelCheck");}; - int idx = (this.getArity() == -1) ? 0 : i; - return this.weights[idx]; - } - - public final int getMinMaxLevel(int i, int j) { - if (this.levelChecked == 0) - {throw new WrongInvocationException("getMinMaxLevel called before levelCheck");}; - if (this.minMaxLevel == null) { - return ConstantLevel; - } - return this.minMaxLevel[i][j]; - } - - public final boolean getOpLevelCond(int i, int j, int k) { - if (this.levelChecked == 0) - {throw new WrongInvocationException("getOpLevelCond called before levelCheck");}; - if (this.opLevelCond == null) { - return false; - } - return this.opLevelCond[i][j][k]; - } - - /** - * toString, levelDataToString and walkGraph methods to implement - * ExploreNode interface - */ -// public final String levelDataToString() { -// return this.body.levelDataToString(); -// } - - - - /** - * The body is the node's only child. - */ - public SemanticNode[] getChildren() { - return new SemanticNode[] {this.body}; - } - - public final void walkGraph(Hashtable semNodesTable) { - Integer uid = new Integer(myUID); - if (semNodesTable.get(uid) != null) return; - semNodesTable.put(new Integer(myUID), this); - if(this.body != null) {this.body.walkGraph(semNodesTable) ;} ; - } - - public final String toString(int depth) { - if (depth <= 0) return ""; - String ret = - "\n*ThmOrAssumpDefNode: " + this.getName().toString() + - " " + super.toString(depth) + - " arity: " + this.arity + - " module: " + (originallyDefinedInModule != null - ? originallyDefinedInModule.getName().toString() - : "<null>" ) ; - if (instantiatedFrom != null) {ret += " instantiatedFrom: " + - instantiatedFrom.getName() ; } ; - if (params != null) { - String tempString = "\nFormal params: " + params.length; - for (int i = 0; i < params.length; i++) { - tempString += Strings.indent(2, ((params[i] != null) - ? params[i].toString(depth-1) - : "\nnull")); - } ; - ret += Strings.indent(2,tempString); - } ; - if (body != null) { - ret += Strings.indent(2, - "\nisTheorem(): " + theorem + - "\nBody:" + - Strings.indent(2, this.body.toString(depth-1)) + - "\nsuffices: " + this.isSuffices()); - } // if - /*********************************************************************** - * The following is the same for all classes that implement the * - * OpDefOrLabelNode interface. * - ***********************************************************************/ - if (labels != null) { - ret += "\n Labels: " ; - Enumeration list = labels.keys() ; - while (list.hasMoreElements()) { - ret += ((UniqueString) list.nextElement()).toString() + " " ; - } ; - } - else {ret += "\n Labels: null";}; - - return ret ; - } - - /** - * most of the information in this class is a duplication of the information - * in the TheoremNode. - * - * We care to export only the name of the theorem (CHECK what hppens when instantiated). - */ - protected String getNodeRef() { - if (theorem) - return "TheoremNodeRef"; - else - return "AssumeNodeRef"; - } - - protected Element getSymbolElement(Document doc, tla2sany.xml.SymbolContext context) { - // since this element doesnt seem to contain any additional information - // over theorems or assumptions, we just refer to them - return thmOrAssump.getLevelElement(doc,context); - } -} +// Copyright (c) 2007 Microsoft Corporation. All rights reserved. + +// last modified on Thu 2 July 2009 at 15:44:33 PST by lamport +/*************************************************************************** +* The ThmOrAssumpDefNode constructor is invoked in * +* semantic/Generator.java to construct the nodes corresponding to the * +* definition in something like * +* * +* THEOREM Foo == ... * +* * +* The following are the only public methods that a tool is likely to * +* call. (Search for the method to find an explanation of what it does.) * +* * +* LevelNode getBody() * +* ModuleNode getOriginallyDefinedInModuleNode() * +* boolean isTheorem() * +* boolean isSuffices() * +* ProofNode getProof() * +* FormalParamNode[] getParams() * +* ModuleNode getInstantiatedFrom() * +***************************************************************************/ + +package tla2sany.semantic; + +import java.util.Enumeration; +import java.util.HashSet; +import java.util.Hashtable; +import java.util.Iterator; + +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +import tla2sany.explorer.ExploreNode; +import tla2sany.explorer.ExplorerVisitor; +import tla2sany.st.TreeNode; +import tla2sany.utilities.Strings; +import tla2sany.utilities.Vector; +import tla2sany.xml.SymbolContext; +import util.UniqueString; +import util.WrongInvocationException; + +/*************************************************************************** +* This node represents the definition of Foo in * +* * +* THEOREM Foo == ... * +* ASSUME Foo == ... * +***************************************************************************/ + +public class ThmOrAssumpDefNode extends SymbolNode + implements OpDefOrLabelNode, AnyDefNode { + + // T.L. 2014 - the actual theorem or assumption associated with this def + // this is required in order to simplify presentation. After all normalizations + // we dont really need this def any more + // this field is being set by TheoremNode or AssumeNode + protected LevelNode thmOrAssump = null; + private LevelNode body = null; + private ModuleNode originallyDefinedInModule = null; + private boolean theorem = true; + /*********************************************************************** + * True if a theorem, false if an assumption. * + ***********************************************************************/ + private boolean suffices = false ; + /*********************************************************************** + * If this represents a theorem, then this is the value of the * + * TheoremNode's suffices field (which is false unless the TheoremNode * + * represents a SUFFICES proof step). This is a kludge added for the * + * following reason. To check if a reference to a subexpression of an * + * ASSUME/PROVE is legal, we need to know whether or not the * + * ASSUME/PROVE lies within a SUFFICES step. However, the reference * + * accesses the ThmOrAssumpDefNode, while it's the TheoremNode that * + * contains the suffices field. It might seem sensible to have the * + * ThmOrAssumpDefNode contain a pointer to the TheoremNode. However, * + * that's problematic. For one thing, instantiation creates a new * + * ThmOrAssumpDefNode for the imported definition, but does not create * + * a new TheoremNode. So, we copy just the suffices field (which will * + * be false for an instantiated node because proof steps don't get * + * instantiated). * + * * + * This field is set by setSuffices() and read by isSuffices() ; * + ***********************************************************************/ + + + /************************************************************************* + * The following field is used to implement the OpDefOrLabel interface. * + * It is a hashtable of OpDefNode objects representing labels within * + * the body that are not within the scope of an inner label or LET * + * definition. * + *************************************************************************/ + private Hashtable<UniqueString, LabelNode> labels = null ; + public Hashtable<UniqueString, LabelNode> getLabelsHT() { + /*********************************************************************** + * Return the labels field. Used to "clone" an OpDefNode for module * + * instantiation. * + ***********************************************************************/ + return labels ; + } + + public int arity = 0 ; + private FormalParamNode[] params = new FormalParamNode[0]; + /*********************************************************************** + * A theorem or assumption definition like * + * * + * THEOREM foo == ... * + * * + * has no parameters. However, it can acquire parameters when * + * the module it's in is instantiated. * + ***********************************************************************/ + ProofNode proof; + /********************************************************************** + * The proof, if there is one; else null. Obviously, only a theorem * + * can have a proof. * + **********************************************************************/ + + private ModuleNode instantiatedFrom = null ; + /*********************************************************************** + * If the theorem or assumption was obtained by instantiation, then * + * this is the module from which it was instantiated. * + ***********************************************************************/ + + /** + * The source field and its methods was added by LL on 15 June 2010. + * I don't know why it wasn't added to this class when it was added + * to OpDefNode. However, it's needed by the Toolbox, so it's being + * added now by trying to duplicate what was done for OpDefNode + */ + private ThmOrAssumpDefNode source = null; + + public ThmOrAssumpDefNode getSource() { + if (source == null) { + return this; + } + return source; + } + + /************************************************************************* + * The Constructor. * + *************************************************************************/ + public ThmOrAssumpDefNode( + UniqueString name, // The thm/assump name. + boolean thm, // true if a theorem, false if an assump. + LevelNode exp, // The body + ModuleNode oModNode, // Name of module. + SymbolTable st, // The current value of + // Generator.symbolTable + TreeNode stn, // The syntax node. + FormalParamNode[] parms, // The parameters, or null if none. + ModuleNode iFrom, // The instantiatedFrom field. + ThmOrAssumpDefNode src // The source field. + ) { + super(ThmOrAssumpDefKind, stn, name) ; + this.theorem = thm; + this.body = exp; + this.originallyDefinedInModule = oModNode; + /********************************************************************* + * Note that when this theorem or assumption is instantiated with a * + * parameter substitution, then the newly created ThmOrAssumpDefNode * + * has this field set to the instantiating module. Thus, this field * + * is not useful to track down the module of origin of a theorem. * + *********************************************************************/ + // On 1 Nov 2012,, LL moved the following two statements to here from + // the end of the constructor. It's necessary for the source field to be + // set before addSymbol is called. + this.instantiatedFrom = iFrom ; + this.source = src; + + if (st != null) {st.addSymbol(name, this);} ; + /********************************************************************* + * By some magic, this eventually puts the name into the current * + * module's context. * + *********************************************************************/ + if (parms != null) { + this.params = parms; + this.arity = parms.length; + } ; + } + + /************************************************************************* + * A place-holder constructor and a method for setting the remaining * + * fields. It is used in Generator.processTheorem, where the object * + * needs to be created before its body is generated. This method is * + * not used when created a ThmOrAssumpDefNode by instantiation, so * + * getInstantitedFrom will be null for this object. * + *************************************************************************/ + public ThmOrAssumpDefNode(UniqueString name, TreeNode stn) { + super(ThmOrAssumpDefKind, stn, name) ; + } + + public void construct( + boolean thm, // true if a theorem, false if an assump. + LevelNode exp, // The body + ModuleNode oModNode, // Name of module. + SymbolTable st, // The current value of + // Generator.symbolTable + FormalParamNode[] parms // The parameters, or null if none. + ) { + this.theorem = thm; + this.body = exp; + this.originallyDefinedInModule = oModNode; + if (st != null) {st.addSymbol(name, this);} ; + /********************************************************************* + * By some magic, this eventually puts the name into the current * + * module's context. * + *********************************************************************/ + if (parms != null) { + this.params = parms; + this.arity = parms.length; + } ; + } + + /************************************************************************* + * The methods. * + *************************************************************************/ + public LevelNode getBody() {return this.body; } ; + /*********************************************************************** + * Note: this is a LevelNode rather than an ExprNode because it could * + * be either an ExprNode, AssumeProve node, or an APSubstInNode whose * + * body is either an ExprNode or an AssumeProve node. To check if a * + * node N is an ExprNode, use the test * + * * + * N instanceof ExprNode * + ***********************************************************************/ + + public ModuleNode getOriginallyDefinedInModuleNode() + /*********************************************************************** + * This is the module for which the node was created, which for a * + * theorem or assumption instantiated with substitution is not the * + * module from which the theorem or assumption came. * + ***********************************************************************/ + { return originallyDefinedInModule; } + + public ModuleNode getInstantiatedFrom() { return this.instantiatedFrom ; } + /*********************************************************************** + * If the theorem or assumption was obtained by instantiation, then * + * this is the module from which it was instantiated. Otherwise, it * + * is null. * + ***********************************************************************/ + + public boolean isTheorem() {return this.theorem ;} + public boolean isSuffices() {return this.suffices ;}; + void setSuffices() {this.suffices = true;}; + + /************************************************************************* + * Return the proof of the theorem, which is null unless this is a * + * theorem and it has a proof. * + *************************************************************************/ + public final ProofNode getProof() {return this.proof;} + + public final FormalParamNode[] getParams() { return this.params; } + + public final boolean isExpr() { return this.body instanceof ExprNode; } + /*********************************************************************** + * Returns true iff the body is an ExprNode (rather than an * + * AssumeProveNode or APSubstInNode). * + ***********************************************************************/ + + + /************************************************************************* + * Implementations of the abstract methods of the SymbolNode superclass. * + *************************************************************************/ + public final int getArity() { return this.arity;} + /*********************************************************************** + * The name of a theorem or assumption has no parameters. * + ***********************************************************************/ + + // Modified by LL on 30 Oct 2012 to handle locally instantiated theorems + // and assumptions. + private boolean local = false ; + public final boolean isLocal() { return local; } + /*********************************************************************** + * Theorem and assumption definitions are local iff imported with a * + * LOCAL instance. * + ***********************************************************************/ + public final void setLocal(boolean localness) { + local = localness ; + } + +// public final ModuleNode getModuleNode() { return this.moduleNode; } + + public final boolean match( OpApplNode test, ModuleNode mn ) { + /*********************************************************************** + * True iff the current object has the same arity as the node operator * + * of the OpApplNode test. * + ***********************************************************************/ + SymbolNode odn = test.getOperator(); + return odn.getArity() == 0; + } + + + /************************************************************************* + * The following methods implement the OpDefOrLabel interface. * + * * + * These are the same as the other classes that implement the interface. * + * There doesn't seem to be any easy way to write these methods only * + * once. * + *************************************************************************/ + public void setLabels(Hashtable<UniqueString, LabelNode> ht) {labels = ht; } + /*********************************************************************** + * Sets the set of labels. * + ***********************************************************************/ + + public LabelNode getLabel(UniqueString us) { + /*********************************************************************** + * If the hashtable `labels' contains a LabelNode with name `us', * + * then that LabelNode is returned; otherwise null is returned. * + ***********************************************************************/ + if (labels == null) {return null;} ; + return (LabelNode) labels.get(us) ; + } + + public boolean addLabel(LabelNode odn) { + /*********************************************************************** + * If the hashtable `labels' contains no OpDefNode with the same name * + * as odn, then odn is added to the set and true is return; else the * + * set is unchanged and false is returned. * + ***********************************************************************/ + if (labels == null) {labels = new Hashtable<>(); } ; + if (labels.containsKey(odn.getName())) {return false ;} ; + labels.put(odn.getName(), odn) ; + return true; + } + + public LabelNode[] getLabels() { + /*********************************************************************** + * Returns an array containing the Label objects in the hashtable * + * `labels'. * + ***********************************************************************/ + if (labels == null) {return new LabelNode[0];} ; + Vector<LabelNode> v = new Vector<>() ; + Enumeration<LabelNode> e = labels.elements() ; + while (e.hasMoreElements()) { v.addElement(e.nextElement()); } ; + LabelNode[] retVal = new LabelNode[v.size()] ; + for (int i = 0 ; i < v.size() ; i++) + {retVal[i] = v.elementAt(i); } ; + return retVal ; + } + +// On 24 Oct 2012 replaced the following levelCheck method with the current +// one, which was obtained by cloning the method from OpDefNode with some +// simplifications that were possible because a theorem or assumption, unlike +// and ordinary definition, cannot appear in the scope of a RECURSIVE declaration. +// He also made the ThmOrAssumpDefNode implement the AnyNode interface. +// See the comments in AnyDefNode.java for an explanation of why. + +// /************************************************************************* +// * Level checking. * +// *************************************************************************/ +// public boolean levelCheck(int iter) { +// if (levelChecked >= iter) {return levelCorrect;} ; +// levelChecked = iter ; +// levelCorrect = this.body.levelCheck(iter) ; +// level = this.body.level ; +// levelParams = this.body.levelParams ; +// allParams = this.body.allParams ; +// levelConstraints = this.body.levelConstraints ; +// argLevelConstraints = this.body.argLevelConstraints ; +// argLevelParams = this.body.argLevelParams ; +// return levelCorrect ; +// } +// +//// /************************************************************************* +//// * The implementation of the LevelNode abstract methods. They simply * +//// * return the corresponding values for the body. * +//// *************************************************************************/ +//// public final LevelNode getBody() {return this.body;} +//// public boolean levelCheck() {return this.body.levelCheck();} +//// public int getLevel() {return this.body.getLevel();} +//// public HashSet getLevelParams() {return this.body.getLevelParams();} +//// public SetOfLevelConstraints getLevelConstraints() +//// {return this.body.getLevelConstraints() ;} +//// public SetOfArgLevelConstraints getArgLevelConstraints() +//// {return this.body.getArgLevelConstraints() ; } +//// public HashSet getArgLevelParams() +//// {return this.body.getArgLevelParams() ; } + + /************************************************************************* + * The fields used for level checking. * + *************************************************************************/ +// These fields are now present in all LevelNode subclasses +// private boolean levelCorrect; +// private int level; +// private HashSet levelParams; +// private SetOfLevelConstraints levelConstraints; +// private SetOfArgLevelConstraints argLevelConstraints; +// private HashSet argLevelParams; + + + int[] maxLevels; + int[] weights; + int[][] minMaxLevel; + /*********************************************************************** + * According to LevelSpec.tla, if this is the OpDefNode for the * + * definition of op, then op.minMaxLevel[i][k] is the minimum value of * + * oparg.maxLevels[k] for the i-th argument of Op. Thus, * + * op.minMaxLevels[i] is a sequence whose length is the number of * + * arguments taken by the i-th argument of Op. (It equals 0 if the * + * i-th argument of Op is not an operator argument.) * + ***********************************************************************/ + + boolean[] isLeibnizArg; + boolean isLeibniz; + /*********************************************************************** + * If this is the OpDefNode for the definition of op, then * + * isLeibnizArg[i] is true iff the i-th argument of op is Leibniz, and * + * isLeibniz = \A i : isLeibnizArg[i] * + ***********************************************************************/ + public boolean[] getIsLeibnizArg() { + return isLeibnizArg; + } + public boolean getIsLeibniz() { + return isLeibniz; + } + private boolean[][][] opLevelCond; + /*********************************************************************** + * According to LevelSpec.tla, if thisnode defines op, * + * then op.opLevelCond[i][j][k] is true iff * + * the i-th argument of op is an operator argument opArg, and the * + * definition of op contains an expression in which the j-th formal * + * parameter of the definition of op appears within the k-th argument * + * of opArg. * + ***********************************************************************/ + @Override + public final boolean levelCheck(int itr) { + if (this.levelChecked >= itr) { return this.levelCorrect; } + this.levelChecked = itr ; + + /*********************************************************************** + * Initialize maxLevels to the least restrictive value and weights to 0.* + * Initialize isLeibniz and all isLeibniz[i] to true. * + ***********************************************************************/ + this.maxLevels = new int[this.params.length]; + this.weights = new int[this.params.length]; + for (int i = 0 ; i < this.params.length ; i++) { + this.maxLevels[i] = MaxLevel ; + this.weights[i] = 0 ; + this.isLeibniz = true; + this.isLeibnizArg = new boolean[this.params.length]; + this.isLeibnizArg[i] = true ; + } ; + + + // Level check the body: + this.levelCorrect = this.body.levelCheck(itr); + this.level = this.body.getLevel(); + + SetOfLevelConstraints lcSet = this.body.getLevelConstraints(); + for (int i = 0; i < this.params.length; i++) { + Object plevel = lcSet.get(params[i]); + if (plevel != null) { + this.maxLevels[i] = ((Integer)plevel).intValue(); + } + } + + for (int i = 0; i < this.params.length; i++) { + if (this.body.getLevelParams().contains(this.params[i])) { + this.weights[i] = this.weights[i]; + } + } + + this.minMaxLevel = new int[this.params.length][]; + SetOfArgLevelConstraints alcSet = this.body.getArgLevelConstraints(); + for (int i = 0; i < this.params.length; i++) { + int alen = this.params[i].getArity(); + this.minMaxLevel[i] = new int[alen]; + for (int j = 0; j < alen; j++) { + Object alevel = alcSet.get(new ParamAndPosition(this.params[i], j)); + if (alevel == null) { + this.minMaxLevel[i][j] = MinLevel; + } + else { + this.minMaxLevel[i][j] = ((Integer)alevel).intValue(); + } + } + } + + this.opLevelCond = new boolean[this.params.length][this.params.length][]; + HashSet alpSet = this.body.getArgLevelParams(); + for (int i = 0; i < this.params.length; i++) { + for (int j = 0; j < this.params.length; j++) { + this.opLevelCond[i][j] = new boolean[this.params[i].getArity()]; + for (int k = 0; k < this.params[i].getArity(); k++) { + ArgLevelParam alp = new ArgLevelParam(this.params[i], k, this.params[j]); + this.opLevelCond[i][j][k] = alpSet.contains(alp); + } + } + } + + this.levelParams.addAll(this.body.getLevelParams()); + this.allParams.addAll(this.body.getAllParams()); + this.nonLeibnizParams.addAll(this.body.getNonLeibnizParams()); + for (int i = 0; i < this.params.length; i++) { + this.levelParams.remove(this.params[i]); + this.allParams.remove(this.params[i]); + if (this.nonLeibnizParams.contains(this.params[i])) { + /******************************************************************* + * The i-th argument is non-Leibniz if this.params[i] is in the * + * body's nonLeibnizParams hashset (and hence now in this node's * + * nonLeibnizParams hashset. * + *******************************************************************/ + this.nonLeibnizParams.remove(this.params[i]) ; + this.isLeibnizArg[i] = false ; + this.isLeibniz = false ; + } ; + } + + this.levelConstraints = (SetOfLevelConstraints)lcSet.clone(); + for (int i = 0; i < this.params.length; i++) { + this.levelConstraints.remove(this.params[i]); + } + + this.argLevelConstraints = (SetOfArgLevelConstraints)alcSet.clone(); + for (int i = 0; i < this.params.length; i++) { + int alen = this.params[i].getArity(); + for (int j = 0; j < alen; j++) { + this.argLevelConstraints.remove(new ParamAndPosition(this.params[i], j)); + } + } + + Iterator iter = alpSet.iterator(); + while (iter.hasNext()) { + ArgLevelParam alp = (ArgLevelParam)iter.next(); + if (!alp.op.occur(this.params) || + !alp.param.occur(this.params)) { + this.argLevelParams.add(alp); + } + } + + return levelCorrect ; + } + + /*************************************************************************** + * The following Asserts can be removed after debugging. * + ***************************************************************************/ + public final int getMaxLevel(int i) { + if (this.levelChecked == 0) + {throw new WrongInvocationException("getMaxLevel called before levelCheck");}; + int idx = (this.getArity() == -1) ? 0 : i; + return this.maxLevels[idx]; + } + + public final int getWeight(int i) { + if (this.levelChecked == 0) + {throw new WrongInvocationException("getWeight called before levelCheck");}; + int idx = (this.getArity() == -1) ? 0 : i; + return this.weights[idx]; + } + + public final int getMinMaxLevel(int i, int j) { + if (this.levelChecked == 0) + {throw new WrongInvocationException("getMinMaxLevel called before levelCheck");}; + if (this.minMaxLevel == null) { + return ConstantLevel; + } + return this.minMaxLevel[i][j]; + } + + public final boolean getOpLevelCond(int i, int j, int k) { + if (this.levelChecked == 0) + {throw new WrongInvocationException("getOpLevelCond called before levelCheck");}; + if (this.opLevelCond == null) { + return false; + } + return this.opLevelCond[i][j][k]; + } + + /** + * toString, levelDataToString and walkGraph methods to implement + * ExploreNode interface + */ +// public final String levelDataToString() { +// return this.body.levelDataToString(); +// } + + + + /** + * The body is the node's only child. + */ + @Override + public SemanticNode[] getChildren() { + return new SemanticNode[] {this.body}; + } + + @Override + public final void walkGraph(Hashtable<Integer, ExploreNode> semNodesTable, ExplorerVisitor visitor) { + Integer uid = Integer.valueOf(myUID); + if (semNodesTable.get(uid) != null) return; + semNodesTable.put(uid, this); + visitor.preVisit(this); + if(this.body != null) {this.body.walkGraph(semNodesTable, visitor) ;} ; + visitor.postVisit(this); + } + + @Override + public final String toString(int depth) { + if (depth <= 0) return ""; + String ret = + "\n*ThmOrAssumpDefNode: " + this.getName().toString() + + " " + super.toString(depth) + + " arity: " + this.arity + + " module: " + (originallyDefinedInModule != null + ? originallyDefinedInModule.getName().toString() + : "<null>" ) ; + if (instantiatedFrom != null) {ret += " instantiatedFrom: " + + instantiatedFrom.getName() ; } ; + if (params != null) { + String tempString = "\nFormal params: " + params.length; + for (int i = 0; i < params.length; i++) { + tempString += Strings.indent(2, ((params[i] != null) + ? params[i].toString(depth-1) + : "\nnull")); + } ; + ret += Strings.indent(2,tempString); + } ; + if (body != null) { + ret += Strings.indent(2, + "\nisTheorem(): " + theorem + + "\nBody:" + + Strings.indent(2, this.body.toString(depth-1)) + + "\nsuffices: " + this.isSuffices()); + } // if + /*********************************************************************** + * The following is the same for all classes that implement the * + * OpDefOrLabelNode interface. * + ***********************************************************************/ + if (labels != null) { + ret += "\n Labels: " ; + Enumeration<UniqueString> list = labels.keys() ; + while (list.hasMoreElements()) { + ret += list.nextElement().toString() + " " ; + } ; + } + else {ret += "\n Labels: null";}; + + return ret ; + } + + /** + * + */ + protected String getNodeRef() { + if (theorem) { + assert(thmOrAssump instanceof TheoremNode); + return "TheoremDefRef"; + } + else { + assert(thmOrAssump instanceof AssumeNode); + return "AssumeDefRef"; + } + } + + protected Element getSymbolElement(Document doc, tla2sany.xml.SymbolContext context) { + assert(this.body != null); //A theorem or assumption definition without a body does not make sense. + Element e = null; + if (theorem) { + e = doc.createElement("TheoremDefNode"); + } + else { + e = doc.createElement("AssumeDef"); + } + + e.appendChild(appendText(doc, "uniquename", getName().toString() )); + e.appendChild(body.export(doc, context)); + return e; + } + + /* overrides LevelNode.export and exports a UID reference instad of the full version*/ + @Override + public Element export(Document doc, SymbolContext context) { + // first add symbol to context + context.put(this, doc); + Element e = doc.createElement(getNodeRef()); + e.appendChild(appendText(doc,"UID",Integer.toString(myUID))); + return e; + } +} diff --git a/tlatools/src/tla2sany/semantic/UseOrHideNode.java b/tlatools/src/tla2sany/semantic/UseOrHideNode.java index 7ed500c0b87ab41a9b85b2866ab65808e094ae02..1aaf34381d3942ed06a7cffeaa614b7f5c0331ec 100644 --- a/tlatools/src/tla2sany/semantic/UseOrHideNode.java +++ b/tlatools/src/tla2sany/semantic/UseOrHideNode.java @@ -1,183 +1,193 @@ -// Copyright (c) 2007 Microsoft Corporation. All rights reserved. -// last modified on Fri 3 July 2009 at 12:41:45 PST by lamport -package tla2sany.semantic; - -import java.util.Hashtable; - -import tla2sany.st.TreeNode; -import tla2sany.utilities.Strings; -import util.UniqueString; - -import org.w3c.dom.Document; -import org.w3c.dom.Element; - -/*************************************************************************** -* This class represents a USE or HIDE statement. It is of kind * -* UseKind or HideKind. * -***************************************************************************/ -public class UseOrHideNode extends LevelNode { - - /************************************************************************* - * The fields. * - * * - * A use or hide has the syntax USE/HIDE [facts] [DEF[S] defs]. The * - * following two fields are the semantic nodes for the facts and defs. * - *************************************************************************/ - public LevelNode[] facts = null ; - /*********************************************************************** - * For each i, facts[i] will be either an ExprNode, a ModuleNode, or * - * an OpDefNode of type ModuleInstanceKind (with no parameters). A * - * proof management tool will probably put restrictions on the class * - * of expressions that can be used as facts. * - * * - * 4 Mar 2009: implemented a restriction that arbitrary expressions * - * can't be used as facts. The only allowable expressions are the * - * names of theorems, assumptions, and steps. * - ***********************************************************************/ - public SymbolNode[] defs = null ; - /*********************************************************************** - * For each i, defs[i] should be a UserDefinedOpDefKind or * - * ModuleInstanceKind OpDefNode or a ThmOrAssumpDefNode * - ***********************************************************************/ - - public boolean isOnly ; - /*********************************************************************** - * True iff this node was formed from an "ONLY" step. This is * - * possible only if the node is of kind UseKind or if it was * - * temporarily constructed for making a LeafProofNode for a "BY ONLY" * - * proof. However, the "ONLY BY" construct might be disabled. * - ***********************************************************************/ - - /** - * If the UseOrHideNode is a proof step, this is the step number. It - * is made a UniqueString for consistency; there's no need to make - * comparison efficient. - * Added by LL on 6 June 2010. - */ - private UniqueString stepName = null; - - public void setStepName(UniqueString stepName) - { - this.stepName = stepName; - } - - /** - * @return the stepName - */ - public UniqueString getStepName() - { - return stepName; - } - /************************************************************************* - * The constructor. * - *************************************************************************/ - public UseOrHideNode(int kind, TreeNode stn, LevelNode[] theFacts, - SymbolNode[] theDefs, boolean only) { - super(kind, stn) ; - this.facts = theFacts ; - this.defs = theDefs ; - this.isOnly = only ; - } ; - - /************************************************************************* - * The following method was added 4 Mar 2009 to check the restriction * - * that only the names of facts (and of modules) can be used as facts in * - * a USE or HIDE. * - * * - * It was modified on 1 Jul 2009 to allow the use of expressions as * - * facts in a USE. * - *************************************************************************/ - public void factCheck() { - if (this.facts == null || this.getKind() == UseKind) { return; }; - for (int i = 0; i < this.facts.length; i++) { - if ( (this.facts[i].getKind() == OpApplKind) - && (((OpApplNode) this.facts[i]).operator.getKind() - != ThmOrAssumpDefKind)) { - errors.addError( - this.facts[i].stn.getLocation(), - "The only expression allowed as a fact in a HIDE " + - "is \nthe name of a theorem, assumption, or step."); - } ; - } // for - } - - public boolean levelCheck(int iter) { - /*********************************************************************** - * Level checking is performed by level-checking the facts. Since the * - * defs should be defined operators, they have already been level * - * checked. * - ***********************************************************************/ - if (this.levelChecked >= iter) return this.levelCorrect; - return this.levelCheckSubnodes(iter, facts) ; - } - - public void walkGraph(Hashtable semNodesTable) { - Integer uid = new Integer(myUID); - if (semNodesTable.get(uid) != null) return; - semNodesTable.put(new Integer(myUID), this); - for (int i = 0; i < facts.length; i++) { - facts[i].walkGraph(semNodesTable); - } ; - /*********************************************************************** - * Note: there's no need to walk the defs array because all the nodes * - * on it are walked from the nodes under which they appear. * - ***********************************************************************/ - } - - /* - * The children are the facts. - * @see tla2sany.semantic.SemanticNode#getChildren() - */ - public SemanticNode[] getChildren() { - if (this.facts == null || this.facts.length == 0) { - return null; - } - SemanticNode[] res = new SemanticNode[this.facts.length]; - for (int i = 0; i < facts.length; i++) { - res[i] = facts[i]; - } - return res; - } - - public String toString(int depth) { - if (depth <= 0) return ""; - String ret = "\n*UseOrHideNode:\n" - + super.toString(depth) - + Strings.indent(2, "\nisOnly: " + this.isOnly) - + Strings.indent(2, "\nfacts:") ; - for (int i = 0 ; i < this.facts.length; i++) { - ret += Strings.indent(4, this.facts[i].toString(1)) ; - } ; - ret += Strings.indent(2, "\ndefs:") ; - for (int i = 0 ; i < this.defs.length; i++) { - ret += Strings.indent(4, this.defs[i].toString(1)) ; - } ; - return ret; - } - - protected Element getLevelElement(Document doc, tla2sany.xml.SymbolContext context) { - //SemanticNode.SymbolContext context = new SemanticNode.SymbolContext(context2); - Element e = doc.createElement("UseOrHideNode"); - - Element factse = doc.createElement("facts"); - Element definitions = doc.createElement("defs"); - - for (int i=0; i<facts.length; i++) factse.appendChild(facts[i].export(doc,context)); - for (int i=0; i<defs.length; i++) definitions.appendChild(defs[i].export(doc,context)); - - e.appendChild(factse); - e.appendChild(definitions); - if(isOnly) e.appendChild(doc.createElement("only")); - if(getKind() == HideKind) e.appendChild(doc.createElement("hide")); - - -/* if (stepName != null) - e.setAttribute("step_name", stepName.toString()); -*/ - - // at the end, we append the context of the symbols used in this node - //e.appendChild(context.getContextElement(doc)); - - return e; - } -} +// Copyright (c) 2007 Microsoft Corporation. All rights reserved. +// last modified on Fri 3 July 2009 at 12:41:45 PST by lamport +package tla2sany.semantic; + +import java.util.Hashtable; + +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +import tla2sany.explorer.ExploreNode; +import tla2sany.explorer.ExplorerVisitor; +import tla2sany.st.TreeNode; +import tla2sany.utilities.Strings; +import tla2sany.xml.SymbolContext; +import util.UniqueString; + +/*************************************************************************** +* This class represents a USE or HIDE statement. It is of kind * +* UseKind or HideKind. * +***************************************************************************/ +public class UseOrHideNode extends LevelNode { + + /************************************************************************* + * The fields. * + * * + * A use or hide has the syntax USE/HIDE [facts] [DEF[S] defs]. The * + * following two fields are the semantic nodes for the facts and defs. * + *************************************************************************/ + public LevelNode[] facts = null ; + /*********************************************************************** + * For each i, facts[i] will be either an ExprNode, a ModuleNode, or * + * an OpDefNode of type ModuleInstanceKind (with no parameters). A * + * proof management tool will probably put restrictions on the class * + * of expressions that can be used as facts. * + * * + * 4 Mar 2009: implemented a restriction that arbitrary expressions * + * can't be used as facts. The only allowable expressions are the * + * names of theorems, assumptions, and steps. * + ***********************************************************************/ + public SymbolNode[] defs = null ; + /*********************************************************************** + * For each i, defs[i] should be a UserDefinedOpDefKind or * + * ModuleInstanceKind OpDefNode or a ThmOrAssumpDefNode * + ***********************************************************************/ + + public boolean isOnly ; + /*********************************************************************** + * True iff this node was formed from an "ONLY" step. This is * + * possible only if the node is of kind UseKind or if it was * + * temporarily constructed for making a LeafProofNode for a "BY ONLY" * + * proof. However, the "ONLY BY" construct might be disabled. * + ***********************************************************************/ + + /** + * If the UseOrHideNode is a proof step, this is the step number. It + * is made a UniqueString for consistency; there's no need to make + * comparison efficient. + * Added by LL on 6 June 2010. + */ + private UniqueString stepName = null; + + public void setStepName(UniqueString stepName) + { + this.stepName = stepName; + } + + /** + * @return the stepName + */ + public UniqueString getStepName() + { + return stepName; + } + /************************************************************************* + * The constructor. * + *************************************************************************/ + public UseOrHideNode(int kind, TreeNode stn, LevelNode[] theFacts, + SymbolNode[] theDefs, boolean only) { + super(kind, stn) ; + this.facts = theFacts ; + this.defs = theDefs ; + this.isOnly = only ; + } ; + + /************************************************************************* + * The following method was added 4 Mar 2009 to check the restriction * + * that only the names of facts (and of modules) can be used as facts in * + * a USE or HIDE. * + * * + * It was modified on 1 Jul 2009 to allow the use of expressions as * + * facts in a USE. * + *************************************************************************/ + public void factCheck() { + if (this.facts == null || this.getKind() == UseKind) { return; }; + for (int i = 0; i < this.facts.length; i++) { + if ( (this.facts[i].getKind() == OpApplKind) + && (((OpApplNode) this.facts[i]).operator.getKind() + != ThmOrAssumpDefKind)) { + errors.addError( + this.facts[i].stn.getLocation(), + "The only expression allowed as a fact in a HIDE " + + "is \nthe name of a theorem, assumption, or step."); + } ; + } // for + } + + @Override + public boolean levelCheck(int iter) { + /*********************************************************************** + * Level checking is performed by level-checking the facts. Since the * + * defs should be defined operators, they have already been level * + * checked. * + ***********************************************************************/ + if (this.levelChecked >= iter) return this.levelCorrect; + return this.levelCheckSubnodes(iter, facts) ; + } + + @Override + public void walkGraph(Hashtable<Integer, ExploreNode> semNodesTable, ExplorerVisitor visitor) { + Integer uid = Integer.valueOf(myUID); + if (semNodesTable.get(uid) != null) return; + semNodesTable.put(uid, this); + visitor.preVisit(this); + for (int i = 0; i < facts.length; i++) { + facts[i].walkGraph(semNodesTable, visitor); + } ; + /*********************************************************************** + * Note: there's no need to walk the defs array because all the nodes * + * on it are walked from the nodes under which they appear. * + ***********************************************************************/ + visitor.postVisit(this); + } + + /* + * The children are the facts. + * @see tla2sany.semantic.SemanticNode#getChildren() + */ + @Override + public SemanticNode[] getChildren() { + if (this.facts == null || this.facts.length == 0) { + return null; + } + SemanticNode[] res = new SemanticNode[this.facts.length]; + for (int i = 0; i < facts.length; i++) { + res[i] = facts[i]; + } + return res; + } + + @Override + public String toString(int depth) { + if (depth <= 0) return ""; + String ret = "\n*UseOrHideNode:\n" + + super.toString(depth) + + Strings.indent(2, "\nisOnly: " + this.isOnly) + + Strings.indent(2, "\nfacts:") ; + for (int i = 0 ; i < this.facts.length; i++) { + ret += Strings.indent(4, this.facts[i].toString(1)) ; + } ; + ret += Strings.indent(2, "\ndefs:") ; + for (int i = 0 ; i < this.defs.length; i++) { + ret += Strings.indent(4, this.defs[i].toString(1)) ; + } ; + return ret; + } + + @Override + protected Element getLevelElement(Document doc, SymbolContext context) { + //SemanticNode.SymbolContext context = new SemanticNode.SymbolContext(context2); + Element e = doc.createElement("UseOrHideNode"); + + Element factse = doc.createElement("facts"); + Element definitions = doc.createElement("defs"); + + for (int i=0; i<facts.length; i++) factse.appendChild(facts[i].export(doc,context)); + for (int i=0; i<defs.length; i++) definitions.appendChild(defs[i].export(doc,context)); + + e.appendChild(factse); + e.appendChild(definitions); + if(isOnly) e.appendChild(doc.createElement("only")); + if(getKind() == HideKind) e.appendChild(doc.createElement("hide")); + + +/* if (stepName != null) + e.setAttribute("step_name", stepName.toString()); +*/ + + // at the end, we append the context of the symbols used in this node + //e.appendChild(context.getContextElement(doc)); + + return e; + } +} diff --git a/tlatools/src/tla2sany/st/Location.java b/tlatools/src/tla2sany/st/Location.java index beb23da1221988e279612e478a6a02565447798c..a56c755a474eb05cda2bc31fcdf41bffc2ebd419 100644 --- a/tlatools/src/tla2sany/st/Location.java +++ b/tlatools/src/tla2sany/st/Location.java @@ -8,9 +8,8 @@ import java.util.regex.Pattern; import pcal.PCalLocation; import pcal.Region; - -import tlc2.output.EC; import util.Assert; +import util.TLAConstants; import util.UniqueString; /** @@ -21,10 +20,10 @@ import util.UniqueString; * @author Leslie Lamport, Simon Zambrovski * @version $Id$ */ -public final class Location +public final class Location implements Comparable<Location> { // strings used in toString() and Regex - private static final String LINE = "line "; + private static final String LINE = TLAConstants.LINE; private static final String LINE_CAP = "Line "; private static final String TO_LINE = " to line "; private static final String COL = ", col "; @@ -36,7 +35,7 @@ public final class Location private static final String NATURAL = "([0-9]+)"; private static final String MODULE_ID = "([A-Za-z_0-9]+)"; private static final String CLOSE_ACTION = ">"; - private static final String OPEN_ACTION = "<Action "; + private static final String OPEN_ACTION = "<[A-Za-z_0-9]+ "; // The regex used to just be "Action" but the most recent TLC prints the name of the action instead of just the location. private static final UniqueString unknown = UniqueString.uniqueStringOf("--unknown--"); @@ -94,7 +93,12 @@ public final class Location protected UniqueString name; protected int bLine, bColumn, eLine, eColumn; - /** + public Location(final String fName, final String bl, final String bc, final String el, final String ec) { + this(UniqueString.uniqueStringOf(fName), Integer.valueOf(bl), Integer.valueOf(bc), Integer.valueOf(el), + Integer.valueOf(ec)); + } + + /** * Constructs a location * @param fName name of the source * @param bl begin line @@ -218,6 +222,11 @@ public final class Location } } + public static Location parseCoordinates(final String source, final String coordinates) { + final String[] c = coordinates.split(" "); + return new Location(source, c[0], c[1], c[2], c[3]); + } + /** * Returns an array of {@link Location} that can be parsed * from the input. More precisely, this method returns all @@ -295,6 +304,10 @@ public final class Location return this.eLine; } + public final int[] getCoordinates() { + return new int[] { bLine, bColumn, eLine, eColumn }; + } + /** * gets the end column number of this location. */ @@ -308,7 +321,7 @@ public final class Location */ public final String source() { - return name.toString(); + return name != null ? name.toString() : null; } /** @@ -357,6 +370,21 @@ public final class Location return false; } + /* (non-Javadoc) + * @see java.lang.Object#hashCode() + */ + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + bColumn; + result = prime * result + bLine; + result = prime * result + eColumn; + result = prime * result + eLine; + result = prime * result + ((name == null) ? 0 : name.hashCode()); + return result; + } + /** * Translates the {@link Location} into a PCal {@link Region} adjusting the * 1-based offset to a 0-based one. @@ -367,4 +395,72 @@ public final class Location return new pcal.Region(new PCalLocation(bLine - 1, bColumn - 1), new PCalLocation(eLine - 1, eColumn - 1)); } + + /** + * @param line + * @return returns true if line is in [beginLine, endLine] + */ + public boolean containsLine(final int line) { + return ((line >= bLine) && (line <= eLine)); + } + + @Override + public int compareTo(final Location other) { + if (this.equals(other)) { + return 0; + } + if (this.name.compareTo(other.name) != 0) { + return this.name.compareTo(other.name); + } + + if (this.bLine > other.bLine) { + return 1; + } else if (this.bLine < other.bLine) { + return -1; + } + + if (this.bColumn > other.bColumn) { + return 1; + } else if (this.bColumn < other.bColumn) { + return -1; + } + + if (this.eLine > other.eLine) { + return 1; + } else if (this.eLine < other.eLine) { + return -1; + } + + if (this.eColumn < other.eColumn) { + return -1; + } + return 1; + } + + /** + * @return true if other is a location within this location (same module file + * and the range of chars is within this range of chars). + */ + public boolean includes(final Location other) { + if (this.name != other.name) { + return false; + } + if (this.bLine > other.bLine) { + return false; + } + if (this.eLine < other.eLine) { + return false; + } + if (this.bColumn > other.bColumn) { + return false; + } + if (this.eColumn < other.eColumn) { + return false; + } + return true; + } + + public String linesAndColumns() { + return toString().replaceAll(OF_MODULE + ".*", ""); + } } diff --git a/tlatools/src/tla2sany/st/TreeNode.java b/tlatools/src/tla2sany/st/TreeNode.java index 7c7bb106ab5f1872619fced4462a2157563d6a3d..e80cc8c547ea2fdaaa8edc2702e06b57168ed117 100644 --- a/tlatools/src/tla2sany/st/TreeNode.java +++ b/tlatools/src/tla2sany/st/TreeNode.java @@ -20,4 +20,5 @@ public interface TreeNode { // public String[] getPostComments(); // always returns an array, never null public boolean local(); public void printST(int indentation); + public String getHumanReadableImage(); } diff --git a/tlatools/src/tla2sany/utilities/Vector.java b/tlatools/src/tla2sany/utilities/Vector.java index f22e094a477f634a421763319b4c0c72d2d9a8ab..21705a210ab01b86b225f8d3f642441d2e33c96d 100644 --- a/tlatools/src/tla2sany/utilities/Vector.java +++ b/tlatools/src/tla2sany/utilities/Vector.java @@ -2,7 +2,7 @@ package tla2sany.utilities; import java.util.Enumeration; -public class Vector { +public class Vector<E> { static int defaultSize = 10; protected Object info[]; @@ -38,7 +38,7 @@ public class Vector { } */ - public final void addElement( Object obj ) { + public final void addElement( E obj ) { if (size == capacity) { Object next[] = new Object[ capacity + increment ]; System.arraycopy( info, 0, next, 0, capacity ); @@ -49,19 +49,22 @@ public class Vector { size++; } - public final Object firstElement() { - return info[0]; + @SuppressWarnings("unchecked") + public final E firstElement() { + return (E)info[0]; } - public final Object lastElement() { - return info[ size-1 ]; + @SuppressWarnings("unchecked") + public final E lastElement() { + return (E)info[ size-1 ]; } - public final Object elementAt(int i) { + @SuppressWarnings("unchecked") + public final E elementAt(int i) { if (i < 0 || i >= size ) throw new ArrayIndexOutOfBoundsException(); else - return info[ i ]; + return (E)info[ i ]; } public final void removeAllElements() { @@ -82,7 +85,7 @@ public class Vector { } } - public final void insertElementAt( Object obj, int i ) { + public final void insertElementAt( E obj, int i ) { if (i < 0 || i >= size ) throw new ArrayIndexOutOfBoundsException(); else if (size == capacity) { @@ -100,25 +103,25 @@ public class Vector { size++; } - public final void setElementAt( Object obj, int i ) { + public final void setElementAt( E obj, int i ) { if (i < 0 || i >= size ) throw new ArrayIndexOutOfBoundsException(); else info[ i ] = obj; } - public final boolean contains (Object obj) { + public final boolean contains (E obj) { for (int i = 0; i < size; i++) { if ( info[ i ] == obj ) return true; } return false; } - public final Enumeration elements() { - return new VectorEnumeration( info, size ); + public final Enumeration<E> elements() { + return new VectorEnumeration<E>( info, size ); } - public final void append( Vector v ) { + public final void append( Vector<E> v ) { if ( v.size + size > capacity ) { Object neo[] = new Object[ capacity + v.capacity ]; capacity += v.capacity; @@ -132,12 +135,13 @@ public class Vector { // Like the append method above, but elements of v will not be added to THIS Vector // if they are already present at least once; repeated elements already in // THIS Vector, however, will not be removed. - public final void appendNoRepeats(Vector v) { + public final void appendNoRepeats(Vector<E> v) { for (int i = 0; i < v.size(); i++) { if ( ! this.contains(v.elementAt(i)) ) this.addElement(v.elementAt(i)); } } + @Override public final String toString() { String ret; ret = "[ "; diff --git a/tlatools/src/tla2sany/utilities/VectorEnumeration.java b/tlatools/src/tla2sany/utilities/VectorEnumeration.java index 2d3dbe35010cce11f6471d1155fd19069c8f8002..9ab1fe506cc05ac61276392df4f6fae3dd457890 100644 --- a/tlatools/src/tla2sany/utilities/VectorEnumeration.java +++ b/tlatools/src/tla2sany/utilities/VectorEnumeration.java @@ -2,7 +2,7 @@ package tla2sany.utilities; import java.util.Enumeration; -final class VectorEnumeration implements Enumeration { +final class VectorEnumeration<E> implements Enumeration<E> { int index = 0; Object data[]; @@ -15,9 +15,10 @@ final class VectorEnumeration implements Enumeration { return index < data.length; } - public final Object nextElement() { + @SuppressWarnings("unchecked") + public final E nextElement() { if (index < data.length) - return data[index++]; + return (E)data[index++]; else throw new java.util.NoSuchElementException(); } diff --git a/tlatools/src/tla2sany/xml/SymbolContext.java b/tlatools/src/tla2sany/xml/SymbolContext.java index ff58215c3d02003c932639f38717d7a4784dea53..c4100a4aca75d7dd705c861e728c1115e2b5e44f 100644 --- a/tlatools/src/tla2sany/xml/SymbolContext.java +++ b/tlatools/src/tla2sany/xml/SymbolContext.java @@ -1,9 +1,11 @@ package tla2sany.xml; -import tla2sany.semantic.SymbolNode; import org.w3c.dom.Document; import org.w3c.dom.Element; -import org.w3c.dom.Node; + +import tla2sany.semantic.AssumeNode; +import tla2sany.semantic.SymbolNode; +import tla2sany.semantic.TheoremNode; /* TL * This class is used to track the occurrence of SymbolNodes @@ -16,18 +18,21 @@ import org.w3c.dom.Node; public class SymbolContext { private java.util.Map<Integer,Element> context; private java.util.Set<Integer> keys; // we need this set since the generated element might spawn new keys + private boolean top_level_entry; // used to detect if a symbol is exported twice. + // only set in put() and reset in SymbolNode.getDefinitionElement // flags list public static final int OTHER_BUG = 0; // some semantic objects are represented using null. this flags array - // is used to tell nodes tio expect them so xml exporting will be done properly + // is used to tell nodes to expect them so xml exporting will be done properly private boolean[] flagArray; public SymbolContext() { context = new java.util.HashMap<Integer,Element>(); keys = new java.util.HashSet<Integer>(); flagArray = new boolean[1]; + top_level_entry = false; } // copy concstructor @@ -35,6 +40,7 @@ public class SymbolContext { context = other.context; keys = other.keys; flagArray = other.flagArray; + top_level_entry = other.top_level_entry; } public void setFlag(int flag) { @@ -50,6 +56,27 @@ public class SymbolContext { if (!keys.contains(k)) { // first add the key as it might be mentioned again inside the definition keys.add(k); + setTop_level_entry(); + context.put(k,nd.exportDefinition(doc,this)); + } + } + + public void put(TheoremNode nd, Document doc) { + Integer k = new Integer(nd.myUID); + if (!keys.contains(k)) { + // first add the key as it might be mentioned again inside the definition + keys.add(k); + setTop_level_entry(); + context.put(k,nd.exportDefinition(doc,this)); + } + } + + public void put(AssumeNode nd, Document doc) { + Integer k = new Integer(nd.myUID); + if (!keys.contains(k)) { + // first add the key as it might be mentioned again inside the definition + keys.add(k); + setTop_level_entry(); context.put(k,nd.exportDefinition(doc,this)); } } @@ -66,4 +93,13 @@ public class SymbolContext { } return ret; } + + public int getContextSize() { + return context.size(); + } + + public boolean isTop_level_entry() { return top_level_entry; } + public void setTop_level_entry() { top_level_entry = true; } + public void resetTop_level_entry() { top_level_entry = false; } + } diff --git a/tlatools/src/tla2sany/xml/XMLExportable.java b/tlatools/src/tla2sany/xml/XMLExportable.java index dfa93113d97fd4671ebff6cc25b222e2e4e4f69b..95bd1b99b6c65a3755291dd58f0c9cc3d5c951b2 100644 --- a/tlatools/src/tla2sany/xml/XMLExportable.java +++ b/tlatools/src/tla2sany/xml/XMLExportable.java @@ -2,12 +2,13 @@ package tla2sany.xml; +import org.w3c.dom.Document; + /** * an interface for being able to export XML content */ import org.w3c.dom.Element; -import org.w3c.dom.Document; public interface XMLExportable { diff --git a/tlatools/src/tla2sany/xml/XMLExporter.java b/tlatools/src/tla2sany/xml/XMLExporter.java index 65b0bc6da0a8792dc918b85aa064a1ab23670412..7ab4275c31657024f600c117e3552a69748bbed8 100644 --- a/tlatools/src/tla2sany/xml/XMLExporter.java +++ b/tlatools/src/tla2sany/xml/XMLExporter.java @@ -3,48 +3,16 @@ package tla2sany.xml; +import java.io.ByteArrayOutputStream; + /** * a tool for exporting the loaded modules to XML format */ import java.io.PrintStream; -import java.io.ByteArrayOutputStream; -import java.util.Iterator; +import java.net.URL; import java.util.LinkedList; -import tla2sany.drivers.SANY; -import tla2sany.drivers.FrontEndException; -import tla2sany.configuration.Configuration; -import tla2sany.explorer.Explorer; -import tla2sany.explorer.ExplorerQuitException; -import tla2sany.modanalyzer.ParseUnit; -import tla2sany.modanalyzer.SpecObj; -import tla2sany.parser.ParseException; -import tla2sany.semantic.AbortException; -import tla2sany.semantic.BuiltInLevel; -import tla2sany.semantic.Context; -import tla2sany.semantic.Errors; -import tla2sany.semantic.ExternalModuleTable; -import tla2sany.semantic.Generator; -import tla2sany.semantic.ModuleNode; -import tla2sany.semantic.SemanticNode; -import tla2sany.st.Location; -import tla2sany.semantic.ModuleNode; -import util.FileUtil; -import util.ToolIO; -import util.UniqueString; -import util.FilenameToStream; -import util.SimpleFilenameToStream; -import tla2sany.semantic.SymbolNode; -import tla2sany.semantic.AssumeNode; -import tla2sany.semantic.TheoremNode; -import tla2sany.semantic.InstanceNode; -import tla2sany.semantic.OpDeclNode; -import tla2sany.semantic.OpDefNode; - - -// XML packages -import java.io.File; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; @@ -53,16 +21,23 @@ import javax.xml.transform.TransformerException; import javax.xml.transform.TransformerFactory; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; -import javax.xml.validation.SchemaFactory; import javax.xml.validation.Schema; +import javax.xml.validation.SchemaFactory; import javax.xml.validation.Validator; -import org.xml.sax.SAXException; -import java.net.URL; -import java.net.MalformedURLException; -import org.w3c.dom.Attr; import org.w3c.dom.Document; import org.w3c.dom.Element; +import org.xml.sax.SAXException; + +import tla2sany.drivers.FrontEndException; +import tla2sany.drivers.SANY; +import tla2sany.modanalyzer.SpecObj; +import tla2sany.semantic.ExternalModuleTable; +import tla2sany.semantic.ModuleNode; +import util.FileUtil; +import util.FilenameToStream; +import util.SimpleFilenameToStream; +import util.ToolIO; public class XMLExporter { @@ -86,25 +61,28 @@ public class XMLExporter { boolean offline_mode = false; int lastarg = -1; // lastarg will be incremented, initialize at -1 - for (int i = 0; i<args.length-1; i++) { - if ("-o".equals(args[i])) { - offline_mode = true; - lastarg = i; - } else if ("-I".equals(args[i])) { - i++; - if (i > args.length-2) throw new IllegalArgumentException("the -I flag must be followed by a directory and at least one .tla file"); - pathsLs.addLast(args[i]); - lastarg = i; - } + for (int i = 0; i < args.length - 1; i++) { + if ("-o".equals(args[i])) { + offline_mode = true; + lastarg = i; + } else if ("-I".equals(args[i])) { + i++; + if (i > args.length - 2) + throw new IllegalArgumentException("the -I flag must be followed by a directory and at least one .tla file"); + pathsLs.addLast(args[i]); + lastarg = i; + } } lastarg++; String[] paths = new String[pathsLs.size()]; - for (int i=0; i<paths.length; i++) paths[i] = (String)pathsLs.get(i); + for (int i = 0; i < paths.length; i++) paths[i] = (String) pathsLs.get(i); - String[] tlas = new String[args.length - lastarg]; - for (int i=0; i<args.length-lastarg; i++) tlas[i] = args[lastarg++]; + if (args.length - lastarg != 1) + throw new IllegalArgumentException("Only one TLA file to check allowed!"); + + String tla_name = args[lastarg++]; FilenameToStream fts = new SimpleFilenameToStream(paths); @@ -112,53 +90,40 @@ public class XMLExporter { PrintStream out = System.out; System.setOut(new PrintStream(new ByteArrayOutputStream())); - SpecObj[] specs = new SpecObj[tlas.length]; - for (int i=0; i<tlas.length; i++) specs[i] = new SpecObj(tlas[i], fts); - - // For each file named on the command line (i.e. in the tlas - // array) (re)initialize the entire system and process that file - // as the root of a new specification. - for (int i = 0; i < tlas.length; i++) { - // continue the loop where the last one left off - // Print documentation line on System.out - ToolIO.out.println("\n****** SANY2 " + SANY.version + "\n") ; - - // Get next file name from command line; then parse, - // semantically analyze, and level check the spec started in - // file Filename leaving the result (normally) in Specification - // spec. - // check if file exists - if (FileUtil.createNamedInputStream(tlas[i], specs[i].getResolver()) != null) - { - try { - SANY.frontEndMain(specs[i], tlas[i], System.err); - if (specs[i].getExternalModuleTable() == null) - throw new XMLExportingException("spec " + specs[i].getName() + " is malformed - does not have an external module table", null); - if (specs[i].getExternalModuleTable().getRootModule() == null) - throw new XMLExportingException("spec " + specs[i].getName() + " is malformed - does not have a root module", null); - } - catch (FrontEndException fe) { - // For debugging - fe.printStackTrace(); - ToolIO.out.println(fe); - return; - } - } else - { - ToolIO.out.println("Cannot find the specified file " + tlas[i] + "."); + SpecObj spec = new SpecObj(tla_name, fts); + + // Print documentation line on System.out + ToolIO.out.println("\n****** SANY2 " + SANY.version + "\n"); + + // Get next file name from command line; then parse, + // semantically analyze, and level check the spec started in + // file Filename leaving the result (normally) in Specification + // spec. + // check if file exists + //ToolIO.out.println("Processing: "+tlas[i]+"\n"+(tlas[i] == null)); + if (FileUtil.createNamedInputStream(tla_name, spec.getResolver()) != null) { + try { + SANY.frontEndMain(spec, tla_name, System.err); + if (spec.getExternalModuleTable() == null) + throw new XMLExportingException("spec " + spec.getName() + " is malformed - does not have an external module table", null); + if (spec.getExternalModuleTable().getRootModule() == null) + throw new XMLExportingException("spec " + spec.getName() + " is malformed - does not have a root module", null); + } catch (FrontEndException fe) { + // For debugging + fe.printStackTrace(); + ToolIO.out.println(fe); + return; } + } else { + ToolIO.out.println("Cannot find the specified file " + tla_name + "."); + return; } + try { DocumentBuilderFactory docFactory = - DocumentBuilderFactory.newInstance(); - /* validation is being done later - docFactory.setNamespaceAware(true); - docFactory.setValidating(true); - docFactory.setAttribute(JAXP_SCHEMA_LANGUAGE, W3C_XML_SCHEMA); - docFactory.setAttribute(JAXP_SCHEMA_SOURCE, TLA_SCHEMA); - */ + DocumentBuilderFactory.newInstance(); // write XML DocumentBuilder docBuilder = docFactory.newDocumentBuilder(); @@ -167,31 +132,44 @@ public class XMLExporter { Document doc = docBuilder.newDocument(); Element rootElement = doc.createElement("modules"); doc.appendChild(rootElement); + // Create symbol context. It will be filled by all symbol references during module export. SymbolContext context = new SymbolContext(); - for (int i=0; i<specs.length; i++) { - Element e = specs[i].getExternalModuleTable().getRootModule().exportDefinition(doc,context); - rootElement.appendChild(e); + + //Export all the external modules + ExternalModuleTable table = spec.getExternalModuleTable(); + //Element e = table.getRootModule().exportDefinition(doc,context); + //rootElement.appendChild(e); + ModuleNode[] externalModules = table.getModuleNodes(); + for (int j = 0; j < externalModules.length; j++) { + //Element ext_e = externalModules[j].exportDefinition(doc, context); + Element ext_e = externalModules[j].export(doc, context); + rootElement.appendChild(ext_e); } - // at the beginning, we append the context of the symbols used in this node + + // Insert the symbol table into the beginning of the XML DOM rootElement.insertBefore(context.getContextElement(doc), rootElement.getFirstChild()); + //Insert name of root module + insertRootName(doc, rootElement, spec); + + //Create XML file TransformerFactory transformerFactory = TransformerFactory.newInstance(); Transformer transformer = transformerFactory.newTransformer(); DOMSource source = new DOMSource(doc); // validate the file, do not fail if there is a URL connection error - if (! offline_mode) { //skip validation in offline mode - try { - SchemaFactory factory = SchemaFactory.newInstance(W3C_XML_SCHEMA); - Schema schema = factory.newSchema(new URL(TLA_SCHEMA)); - // create a Validator instance, which can be used to validate an instance document - Validator validator = schema.newValidator(); - //validate the DOM tree - validator.validate(source); - } catch (java.io.IOException ioe) { - // do nothing if there is no internet connection - // but fail for other errors - } + if (!offline_mode) { //skip validation in offline mode + try { + SchemaFactory factory = SchemaFactory.newInstance(W3C_XML_SCHEMA); + Schema schema = factory.newSchema(new URL(TLA_SCHEMA)); + // create a Validator instance, which can be used to validate an instance document + Validator validator = schema.newValidator(); + //validate the DOM tree + validator.validate(source); + } catch (java.io.IOException ioe) { + // do nothing if there is no internet connection + // but fail for other errors + } /*catch (org.xml.sax.SAXParseException spe) { // do nothing if there is no internet connection // but fail for other errors @@ -200,13 +178,19 @@ public class XMLExporter { StreamResult result = new StreamResult(out); transformer.transform(source, result); - } - catch (ParserConfigurationException pce) { + } catch (ParserConfigurationException pce) { throw new XMLExportingException("failed to write XML", pce); } catch (TransformerException tfe) { throw new XMLExportingException("failed to write XML", tfe); } catch (SAXException se) { throw new XMLExportingException("failed to validate XML", se); } + + } + + static void insertRootName(Document doc, Element rootElement, SpecObj spec) { + Element el = doc.createElement("RootModule"); + el.appendChild(doc.createTextNode(spec.getName())); + rootElement.insertBefore(el, rootElement.getFirstChild()); } } diff --git a/tlatools/src/tla2sany/xml/XMLExportingException.java b/tlatools/src/tla2sany/xml/XMLExportingException.java index f988a2db7b49462ca5de510d5a44a7747b5d724d..50521ff22ec8bb8932745d9abe7f764058b02a30 100644 --- a/tlatools/src/tla2sany/xml/XMLExportingException.java +++ b/tlatools/src/tla2sany/xml/XMLExportingException.java @@ -2,7 +2,8 @@ // Copyright (c) 2013 INRIA-MSR. All rights reserved. package tla2sany.xml; -import java.io.*; +import java.io.PrintWriter; +import java.io.StringWriter; /** * an exception for erroes in the exporter diff --git a/tlatools/src/tla2sany/xml/sany.xsd b/tlatools/src/tla2sany/xml/sany.xsd index d12701d1cb919105976f322f18d4cdb3df46ac94..6201603de0c611cf2ca2301f97aa0bdecdadafff 100644 --- a/tlatools/src/tla2sany/xml/sany.xsd +++ b/tlatools/src/tla2sany/xml/sany.xsd @@ -10,18 +10,22 @@ <xs:element name="modules"> <xs:complexType> <xs:sequence> - <xs:element ref="context"/> + <xs:element name="RootModule" type="xs:string"/> + <xs:element ref="context"/> <xs:sequence maxOccurs="unbounded"> - <xs:element ref="ModuleNode"/> + <xs:choice> + <xs:element ref="ModuleNode"/> + <xs:element ref="ModuleNodeRef"/> + </xs:choice> </xs:sequence> </xs:sequence> </xs:complexType> </xs:element> - + <!-- contextes and symbols references --> <!-- everything which has a uniquename is defined only once, here, and is referred by its name. this element resides within a certain scope and affects only this scope --> - <xs:element name="context"> + <xs:element name="context"> <xs:complexType> <xs:sequence minOccurs="0" maxOccurs="unbounded"> <xs:element name="entry"> @@ -35,6 +39,9 @@ <xs:group ref="OpDefNode"/> <xs:element ref="TheoremNode"/> <xs:element ref="AssumeNode"/> + <xs:element ref="APSubstInNode"/> + <xs:element ref="TheoremDefNode"/> + <xs:element ref="AssumeDef"/> </xs:choice> </xs:sequence> </xs:complexType> @@ -42,12 +49,14 @@ </xs:sequence> </xs:complexType> </xs:element> - + <xs:group name="OpDefNodeRef"> <xs:choice> <xs:element ref="ModuleInstanceKindRef"/> <xs:element ref="UserDefinedOpKindRef"/> <xs:element ref="BuiltInKindRef"/> + <xs:element ref="TheoremDefRef"/> + <xs:element ref="AssumeDefRef"/> </xs:choice> </xs:group> <xs:element name="ModuleInstanceKindRef"> @@ -71,7 +80,7 @@ </xs:sequence> </xs:complexType> </xs:element> - + <xs:element name="FormalParamNodeRef"> <xs:complexType> <xs:sequence> @@ -79,7 +88,7 @@ </xs:sequence> </xs:complexType> </xs:element> - + <xs:element name="ModuleNodeRef"> <xs:complexType> <xs:sequence> @@ -87,7 +96,7 @@ </xs:sequence> </xs:complexType> </xs:element> - + <xs:element name="TheoremNodeRef"> <xs:complexType> <xs:sequence> @@ -95,7 +104,15 @@ </xs:sequence> </xs:complexType> </xs:element> - + + <xs:element name="TheoremDefRef"> + <xs:complexType> + <xs:sequence> + <xs:element ref="UID"/> + </xs:sequence> + </xs:complexType> + </xs:element> + <xs:element name="AssumeNodeRef"> <xs:complexType> <xs:sequence> @@ -103,7 +120,15 @@ </xs:sequence> </xs:complexType> </xs:element> - + + <xs:element name="AssumeDefRef"> + <xs:complexType> + <xs:sequence> + <xs:element ref="UID"/> + </xs:sequence> + </xs:complexType> + </xs:element> + <xs:element name="OpDeclNodeRef"> <xs:complexType> <xs:sequence> @@ -111,7 +136,7 @@ </xs:sequence> </xs:complexType> </xs:element> - + <!-- A level node is a wrapper around all other nodes containing location and level information --> <xs:group name="node"> <xs:sequence> @@ -119,7 +144,7 @@ <xs:element minOccurs="0" ref="level"/> </xs:sequence> </xs:group> - + <!-- ExprNode is a grouping of the following nodes --> <xs:group name="ExprNode"> <xs:choice> @@ -131,9 +156,11 @@ <xs:element ref="OpApplNode"/> <xs:element ref="StringNode"/> <xs:element ref="SubstInNode"/> + <xs:element ref="TheoremDefRef"/> + <xs:element ref="AssumeDefRef"/> </xs:choice> </xs:group> - + <!-- Location --> <xs:element name="location"> <xs:complexType> @@ -163,7 +190,7 @@ <xs:element name="filename" type="xs:string"/> <xs:element name="begin" type="xs:integer"/> <xs:element name="end" type="xs:integer"/> - + <!-- Level --> <xs:element name="level"> <xs:simpleType> @@ -171,16 +198,16 @@ <xs:minInclusive value="0"/> <xs:maxInclusive value="3"/> <!-- - ActionLevel = 2 - ConstantLevel = 0 - TemporalLevel = 3 - VariableLevel = 1 + ConstantLevel = 0 (Constant) + VariableLevel = 1 (State) + ActionLevel = 2 (Transition) + TemporalLevel = 3 (Temporal) --> </xs:restriction> </xs:simpleType> </xs:element> - - + + <!-- Instantiations and substitutions --> <!-- Operator substitution? --> <!-- A list of substitutions and the expression they apply to --> @@ -198,28 +225,29 @@ <xs:element name="body"> <xs:complexType> <xs:choice> - <xs:element ref="APSubstInNode"/> - <xs:element ref="AssumeProveNode"/> - <xs:element ref="DefStepNode"/> <xs:group ref="ExprNode"/> - <xs:element ref="OpArgNode"/> - <xs:element ref="InstanceNode"/> - <xs:element ref="NewSymbNode"/> - <xs:group ref="ProofNode"/> - <xs:element ref="FormalParamNodeRef"/> - <xs:element ref="ModuleNodeRef"/> - <xs:element ref="OpDeclNodeRef"/> - <xs:group ref="OpDefNodeRef"/> - <xs:element ref="AssumeNodeRef"/> - <xs:element ref="TheoremNodeRef"/> - <xs:element ref="UseOrHideNode"/> + <xs:group ref="AssumeProveLike"/> </xs:choice> </xs:complexType> </xs:element> + <xs:element name="instFrom"> + <xs:complexType> + <xs:sequence> + <xs:element ref="ModuleNodeRef"/> + </xs:sequence> + </xs:complexType> + </xs:element> + <xs:element name="instTo"> + <xs:complexType> + <xs:sequence> + <xs:element ref="ModuleNodeRef"/> + </xs:sequence> + </xs:complexType> + </xs:element> </xs:sequence> </xs:complexType> </xs:element> - + <!-- Expr substitution --> <!-- A list of substitutions and the expression they apply to --> <xs:element name="SubstInNode"> @@ -233,28 +261,45 @@ </xs:sequence> </xs:complexType> </xs:element> - <xs:element name="body"> + <xs:element name="body"> <xs:complexType> <xs:sequence> <xs:group ref="ExprNode"/> </xs:sequence> </xs:complexType> </xs:element> + <xs:element name="instFrom"> + <xs:complexType> + <xs:sequence> + <xs:element ref="ModuleNodeRef"/> + </xs:sequence> + </xs:complexType> + </xs:element> + <xs:element name="instTo"> + <xs:complexType> + <xs:sequence> + <xs:element ref="ModuleNodeRef"/> + </xs:sequence> + </xs:complexType> + </xs:element> </xs:sequence> </xs:complexType> </xs:element> - + <!-- Module substitution --> <!-- A list of substitutions, the name of the instance and list of instance params --> <xs:element name="InstanceNode"> <xs:complexType> <xs:sequence> <xs:group ref="node"/> - <xs:element ref="uniquename"/> + <xs:sequence> + <xs:element minOccurs="0" maxOccurs="1" ref="uniquename"/> + </xs:sequence> + <xs:element name="module" type="xs:string"/> <xs:element name="substs"> <xs:complexType> <xs:sequence> - <xs:element maxOccurs="unbounded" ref="Subst"/> + <xs:element minOccurs="0" maxOccurs="unbounded" ref="Subst"/> </xs:sequence> </xs:complexType> </xs:element> @@ -268,7 +313,7 @@ </xs:sequence> </xs:complexType> </xs:element> - + <!-- Mapping from the OpDeclNode to either of the others --> <xs:element name="Subst"> <xs:complexType> @@ -281,33 +326,72 @@ </xs:sequence> </xs:complexType> </xs:element> - + <!-- This is a unique name in the hierarchy --> <xs:element name="uniquename" type="xs:string"/> - + <!-- This is a unique ID in the system --> <xs:element name="UID" type="xs:integer"/> - + <!-- Assumptions and Theorems --> <!-- An assumption named by name and described by ExprNode --> <xs:element name="AssumeNode"> <xs:complexType> <xs:sequence> <xs:group ref="node"/> - <xs:group ref="ExprNode"/> + <xs:element minOccurs="0" name="definition"> + <xs:complexType> + <xs:choice> + <xs:element ref="AssumeDefRef"/> + </xs:choice> + </xs:complexType> + </xs:element> + <xs:element name="body"> <!-- Invariant: definition body is assumption body, if definition exists --> + <xs:complexType> + <xs:choice> + <xs:group ref="ExprNode"/> + <xs:element ref="AssumeDef"/> + </xs:choice> + </xs:complexType> + </xs:element> + <!-- reference to AssumeDef included in ExprNode --> </xs:sequence> </xs:complexType> </xs:element> - - <!-- Similar to assumption but may contain a proof. It can also refer to a proof step --> - <xs:element name="TheoremNode"> + + <xs:element name="AssumeDef"> <xs:complexType> <xs:sequence> <xs:group ref="node"/> + <xs:element name="uniquename" type="xs:string"/> <xs:choice> <xs:group ref="ExprNode"/> - <xs:element ref="AssumeProveNode"/> + <xs:group ref="AssumeProveLike"/> </xs:choice> + </xs:sequence> + </xs:complexType> + </xs:element> + + <!-- Similar to assumption but may contain a proof. It can also refer to a proof step --> + <xs:element name="TheoremNode"> + <xs:complexType> + <xs:sequence> + <xs:group ref="node"/> + <xs:element minOccurs="0" name="definition"> + <xs:complexType> + <xs:choice> + <xs:element ref="TheoremDefRef"/> + </xs:choice> + </xs:complexType> + </xs:element> + <xs:element name="body"> <!-- Invariant: definition body is theorem body, if definition exists --> + <xs:complexType> + <xs:choice> + <xs:group ref="ExprNode"/> + <xs:group ref="AssumeProveLike"/> + </xs:choice> + </xs:complexType> + </xs:element> <xs:group minOccurs="0" ref="ProofNode"/> <xs:element minOccurs="0" ref="suffices"/> </xs:sequence> @@ -316,37 +400,57 @@ <xs:element name="suffices"> <xs:complexType/> </xs:element> - + + <xs:element name="TheoremDefNode"> + <xs:complexType> + <xs:sequence> + <xs:group ref="node"/> + <xs:element name="uniquename" type="xs:string"/> + <xs:choice> + <xs:group ref="ExprNode"/> + <xs:group ref="AssumeProveLike"/> + </xs:choice> + </xs:sequence> + </xs:complexType> + </xs:element> + <!-- A non empty list of assumptions and a prove expressions with possible suffices and boxed --> <xs:element name="AssumeProveNode"> <xs:complexType> <xs:sequence> - <xs:group ref="node"/> + <xs:group ref="node"/> <xs:element name="assumes"> <xs:complexType> <xs:choice maxOccurs="unbounded"> - <xs:element ref="AssumeProveNode"/> + <xs:group ref="AssumeProveLike"/> <xs:group ref="ExprNode"/> <xs:element ref="NewSymbNode"/> </xs:choice> </xs:complexType> - </xs:element> + </xs:element> <xs:element name="prove"> - <xs:complexType> + <xs:complexType> <xs:group ref="ExprNode"/> </xs:complexType> - </xs:element> + </xs:element> <xs:element minOccurs="0" ref="suffices"/> <xs:element minOccurs="0" ref="boxed"/> </xs:sequence> </xs:complexType> </xs:element> - + + <xs:group name="AssumeProveLike"> + <xs:choice> + <xs:element ref="AssumeProveNode"/> + <xs:element ref="APSubstInNode"/> + </xs:choice> + </xs:group> + <!-- Signals a boxed scope (not clear if it is implemented in SANY) --> <xs:element name="boxed"> <xs:complexType/> </xs:element> - + <!-- Represents a new declaration + possible domain --> <xs:element name="NewSymbNode"> <xs:complexType> @@ -357,8 +461,8 @@ </xs:sequence> </xs:complexType> </xs:element> - - + + <!-- Operator definitions --> <!-- OpDefNode is classified according to its kinds (is NumberedProofStepKind relevant, since it is never referenced in by or use?) --> <xs:group name="OpDefNode"> @@ -368,7 +472,7 @@ <xs:element ref="BuiltInKind"/> </xs:choice> </xs:group> - + <!-- Reprensets the name of an instantiated module --> <xs:element name="ModuleInstanceKind"> <xs:complexType> @@ -378,12 +482,12 @@ </xs:sequence> </xs:complexType> </xs:element> - + <!-- An operator and its definition, arity and list of arguments --> <xs:element name="UserDefinedOpKind"> <xs:complexType> - <xs:sequence> - <xs:group ref="node"/> + <xs:sequence> + <xs:group ref="node"/> <xs:element ref="uniquename"/> <xs:element ref="arity"/> <xs:element name="body"> @@ -402,13 +506,13 @@ </xs:sequence> </xs:complexType> </xs:element> - + <xs:element name="recursive"> <xs:complexType/> </xs:element> - + <xs:element name="arity" type="xs:integer"/> - + <xs:element name="leibnizparam"> <xs:complexType> <xs:sequence> @@ -417,16 +521,16 @@ </xs:sequence> </xs:complexType> </xs:element> - + <xs:element name="leibniz"> <xs:complexType/> </xs:element> - + <!-- This node represents all builtins, including quantifiers, set operatios, etc. --> <xs:element name="BuiltInKind"> <xs:complexType> <xs:sequence> - <xs:group ref="node"/> + <xs:group ref="node"/> <xs:element ref="uniquename"/> <xs:element ref="arity"/> <xs:element minOccurs="0" name="params"> @@ -444,12 +548,23 @@ <xs:complexType> <xs:sequence> <xs:group ref="node"/> - <xs:element ref="uniquename"/> - <xs:element ref="arity"/> + <xs:element name="argument"> + <xs:complexType> + <xs:choice> + <xs:element ref="FormalParamNodeRef"/> + <xs:element ref="ModuleNodeRef"/> + <xs:element ref="OpDeclNodeRef"/> + <xs:group ref="OpDefNodeRef"/> + <xs:element ref="TheoremNodeRef"/> + <xs:element ref="AssumeNodeRef"/> + <xs:element ref="APSubstInNode"/> + </xs:choice> + </xs:complexType> + </xs:element> </xs:sequence> </xs:complexType> </xs:element> - + <!-- Represents params of user definitions --> <xs:element name="FormalParamNode"> <xs:complexType> @@ -460,7 +575,7 @@ </xs:sequence> </xs:complexType> </xs:element> - + <!-- Represents constants, variables, bound variables and new symbols --> <xs:element name="OpDeclNode"> <xs:complexType> @@ -481,12 +596,12 @@ <xs:enumeration value="28"/> <!--NewTemporalKind--> </xs:restriction> </xs:simpleType> - </xs:element> + </xs:element> </xs:sequence> </xs:complexType> </xs:element> - - + + <!-- Proofs --> <!-- The first three correspond to LeafProofNode, the last to NonLeafProofNode --> <xs:group name="ProofNode"> @@ -499,7 +614,7 @@ <xs:element name="steps"> <xs:complexType> <xs:sequence> - <xs:group ref="node"/> + <xs:group ref="node"/> <xs:sequence maxOccurs="unbounded"> <xs:group ref="step"/> </xs:sequence> @@ -507,21 +622,21 @@ </xs:complexType> </xs:element> </xs:choice> - </xs:sequence> + </xs:sequence> </xs:group> - + <xs:element name="omitted"> <xs:complexType> <xs:group ref="node"/> </xs:complexType> </xs:element> - + <xs:element name="obvious"> <xs:complexType> <xs:group ref="node"/> </xs:complexType> </xs:element> - + <!-- Facts and Defs are defined to be of these kinds according to comments in the Java code, seems step names cannot be referenced? --> <xs:element name="by"> <xs:complexType> @@ -541,20 +656,20 @@ <xs:choice minOccurs="0" maxOccurs="unbounded"> <xs:element ref="UserDefinedOpKindRef"/> <xs:element ref="ModuleInstanceKindRef"/> - <xs:element ref="TheoremNodeRef"/> - <xs:element ref="AssumeNodeRef"/> + <xs:element ref="TheoremDefRef"/> + <xs:element ref="AssumeDefRef"/> </xs:choice> </xs:complexType> - </xs:element> + </xs:element> <xs:element minOccurs="0" ref="only"/> </xs:sequence> </xs:complexType> </xs:element> - + <xs:element name="only"> <xs:complexType/> </xs:element> - + <!-- Steps are defined to be of this types according to comments in the Java code --> <xs:group name="step"> <xs:choice> @@ -565,7 +680,7 @@ <xs:element ref="TheoremNode"/> <!-- in case of unnamed theorem --> </xs:choice> </xs:group> - + <xs:element name="DefStepNode"> <xs:complexType> <xs:sequence> @@ -573,10 +688,10 @@ <xs:group maxOccurs="unbounded" ref="OpDefNodeRef"/> </xs:sequence> </xs:complexType> -<!-- <xs:element minOccurs="0" ref="stepnumber"/> this is cancelled out as it is not clear what exactly is referenced by USE facts (or BY proofs) -when the xml is generated, check what for the stepnumber take (maybe as simple strings? then we need to return this field)--> +<!-- <xs:element minOccurs="0" ref="stepnumber"/> this is cancelled out as it is not clear what exactly is referenced by USE facts (or BY proofs) +when the xml is generated, check what for the stepnumber take (maybe as simple strings? then we need to return this field)--> </xs:element> - + <xs:element name="UseOrHideNode"> <xs:complexType> <xs:sequence> @@ -602,19 +717,19 @@ when the xml is generated, check what for the stepnumber take (maybe as simple s </xs:element> <xs:element minOccurs="0" ref="only"/> <xs:element minOccurs="0" ref="hide"/> -<!-- <xs:element minOccurs="0" ref="stepnumber"/> this is cancelled out as it is not clear what exactly is referenced by USE facts (or BY proofs) -when the xml is generated, check what for the stepnumber take (maybe as simple strings? then we need to return this field)--> +<!-- <xs:element minOccurs="0" ref="stepnumber"/> this is cancelled out as it is not clear what exactly is referenced by USE facts (or BY proofs) +when the xml is generated, check what for the stepnumber take (maybe as simple strings? then we need to return this field)--> </xs:sequence> </xs:complexType> </xs:element> - + <xs:element name="hide"> <xs:complexType/> </xs:element> - + <xs:element name="stepnumber" type="xs:string"/> - - + + <!-- Expressions --> <!-- Occurrences of @ by supplying the innermost enclosing $Except and the innermost $Pair containing this @ --> <xs:element name="AtNode"> @@ -632,7 +747,7 @@ when the xml is generated, check what for the stepnumber take (maybe as simple s </xs:sequence> </xs:complexType> </xs:element> - + <!-- A decimal number (read Java code and java API for more information)--> <xs:element name="DecimalNode"> <xs:complexType> @@ -642,7 +757,7 @@ when the xml is generated, check what for the stepnumber take (maybe as simple s </xs:sequence> </xs:complexType> </xs:element> - + <!-- Represents a labelled expression nm(x,y): (ExprNode|AssumeProveNode) --> <xs:element name="LabelNode"> <xs:complexType> @@ -651,10 +766,10 @@ when the xml is generated, check what for the stepnumber take (maybe as simple s <xs:element ref="uniquename"/> <xs:element ref="arity"/> <xs:element name="body"> - <xs:complexType> + <xs:complexType> <xs:choice> <xs:group ref="ExprNode"/> - <xs:element ref="AssumeProveNode"/> + <xs:group ref="AssumeProveLike"/> </xs:choice> </xs:complexType> </xs:element> @@ -668,14 +783,14 @@ when the xml is generated, check what for the stepnumber take (maybe as simple s </xs:sequence> </xs:complexType> </xs:element> - + <!-- A list of definitions and the body --> <xs:element name="LetInNode"> <xs:complexType> <xs:sequence> - <xs:group ref="node"/> + <xs:group ref="node"/> <xs:element name="body"> - <xs:complexType> + <xs:complexType> <xs:group ref="ExprNode"/> </xs:complexType> </xs:element> @@ -693,7 +808,7 @@ when the xml is generated, check what for the stepnumber take (maybe as simple s </xs:sequence> </xs:complexType> </xs:element> - + <!-- An integer number --> <xs:element name="NumeralNode"> <xs:complexType> @@ -703,7 +818,7 @@ when the xml is generated, check what for the stepnumber take (maybe as simple s </xs:sequence> </xs:complexType> </xs:element> - + <!-- An string --> <xs:element name="StringNode"> <xs:complexType> @@ -713,12 +828,12 @@ when the xml is generated, check what for the stepnumber take (maybe as simple s </xs:sequence> </xs:complexType> </xs:element> - + <!-- A general node for applications. FIrst the operator and then the operands --> <xs:element name="OpApplNode"> <xs:complexType> <xs:sequence> - <xs:group ref="node"/> + <xs:group ref="node"/> <xs:element name="operator"> <xs:complexType> <xs:choice> @@ -750,7 +865,7 @@ when the xml is generated, check what for the stepnumber take (maybe as simple s </xs:sequence> </xs:complexType> </xs:element> - + <xs:element name="unbound"> <xs:complexType> <xs:sequence> @@ -759,7 +874,7 @@ when the xml is generated, check what for the stepnumber take (maybe as simple s </xs:sequence> </xs:complexType> </xs:element> - + <xs:element name="bound"> <xs:complexType> <xs:sequence> @@ -774,52 +889,25 @@ when the xml is generated, check what for the stepnumber take (maybe as simple s <xs:element name="tuple"> <xs:complexType/> </xs:element> - + <!-- Module(constants,variables,operators,assumptions,theorems) --> <xs:element name="ModuleNode"> <xs:complexType> <xs:sequence> - <xs:group ref="node"/> + <xs:group ref="node"/> <xs:element ref="uniquename"/> - <xs:element name="constants"> - <xs:complexType> - <xs:sequence> - <xs:element minOccurs="0" maxOccurs="unbounded" ref="OpDeclNodeRef"/> - </xs:sequence> - </xs:complexType> - </xs:element> - <xs:element name="variables"> - <xs:complexType> - <xs:sequence> - <xs:element minOccurs="0" maxOccurs="unbounded" ref="OpDeclNodeRef"/> - </xs:sequence> - </xs:complexType> - </xs:element> - <xs:element name="definitions"> - <xs:complexType> - <xs:sequence> - <xs:group minOccurs="0" maxOccurs="unbounded" ref="OpDefNodeRef"/> - </xs:sequence> - </xs:complexType> - </xs:element> - <xs:element name="assumptions"> - <xs:complexType> - <xs:choice minOccurs="0" maxOccurs="unbounded"> - <xs:element ref="AssumeNodeRef"/> - <xs:element ref="AssumeNode"/> - </xs:choice> - </xs:complexType> - </xs:element> - <xs:element name="theorems"> - <xs:complexType> - <xs:choice minOccurs="0" maxOccurs="unbounded"> - <xs:element ref="TheoremNodeRef"/> - <xs:element ref="TheoremNode"/> - </xs:choice> - </xs:complexType> - </xs:element> + <xs:sequence minOccurs="0" maxOccurs="unbounded"> + <xs:choice> + <xs:element ref="OpDeclNodeRef"/> + <xs:group ref="OpDefNodeRef"/> + <xs:element ref="AssumeNodeRef"/> + <xs:element ref="InstanceNode"/> + <xs:element ref="UseOrHideNode"/> + <xs:element ref="TheoremNodeRef"/> + </xs:choice> + </xs:sequence> </xs:sequence> </xs:complexType> </xs:element> - + </xs:schema> diff --git a/tlatools/src/tla2tex/BuiltInSymbols.java b/tlatools/src/tla2tex/BuiltInSymbols.java index 55f5f07afb08e540e91bdf5f019cdc75f120aceb..f378c7042dd5e7220be2b62a074640765db56b15 100644 --- a/tlatools/src/tla2tex/BuiltInSymbols.java +++ b/tlatools/src/tla2tex/BuiltInSymbols.java @@ -33,6 +33,8 @@ package tla2tex; import java.util.Enumeration; import java.util.Hashtable; +import util.TLAConstants; + public final class BuiltInSymbols { /*********************************************************************** @@ -145,7 +147,7 @@ public final class BuiltInSymbols } ; public static boolean IsStringChar(char ch) - { return stringCharTable.containsKey("" + ch) ; + { return stringCharTable.containsKey(String.valueOf(ch)) ; } ; public static boolean CanPrecedeLabel(String str) { @@ -164,7 +166,7 @@ public final class BuiltInSymbols + "0123456789" ; int n = 0 ; while (n < legalChars.length()) - { stringCharTable.put("" + legalChars.charAt(n), nullString); + { stringCharTable.put(String.valueOf(legalChars.charAt(n)), nullString); n = n + 1 ; } } ; @@ -224,7 +226,7 @@ public final class BuiltInSymbols // Changed to INFIX from KEYWORD by LL on 21 July 2012 to allow // left-aligning with []. It produces something reasonable when // a bunch of [] symbols are right-aligned with CASE as well. - add("CONSTANT", "{\\CONSTANT}", Symbol.KEYWORD, 0); + add(TLAConstants.KeyWords.CONSTANT, "{\\CONSTANT}", Symbol.KEYWORD, 0); add("CONSTANTS", "{\\CONSTANTS}", Symbol.KEYWORD, 0); add("EXCEPT", "{\\EXCEPT}", Symbol.KEYWORD, 0); add("EXTENDS", "{\\EXTENDS}", Symbol.KEYWORD, 0); @@ -237,7 +239,7 @@ public final class BuiltInSymbols add("STRING", "{\\STRING}", Symbol.KEYWORD, 0); add("THEOREM", "{\\THEOREM}", Symbol.KEYWORD, 0); add("TRUE", "{\\TRUE}", Symbol.KEYWORD, 0); - add("VARIABLE", "{\\VARIABLE}", Symbol.KEYWORD, 0); + add(TLAConstants.KeyWords.VARIABLE, "{\\VARIABLE}", Symbol.KEYWORD, 0); add("VARIABLES", "{\\VARIABLES}", Symbol.KEYWORD, 0); add("WITH", "{\\WITH}", Symbol.KEYWORD, 0); // The following added for tla2tex diff --git a/tlatools/src/tla2tex/FindAlignments.java b/tlatools/src/tla2tex/FindAlignments.java index c540837b07ae55bc4f563cd3bf1ec5d85a78c2fe..19ceb885f8fad9d0253dfc7a1156891cc8abedeb 100644 --- a/tlatools/src/tla2tex/FindAlignments.java +++ b/tlatools/src/tla2tex/FindAlignments.java @@ -1,1100 +1,1104 @@ -// Copyright (c) 2003 Compaq Corporation. All rights reserved. -// -// This is the version of FindAlignments modified to be consistent -// with the versions of TokenizeSpec, BuiltInSymbols, etc. that have -// been modified to handle PlusCal, but with no special PlusCal alignments -// added. It is also being modified to add a special rule for -// aligning labels -- or more precisely, aligning tokens that -// follow labels with other tokens. - -/*************************************************************************** -* CLASS FindAlignments * -* * -* Contains the one public method * -* * -* public static void FindAlignments(Token[][] spec) * -* * -* that sets the aboveAlign, belowAlign, and isAlignmentPoint fields for * -* all tokens in the tokenized spec spec--except for NULL and MULTI * -* comment tokens, for which the aboveAlign field is set by * -* CommentToken.processComments. (This method should be called after * -* CommentToken.processComments.) * -* * -* There are six kinds of alignments, illustrated by the following * -* example: * -* * -* Alignment Type Tokens so aligned * -* -------------- ----------------- * -* FirstNonLeftComment foo, x, + , /\ , y/comment r, u/comment k * -* ^^^^^^^^^^^ * -* I think this should be +/comment r * -* LeftComment comments j * -* CommentInner comments p * -* InfixInner ==, => , > * -* AfterInfixInner m, c, * -* InfixArg a, z/z, y/z * -* * -* foo == LET x == /\ a => m * n > c * -* | | | | | | | | | | * -* (* j *) | | | | /\ a => m / q > c (* p *) * -* | | | | | | * -* (* j *) | | x == z | * -* | | | | * -* | | + z \* p * -* | | | | | * -* | | | | (* p *) * -* | | | | | * -* | | + y (* p *) * -* | | | * -* | | \* r * -* | | * -* foo == * -* | * -* (* k *) * -* | * -* u + v * -* * -* For InfixInner and CommentInner alignment, a token's belowAlign * -* field points to the token directly below it with which it is * -* aligned. * -* * -* For all alignment types, if a token t1 is aligned with a token * -* t2 above it, then t1.aboveAlign points to: * -* * -* IF t2 has no aboveAlign pointer THEN t2 * -* ELSE t2.abovealign. * -* * -* Thus, in the example above, the aboveAlign pointer for all the p * -* comments point to the first p comment. * -* * -* To define the types of alignment, we use the following definitions: * -* * -* - A LEFT-COMMENT is a comment that is the first token on its line but * -* not the last token on its line. (The comments (* j *) in the * -* example are left-comments.) For all the types of alignment except * -* CommentInner, left-comments are treated as if they didn't exist. * -* * -* - A RIGHT-COMMENT is a comment that is the last token on its line. * -* * -* Any comment other than a left-comment or a right-comment is treated * -* like any other non-built-in token. We also define: * -* * -* - The COVERING TOKEN of a token t is the right-most token ct that * -* covers t on the last line (one with biggest line number) containing * -* a token that covers t, where a token t1 COVERS a token t2 if t1 * -* lies on an earlier line than t2 and t1.column \leq t2.column. * -* However, if there is a DASH between ct and t, or * -* on the same line as ct, then t has no covering token. * -* (This definition has two versions, depending on whether or not * -* left-comments are ignored.) * -* * -* - The BLOCKING TOKEN of a token t is the left-most token s with * -* s.column \geq t.column on the last line before t's line containing * -* such a non-left-comment token s. * -* * -* Here are the descriptions of the different types of alignment. * -* * -* * -* CommentInner: * -* Token t at position pos is CommentInner above-aligned with its * -* blocking token bt at position bpos iff: * -* /\ t is a right-comment * -* /\ bt is a right-comment * -* /\ t.column = bt.column * -* /\ \/ bt is not above-aligned with anything * -* \/ bt is CommentInner aligned with a token above it * -* * -* FirstNonLeftComment * -* If t is the first token on a line that is not a left-comment, and is * -* not a right-comment that is CommentInner aligned with a token above * -* it, then t is FirstNonLeftComment aligned with its covering token. * -* * -* LeftComment: * -* If t is a left-comment token, then it is LeftComment aligned * -* with its covering token (where left-comments are visible). * -* * -* InfixInner: * -* Token t at position pos is InfixInner aligned with its covering * -* token ct at position cpos iff t is not FirstNonLeftComment aligned, * -* and both t and ct are built-in symbols with the same nonzero alignment * -* class. (The name InfixInner is misleading because non-infix symbols * -* like ":" get aligned by this mechanism.) * -* * -* AfterInfixInner: * -* Token t is After InfixInner aligned with the token above it iff * -* * -* LET lt == the token to the left of t * -* alt == the token at position lt.aboveAlign * -* at == the token to the right of alt * -* IN /\ t is not the first token on the line * -* /\ lt is InnerAligned with token alt above it * -* /\ alt is not the last token on its line. * -* /\ at is the covering token of t * -* /\ t.column = at.column * -* /\ at is not a comment * -* (The name AfterInfixInner means "after InfixInner alignment". * -* Remember that some non-infix symbols get InfixInner aligned.) * -* * -* InfixArg: * -* Token t is InfixArg aligned with its covering token ct iff: * -* LET lt == the token to the left of t * -* alt == the token at position lt.aboveAlign * -* IN /\ t is not the first token on the line * -* /\ lt is the first non-left-comment token * -* /\ lt is an infix operator * -* /\ lt is not InfixInner aligned with any token above it. * -* /\ t.column = ct.column * -* /\ alt is the token to the left of ct * -* /\ lt is either aligned with or lies entirely to the * -* right of alt. * -* * -* Note: The large number of conditions are an attempt to rule * -* out spurious alignments. * -* ---------------------------------------------------------------------- * -* * -* PlusCal Alignment * -* ----------------- * -* * -* The following rule is added for PlusCal. * -* * -* Define a token t to be a FIRST NON-LABEL if either * -* (a) it is the first token on the line, or * -* (b) it is the 2nd token on its line and the first token on * -* the line is a label. * -* * -* AfterLabel: * -* A token t is AfterLabel aligned with a token ta iff: * -* /\ t is a first non-label that is the 2nd token on its line * -* /\ t and ta are left-aligned * -* /\ there is no line between t and ta containing any token * -* to the left of t other than a label that is the line's * -* first token. * -* /\ \/ ta is a first non-label * -* \/ /\ ta is above t * -* /\ \/ ta is the first token on the line * -* \/ the token to the left of ta is a label or * -* a PlusCal symbol * -* * -* Here's an example of AfterLabel alignment. Token b and all the x * -* tokens are AfterLabel aligned with token A. Tokens A and B and all the * -* other x tokens are also AfterLabel aligned with the first and last x * -* tokens. * -* * -* { B * -* l: x * -* m: z * -* x * -* l: A * -* z * -* x * -* l: x * -* } z * -* * -* Note that B is the only token whose alignment is due to the second * -* disjunction of the last conjunction. That disjunct is designed to try * -* to rule out bogus alignments such as the alignment of the y's with the * -* 0 in * -* * -* if (x = 0) { * -* lab: y := 1; * -* lab: y := 2 } * -* * -* while aligning the the y's in * -* * -* if (x = 0) { y := 0; * -* lab: y := 1; * -* lab: y := 2 } * -* * -* and in * -* * -* if (x = 0) { y := 0; * -* lab: y := 1; * -* lab: y := 2 } * -* * -* ---------------------------------------------------------------------- * -* * -* Note: A possible addition is a special case to recognize alignments * -* after a comma, as in * -* * -* <<abcdef, ghi + 7, jklmn>> * -* | | * -* <<a, bc, def>> * -* * -* However, that should be done only if there's a clear intention to align * -* them--as evidenced here by the extra spaces between the "a,", the "cb," * -* and the "def". Otherwise, there would be too many chance alignments. * -***************************************************************************/ -package tla2tex; - -public class FindAlignments -{ public static void FindAlignments(Token[][] spec) - { setSubscriptField(spec) ; - /******************************************************************* - * Set the subscript fields of the tokens. * - *******************************************************************/ - int line = 0 ; - - /********************************************************************* - * Skip all lines beginning with a prolog token. This means that if * - * the module begins on the same line as a prolog token, then the * - * first line of the spec is ignored for alignment. Tant pis! * - *********************************************************************/ - boolean inProlog = true ; - while (inProlog && (line < spec.length)) - { if ( (spec[line].length > 0) - && (spec[line][0].type != Token.PROLOG)) - { inProlog = false;} - else - { line = line + 1;} - }; - - /********************************************************************* - * Set aboveAlign and belowAlign "pointers". * - *********************************************************************/ - while (line < spec.length) - { int item = 0 ; - boolean prevInfixInner = false ; - /**************************************************************** - * True iff the previous item on the line was InfixInner * - * aligned. * - ****************************************************************/ - while (item < spec[line].length) - { - /*************************************************************** - * Set token, pos to the current token and its position. * - ***************************************************************/ - Position pos = new Position(line, item) ; - Token token = spec[line][item] ; - - if (! token.subscript) - {/************************************************************* - * Do not align subscript tokens. * - *************************************************************/ - if (isRightComment(spec, pos)) - { /*********************************************************** - * This is a RightComment. First check if it should be * - * CommentInner aligned. If not, check if it should be * - * FirstNonLeftComment aligned. * - ***********************************************************/ - - /*********************************************************** - * Set btoken, bpos to the blocking token of `token' and * - * its position. * - ***********************************************************/ - Position bpos = blockingPosition(spec, pos); - Token btoken = null ; - if (bpos.line != -1) {btoken = bpos.toToken(spec);}; - - /*********************************************************** - * Set ctok to be a CommentToken alias for token. * - ***********************************************************/ - CommentToken ctok = (CommentToken) token ; - if ( (ctok.subtype == CommentToken.MULTI) - || (ctok.subtype == CommentToken.NULL)) - { /******************************************************** - * This is the continuation of a multi-column comment. * - ********************************************************/ - Debug.Assert( (btoken != null) - && (btoken.type == Token.COMMENT), - "Bad blocking token for a MULTI or NULL token"); - - /******************************************************** - * Set token's aboveAlign to point to the BEGIN_MULTI * - * token beginning the commnet. * - ********************************************************/ - if ( ((CommentToken) btoken).subtype - == CommentToken.BEGIN_MULTI) - { token.aboveAlign = bpos ; } - else - { token.aboveAlign = btoken.aboveAlign ; }; - - /******************************************************** - * Make the blocking token's belowAlign pointer point * - * to token. * - ********************************************************/ - btoken.belowAlign = pos ; - } // END then OF if if ((ctok.subtype == .. )) - else - { /******************************************************** - * This is not the continuation of a multi-line * - * comment. Try to CommentInner align it. * - ********************************************************/ - - if ( (bpos.line != -1) - && isRightComment(spec, bpos) - && (btoken.column == token.column) - && ( ( (btoken.aboveAlign.line == -1) - && (bpos.item > 0)) - || ( (btoken.aboveAlign.line != -1) - && (btoken.aboveAlign. - toToken(spec).belowAlign.line != -1)) - /********************************************** - * Asserts that bpos is aboveAligned with a * - * token that is belowAligned with something, * - * which is possible only if bpos is * - * CommentInner aligned. * - **********************************************/ - ) - ) - { /**************************************************** - * CommentInner align. * - ****************************************************/ - btoken.belowAlign = pos ; - if (btoken.aboveAlign.line == -1) - { token.aboveAlign = bpos; } - else - { token.aboveAlign = btoken.aboveAlign ;}; - } // END then OF if ((bpos.line != -1)...) - else - { /******************************************************* - * FirstNonLeftComment align iff it is the first * - * non-left comment. * - *******************************************************/ - if ( (item == 0) - || ( (item == 1) - && (spec[line][0].type == Token.COMMENT))) - { pos.toToken(spec).aboveAlign = - coveringPosition(spec, pos, true) ; - } ; - }; // END else OF if ((bpos.line != -1)...) - - - } // END else OF if if ((ctok.subtype == .. )) - prevInfixInner = false ; - } // END then OF if (isRightComment(spec, pos)) - - { /*********************************************************** - * This is not a right-comment. Check for every kind of * - * alignment except CommentInner. * - ***********************************************************/ - if (prevInfixInner) - { /******************************************************** - * Check for AfterInfixInner alignment. * - * * - * In the following, the positions lPos, alPos, and * - * aPos are defined by * - * alPos --- aPos * - * | * - * | * - * lPos --- pos * - ********************************************************/ - Debug.Assert(pos.item > 0, - "prevInfixInner true for first item on line"); - Position lPos = new Position(pos.line, pos.item - 1); - Debug.Assert(lPos.toToken(spec).aboveAlign.line != -1, - "prevInfixInner true, but token to left not aligned"); - Position alPos = lPos.toToken(spec).aboveAlign ; - Token alToken = alPos.toToken(spec); - if (alPos.item + 1 < spec[alPos.line].length) - { Position aPos = - new Position(alPos.line, alPos.item + 1) ; - Token atoken = aPos.toToken(spec); - Position cPos = coveringPosition(spec, pos, true); - if ( (cPos.line == aPos.line) - && (cPos.item == aPos.item) - && (token.column == atoken.column) - && (atoken.type != Token.COMMENT) ) - { /************************************************* - * AfterInfixInner aligned. * - *************************************************/ - token.aboveAlign = aPos; - } ; - } // END if (alPos.item + 1 < spec[alPos.line].length) - prevInfixInner = false; - } // END then OF if (prevInfixInner) - else - { /******************************************************** - * Check for every kind of alignment except * - * AfterInfixInner and CommentInner. * - * * - * First, check if FirstNonLeftComment aligned. * - ********************************************************/ - if ( ((item == 0) && (token.type != Token.COMMENT)) - || ((item == 1) && (spec[line][0].type - == Token.COMMENT))) - { /***************************************************** - * FirstNonLeftCommentAligned. * - *****************************************************/ - token.aboveAlign = - coveringPosition(spec, pos, true); - } - else - { /***************************************************** - * Not FirstNonLeftCommentAligned. * - * * - * Next, check if LeftComment aligned. * - *****************************************************/ - if (isLeftComment(spec, pos)) - { token.aboveAlign = - coveringPosition(spec, pos, false); - } - else - { /************************************************* - * Next, check if InfixInner aligned. * - *************************************************/ - Position cpos = coveringPosition(spec, pos, true) ; - Token ctoken = null ; - /*********************************************** - * cpos and ctoken are the covering position * - * and covering token of the current token. * - ***********************************************/ - if (cpos.line != -1) - { ctoken = cpos.toToken(spec);}; - int alignClass = 0 ; // The alignment classes - int calignClass = 0; // of pos and cpos. - if (token.type == Token.BUILTIN) - { alignClass = - BuiltInSymbols.GetBuiltInSymbol( - token.string, true).alignmentType ; } ; - if ( (ctoken != null) - && (ctoken.type == Token.BUILTIN)) - { calignClass = - BuiltInSymbols.GetBuiltInSymbol( - ctoken.string, true).alignmentType ; } ; - if ( (ctoken != null) - && (token.column == ctoken.column) - && (alignClass != 0) - && (alignClass == calignClass)) - { /********************************************** - * InfixInner alignment. * - **********************************************/ - ctoken.belowAlign = pos ; - if (ctoken.aboveAlign.line == -1) - {token.aboveAlign = cpos ; } - else - { token.aboveAlign = ctoken.aboveAlign ; }; - prevInfixInner = true; - } // END then OF if ((token.column == ...)) - else - { /********************************************** - * Not InfixInner alignment. Check last * - * possibility, which is InfixArg alignment. * - **********************************************/ - if ( ( (item == 1) - || ( (item == 2) - && (spec[line][0].type == - Token.COMMENT))) - && (spec[line][item-1].type == Token.BUILTIN) - && (BuiltInSymbols.GetBuiltInSymbol( - spec[line][item-1].string, true).symbolType - == Symbol.INFIX) - /****************************************** - * Correction made 7 Nov 2001. * - * The conjunct above replaced the * - * following. * - * * - * && (BuiltInSymbols.GetBuiltInSymbol( * - * spec[line][item-1].string * - * ).alignmentType != 0) * - * * - * It seems reasonable that this * - * alignment should be performed only * - * when the token to the left is an infix * - * operator. * - ******************************************/ - && (ctoken != null) - && (token.column == ctoken.column) - && (spec[line][item-1].aboveAlign.line - != -1) - ) - { /******************************************* - * Possible InfixArg alignment. * - *******************************************/ - // This can happen and seems harmless. - // Debug.Assert(ctoken.belowAlign.line == -1, - // "Trying to InfixArg align with token " - // + "that is not aligned with token below it"); - - Token lTok = spec[line][item-1] ; - /**************************************** - * The token to the left of the current * - * token. * - ****************************************/ - Position alPos = lTok.aboveAlign ; - Token alTok = alPos.toToken(spec); - /***************************************** - * The token with which lTok is aligned * - * above. * - *****************************************/ - if ( (alPos.line == cpos.line) - && (alPos.item == cpos.item - 1) - ) - { /**************************************** - * InfixArg alignment with a token * - * having a token to its left, as in * - * * - * x == a * - * | * - * + b * - ****************************************/ - token.aboveAlign = cpos; - // following fixes the mis-alignment that occurs if - // the "x == " occupies less space than the "+". - // However, for safety, I'm only fixing it for the - // case of labels, which should be more common. - // However, I'm not doing it until I find - // some example of the bug for fear that it - // might break something else. - // - // ctoken.belowAlign = pos ; - } - else - { if ( (cpos.item == 0) - || ( (cpos.item == 1) - && (spec[cpos.line][0].type - == Token.COMMENT))) - { /***************************************** - * InfixArg alignment with a token * - * having no token to its left. In * - * this case, we can have a * - * situation like * - * * - * x == * - * a * - * + b * - * * - * where a is both above-aligned * - * with x and below-aligned with b. * - * I hope this doesn't cause * - * problems. * - *****************************************/ - token.aboveAlign = cpos; - ctoken.belowAlign = pos ; - } ; - } - }; - };// END else OF if ((token.column == ...)) - }; // END else of if (isLeftComment(spec, pos)) - }; // END else OF if ( ((item == 0) && ... )) - }; // END else OF if (prevInfixInner) - }; // END else OF (isRightComment(spec, pos)) - } // END then OF if (! token.subscript) - else - { prevInfixInner = false ; - /********************************************************** - * Need to reset prevInfixInner for the tokens following * - * a ^ or _. * - **********************************************************/ - } ; - item = item + 1 ; - } ; // END while (item < spec[line].length) - - line = line + 1; - /****************************************************************** - * Skip over epilog lines. * - ******************************************************************/ - if ( (line < spec.length) - && (spec[line].length > 0) - && (spec[line][0].type == Token.EPILOG)) - { line = spec.length ;} ; - }; // END while (line < spec.length) - - // Add the AfterLabel alignments. - FindLabelAlignments(spec) ; - - /********************************************************************* - * Set isAlignmentPoint flags. For simplicity, it is set true for * - * any token that is not the first on the line and is either the * - * source or target of a belowAlign pointer * - *********************************************************************/ - line = 0 ; - while (line < spec.length) - { int item = 0 ; - while (item < spec[line].length) - { Token token = spec[line][item] ; - if (token.aboveAlign.line != -1) - { if (item > 0) - {token.isAlignmentPoint = true ; - }; - if (token.aboveAlign.item != 0) - /******************************************************** - * Corrected apparent bug on 16 Jan 00: This if * - * condition was: token.aboveAlign.line != 0. * - ********************************************************/ - { token.aboveAlign.toToken(spec).isAlignmentPoint - = true; - }; - } ; - - if (token.belowAlign.line != -1) - { if (item > 0) - {token.isAlignmentPoint = true ; - }; - if (token.belowAlign.line != 0) - { token.belowAlign.toToken(spec).isAlignmentPoint - = true; - }; - } ; - - item = item + 1 ; - } ; // END while (item < spec[line].length) - - line = line + 1; - }; // END while (line < spec.length) - } ; - - /** - * Adds AfterLabel alignments to spec. More precisely, for something like - * - * if (x) { stmt - * label: stmt - * stmt - * label: stmt - * stmt - * stmt - * } - * - * it makes the belowAlign field of all stmts but the last one point to the - * stmt below it, and the aboveAlign field of all but the first stmt point to - * the one above it. In this case - * - * while x - * stmt - * label: stmt - * - * the first stmt's aboveAlign field should point to x by the FirstNonLeftComment - * alignment rule. For - * - * while x - * label: stmt - * label: stmt - * - * the method sets the first stmt's aboveAlign field - * - * @param spec - */ - public static void FindLabelAlignments(Token[][] spec) { - /* - * Do nothing if there is no PlusCal algorithm. - */ - if (!TokenizeSpec.hasPcal) { - return ; - } - - /* - * We get the first and last line that may begin with a label or a - * PlusCal statement. Since the algorithm begins with --algorithm - * or --fair, we skip the algorithm's first line. - */ - int pcalStartLine = TokenizeSpec.pcalStart.line + 1 ; - int pcalEndLine = TokenizeSpec.pcalEnd.line ; - - /* - * The algorithm works by repeatedly searching for the next line beginning - * with a label followed by a token tok. It then performs the following - * two steps - * - * 1. Searche downward for tokens AfterLabel aligned with tok, setting - * aboveAlign/belowAlign fields as it finds them. It stops when it reaches a - * line in which tok is not not aligned with the first token on the - * line. If there's a token etok on that line that is AfterLabel - * aligned with tok, so it must follow a label, then the aboveAlign - * field of etok is set to point to the AfterLabel aligned token above - * it, whose belowAlign field points to etok. - * - * 2. IF tok's aboveAlign field points to a token whose belowAlign field - * points to a field whose belowAlign field points to tok - * THEN do nothing. I think this is possible only if those fields were set - * by step 1 for a previous label. If I'm wrong and there's some weird - * situation that caused this alignment (which I think can only be - * the case if tok is a comment), then not stopping will probably - * do more harm than good. - * ELSE search upwards to set aboveAlign/belowAlign fields of tokens - * AfterLabel aligned with tok. As indicated above, if there - * are no tokens above tok that are AfterLabel aligned with it, - * then tok's aboveAlign field must be set to point to its - * covering token. - */ - int curLabelLine = pcalStartLine ; - - while ( (curLabelLine <= pcalEndLine) - && (curLabelLine < spec.length) ) { - /* - * set curLabelLine to the first line at or below its current - * position that begins with a label. - */ - if ( (spec[curLabelLine].length > 1) - && (spec[curLabelLine][0].type == Token.PCAL_LABEL) ) { - - Token tok = spec[curLabelLine][1] ; - int alignCol = tok.column ; - - // Perform step 1 for token tok - int curLine = curLabelLine + 1 ; - - // spec[alignLine][alignItem] is the token to which the - // next token to be aligned is aligned with. - int alignLine = curLabelLine ; - int alignItem = 1 ; - boolean notDone = true ; - while (notDone) { - int curItem = 0 ; - // If spec[alignLine][alignItem] is to be aligned with a token - // on this line, then the token is spec[curLine][nextItem] - // - boolean shouldSkip = false ; - if (spec[curLine].length != 0) { - if (spec[curLine][0].type == Token.PCAL_LABEL) { - // line begins with label - if (spec[curLine].length > 1) { - curItem = 1 ; - } - else { - // The label is the only token on the line. Stop - // iff the label is to the left of the alignment - // column. - notDone = (spec[curLine][0].column >= alignCol) ; - shouldSkip = true ; - } - } - else { - // line doesn't begin with label - } - if (!shouldSkip) { - if (spec[curLine][curItem].column < alignCol) { - notDone = false ; - } - else if (spec[curLine][curItem].column == alignCol) { - spec[alignLine][alignItem].belowAlign = - new Position(curLine, curItem) ; - spec[curLine][curItem].aboveAlign = - new Position(alignLine, alignItem) ; - alignLine = curLine ; - alignItem = curItem ; - } - } - } - curLine++ ; - if ( (curLine > pcalEndLine) - || (curLine >= spec.length)) { - notDone = false ; - } - } - - // Perform step 2 for token tok - - if ( (tok.aboveAlign.line != -1) - && (tok.aboveAlign.toToken(spec).belowAlign.equals(new Position(curLabelLine, 1)))) { - // already aligned with above tokens so do nothing - } - else { - curLine = curLabelLine - 1 ; - alignLine = curLabelLine ; - alignItem = 1 ; - notDone = true ; - while (notDone) { - if ( (spec[curLine].length > 0 ) - && (spec[curLine][0].column <= alignCol) - && ( (spec[curLine][0].type != Token.PCAL_LABEL) - || ( (spec[curLine].length > 1) - && (spec[curLine][1].column <= alignCol) ) ) - // the conjunct above causes the line to be skipped - // if the only token to the left of tok on this - // line is an initial label followed by a token - // to the right of tok. - ) { - if (spec[curLine][0].column == alignCol) { - // spec[curLine][0] is to be aligned with - // spec[alignLine][alignItem] - spec[alignLine][alignItem].aboveAlign = - new Position(curLine, 0) ; - spec[curLine][0].belowAlign = - new Position(alignLine, alignItem) ; - alignLine = curLine ; - alignItem = 0 ; - } - else if (spec[alignLine][alignItem].aboveAlign.line == -1) { - // we need to align spec[alignLine][alignItem] with the - // right-most token on line curLine with column \leq alignCol - int item = 0 ; - while ( (item < spec[curLine].length) - && (spec[curLine][item].column <= alignCol)) { - item++; - } - // item is now either off the line or pointing to a token - // to the right of the alignment token ; - item-- ; - - // We set the aboveAlign pointer of - // to point to spec[curLine][item] if the latter token is either - // the first one on its line, or else the token to its left is - // either a label or a PlusCal token. - // If that's the case, we also set the belowAlign pointer of - // spec[curLine][item] if that token is in the same column - // as spec[alignLine][alignItem]. - // - Token altok = null ; - if (item > 0) { - altok = spec[curLine][item-1] ; - } - if ( (altok != null) - && ( (altok.type == Token.PCAL_LABEL) - || ( (altok.type == Token.BUILTIN) - && BuiltInSymbols.IsBuiltInSymbol(altok.string, true) - && ! BuiltInSymbols.IsBuiltInSymbol(altok.string, false) - ) - ) - ) { - spec[alignLine][alignItem].aboveAlign = - new Position(curLine, item) ; - - if (spec[curLine][item].column == alignCol) { - spec[curLine][item].belowAlign = - new Position(alignLine, alignItem) ; - } - } - // This ends step 2 - notDone = false ; - } - else { - notDone = false ; - } - } - curLine-- ; - if (curLine < pcalStartLine) { - notDone = false ; - } - } - } - } - curLabelLine++ ; - } - } - /************************************************************************* - * The following are functions used in FindAlignments. * - *************************************************************************/ - private static boolean isLeftComment(Token[][] spec, Position p) - /*********************************************************************** - * A left-comment is a comment token that is the first token on a line * - * and has another token to its right. This method returns true iff * - * the token at position p of spec is a left-comment. * - ***********************************************************************/ - { return (p.item == 0) - && (spec[p.line][p.item].type == Token.COMMENT) - && (spec[p.line].length > 1) ; - } ; - - private static boolean isRightComment(Token[][] spec, Position p) - /*********************************************************************** - * A right-comment is a comment token that is the last token on its * - * line. This method returns true iff the token at position p in spec * - * is a right-comment. * - ***********************************************************************/ - { return (p.item == spec[p.line].length - 1) - && (spec[p.line][p.item].type == Token.COMMENT) ; - } ; - - private static Position coveringPosition( - Token[][] spec, Position p, boolean ignore) - /*********************************************************************** - * A token t1 COVERS a token t2 if t1 lies on an earlier line than t2 * - * and t1.column \leq t2.column. This method searches upwards to find * - * the first line with a token that covers the token at position p, * - * and then returns the position of the right-most token on that line * - * that covers the token at p. When searching for the covering token, * - * left-comments are ignored iff the ignore parameter is true. * - ***********************************************************************/ - { /********************************************************************* - * Find covering line. * - *********************************************************************/ - int line = p.line - 1 ; - Token tok = p.toToken(spec) ; - boolean notDone = true ; - while ((line >= 0) && notDone) - { if (spec[line].length > 0) - { if (spec[line][0].type == Token.PROLOG) - { line = -1 ; - notDone = false; - } - else - { int item = 0 ; - if (ignore && isLeftComment(spec, new Position(line, 0))) - { item = 1 ; } - if (spec[line][item].column <= tok.column) - { notDone = false ;} ; - } - }; // END if (spec[line].length > 0) - if (notDone) {line = line - 1 ;}; - } // END while ((line >= 0) && notDone) - - /********************************************************************** - * If no covering line, return (-1, 0). * - **********************************************************************/ - if (line == -1) {return new Position(-1, 0);} ; - - /********************************************************************** - * Find covering item. * - **********************************************************************/ - int item = 0; - int nsItem = 0; - /******************************************************************** - * item is the current item. * - * nsItem is the last non-subscript item found that covers tok. * - ********************************************************************/ - boolean dashFound = false ; - if (spec[line][0].type == Token.DASHES) - { dashFound = true;} ; - while ( (! dashFound) - && (item + 1 < spec[line].length) - && (spec[line][item + 1].column <= tok.column)) - { if (spec[line][item+1].type == Token.DASHES) - {dashFound = true;} ; - item = item + 1; - if (!spec[line][item].subscript) - {nsItem = item;}; - }; - if (dashFound) {return new Position(-1, 0);} ; - - /********************************************************************** - * Return (line, nsItem). * - **********************************************************************/ - return new Position(line, nsItem); - } ; - - private static Position blockingPosition(Token[][] spec, Position p) - /*********************************************************************** - * Searches upwards from position p to find the first token at the * - * same column or to the right of the token at p that is not * - * a subscript token. * - ***********************************************************************/ - { int line = p.line - 1 ; - int item = 0 ; - Token tok = p.toToken(spec) ; - boolean notDone = true ; - while ((line >= 0) && notDone) - { if (spec[line].length > 0) - { if (spec[line][0].type == Token.PROLOG) - { line = -1 ; - notDone = false; - } - else - { item = 0 ; - if (isLeftComment(spec, new Position(line, 0))) - { item = 1 ; } ; - while (notDone && (item < spec[line].length)) - { if ( (spec[line][item].column >= tok.column) - && (! spec[line][item].subscript)) - { notDone = false ; } - else - { item = item+1; }; - } ; - } ; - }; // END if (spec[line].length > 0) - if (notDone) {line = line - 1 ;} ; - } // END while ((line >= 0) && notDone) - - /********************************************************************** - * If no token found, return (-1, 0). * - **********************************************************************/ - if (line == -1) {return new Position(-1, 0);} ; - - /********************************************************************** - * Return (line, item). * - **********************************************************************/ - return new Position(line, item); - } ; - - private static void setSubscriptField(Token[][] spec) - /*********************************************************************** - * Sets the subscript field of the tokens. (This field is true iff * - * the token is part of a sub- or superscript.) Upon encountering a * - * "^" or a token that is a built-in symbol with symbolType - * - * Symbol.SUBSCRIPTED token, the next token or all tokens in the * - * properly parenthesized expression that follows it are * - * subscripted--iff all those tokens lie on the same line. * - ***********************************************************************/ - { int line = 0 ; - while (line < spec.length) - { int item = 0 ; - int startSub = -1 ; - /**************************************************************** - * If a subscript has begun, then this is its first item; * - * otherwise, it equals -1. * - ****************************************************************/ - int nestingDepth = 0 ; - /**************************************************************** - * The current parenthesis nesting level inside a subscript, or * - * 0 if not in a subscript. * - ****************************************************************/ - - - while (item < spec[line].length) - { Token tok = spec[line][item] ; - /************************************************************* - * tok is the current token. * - *************************************************************/ - if (startSub == -1) - { /************************************************************ - * A subscript has not yet begun. * - ************************************************************/ - if ( (tok.type == Token.BUILTIN) - && ( (BuiltInSymbols.GetBuiltInSymbol( - tok.string, true).symbolType - == Symbol.SUBSCRIPTED) - || (tok.string.equals("^")))) - { - startSub = item + 1 ; - } // END then OF if ((tok.type == Token.BUILTIN) ...) - else - { /********************************************************* - * Do nothing. * - *********************************************************/ - }; // END else OF if ((tok.type = Token.BUILTIN) ...) - - } // END then OF if (startSub == -1) - else - { /************************************************************ - * A subscript has begun. * - * * - * Set symType to the symbol type of the token, or * - * NOT_A_SYMBOL if it isn't a symbol. * - ************************************************************/ - int symType = Symbol.NOT_A_SYMBOL ; - if (tok.type == Token.BUILTIN) - { symType = - BuiltInSymbols.GetBuiltInSymbol( - tok.string, true).symbolType ; - }; - - if ( ( (nestingDepth == 0) - && (symType != Symbol.LEFT_PAREN)) - || ( (nestingDepth == 1) - && (symType == Symbol.RIGHT_PAREN))) - { /********************************************************* - * This ends the subscript. Set the subscript field of * - * all tokens from startSub to item and reset startSub. * - *********************************************************/ - nestingDepth = 0 ; - while (startSub <= item) - { spec[line][startSub].subscript = true ; - startSub = startSub + 1; - } - startSub = -1 ; - } // END then OF if (((nestingDepth == 0)... )) - else - { /********************************************************* - * The subscript continues. * - *********************************************************/ - if (symType == Symbol.LEFT_PAREN) - { nestingDepth = nestingDepth + 1; } - else - { if (symType == Symbol.RIGHT_PAREN) - { nestingDepth = nestingDepth - 1; }; - }; - }; // END else OF if (((nestingDepth == 0)... )) - - }; // END else OF if (startSub == -1) - - item = item + 1 ; - } ; // END while (item < spec[line].length) - - - line = line + 1; - } // END while (line < spec.length) - - } ; - - -} - -/* last modified on Sun 5 August 2012 at 17:07:48 PST by lamport */ +// Copyright (c) 2003 Compaq Corporation. All rights reserved. +// +// This is the version of FindAlignments modified to be consistent +// with the versions of TokenizeSpec, BuiltInSymbols, etc. that have +// been modified to handle PlusCal, but with no special PlusCal alignments +// added. It is also being modified to add a special rule for +// aligning labels -- or more precisely, aligning tokens that +// follow labels with other tokens. + +/*************************************************************************** +* CLASS FindAlignments * +* * +* Contains the one public method * +* * +* public static void FindAlignments(Token[][] spec) * +* * +* that sets the aboveAlign, belowAlign, and isAlignmentPoint fields for * +* all tokens in the tokenized spec spec--except for NULL and MULTI * +* comment tokens, for which the aboveAlign field is set by * +* CommentToken.processComments. (This method should be called after * +* CommentToken.processComments.) * +* * +* There are six kinds of alignments, illustrated by the following * +* example: * +* * +* Alignment Type Tokens so aligned * +* -------------- ----------------- * +* FirstNonLeftComment foo, x, + , /\ , y/comment r, u/comment k * +* ^^^^^^^^^^^ * +* I think this should be +/comment r * +* LeftComment comments j * +* CommentInner comments p * +* InfixInner ==, => , > * +* AfterInfixInner m, c, * +* InfixArg a, z/z, y/z * +* * +* foo == LET x == /\ a => m * n > c * +* | | | | | | | | | | * +* (* j *) | | | | /\ a => m / q > c (* p *) * +* | | | | | | * +* (* j *) | | x == z | * +* | | | | * +* | | + z \* p * +* | | | | | * +* | | | | (* p *) * +* | | | | | * +* | | + y (* p *) * +* | | | * +* | | \* r * +* | | * +* foo == * +* | * +* (* k *) * +* | * +* u + v * +* * +* For InfixInner and CommentInner alignment, a token's belowAlign * +* field points to the token directly below it with which it is * +* aligned. * +* * +* For all alignment types, if a token t1 is aligned with a token * +* t2 above it, then t1.aboveAlign points to: * +* * +* IF t2 has no aboveAlign pointer THEN t2 * +* ELSE t2.abovealign. * +* * +* Thus, in the example above, the aboveAlign pointer for all the p * +* comments point to the first p comment. * +* * +* To define the types of alignment, we use the following definitions: * +* * +* - A LEFT-COMMENT is a comment that is the first token on its line but * +* not the last token on its line. (The comments (* j *) in the * +* example are left-comments.) For all the types of alignment except * +* CommentInner, left-comments are treated as if they didn't exist. * +* * +* - A RIGHT-COMMENT is a comment that is the last token on its line. * +* * +* Any comment other than a left-comment or a right-comment is treated * +* like any other non-built-in token. We also define: * +* * +* - The COVERING TOKEN of a token t is the right-most token ct that * +* covers t on the last line (one with biggest line number) containing * +* a token that covers t, where a token t1 COVERS a token t2 if t1 * +* lies on an earlier line than t2 and t1.column \leq t2.column. * +* However, if there is a DASH between ct and t, or * +* on the same line as ct, then t has no covering token. * +* (This definition has two versions, depending on whether or not * +* left-comments are ignored.) * +* * +* - The BLOCKING TOKEN of a token t is the left-most token s with * +* s.column \geq t.column on the last line before t's line containing * +* such a non-left-comment token s. * +* * +* Here are the descriptions of the different types of alignment. * +* * +* * +* CommentInner: * +* Token t at position pos is CommentInner above-aligned with its * +* blocking token bt at position bpos iff: * +* /\ t is a right-comment * +* /\ bt is a right-comment * +* /\ t.column = bt.column * +* /\ \/ bt is not above-aligned with anything * +* \/ bt is CommentInner aligned with a token above it * +* * +* FirstNonLeftComment * +* If t is the first token on a line that is not a left-comment, and is * +* not a right-comment that is CommentInner aligned with a token above * +* it, then t is FirstNonLeftComment aligned with its covering token. * +* * +* LeftComment: * +* If t is a left-comment token, then it is LeftComment aligned * +* with its covering token (where left-comments are visible). * +* * +* InfixInner: * +* Token t at position pos is InfixInner aligned with its covering * +* token ct at position cpos iff t is not FirstNonLeftComment aligned, * +* and both t and ct are built-in symbols with the same nonzero alignment * +* class. (The name InfixInner is misleading because non-infix symbols * +* like ":" get aligned by this mechanism.) * +* * +* AfterInfixInner: * +* Token t is After InfixInner aligned with the token above it iff * +* * +* LET lt == the token to the left of t * +* alt == the token at position lt.aboveAlign * +* at == the token to the right of alt * +* IN /\ t is not the first token on the line * +* /\ lt is InnerAligned with token alt above it * +* /\ alt is not the last token on its line. * +* /\ at is the covering token of t * +* /\ t.column = at.column * +* /\ at is not a comment * +* (The name AfterInfixInner means "after InfixInner alignment". * +* Remember that some non-infix symbols get InfixInner aligned.) * +* * +* InfixArg: * +* Token t is InfixArg aligned with its covering token ct iff: * +* LET lt == the token to the left of t * +* alt == the token at position lt.aboveAlign * +* IN /\ t is not the first token on the line * +* /\ lt is the first non-left-comment token * +* /\ lt is an infix operator * +* /\ lt is not InfixInner aligned with any token above it. * +* /\ t.column = ct.column * +* /\ alt is the token to the left of ct * +* /\ lt is either aligned with or lies entirely to the * +* right of alt. * +* * +* Note: The large number of conditions are an attempt to rule * +* out spurious alignments. * +* ---------------------------------------------------------------------- * +* * +* PlusCal Alignment * +* ----------------- * +* * +* The following rule is added for PlusCal. * +* * +* Define a token t to be a FIRST NON-LABEL if either * +* (a) it is the first token on the line, or * +* (b) it is the 2nd token on its line and the first token on * +* the line is a label. * +* * +* AfterLabel: * +* A token t is AfterLabel aligned with a token ta iff: * +* /\ t is a first non-label that is the 2nd token on its line * +* /\ t and ta are left-aligned * +* /\ there is no line between t and ta containing any token * +* to the left of t other than a label that is the line's * +* first token. * +* /\ \/ ta is a first non-label * +* \/ /\ ta is above t * +* /\ \/ ta is the first token on the line * +* \/ the token to the left of ta is a label or * +* a PlusCal symbol * +* * +* Here's an example of AfterLabel alignment. Token b and all the x * +* tokens are AfterLabel aligned with token A. Tokens A and B and all the * +* other x tokens are also AfterLabel aligned with the first and last x * +* tokens. * +* * +* { B * +* l: x * +* m: z * +* x * +* l: A * +* z * +* x * +* l: x * +* } z * +* * +* Note that B is the only token whose alignment is due to the second * +* disjunction of the last conjunction. That disjunct is designed to try * +* to rule out bogus alignments such as the alignment of the y's with the * +* 0 in * +* * +* if (x = 0) { * +* lab: y := 1; * +* lab: y := 2 } * +* * +* while aligning the the y's in * +* * +* if (x = 0) { y := 0; * +* lab: y := 1; * +* lab: y := 2 } * +* * +* and in * +* * +* if (x = 0) { y := 0; * +* lab: y := 1; * +* lab: y := 2 } * +* * +* ---------------------------------------------------------------------- * +* * +* Note: A possible addition is a special case to recognize alignments * +* after a comma, as in * +* * +* <<abcdef, ghi + 7, jklmn>> * +* | | * +* <<a, bc, def>> * +* * +* However, that should be done only if there's a clear intention to align * +* them--as evidenced here by the extra spaces between the "a,", the "cb," * +* and the "def". Otherwise, there would be too many chance alignments. * +***************************************************************************/ +package tla2tex; + +public class FindAlignments +{ public static void FindAlignments(Token[][] spec) + { setSubscriptField(spec) ; + /******************************************************************* + * Set the subscript fields of the tokens. * + *******************************************************************/ + int line = 0 ; + + /********************************************************************* + * Skip all lines beginning with a prolog token. This means that if * + * the module begins on the same line as a prolog token, then the * + * first line of the spec is ignored for alignment. Tant pis! * + *********************************************************************/ + boolean inProlog = true ; + while (inProlog && (line < spec.length)) + { if ( (spec[line].length > 0) + && (spec[line][0].type != Token.PROLOG)) + { inProlog = false;} + else + { line = line + 1;} + }; + + /********************************************************************* + * Set aboveAlign and belowAlign "pointers". * + *********************************************************************/ + while (line < spec.length) + { int item = 0 ; + boolean prevInfixInner = false ; + /**************************************************************** + * True iff the previous item on the line was InfixInner * + * aligned. * + ****************************************************************/ + while (item < spec[line].length) + { + /*************************************************************** + * Set token, pos to the current token and its position. * + ***************************************************************/ + Position pos = new Position(line, item) ; + Token token = spec[line][item] ; + + if (! token.subscript) + {/************************************************************* + * Do not align subscript tokens. * + *************************************************************/ + if (isRightComment(spec, pos)) + { /*********************************************************** + * This is a RightComment. First check if it should be * + * CommentInner aligned. If not, check if it should be * + * FirstNonLeftComment aligned. * + ***********************************************************/ + + /*********************************************************** + * Set btoken, bpos to the blocking token of `token' and * + * its position. * + ***********************************************************/ + Position bpos = blockingPosition(spec, pos); + Token btoken = null ; + if (bpos.line != -1) {btoken = bpos.toToken(spec);}; + + /*********************************************************** + * Set ctok to be a CommentToken alias for token. * + ***********************************************************/ + CommentToken ctok = (CommentToken) token ; + if ( (ctok.subtype == CommentToken.MULTI) + || (ctok.subtype == CommentToken.NULL)) + { /******************************************************** + * This is the continuation of a multi-column comment. * + ********************************************************/ + Debug.Assert( (btoken != null) + && (btoken.type == Token.COMMENT), + "Bad blocking token for a MULTI or NULL token"); + + /******************************************************** + * Set token's aboveAlign to point to the BEGIN_MULTI * + * token beginning the commnet. * + ********************************************************/ + if ( ((CommentToken) btoken).subtype + == CommentToken.BEGIN_MULTI) + { token.aboveAlign = bpos ; } + else + { token.aboveAlign = btoken.aboveAlign ; }; + + /******************************************************** + * Make the blocking token's belowAlign pointer point * + * to token. * + ********************************************************/ + btoken.belowAlign = pos ; + } // END then OF if if ((ctok.subtype == .. )) + else + { /******************************************************** + * This is not the continuation of a multi-line * + * comment. Try to CommentInner align it. * + ********************************************************/ + + if ( (bpos.line != -1) + && isRightComment(spec, bpos) + && (btoken.column == token.column) + && ( ( (btoken.aboveAlign.line == -1) + && (bpos.item > 0)) + || ( (btoken.aboveAlign.line != -1) + && (btoken.aboveAlign. + toToken(spec).belowAlign.line != -1)) + /********************************************** + * Asserts that bpos is aboveAligned with a * + * token that is belowAligned with something, * + * which is possible only if bpos is * + * CommentInner aligned. * + **********************************************/ + ) + ) + { /**************************************************** + * CommentInner align. * + ****************************************************/ + btoken.belowAlign = pos ; + if (btoken.aboveAlign.line == -1) + { token.aboveAlign = bpos; } + else + { token.aboveAlign = btoken.aboveAlign ;}; + } // END then OF if ((bpos.line != -1)...) + else + { /******************************************************* + * FirstNonLeftComment align iff it is the first * + * non-left comment. * + *******************************************************/ + if ( (item == 0) + || ( (item == 1) + && (spec[line][0].type == Token.COMMENT))) + { pos.toToken(spec).aboveAlign = + coveringPosition(spec, pos, true) ; + } ; + }; // END else OF if ((bpos.line != -1)...) + + + } // END else OF if if ((ctok.subtype == .. )) + prevInfixInner = false ; + } // END then OF if (isRightComment(spec, pos)) + + { /*********************************************************** + * This is not a right-comment. Check for every kind of * + * alignment except CommentInner. * + ***********************************************************/ + if (prevInfixInner) + { /******************************************************** + * Check for AfterInfixInner alignment. * + * * + * In the following, the positions lPos, alPos, and * + * aPos are defined by * + * alPos --- aPos * + * | * + * | * + * lPos --- pos * + ********************************************************/ + Debug.Assert(pos.item > 0, + "prevInfixInner true for first item on line"); + Position lPos = new Position(pos.line, pos.item - 1); + Debug.Assert(lPos.toToken(spec).aboveAlign.line != -1, + "prevInfixInner true, but token to left not aligned"); + Position alPos = lPos.toToken(spec).aboveAlign ; + Token alToken = alPos.toToken(spec); + if (alPos.item + 1 < spec[alPos.line].length) + { Position aPos = + new Position(alPos.line, alPos.item + 1) ; + Token atoken = aPos.toToken(spec); + Position cPos = coveringPosition(spec, pos, true); + if ( (cPos.line == aPos.line) + && (cPos.item == aPos.item) + && (token.column == atoken.column) + && (atoken.type != Token.COMMENT) ) + { /************************************************* + * AfterInfixInner aligned. * + *************************************************/ + token.aboveAlign = aPos; + } ; + } // END if (alPos.item + 1 < spec[alPos.line].length) + prevInfixInner = false; + } // END then OF if (prevInfixInner) + else + { /******************************************************** + * Check for every kind of alignment except * + * AfterInfixInner and CommentInner. * + * * + * First, check if FirstNonLeftComment aligned. * + ********************************************************/ + if ( ((item == 0) && (token.type != Token.COMMENT)) + || ((item == 1) && (spec[line][0].type + == Token.COMMENT))) + { /***************************************************** + * FirstNonLeftCommentAligned. * + *****************************************************/ + token.aboveAlign = + coveringPosition(spec, pos, true); + } + else + { /***************************************************** + * Not FirstNonLeftCommentAligned. * + * * + * Next, check if LeftComment aligned. * + *****************************************************/ + if (isLeftComment(spec, pos)) + { token.aboveAlign = + coveringPosition(spec, pos, false); + } + else + { /************************************************* + * Next, check if InfixInner aligned. * + *************************************************/ + Position cpos = coveringPosition(spec, pos, true) ; + Token ctoken = null ; + /*********************************************** + * cpos and ctoken are the covering position * + * and covering token of the current token. * + ***********************************************/ + if (cpos.line != -1) + { ctoken = cpos.toToken(spec);}; + int alignClass = 0 ; // The alignment classes + int calignClass = 0; // of pos and cpos. + if (token.type == Token.BUILTIN) + { alignClass = + BuiltInSymbols.GetBuiltInSymbol( + token.string, true).alignmentType ; } ; + if ( (ctoken != null) + && (ctoken.type == Token.BUILTIN)) + { calignClass = + BuiltInSymbols.GetBuiltInSymbol( + ctoken.string, true).alignmentType ; } ; + if ( (ctoken != null) + && (token.column == ctoken.column) + && (alignClass != 0) + && (alignClass == calignClass)) + { /********************************************** + * InfixInner alignment. * + **********************************************/ + ctoken.belowAlign = pos ; + if (ctoken.aboveAlign.line == -1) + {token.aboveAlign = cpos ; } + else + { token.aboveAlign = ctoken.aboveAlign ; }; + prevInfixInner = true; + } // END then OF if ((token.column == ...)) + else + { /********************************************** + * Not InfixInner alignment. Check last * + * possibility, which is InfixArg alignment. * + **********************************************/ + if ( ( (item == 1) + || ( (item == 2) + && (spec[line][0].type == + Token.COMMENT))) + && (spec[line][item-1].type == Token.BUILTIN) + && (BuiltInSymbols.GetBuiltInSymbol( + spec[line][item-1].string, true).symbolType + == Symbol.INFIX) + /****************************************** + * Correction made 7 Nov 2001. * + * The conjunct above replaced the * + * following. * + * * + * && (BuiltInSymbols.GetBuiltInSymbol( * + * spec[line][item-1].string * + * ).alignmentType != 0) * + * * + * It seems reasonable that this * + * alignment should be performed only * + * when the token to the left is an infix * + * operator. * + ******************************************/ + && (ctoken != null) + && (token.column == ctoken.column) + && (spec[line][item-1].aboveAlign.line + != -1) + ) + { /******************************************* + * Possible InfixArg alignment. * + *******************************************/ + // This can happen and seems harmless. + // Debug.Assert(ctoken.belowAlign.line == -1, + // "Trying to InfixArg align with token " + // + "that is not aligned with token below it"); + + Token lTok = spec[line][item-1] ; + /**************************************** + * The token to the left of the current * + * token. * + ****************************************/ + Position alPos = lTok.aboveAlign ; + Token alTok = alPos.toToken(spec); + /***************************************** + * The token with which lTok is aligned * + * above. * + *****************************************/ + if ( (alPos.line == cpos.line) + && (alPos.item == cpos.item - 1) + ) + { /**************************************** + * InfixArg alignment with a token * + * having a token to its left, as in * + * * + * x == a * + * | * + * + b * + ****************************************/ + token.aboveAlign = cpos; + // following fixes the mis-alignment that occurs if + // the "x == " occupies less space than the "+". + // However, for safety, I'm only fixing it for the + // case of labels, which should be more common. + // However, I'm not doing it until I find + // some example of the bug for fear that it + // might break something else. + // + // ctoken.belowAlign = pos ; + } + else + { if ( (cpos.item == 0) + || ( (cpos.item == 1) + && (spec[cpos.line][0].type + == Token.COMMENT))) + { /***************************************** + * InfixArg alignment with a token * + * having no token to its left. In * + * this case, we can have a * + * situation like * + * * + * x == * + * a * + * + b * + * * + * where a is both above-aligned * + * with x and below-aligned with b. * + * I hope this doesn't cause * + * problems. * + *****************************************/ + token.aboveAlign = cpos; + ctoken.belowAlign = pos ; + } ; + } + }; + };// END else OF if ((token.column == ...)) + }; // END else of if (isLeftComment(spec, pos)) + }; // END else OF if ( ((item == 0) && ... )) + }; // END else OF if (prevInfixInner) + }; // END else OF (isRightComment(spec, pos)) + } // END then OF if (! token.subscript) + else + { prevInfixInner = false ; + /********************************************************** + * Need to reset prevInfixInner for the tokens following * + * a ^ or _. * + **********************************************************/ + } ; + item = item + 1 ; + } ; // END while (item < spec[line].length) + + line = line + 1; + /****************************************************************** + * Skip over epilog lines. * + ******************************************************************/ + if ( (line < spec.length) + && (spec[line].length > 0) + && (spec[line][0].type == Token.EPILOG)) + { line = spec.length ;} ; + }; // END while (line < spec.length) + + // Add the AfterLabel alignments. + FindLabelAlignments(spec) ; + + /********************************************************************* + * Set isAlignmentPoint flags. For simplicity, it is set true for * + * any token that is not the first on the line and is either the * + * source or target of a belowAlign pointer * + *********************************************************************/ + line = 0 ; + while (line < spec.length) + { int item = 0 ; + while (item < spec[line].length) + { Token token = spec[line][item] ; + if (token.aboveAlign.line != -1) + { if (item > 0) + {token.isAlignmentPoint = true ; + }; + if (token.aboveAlign.item != 0) + /******************************************************** + * Corrected apparent bug on 16 Jan 00: This if * + * condition was: token.aboveAlign.line != 0. * + ********************************************************/ + { token.aboveAlign.toToken(spec).isAlignmentPoint + = true; + }; + } ; + + if (token.belowAlign.line != -1) + { if (item > 0) + {token.isAlignmentPoint = true ; + }; + if (token.belowAlign.line != 0) + { token.belowAlign.toToken(spec).isAlignmentPoint + = true; + }; + } ; + + item = item + 1 ; + } ; // END while (item < spec[line].length) + + line = line + 1; + }; // END while (line < spec.length) + } ; + + /** + * Adds AfterLabel alignments to spec. More precisely, for something like + * + * if (x) { stmt + * label: stmt + * stmt + * label: stmt + * stmt + * stmt + * } + * + * it makes the belowAlign field of all stmts but the last one point to the + * stmt below it, and the aboveAlign field of all but the first stmt point to + * the one above it. In this case + * + * while x + * stmt + * label: stmt + * + * the first stmt's aboveAlign field should point to x by the FirstNonLeftComment + * alignment rule. For + * + * while x + * label: stmt + * label: stmt + * + * the method sets the first stmt's aboveAlign field + * + * @param spec + */ + public static void FindLabelAlignments(Token[][] spec) { + /* + * Do nothing if there is no PlusCal algorithm. + */ + if (!TokenizeSpec.hasPcal) { + return ; + } + + /* + * We get the first and last line that may begin with a label or a + * PlusCal statement. Since the algorithm begins with --algorithm + * or --fair, we skip the algorithm's first line. + */ + int pcalStartLine = TokenizeSpec.pcalStart.line + 1 ; + int pcalEndLine = TokenizeSpec.pcalEnd.line ; + + /* + * The algorithm works by repeatedly searching for the next line beginning + * with a label followed by a token tok. It then performs the following + * two steps + * + * 1. Searche downward for tokens AfterLabel aligned with tok, setting + * aboveAlign/belowAlign fields as it finds them. It stops when it reaches a + * line in which tok is not not aligned with the first token on the + * line. If there's a token etok on that line that is AfterLabel + * aligned with tok, so it must follow a label, then the aboveAlign + * field of etok is set to point to the AfterLabel aligned token above + * it, whose belowAlign field points to etok. + * + * 2. IF tok's aboveAlign field points to a token whose belowAlign field + * points to a field whose belowAlign field points to tok + * THEN do nothing. I think this is possible only if those fields were set + * by step 1 for a previous label. If I'm wrong and there's some weird + * situation that caused this alignment (which I think can only be + * the case if tok is a comment), then not stopping will probably + * do more harm than good. + * ELSE search upwards to set aboveAlign/belowAlign fields of tokens + * AfterLabel aligned with tok. As indicated above, if there + * are no tokens above tok that are AfterLabel aligned with it, + * then tok's aboveAlign field must be set to point to its + * covering token. + */ + int curLabelLine = pcalStartLine ; + + while ( (curLabelLine <= pcalEndLine) + && (curLabelLine < spec.length) ) { + /* + * set curLabelLine to the first line at or below its current + * position that begins with a label. + */ + if ( (spec[curLabelLine].length > 1) + && (spec[curLabelLine][0].type == Token.PCAL_LABEL) ) { + + Token tok = spec[curLabelLine][1] ; + int alignCol = tok.column ; + + // Perform step 1 for token tok + int curLine = curLabelLine + 1 ; + + // spec[alignLine][alignItem] is the token to which the + // next token to be aligned is aligned with. + int alignLine = curLabelLine ; + int alignItem = 1 ; + boolean notDone = true ; + // Test of curLine < spec.length added 4 Aug 2015 to fix + // bug caused by running off the end of the spec when called by + // TLATeX to format a snippet of PlusCal code. See comment from + // this date in TokenizeSpec. + while (notDone && (curLine < spec.length)) { + int curItem = 0 ; + // If spec[alignLine][alignItem] is to be aligned with a token + // on this line, then the token is spec[curLine][nextItem] + // + boolean shouldSkip = false ; + if (spec[curLine].length != 0) { + if (spec[curLine][0].type == Token.PCAL_LABEL) { + // line begins with label + if (spec[curLine].length > 1) { + curItem = 1 ; + } + else { + // The label is the only token on the line. Stop + // iff the label is to the left of the alignment + // column. + notDone = (spec[curLine][0].column >= alignCol) ; + shouldSkip = true ; + } + } + else { + // line doesn't begin with label + } + if (!shouldSkip) { + if (spec[curLine][curItem].column < alignCol) { + notDone = false ; + } + else if (spec[curLine][curItem].column == alignCol) { + spec[alignLine][alignItem].belowAlign = + new Position(curLine, curItem) ; + spec[curLine][curItem].aboveAlign = + new Position(alignLine, alignItem) ; + alignLine = curLine ; + alignItem = curItem ; + } + } + } + curLine++ ; + if ( (curLine > pcalEndLine) + || (curLine >= spec.length)) { + notDone = false ; + } + } + + // Perform step 2 for token tok + + if ( (tok.aboveAlign.line != -1) + && (tok.aboveAlign.toToken(spec).belowAlign.equals(new Position(curLabelLine, 1)))) { + // already aligned with above tokens so do nothing + } + else { + curLine = curLabelLine - 1 ; + alignLine = curLabelLine ; + alignItem = 1 ; + notDone = true ; + while (notDone) { + if ( (spec[curLine].length > 0 ) + && (spec[curLine][0].column <= alignCol) + && ( (spec[curLine][0].type != Token.PCAL_LABEL) + || ( (spec[curLine].length > 1) + && (spec[curLine][1].column <= alignCol) ) ) + // the conjunct above causes the line to be skipped + // if the only token to the left of tok on this + // line is an initial label followed by a token + // to the right of tok. + ) { + if (spec[curLine][0].column == alignCol) { + // spec[curLine][0] is to be aligned with + // spec[alignLine][alignItem] + spec[alignLine][alignItem].aboveAlign = + new Position(curLine, 0) ; + spec[curLine][0].belowAlign = + new Position(alignLine, alignItem) ; + alignLine = curLine ; + alignItem = 0 ; + } + else if (spec[alignLine][alignItem].aboveAlign.line == -1) { + // we need to align spec[alignLine][alignItem] with the + // right-most token on line curLine with column \leq alignCol + int item = 0 ; + while ( (item < spec[curLine].length) + && (spec[curLine][item].column <= alignCol)) { + item++; + } + // item is now either off the line or pointing to a token + // to the right of the alignment token ; + item-- ; + + // We set the aboveAlign pointer of + // to point to spec[curLine][item] if the latter token is either + // the first one on its line, or else the token to its left is + // either a label or a PlusCal token. + // If that's the case, we also set the belowAlign pointer of + // spec[curLine][item] if that token is in the same column + // as spec[alignLine][alignItem]. + // + Token altok = null ; + if (item > 0) { + altok = spec[curLine][item-1] ; + } + if ( (altok != null) + && ( (altok.type == Token.PCAL_LABEL) + || ( (altok.type == Token.BUILTIN) + && BuiltInSymbols.IsBuiltInSymbol(altok.string, true) + && ! BuiltInSymbols.IsBuiltInSymbol(altok.string, false) + ) + ) + ) { + spec[alignLine][alignItem].aboveAlign = + new Position(curLine, item) ; + + if (spec[curLine][item].column == alignCol) { + spec[curLine][item].belowAlign = + new Position(alignLine, alignItem) ; + } + } + // This ends step 2 + notDone = false ; + } + else { + notDone = false ; + } + } + curLine-- ; + if (curLine < pcalStartLine) { + notDone = false ; + } + } + } + } + curLabelLine++ ; + } + } + /************************************************************************* + * The following are functions used in FindAlignments. * + *************************************************************************/ + private static boolean isLeftComment(Token[][] spec, Position p) + /*********************************************************************** + * A left-comment is a comment token that is the first token on a line * + * and has another token to its right. This method returns true iff * + * the token at position p of spec is a left-comment. * + ***********************************************************************/ + { return (p.item == 0) + && (spec[p.line][p.item].type == Token.COMMENT) + && (spec[p.line].length > 1) ; + } ; + + private static boolean isRightComment(Token[][] spec, Position p) + /*********************************************************************** + * A right-comment is a comment token that is the last token on its * + * line. This method returns true iff the token at position p in spec * + * is a right-comment. * + ***********************************************************************/ + { return (p.item == spec[p.line].length - 1) + && (spec[p.line][p.item].type == Token.COMMENT) ; + } ; + + private static Position coveringPosition( + Token[][] spec, Position p, boolean ignore) + /*********************************************************************** + * A token t1 COVERS a token t2 if t1 lies on an earlier line than t2 * + * and t1.column \leq t2.column. This method searches upwards to find * + * the first line with a token that covers the token at position p, * + * and then returns the position of the right-most token on that line * + * that covers the token at p. When searching for the covering token, * + * left-comments are ignored iff the ignore parameter is true. * + ***********************************************************************/ + { /********************************************************************* + * Find covering line. * + *********************************************************************/ + int line = p.line - 1 ; + Token tok = p.toToken(spec) ; + boolean notDone = true ; + while ((line >= 0) && notDone) + { if (spec[line].length > 0) + { if (spec[line][0].type == Token.PROLOG) + { line = -1 ; + notDone = false; + } + else + { int item = 0 ; + if (ignore && isLeftComment(spec, new Position(line, 0))) + { item = 1 ; } + if (spec[line][item].column <= tok.column) + { notDone = false ;} ; + } + }; // END if (spec[line].length > 0) + if (notDone) {line = line - 1 ;}; + } // END while ((line >= 0) && notDone) + + /********************************************************************** + * If no covering line, return (-1, 0). * + **********************************************************************/ + if (line == -1) {return new Position(-1, 0);} ; + + /********************************************************************** + * Find covering item. * + **********************************************************************/ + int item = 0; + int nsItem = 0; + /******************************************************************** + * item is the current item. * + * nsItem is the last non-subscript item found that covers tok. * + ********************************************************************/ + boolean dashFound = false ; + if (spec[line][0].type == Token.DASHES) + { dashFound = true;} ; + while ( (! dashFound) + && (item + 1 < spec[line].length) + && (spec[line][item + 1].column <= tok.column)) + { if (spec[line][item+1].type == Token.DASHES) + {dashFound = true;} ; + item = item + 1; + if (!spec[line][item].subscript) + {nsItem = item;}; + }; + if (dashFound) {return new Position(-1, 0);} ; + + /********************************************************************** + * Return (line, nsItem). * + **********************************************************************/ + return new Position(line, nsItem); + } ; + + private static Position blockingPosition(Token[][] spec, Position p) + /*********************************************************************** + * Searches upwards from position p to find the first token at the * + * same column or to the right of the token at p that is not * + * a subscript token. * + ***********************************************************************/ + { int line = p.line - 1 ; + int item = 0 ; + Token tok = p.toToken(spec) ; + boolean notDone = true ; + while ((line >= 0) && notDone) + { if (spec[line].length > 0) + { if (spec[line][0].type == Token.PROLOG) + { line = -1 ; + notDone = false; + } + else + { item = 0 ; + if (isLeftComment(spec, new Position(line, 0))) + { item = 1 ; } ; + while (notDone && (item < spec[line].length)) + { if ( (spec[line][item].column >= tok.column) + && (! spec[line][item].subscript)) + { notDone = false ; } + else + { item = item+1; }; + } ; + } ; + }; // END if (spec[line].length > 0) + if (notDone) {line = line - 1 ;} ; + } // END while ((line >= 0) && notDone) + + /********************************************************************** + * If no token found, return (-1, 0). * + **********************************************************************/ + if (line == -1) {return new Position(-1, 0);} ; + + /********************************************************************** + * Return (line, item). * + **********************************************************************/ + return new Position(line, item); + } ; + + private static void setSubscriptField(Token[][] spec) + /*********************************************************************** + * Sets the subscript field of the tokens. (This field is true iff * + * the token is part of a sub- or superscript.) Upon encountering a * + * "^" or a token that is a built-in symbol with symbolType - * + * Symbol.SUBSCRIPTED token, the next token or all tokens in the * + * properly parenthesized expression that follows it are * + * subscripted--iff all those tokens lie on the same line. * + ***********************************************************************/ + { int line = 0 ; + while (line < spec.length) + { int item = 0 ; + int startSub = -1 ; + /**************************************************************** + * If a subscript has begun, then this is its first item; * + * otherwise, it equals -1. * + ****************************************************************/ + int nestingDepth = 0 ; + /**************************************************************** + * The current parenthesis nesting level inside a subscript, or * + * 0 if not in a subscript. * + ****************************************************************/ + + + while (item < spec[line].length) + { Token tok = spec[line][item] ; + /************************************************************* + * tok is the current token. * + *************************************************************/ + if (startSub == -1) + { /************************************************************ + * A subscript has not yet begun. * + ************************************************************/ + if ( (tok.type == Token.BUILTIN) + && ( (BuiltInSymbols.GetBuiltInSymbol( + tok.string, true).symbolType + == Symbol.SUBSCRIPTED) + || (tok.string.equals("^")))) + { + startSub = item + 1 ; + } // END then OF if ((tok.type == Token.BUILTIN) ...) + else + { /********************************************************* + * Do nothing. * + *********************************************************/ + }; // END else OF if ((tok.type = Token.BUILTIN) ...) + + } // END then OF if (startSub == -1) + else + { /************************************************************ + * A subscript has begun. * + * * + * Set symType to the symbol type of the token, or * + * NOT_A_SYMBOL if it isn't a symbol. * + ************************************************************/ + int symType = Symbol.NOT_A_SYMBOL ; + if (tok.type == Token.BUILTIN) + { symType = + BuiltInSymbols.GetBuiltInSymbol( + tok.string, true).symbolType ; + }; + + if ( ( (nestingDepth == 0) + && (symType != Symbol.LEFT_PAREN)) + || ( (nestingDepth == 1) + && (symType == Symbol.RIGHT_PAREN))) + { /********************************************************* + * This ends the subscript. Set the subscript field of * + * all tokens from startSub to item and reset startSub. * + *********************************************************/ + nestingDepth = 0 ; + while (startSub <= item) + { spec[line][startSub].subscript = true ; + startSub = startSub + 1; + } + startSub = -1 ; + } // END then OF if (((nestingDepth == 0)... )) + else + { /********************************************************* + * The subscript continues. * + *********************************************************/ + if (symType == Symbol.LEFT_PAREN) + { nestingDepth = nestingDepth + 1; } + else + { if (symType == Symbol.RIGHT_PAREN) + { nestingDepth = nestingDepth - 1; }; + }; + }; // END else OF if (((nestingDepth == 0)... )) + + }; // END else OF if (startSub == -1) + + item = item + 1 ; + } ; // END while (item < spec[line].length) + + + line = line + 1; + } // END while (line < spec.length) + + } ; + + +} + +/* last modified on Sun 5 August 2012 at 17:07:48 PST by lamport */ diff --git a/tlatools/src/tla2tex/LaTeXOutput.java b/tlatools/src/tla2tex/LaTeXOutput.java index 051caaa6f6baebf5b49ca2d98061bc36f74d86dc..001b9aaa128ee9b3741d23611d87cc955d091637 100644 --- a/tlatools/src/tla2tex/LaTeXOutput.java +++ b/tlatools/src/tla2tex/LaTeXOutput.java @@ -41,6 +41,7 @@ import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStreamReader; +import java.util.Arrays; import java.util.Vector; import util.ToolIO; @@ -566,7 +567,7 @@ private static void InnerWriteAlignmentFile(Token[][] spec, line = line + 1; } ; - PosAndCol.sort(posCols); + Arrays.sort(posCols); /************************************************************************ * Compute preSpace fields for tokens in the order they appear in the * diff --git a/tlatools/src/tla2tex/PosAndCol.java b/tlatools/src/tla2tex/PosAndCol.java index 828613fb0a932b67875a25e1a9470522c352113a..d33ab345896177cef279f8edbb55c5126b86682e 100644 --- a/tlatools/src/tla2tex/PosAndCol.java +++ b/tlatools/src/tla2tex/PosAndCol.java @@ -13,7 +13,7 @@ package tla2tex; // import tlatex.Position; //import java.util.Comparator; -public class PosAndCol extends Position +public class PosAndCol extends Position implements Comparable<PosAndCol> { public int column ; public PosAndCol(int l, int i, int c) @@ -22,72 +22,19 @@ public class PosAndCol extends Position column = c ; } ; - /************************************************************************ - * The following heap-sorting method was obtained by trivially modifying * - * a method found on the web with the following header: * - * * - * HeapSortAlgorithm.java 1.0 95/06/23 Jason Harrison * - * Copyright (c) 1995 University of British Columbia * - * * - * Permission to use, copy, modify, and distribute this software * - * and its documentation for NON-COMMERCIAL purposes and without * - * fee is hereby granted provided that this copyright notice * - * appears in all copies. * - * * - * UBC MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY * - * OF THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT * - * LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS * - * FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT. UBC SHALL NOT BE * - * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF * - * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS * - * DERIVATIVES * - ************************************************************************/ - public static void sort(PosAndCol a[]) - { int N = a.length; - for (int k = N/2; k > 0; k--) - { downHeap(a, k, N); - } - do { PosAndCol T = a[0]; - a[0] = a[N - 1]; - a[N - 1] = T; - N = N - 1; - downHeap(a, 1, N); - } - while (N > 1); - } - private static void downHeap(PosAndCol a[], int k, int N) - { PosAndCol T = a[k - 1]; - while (k <= N/2) - { int j = k + k; - if ((j < N) && PosAndCol.lessThan(a[j - 1],a[j])) - { j++; } - if ( ! PosAndCol.lessThan(T, a[j - 1]) ) - { break; } - else - { a[k - 1] = a[j - 1]; - k = j; - } - } - a[k - 1] = T; - } - - private static boolean lessThan(PosAndCol pc1, PosAndCol pc2) - { if (pc1.column < pc2.column) - { return true ;} - else - { if (pc1.column == pc2.column) - { return (pc1.line < pc2.line) ; - } - else - { return false ; - } - } - } public String toString() { return "[line |-> " + line + ", item |-> " + item + ", col |-> " + column + "]"; } + + @Override + public int compareTo(final PosAndCol other) { + if (column == other.column) { + return Integer.compare(line, other.line); + } + return Integer.compare(column, other.column); + } } /* Last modified on Tue 18 Sep 2007 at 6:51:12 PST0by lamport */ diff --git a/tlatools/src/tla2tex/README b/tlatools/src/tla2tex/README index d71e841d9028a3d1bc96ec18a96f3f5c92c6ea99..dec1fad9fc82fd6ffaf5b9c65d5e9b64306ac6c4 100644 --- a/tlatools/src/tla2tex/README +++ b/tlatools/src/tla2tex/README @@ -1,114 +1,121 @@ -FILES ------ -These are the source files for the TLATeX program. The main program -is in TLA.java. In addition to the .java and .class files, there are: - -tlatex.sty : A style file used by the LaTeX output. - -help.txt : A file with the help message. - -info.txt : A file with a reasonably complete description of TLATeX. - -words.all : - A list of common English words. It was obtained by taking the - lower-case words from a Linux list, and adding the following, - which were inexplicably missing. - - a may I TLA miscellany - -old : a directory of old versions. - -POSSIBLE ENHANCEMENTS ---------------------- -1. When formatting an individual comment, TLATeX keeps track of - which identifiers appear in the comment as TLA tokens. It - turns ambiguous instances of those identifiers into TLA tokens - only for identifiers that are not words. Possibly do it - even if they are words--or perhaps have a sublist of common - words and do it only for uncommon words. - -2. Introduce a mechanism for turning operators into TeX macros--for - example, so Integral(a+b, c, d) gets typeset as - - \int^{a+b}_{c} d - - This is nontrivial because it requires finding the arguments, - which in turn requires counting parentheses and looking for commas - and right-parens. A simpler possibility is just replacing - certain identifiers by symbols--e.g., alpha -> \alpha. - - The specification of such replacements would be in the prolog or - epilog, so we'd have to introduce some notation for identifying - pro/epilog lines as TLATeX commands. (This notation should generalize - to arbitrary tools.) - - - -BUGS ----- -Here are the known bugs in TLATeX: - -1. If a ' in a comment is immediately followed by a character, as in - - `token'. - - then TLATeX inserts a spurious space before the ".". Search - for "LEFT_SQUOTE:" in TokenizeComment.java to see how to fix this. - But... a similar problem occurs with `^ ... ^' text, since the `^ - and ^' effectively act as spaces for determining whether to add space - between adjacent tokens. - - The same problem also occurs with a `~ ... ~' term, but in that case, - there may be no solution because the entire term has to be disappeared. - - Some uniform approach to handling this is called for. It seems like - the best approach is to try to increase the size of an immediately - following token by a ` or `^, and of an immediately preceding token - by the ' or ^'. This requires adding an explicit width field to - a CToken object, or something equivalent. - -2. The record component foo.WF_foo will be printed as - - foo.WF - foo - - Fixing this unlikely problem requires fixing the lexing algorithm - in TokenizeSpec.java. - -3. The input - - LET x... - IN x... - -produces - - LET x... - IN x... - -It looks like the two spaces after the "IN" produce the extra space. -The input - - LET x... - IN x... - -produces the correct output. - ----------- - - -Revision History -^^^^^^^^^^^^^^^^ -Version 0.95 of 11 November 2001 - - Modified FindAlignments to do InfixArg alignment only if the - symbol to the left is an infix operator. - - Modified LaTeXOutput.InnerWriteLaTeXFile to end a multiline - comment with a comment line that is below-aligned with a - non-comment line. (Previously, that case had been considered - an error.) - - Fixed the problem of TLATeX hanging if latex or dvips produced - a lot of output by: - * Adding classes ExecuteCommand and GobbleOutput - * Modifying TLA.MakePSFile and LaTeXOutput.RunLatex to call - ExecuteCommand. - -Last modified on Wed 8 February 2012 at 13:56:11 PST by lamport +FILES +----- +These are the source files for the TLATeX program. The main program +is in TLA.java. In addition to the .java and .class files, there are: + +tlatex.sty : A style file used by the LaTeX output. + +help.txt : A file with the help message. + +info.txt : A file with a reasonably complete description of TLATeX. + +words.all : + A list of common English words. It was obtained by taking the + lower-case words from a Linux list, and adding the following, + which were inexplicably missing. + + a may I TLA miscellany + +old : a directory of old versions. + +POSSIBLE ENHANCEMENTS +--------------------- +1. When formatting an individual comment, TLATeX keeps track of + which identifiers appear in the comment as TLA tokens. It + turns ambiguous instances of those identifiers into TLA tokens + only for identifiers that are not words. Possibly do it + even if they are words--or perhaps have a sublist of common + words and do it only for uncommon words. + +2. Introduce a mechanism for turning operators into TeX macros--for + example, so Integral(a+b, c, d) gets typeset as + + \int^{a+b}_{c} d + + This is nontrivial because it requires finding the arguments, + which in turn requires counting parentheses and looking for commas + and right-parens. A simpler possibility is just replacing + certain identifiers by symbols--e.g., alpha -> \alpha. + + The specification of such replacements would be in the prolog or + epilog, so we'd have to introduce some notation for identifying + pro/epilog lines as TLATeX commands. (This notation should generalize + to arbitrary tools.) + + + +BUGS +---- +Here are the known bugs in TLATeX: + +1. If a ' in a comment is immediately followed by a character, as in + + `token'. + + then TLATeX inserts a spurious space before the ".". Search + for "LEFT_SQUOTE:" in TokenizeComment.java to see how to fix this. + But... a similar problem occurs with `^ ... ^' text, since the `^ + and ^' effectively act as spaces for determining whether to add space + between adjacent tokens. + + The same problem also occurs with a `~ ... ~' term, but in that case, + there may be no solution because the entire term has to be disappeared. + + Some uniform approach to handling this is called for. It seems like + the best approach is to try to increase the size of an immediately + following token by a ` or `^, and of an immediately preceding token + by the ' or ^'. This requires adding an explicit width field to + a CToken object, or something equivalent. + +2. The record component foo.WF_foo will be printed as + + foo.WF + foo + + Fixing this unlikely problem requires fixing the lexing algorithm + in TokenizeSpec.java. + +3. The input + + LET x... + IN x... + +produces + + LET x... + IN x... + +It looks like the two spaces after the "IN" produce the extra space. +The input + + LET x... + IN x... + +produces the correct output. + + +4. It looks like something goes wrong with the formatting of tokens + that occur at the beginning of a line of a multiline comment. For + example, something like Brxwee that would normally be italicized + if it occured in the middle of text is not italicized if it's + at the beginning of the comment line (in the .tla file). + +---------- + + +Revision History +^^^^^^^^^^^^^^^^ +Version 0.95 of 11 November 2001 + - Modified FindAlignments to do InfixArg alignment only if the + symbol to the left is an infix operator. + - Modified LaTeXOutput.InnerWriteLaTeXFile to end a multiline + comment with a comment line that is below-aligned with a + non-comment line. (Previously, that case had been considered + an error.) + - Fixed the problem of TLATeX hanging if latex or dvips produced + a lot of output by: + * Adding classes ExecuteCommand and GobbleOutput + * Modifying TLA.MakePSFile and LaTeXOutput.RunLatex to call + ExecuteCommand. + +Last modified on Wed 8 February 2012 at 13:56:11 PST by lamport diff --git a/tlatools/src/tla2tex/TLA.java b/tlatools/src/tla2tex/TLA.java index 11eeace340a36009a88c7553233c344dddeb62ee..7109379a265aa1b0c0d8d0b1a5566f21ea93f6ce 100644 --- a/tlatools/src/tla2tex/TLA.java +++ b/tlatools/src/tla2tex/TLA.java @@ -126,6 +126,7 @@ import java.io.File; import java.io.IOException; import util.FileUtil; +import util.TLAConstants; import util.ToolIO; public class TLA @@ -437,7 +438,7 @@ public class TLA Parameters.TLAOutFile = args[nextArg]; if (Parameters.TLAOutFile.indexOf(".") == -1) { - Parameters.TLAOutFile = Parameters.TLAOutFile + ".tla"; + Parameters.TLAOutFile = Parameters.TLAOutFile + TLAConstants.Files.TLA_EXTENSION; } if (HasPathPrefix(Parameters.TLAOutFile)) { @@ -636,7 +637,7 @@ public class TLA ********************************************************************/ if (args[maxArg].indexOf(".") == -1) { - Parameters.TLAInputFile = args[maxArg] + ".tla"; + Parameters.TLAInputFile = args[maxArg] + TLAConstants.Files.TLA_EXTENSION; } else { Parameters.TLAInputFile = args[maxArg]; diff --git a/tlatools/src/tla2tex/TokenizeSpec.java b/tlatools/src/tla2tex/TokenizeSpec.java index 41ce234d505d5b747704a7e4e0d9fd34c3618044..993d631d2d1373e7223fb7415e27683960e65d6a 100644 --- a/tlatools/src/tla2tex/TokenizeSpec.java +++ b/tlatools/src/tla2tex/TokenizeSpec.java @@ -469,9 +469,6 @@ package tla2tex; import java.util.Hashtable; import java.util.Vector; -import pcal.MappingObject.EndTLAToken; -import pcal.PCalLocation; - /** * @author lamport * @@ -1737,6 +1734,17 @@ public class TokenizeSpec /* * Prevent null pointer exception in PlusCal algorithm that was * not ended. + * + * Note added 4 Aug 2015 by LL. It appears that we will get here with hasPcal + * = true and pcalEnd = null TLA2TeX is called to format a snippet of PCal + * code. So, I would expect setting pcalEnd to this dummy non-null value l + * often to cause bugs later on in the processing. However, today is the first + * time I encountered such a bug. I seem to have fixed this with a patch + * to FindAlignments.FindLabelAlignments described in a comment dated today. + * I didn't try to understand that code to know if the bug actually was a + * reasonable fix or whether it prevents an exception but can produce + * unreasonably bad formatting. Moreover, it seems likely that this dummy + * value of pcalEnd will trigger other bugs that I haven't encountered yet. */ if (hasPcal) { if (pcalEnd == null) { diff --git a/tlatools/src/tlc2/Generator.java b/tlatools/src/tlc2/Generator.java deleted file mode 100644 index 096be20ef479a84b5b36a2764871fcff285a6533..0000000000000000000000000000000000000000 --- a/tlatools/src/tlc2/Generator.java +++ /dev/null @@ -1,189 +0,0 @@ -// Copyright (c) 2003 Compaq Corporation. All rights reserved. -// Portions Copyright (c) 2003 Microsoft Corporation. All rights reserved. -// Last modified on Mon 30 Apr 2007 at 16:11:13 PST by lamport -// modified on Fri Jun 1 15:26:01 PDT 2001 by yuanyu - -package tlc2; - -import tlc2.output.EC; -import tlc2.output.MP; -import tlc2.tool.Simulator; -import tlc2.util.RandomGenerator; -import util.ToolIO; - -public class Generator { - /* - * This TLA trace generator generates random execution traces: - * srcjava tlc.Generator spec[.tla] - * - * The command line provides the following options: - * o -deadlock: do not check for deadlock. - * Defaults to checking deadlock if not specified - * o -f file: the file storing the traces. Defaults to spec_trace - * o -d num: the max length of the trace. Defaults to 10 - * o -config file: the config file. Defaults to spec.cfg - * o -coverage seconds: collect coverage information on the spec, - * print out the information every seconds - * Defaults to no coverage if not specified - * o -n num: the max number of traces. Defaults to 10 - * o -s: the seed for random simulation - * Defaults to a random seed if not specified - * o -aril num: Adjust the seed for random simulation - * Defaults to 0 if not specified - */ - public static void main(String[] args) { - System.out.println("TLC trace generator, " + TLCGlobals.versionOfTLC); - - String mainFile = null; - String traceFile = null; - boolean deadlock = true; - String configFile = null; - int traceDepth = 10; - int traceNum = 10; - boolean noSeed = true; - long seed = 0; - long aril = 0; - - int index = 0; - while (index < args.length) { - if (args[index].equals("-f")) { - index++; - if (index >= args.length) { - printErrorMsg("Error: no argument given for -f option."); - return; - } - traceFile = args[index++]; - } - else if (args[index].equals("-deadlock")) { - index++; - deadlock = false; - } - else if (args[index].equals("-d")) { - index++; - if (index >= args.length) { - printErrorMsg("Error: no argument given for -d option."); - return; - } - traceDepth = Integer.parseInt(args[index]); - index++; - } - else if (args[index].equals("-n")) { - index++; - if (index >= args.length) { - printErrorMsg("Error: no argument given for -n option."); - return; - } - traceNum = Integer.parseInt(args[index]); - index++; - } - else if (args[index].equals("-coverage")) { - index++; - if (index < args.length) { - try { - TLCGlobals.coverageInterval = Integer.parseInt(args[index]) * 60 * 1000; - if (TLCGlobals.coverageInterval < 0) { - printErrorMsg("Error: expect a nonnegative integer for -coverage option."); - return; - } - index++; - } - catch (Exception e) { - printErrorMsg("Error: An integer for coverage report interval required." + - " But encountered " + args[index]); - return; - } - } - else { - printErrorMsg("Error: coverage report interval required."); - return; - } - } - else if (args[index].equals("-s")) { - index++; - if (index < args.length) { - seed = Long.parseLong(args[index++]); - noSeed = false; - } - else { - printErrorMsg("Error: seed required."); - return; - } - } - else if (args[index].equals("-aril")) { - index++; - if (index < args.length) { - aril = Long.parseLong(args[index++]); - } - else { - printErrorMsg("Error: aril required."); - return; - } - } - else if (args[index].equals("-config")) { - index++; - if (index < args.length) { - configFile = args[index]; - int len = configFile.length(); - if (configFile.startsWith(".cfg", len-4)) { - configFile = configFile.substring(0, len-4); - } - index++; - } - else { - printErrorMsg("Error: config file required."); - return; - } - } - else { - if (args[index].charAt(0) == '-') { - printErrorMsg("Error: unrecognized option: " + args[index]); - return; - } - if (mainFile != null) { - printErrorMsg("Error: more than one input files: " + mainFile - + " and " + args[index]); - return; - } - mainFile = args[index++]; - int len = mainFile.length(); - if (mainFile.startsWith(".tla", len-4)) { - mainFile = mainFile.substring(0, len-4); - } - } - } - if (mainFile == null) { - printErrorMsg("Error: Missing input TLA+ module."); - return; - } - if (traceFile == null) traceFile = mainFile + "_trace"; - if (configFile == null) configFile = mainFile; - - // Start generating traces: - try { - RandomGenerator rng = new RandomGenerator(); - if (noSeed) { - seed = rng.nextLong(); - rng.setSeed(seed); - } - else { - rng.setSeed(seed, aril); - } - ToolIO.out.println("Generating random traces with seed " + seed + "."); - Simulator simulator = new Simulator(mainFile, configFile, traceFile, - deadlock, traceDepth, traceNum, - rng, seed, true, null, /* no spec obj */ null); - simulator.simulate(); - } - catch (Exception e) { - // Assert.printStack(e); - MP.printError(EC.GENERAL, "generating traces", e); // LL changed call 7 April 2012 - } - //System.exit(0); //SZ: no-op removed - } - - // TODO replace - private static void printErrorMsg(String msg) { - MP.printError(EC.WRONG_COMMANDLINE_PARAMS_SIMULATOR, msg); - } - -} diff --git a/tlatools/src/tlc2/REPLSpecWriter.java b/tlatools/src/tlc2/REPLSpecWriter.java new file mode 100644 index 0000000000000000000000000000000000000000..74af71313721d8eaca7062befd20b187a1fdffd5 --- /dev/null +++ b/tlatools/src/tlc2/REPLSpecWriter.java @@ -0,0 +1,84 @@ +package tlc2; + +import java.io.ByteArrayOutputStream; +import java.util.List; + +import tlc2.output.AbstractSpecWriter; +import tlc2.output.SpecWriterUtilities; + +class REPLSpecWriter extends AbstractSpecWriter { + private static final String EXPRESSION_OPEN = "\"EXPR_TLATE_BEGIN\""; + private static final String EXPRESSION_CLOSE = "\"EXPR_TLATE_END\""; + private static final String ASSUME_PREFIX = "ASSUME PrintT(" + EXPRESSION_OPEN + ") /\\ PrintT(\n"; + private static final String ASSUME_SUFFIX = "\n) /\\ PrintT(" + EXPRESSION_CLOSE + ")\n"; + + REPLSpecWriter(final String specName, final List<String> expressions) { + super(true); + + tlaBuffer.append(SpecWriterUtilities.getExtendingModuleContent(specName, + "Naturals", "Reals", "Sequences", "Bags", + "FiniteSets", "TLC")); + tlaBuffer.append(ASSUME_PREFIX); + tlaBuffer.append(String.join("\n", expressions)); + tlaBuffer.append(ASSUME_SUFFIX); + } + + + static class REPLLogConsumerStream extends ByteArrayOutputStream { + private boolean inResponse; + private boolean haveCollected; + private StringBuilder currentLine; + + private String collectedContent; + + REPLLogConsumerStream() { + inResponse = false; + haveCollected = false; + currentLine = new StringBuilder(); + } + + String getCollectedContent() { + return collectedContent; + } + + // Being wrapped in a Buffer invokes this method, we sub-optimally pass it off to our real digestion below + @Override + public void write(final byte b[], final int off, final int len) { + for (int i = off; i < (off + len); i++) { + write(b[i]); + } + } + + @Override + public void write(final int b) { + if (haveCollected) { + return; + } else { + if (b == '\n') { + if (inResponse) { + if (EXPRESSION_CLOSE.equals(currentLine.toString())) { + haveCollected = true; + + final String allCollectedContent = toString(); + final int length = allCollectedContent.length(); + collectedContent = allCollectedContent.substring(0, (length - (EXPRESSION_CLOSE.length() + 1))); + } else { + super.write(b); + } + } else if (EXPRESSION_OPEN.equals(currentLine.toString())) { + inResponse = true; + } + + currentLine = new StringBuilder(); + } else { + if (inResponse) { + super.write(b); + } + + // casting to char is probably not the best thing - suffices for the majority of the presumed cases + currentLine.append((char)b); + } + } + } + } +} diff --git a/tlatools/src/tlc2/TLC.java b/tlatools/src/tlc2/TLC.java index f8e2b456b4b5d122316eeffee76b7159bd808f8f..2be228550dbfe50d760faf65896e8f05b7d78ebd 100644 --- a/tlatools/src/tlc2/TLC.java +++ b/tlatools/src/tlc2/TLC.java @@ -5,91 +5,147 @@ package tlc2; +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.BufferedWriter; import java.io.File; -import java.io.FileNotFoundException; -import java.net.UnknownHostException; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.FileWriter; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.PipedInputStream; +import java.io.PipedOutputStream; +import java.io.PrintStream; +import java.text.SimpleDateFormat; import java.util.ArrayList; -import java.util.Enumeration; +import java.util.Date; +import java.util.LinkedHashMap; import java.util.List; +import java.util.Map; +import java.util.Random; +import java.util.TimeZone; +import java.util.concurrent.Phaser; import model.InJarFilenameToStream; import model.ModelInJar; -import tla2sany.modanalyzer.ParseUnit; -import tla2sany.modanalyzer.SpecObj; +import tlc2.input.MCOutputPipeConsumer; +import tlc2.input.MCParser; +import tlc2.input.MCParserResults; import tlc2.output.EC; import tlc2.output.MP; -import tlc2.tool.AbstractChecker; -import tlc2.tool.Cancelable; +import tlc2.output.Messages; +import tlc2.output.TLAMonolithCreator; +import tlc2.output.TeeOutputStream; import tlc2.tool.DFIDModelChecker; import tlc2.tool.ModelChecker; import tlc2.tool.Simulator; import tlc2.tool.fp.FPSet; import tlc2.tool.fp.FPSetConfiguration; +import tlc2.tool.fp.FPSetFactory; +import tlc2.tool.impl.FastTool; +import tlc2.tool.impl.ModelConfig; +import tlc2.tool.impl.SpecProcessor; import tlc2.tool.management.ModelCheckerMXWrapper; import tlc2.tool.management.TLCStandardMBean; +import tlc2.util.DotStateWriter; import tlc2.util.FP64; +import tlc2.util.IStateWriter; +import tlc2.util.NoopStateWriter; import tlc2.util.RandomGenerator; -import tlc2.value.Value; +import tlc2.util.StateWriter; +import tlc2.value.RandomEnumerableValues; +import util.Assert.TLCRuntimeException; import util.DebugPrinter; +import util.ExecutionStatisticsCollector; import util.FileUtil; import util.FilenameToStream; import util.MailSender; import util.SimpleFilenameToStream; +import util.TLAConstants; +import util.TLCRuntime; import util.ToolIO; import util.UniqueString; +import util.UsageGenerator; /** - * Main TLC starter class + * Main TLC starter class. + * + * <b>Note:</b> If you are using a new instance of this class in an already existant JVM which has done processing with + * tlc2 parsers, please see the class javadocs for {@link TLCRunner} + * * @author Yuan Yu * @author Leslie Lamport * @author Simon Zambrovski - * @version $Id$ */ -public class TLC -{ - +public class TLC { private static boolean MODEL_PART_OF_JAR = false; + + private enum RunMode { + MODEL_CHECK, SIMULATE; + } + // SZ Feb 20, 2009: the class has been // transformed from static to dynamic - private boolean isSimulate; + + private RunMode runMode; private boolean cleanup; private boolean deadlock; private boolean noSeed; private long seed; private long aril; + + private long startTime; private String mainFile; private String configFile; - private String dumpFile; + private String metadir; + /** + * If instantiated with a non-Noop* instance, the trace will be written to the + * user provided file (-dump parameter). + * <p> + * Contrary to plain -dump, -dot will also write out transitions from state s to + * s' if s' is already known. Thus, the resulting graph shows all successors of + * s instead of just s's unexplored ones. + * <p> + * Off (NoopStateWriter) by default. + */ + private IStateWriter stateWriter = new NoopStateWriter(); + private String fromChkpt; private int fpIndex; /** * The number of traces/behaviors to generate in simulation mode */ - public static long traceNum = Long.MAX_VALUE; + private static long traceNum = Long.MAX_VALUE; + private String traceFile = null; private int traceDepth; private FilenameToStream resolver; - private SpecObj specObj; // flag if the welcome message is already printed private boolean welcomePrinted; - // handle to the cancellable instance (MC or Simulator) - private Cancelable instance; - private FPSetConfiguration fpSetConfiguration; + private File temporaryMCOutputLogFile; + private FileOutputStream temporaryMCOutputStream; + private File specTETLAFile; + private MCParserResults mcParserResults; + private MCOutputPipeConsumer mcOutputConsumer; + private boolean avoidMonolithSpecTECreation; + private final Phaser waitingOnGenerationCompletion; + /** * Initialization */ - public TLC() - { + public TLC() { welcomePrinted = false; - isSimulate = false; // Default to model checking + runMode = RunMode.MODEL_CHECK; cleanup = false; deadlock = true; @@ -99,136 +155,222 @@ public class TLC mainFile = null; configFile = null; - dumpFile = null; fromChkpt = null; resolver = null; - fpIndex = 0; + fpIndex = new Random().nextInt(FP64.Polys.length); traceDepth = 100; - - // instance is not set - instance = null; fpSetConfiguration = new FPSetConfiguration(); - } + + avoidMonolithSpecTECreation = false; + waitingOnGenerationCompletion = new Phaser(); + waitingOnGenerationCompletion.register(); + } /* * This TLA checker (TLC) provides the following functionalities: - * 1. Simulation of TLA+ specs: java tlc2.TLC -simulate spec[.tla] - * 2. Model checking of TLA+ specs: java tlc2.TLC [-modelcheck] spec[.tla] + * 1. Simulation of TLA+ specs: + * java tlc2.TLC -simulate spec[.tla] + * 2. Model checking of TLA+ specs: + * java tlc2.TLC [-modelcheck] spec[.tla] * - * The command line also provides the following options: + * The command line also provides the following options observed for functionalities 1. & 2.: * o -config file: provide the config file. - * Defaults to spec.cfg if not provided + * Defaults to spec.cfg if not provided * o -deadlock: do not check for deadlock. - * Defaults to checking deadlock if not specified + * Defaults to checking deadlock if not specified * o -depth num: specify the depth of random simulation - * Defaults to 100 if not specified + * Defaults to 100 if not specified * o -seed num: provide the seed for random simulation - * Defaults to a random seed if not specified + * Defaults to a random seed if not specified * o -aril num: Adjust the seed for random simulation - * Defaults to 0 if not specified + * Defaults to 0 if not specified * o -recover path: recover from the checkpoint at path - * Defaults to scratch run if not specified - * o -bound: The upper limit for sets effectively limiting the number of init states - * (@see http://bugzilla.tlaplus.net/show_bug.cgi?id=264) - * Defaults to 1000000 if not specified + * Defaults to scratch run if not specified * o -metadir path: store metadata in the directory at path - * Defaults to specdir/states if not specified + * Defaults to specdir/states if not specified + * o -userFile file: A full qualified/absolute path to a file to log user + * output (Print/PrintT/...) to * o -workers num: the number of TLC worker threads - * Defaults to 1 + * Defaults to 1 * o -dfid num: use depth-first iterative deepening with initial depth num * o -cleanup: clean up the states directory - * o -dump file: dump all the states into file + * o -dump [dot] file: dump all the states into file. If "dot" as sub-parameter + * is given, the output will be in dot notation. * o -difftrace: when printing trace, show only - * the differences between successive states - * Defaults to printing full state descriptions if not specified - * (Added by Rajeev Joshi) + * the differences between successive states + * Defaults to printing full state descriptions if not specified + * (Added by Rajeev Joshi) * o -terse: do not expand values in Print statement - * Defaults to expand value if not specified + * Defaults to expand value if not specified * o -coverage minutes: collect coverage information on the spec, - * print out the information every minutes. - * Defaults to no coverage if not specified + * print out the information every minutes. + * Defaults to no coverage if not specified * o -continue: continue running even when invariant is violated - * Defaults to stop at the first violation if not specified + * Defaults to stop at the first violation if not specified + * o -lncheck: Check liveness properties at different times + * of model checking. + * Defaults to false increasing the overall model checking time. * o -nowarning: disable all the warnings - * Defaults to report warnings if not specified + * Defaults to report warnings if not specified * o -fp num: use the num'th irreducible polynomial from the list - * stored in the class FP64. + * stored in the class FP64. * o -view: apply VIEW (if provided) when printing out states. * o -gzip: control if gzip is applied to value input/output stream. - * Defaults to use gzip. + * Defaults to off if not specified * o -debug: debbuging information (non-production use) * o -tool: tool mode (put output codes on console) + * o -generateSpecTE: will generate SpecTE assets if error-states are + * encountered during model checking; this will change + * to tool mode regardless of whether '-tool' was + * explicitly specified; add on 'nomonolith' to not + * embed the dependencies in the SpecTE * o -checkpoint num: interval for check pointing (in minutes) - * Defaults to 30 - * o -fpmem num: the number of megabytes of memory used to store - * the fingerprints of found states. - * Defaults to 1/4 physical memory. (Added 6 Apr 2010 by Yuan Yu.) + * Defaults to 30 + * o -fpmem num: a value between 0 and 1, exclusive, representing the ratio + * of total system memory used to store the fingerprints of + * found states. + * Defaults to 1/4 physical memory. (Added 6 Apr 2010 by Yuan Yu.) * o -fpbits num: the number of msb used by MultiFPSet to create nested FPSets. - * Defaults to 1 + * Defaults to 1 * o -maxSetSize num: the size of the largest set TLC will enumerate. - * default: 1000000 - * + * Defaults to 1000000 + * */ - public static void main(String[] args) throws UnknownHostException, FileNotFoundException + public static void main(String[] args) throws Exception { - TLC tlc = new TLC(); + final TLC tlc = new TLC(); - // handle parameters - if (tlc.handleParameters(args)) - { - final MailSender ms = new MailSender(tlc.mainFile); - if (MODEL_PART_OF_JAR) { - tlc.setResolver(new InJarFilenameToStream(ModelInJar.PATH)); - } else { - tlc.setResolver(new SimpleFilenameToStream()); - } - // call the actual processing method - tlc.process(); - - // Send logged output by email - boolean success = ms.send(tlc.getModuleFiles()); - - // In case sending the mail has failed, we treat this as an error. - // This is needed when TLC runs on another host and email is - // the only means for the user to get access to the model checking - // results. - // With distributed TLC and CloudDistributedTLCJob in particular, - // the cloud node is immediately turned off once the TLC process has - // finished. If we were to shutdown the node even when sending out - // the email has failed, the result would be lost. - if (!success) { - System.exit(1); + // Try to parse parameters. + if (!tlc.handleParameters(args)) { + // This is a tool failure. We must exit with a non-zero exit + // code or else we will mislead system tools and scripts into + // thinking everything went smoothly. + // + // FIXME: handleParameters should return an error object (or + // null), where the error object contains an error message. + // This makes handleParameters a function we can test. + System.exit(1); + } + + if (!tlc.checkEnvironment()) { + System.exit(1); + } + + final MailSender ms = new MailSender(); + // Setup how spec files will be resolved in the filesystem. + if (MODEL_PART_OF_JAR) { + // There was not spec file given, it instead exists in the + // .jar file being executed. So we need to use a special file + // resolver to parse it. + tlc.setResolver(new InJarFilenameToStream(ModelInJar.PATH)); + } else { + // The user passed us a spec file directly. To ensure we can + // recover it during semantic parsing, we must include its + // parent directory as a library path in the file resolver. + // + // If the spec file has no parent directory, use the "standard" + // library paths provided by SimpleFilenameToStream. + final String dir = FileUtil.parseDirname(tlc.getMainFile()); + if (!dir.isEmpty()) { + tlc.setResolver(new SimpleFilenameToStream(dir)); + } else { + tlc.setResolver(new SimpleFilenameToStream()); } + } + + // Setup MailSender *before* calling tlc.process. The MailSender's task it to + // write the MC.out file. The MC.out file is e.g. used by CloudTLC to feed + // progress back to the Toolbox (see CloudDistributedTLCJob). + ms.setModelName(tlc.getModelName()); + ms.setSpecName(tlc.getSpecName()); + + // Execute TLC. + final int errorCode = tlc.process(); + + // Send logged output by email. + // + // This is needed when TLC runs on another host and email is + // the only means for the user to get access to the model + // checking results. + boolean mailSent = ms.send(tlc.getModuleFiles()); + + // Treat failure to send mail as a tool failure. + // + // With distributed TLC and CloudDistributedTLCJob in particular, + // the cloud node is immediately turned off once the TLC process + // has finished. If we were to shutdown the node even when sending + // out the email has failed, the result would be lost. + if (!mailSent) { + System.exit(1); } - // terminate - System.exit(0); + + // Be explicit about tool success. + System.exit(EC.ExitStatus.errorConstantToExitStatus(errorCode)); } + + // false if the environment (JVM, OS, ...) makes model checking impossible. + // Might also result in warnings. + private boolean checkEnvironment() { + // Not a reasons to refuse startup but warn about non-ideal garbage collector. + // See https://twitter.com/lemmster/status/1089656514892070912 for actual + // performance penalty. + if (!TLCRuntime.getInstance().isThroughputOptimizedGC()) { + MP.printWarning(EC.TLC_ENVIRONMENT_JVM_GC); + } + + return true; + } + + public static void setTraceNum(int aTraceNum) { + traceNum = aTraceNum; + } /** * This method handles parameter arguments and prepares the actual call * <strong>Note:</strong> This method set ups the static TLCGlobals variables * @return status of parsing: true iff parameter check was ok, false otherwise + * @throws IOException */ // SZ Feb 23, 2009: added return status to indicate the error in parsing - @SuppressWarnings("deprecation") + @SuppressWarnings("deprecation") // we're emitting a warning to the user, but still accepting fpmem values > 1 public boolean handleParameters(String[] args) { + String dumpFile = null; + boolean asDot = false; + boolean colorize = false; + boolean actionLabels = false; + boolean snapshot = false; + // SZ Feb 20, 2009: extracted this method to separate the // parameter handling from the actual processing - int index = 0; while (index < args.length) { if (args[index].equals("-simulate")) { - isSimulate = true; - index++; - } else if (args[index].equals("-modelcheck")) - { - isSimulate = false; + runMode = RunMode.SIMULATE; index++; + + // Simulation args can be: + // file=/path/to/file,num=4711 or num=4711,file=/path/to/file or num=4711 or + // file=/path/to/file + // "file=..." and "num=..." are only relevant for simulation which is why they + // are args to "-simulate". + if (((index + 1) < args.length) && (args[index].contains("file=") || args[index].contains("num="))) { + final String[] simArgs = args[index].split(","); + index++; // consume simulate args + for (String arg : simArgs) { + if (arg.startsWith("num=")) { + traceNum = Integer.parseInt(arg.replace("num=", "")); + } else if (arg.startsWith("file=")) { + traceFile = arg.replace("file=", ""); + } + } + } + } else if (args[index].equals("-modelcheck")) { + index++; } else if (args[index].equals("-difftrace")) { index++; @@ -248,11 +390,11 @@ public class TLC } else if (args[index].equals("-gzip")) { index++; - TLCGlobals.useGZIP = false; + TLCGlobals.useGZIP = true; } else if (args[index].equals("-terse")) { index++; - Value.expand = false; + TLCGlobals.expand = false; } else if (args[index].equals("-continue")) { index++; @@ -269,40 +411,152 @@ public class TLC { index++; TLCGlobals.tool = true; - } else if (args[index].equals("-help")) + } else if (args[index].equals("-generateSpecTE")) { + index++; + + TLCGlobals.tool = true; + + if ((index < args.length) && args[index].equals("nomonolith")) { + index++; + avoidMonolithSpecTECreation = true; + } + try { + temporaryMCOutputLogFile = File.createTempFile("mcout_", ".out"); + temporaryMCOutputLogFile.deleteOnExit(); + temporaryMCOutputStream = new FileOutputStream(temporaryMCOutputLogFile); + final BufferedOutputStream bos = new BufferedOutputStream(temporaryMCOutputStream); + final PipedInputStream pis = new PipedInputStream(); + final TeeOutputStream tos1 = new TeeOutputStream(bos, new PipedOutputStream(pis)); + final TeeOutputStream tos2 = new TeeOutputStream(ToolIO.out, tos1); + ToolIO.out = new PrintStream(tos2); + mcOutputConsumer = new MCOutputPipeConsumer(pis, null); + + // Note, this runnable's thread will not finish consuming output until just + // before the app exits and we will use the output consumer in the TLC main + // thread while it is still consuming (but at a point where the model checking + // itself has finished and so the consumer is as populated as we need it to be + // - but prior to the output consumer encountering the EC.TLC_FINISHED message.) + final Runnable r = () -> { + boolean haveClosedOutputStream = false; + try { + waitingOnGenerationCompletion.register(); + mcOutputConsumer.consumeOutput(false); + + bos.flush(); + temporaryMCOutputStream.close(); + haveClosedOutputStream = true; + + if (mcOutputConsumer.getError() != null) { + final File tempTLA = File.createTempFile("temp_tlc_tla_", ".tla"); + tempTLA.deleteOnExit(); + FileUtil.copyFile(specTETLAFile, tempTLA); + + final FileOutputStream fos = new FileOutputStream(specTETLAFile); + final FileInputStream mcOutFIS = new FileInputStream(temporaryMCOutputLogFile); + copyStream(mcOutFIS, fos); + + final FileInputStream tempTLAFIS = new FileInputStream(tempTLA); + copyStream(tempTLAFIS, fos); + + fos.close(); + mcOutFIS.close(); + tempTLAFIS.close(); + + final TLAMonolithCreator monolithCreator; + if (avoidMonolithSpecTECreation) { + monolithCreator + = new TLAMonolithCreator(TLAConstants.TraceExplore.ERROR_STATES_MODULE_NAME, + mcOutputConsumer.getSourceDirectory(), + mcParserResults.getAllExtendedModules()); + } else { + final List<File> extendedModules = mcOutputConsumer.getExtendedModuleLocations(); + monolithCreator + = new TLAMonolithCreator(TLAConstants.TraceExplore.ERROR_STATES_MODULE_NAME, + mcOutputConsumer.getSourceDirectory(), + extendedModules, + mcParserResults.getAllExtendedModules(), + mcParserResults.getAllInstantiatedModules()); + } + + monolithCreator.copy(); + } + + waitingOnGenerationCompletion.arriveAndDeregister(); + } catch (final Exception e) { + MP.printMessage(EC.GENERAL, + "A model checking error occurred while parsing tool output; the execution " + + "ended before the potential " + + TLAConstants.TraceExplore.ERROR_STATES_MODULE_NAME + + " generation stage."); + } finally { + if (!haveClosedOutputStream) { + try { + bos.flush(); + temporaryMCOutputStream.close(); + } catch (final Exception e) { } + } + } + }; + (new Thread(r)).start(); + + MP.printMessage(EC.GENERAL, + "Will generate a " + TLAConstants.TraceExplore.ERROR_STATES_MODULE_NAME + + " file pair if error states are encountered."); + } catch (final IOException ioe) { + printErrorMsg("Failed to set up piped output consumers; no potential " + + TLAConstants.TraceExplore.ERROR_STATES_MODULE_NAME + " will be generated: " + + ioe.getMessage()); + mcOutputConsumer = null; + } + } else if (args[index].equals("-help") || args[index].equals("-h")) { printUsage(); return false; - } else if (args[index].equals("-config")) + } else if (args[index].equals("-lncheck")) { index++; if (index < args.length) { - configFile = args[index]; - int len = configFile.length(); - if (configFile.startsWith(".cfg", len - 4)) - { - configFile = configFile.substring(0, len - 4); - } + TLCGlobals.lnCheck = args[index].toLowerCase(); index++; } else { - printErrorMsg("Error: expect a file name for -config option."); + printErrorMsg("Error: expect a strategy such as final for -lncheck option."); return false; } - } else if (args[index].equals("-dump")) + } else if (args[index].equals("-config")) { index++; if (index < args.length) { - dumpFile = args[index]; - int len = dumpFile.length(); - if (!(dumpFile.startsWith(".dump", len - 5))) - { - dumpFile = dumpFile + ".dump"; - } + configFile = args[index]; + if (configFile.endsWith(TLAConstants.Files.CONFIG_EXTENSION)) { + configFile = configFile.substring(0, + (configFile.length() - TLAConstants.Files.CONFIG_EXTENSION.length())); + } index++; } else + { + printErrorMsg("Error: expect a file name for -config option."); + return false; + } + } else if (args[index].equals("-dump")) + { + index++; // consume "-dump". + if (((index + 1) < args.length) && args[index].startsWith("dot")) + { + final String dotArgs = args[index].toLowerCase(); + index++; // consume "dot...". + asDot = true; + colorize = dotArgs.contains("colorize"); + actionLabels = dotArgs.contains("actionlabels"); + snapshot = dotArgs.contains("snapshot"); + dumpFile = getDumpFile(args[index++], ".dot"); + } + else if (index < args.length) + { + dumpFile = getDumpFile(args[index++], ".dump"); + } else { printErrorMsg("Error: A file name for dumping states required."); return false; @@ -466,6 +720,24 @@ public class TLC printErrorMsg("Error: need to specify the metadata directory."); return false; } + } else if (args[index].equals("-userFile")) + { + index++; + if (index < args.length) + { + try { + // Most problems will only show when TLC eventually tries + // to write to the file. + tlc2.module.TLC.OUTPUT = new BufferedWriter(new FileWriter(new File(args[index++]))); + } catch (IOException e) { + printErrorMsg("Error: Failed to create user output log file."); + return false; + } + } else + { + printErrorMsg("Error: need to specify the full qualified file."); + return false; + } } else if (args[index].equals("-workers")) { index++; @@ -473,7 +745,9 @@ public class TLC { try { - int num = Integer.parseInt(args[index]); + int num = args[index].trim().toLowerCase().equals("auto") + ? Runtime.getRuntime().availableProcessors() + : Integer.parseInt(args[index]); if (num < 1) { printErrorMsg("Error: at least one worker required."); @@ -483,12 +757,12 @@ public class TLC index++; } catch (Exception e) { - printErrorMsg("Error: worker number required. But encountered " + args[index]); + printErrorMsg("Error: worker number or 'auto' required. But encountered " + args[index]); return false; } } else { - printErrorMsg("Error: expect an integer for -workers option."); + printErrorMsg("Error: expect an integer or 'auto' for -workers option."); return false; } } else if (args[index].equals("-dfid")) @@ -538,7 +812,7 @@ public class TLC } } else { - printErrorMsg("Error: expect an integer for -workers option."); + printErrorMsg("Error: expect an integer for -fp option."); return false; } } else if (args[index].equals("-fpmem")) @@ -627,16 +901,16 @@ public class TLC return false; } mainFile = args[index++]; - int len = mainFile.length(); - if (mainFile.startsWith(".tla", len - 4)) + if (mainFile.endsWith(TLAConstants.Files.TLA_EXTENSION)) { - mainFile = mainFile.substring(0, len - 4); + mainFile = mainFile.substring(0, (mainFile.length() - TLAConstants.Files.TLA_EXTENSION.length())); } } } - - if (mainFile == null) - { + + startTime = System.currentTimeMillis(); + + if (mainFile == null) { // command line omitted name of spec file, take this as an // indicator to check the in-jar model/ folder for a spec. // If a spec is found, use it instead. @@ -645,44 +919,98 @@ public class TLC ModelInJar.loadProperties(); TLCGlobals.tool = true; // always run in Tool mode (to parse output by Toolbox later) TLCGlobals.chkptDuration = 0; // never use checkpoints with distributed TLC (highly inefficient) - mainFile = "MC"; + mainFile = TLAConstants.Files.MODEL_CHECK_FILE_BASENAME; } else { printErrorMsg("Error: Missing input TLA+ module."); return false; } - } - if (configFile == null) - { - configFile = mainFile; - } + } + + // The functionality to start TLC from an (absolute) path /path/to/spec/file.tla + // seems to have eroded over the years which is why this block of code is a + // clutch. It essentially massages the variable values for mainFile, specDir and + // the user dir to make the code below - as well as the FilenameToStream + // resolver - + // work. Original issues was https://github.com/tlaplus/tlaplus/issues/24. + final File f = new File(mainFile); + String specDir = ""; + if (f.isAbsolute()) { + specDir = f.getParent() + FileUtil.separator; + mainFile = f.getName(); + // Not setting user dir causes a ConfigFileException when the resolver + // tries to read the .cfg file later in the game. + ToolIO.setUserDir(specDir); + } + + if (configFile == null) { + configFile = mainFile; + } + + if (cleanup && (fromChkpt == null)) { + // clean up the states directory only when not recovering + FileUtil.deleteDir(TLCGlobals.metaRoot, true); + } + + // Check if mainFile is an absolute or relative file system path. If it is + // absolute, the parent gets used as TLC's meta directory (where it stores + // states...). Otherwise, no meta dir is set causing states etc. to be stored in + // the current directory. + metadir = FileUtil.makeMetaDir(new Date(startTime), specDir, fromChkpt); + + if (dumpFile != null) { + if (dumpFile.startsWith("${metadir}")) { + // prefix dumpfile with the known value of this.metadir. There + // is no way to determine the actual value of this.metadir + // before TLC startup and thus it's impossible to make the + // dumpfile end up in the metadir if desired. + dumpFile = dumpFile.replace("${metadir}", metadir); + } + try { + if (asDot) { + this.stateWriter = new DotStateWriter(dumpFile, colorize, actionLabels, snapshot); + } else { + this.stateWriter = new StateWriter(dumpFile); + } + } catch (IOException e) { + printErrorMsg(String.format("Error: Given file name %s for dumping states invalid.", dumpFile)); + return false; + } + } - if (TLCGlobals.debug) - { - StringBuffer buffer = new StringBuffer("TLC argumens:"); - for (int i=0; i < args.length; i++) - { - buffer.append(args[i]); - if (i < args.length - 1) - { - buffer.append(" "); - } - } - buffer.append("\n"); - DebugPrinter.print(buffer.toString()); - } + if (TLCGlobals.debug) { + final StringBuilder buffer = new StringBuilder("TLC arguments:"); + for (int i = 0; i < args.length; i++) { + buffer.append(args[i]); + if (i < args.length - 1) { + buffer.append(" "); + } + } + buffer.append("\n"); + DebugPrinter.print(buffer.toString()); + } // if no errors, print welcome message printWelcome(); return true; - } - - /** + } + + /** + * Require a $suffix file extension unless already given. It is not clear why + * this is enforced. + */ + private static String getDumpFile(String dumpFile, String suffix) { + if (dumpFile.endsWith(suffix)) { + return dumpFile; + } + return dumpFile + suffix; + } + + /** * The processing method */ - public void process() + public int process() { - ToolIO.cleanToolObjects(TLCGlobals.ToolId); // UniqueString.initialize(); // a JMX wrapper that exposes runtime statistics @@ -698,18 +1026,13 @@ public class TLC // We must recover the intern var table as early as possible UniqueString.internTbl.recover(fromChkpt); } - if (cleanup && fromChkpt == null) - { - // clean up the states directory only when not recovering - FileUtil.deleteDir(TLCGlobals.metaRoot, true); - } FP64.Init(fpIndex); - + + final RandomGenerator rng = new RandomGenerator(); // Start checking: - if (isSimulate) - { + final int result; + if (RunMode.SIMULATE.equals(runMode)) { // random simulation - RandomGenerator rng = new RandomGenerator(); if (noSeed) { seed = rng.nextLong(); @@ -718,75 +1041,203 @@ public class TLC { rng.setSeed(seed, aril); } - MP.printMessage(EC.TLC_MODE_SIMU, String.valueOf(seed)); - Simulator simulator = new Simulator(mainFile, configFile, null, deadlock, traceDepth, - traceNum, rng, seed, true, resolver, specObj); -// The following statement moved to Spec.processSpec by LL on 10 March 2011 -// MP.printMessage(EC.TLC_STARTING); - instance = simulator; - simulator.simulate(); - } else - { - // model checking - MP.printMessage(EC.TLC_MODE_MC); - - AbstractChecker mc = null; - if (TLCGlobals.DFIDMax == -1) + printStartupBanner(EC.TLC_MODE_SIMU, getSimulationRuntime(seed)); + + Simulator simulator = new Simulator(mainFile, configFile, traceFile, deadlock, traceDepth, + traceNum, rng, seed, resolver, TLCGlobals.getNumWorkers()); + TLCGlobals.simulator = simulator; + result = simulator.simulate(); + } else { // RunMode.MODEL_CHECK + if (noSeed) { + seed = rng.nextLong(); + } + // Replace seed with tlc2.util.FP64.Polys[fpIndex]? + // + No need to print seed in startup-banner for BFS and DFS + // - Only 131 different seeds + // RandomEnumerableValues.setSeed(tlc2.util.FP64.Polys[fpIndex]); + RandomEnumerableValues.setSeed(seed); + + // Print startup banner before SANY writes its output. + printStartupBanner(isBFS() ? EC.TLC_MODE_MC : EC.TLC_MODE_MC_DFS, getModelCheckingRuntime(fpIndex, fpSetConfiguration)); + + // model checking + final FastTool tool = new FastTool(mainFile, configFile, resolver); + deadlock = deadlock && tool.getModelConfig().getCheckDeadlock(); + if (isBFS()) { - mc = new ModelChecker(mainFile, configFile, dumpFile, deadlock, fromChkpt, resolver, specObj, fpSetConfiguration); - TLCGlobals.mainChecker = (ModelChecker) mc; - modelCheckerMXWrapper = new ModelCheckerMXWrapper((ModelChecker) mc); + TLCGlobals.mainChecker = new ModelChecker(tool, metadir, stateWriter, deadlock, fromChkpt, + FPSetFactory.getFPSetInitialized(fpSetConfiguration, metadir, new File(mainFile).getName()), + startTime); + modelCheckerMXWrapper = new ModelCheckerMXWrapper((ModelChecker) TLCGlobals.mainChecker, this); + result = TLCGlobals.mainChecker.modelCheck(); } else { - mc = new DFIDModelChecker(mainFile, configFile, dumpFile, deadlock, fromChkpt, true, resolver, specObj); + TLCGlobals.mainChecker = new DFIDModelChecker(tool, metadir, stateWriter, deadlock, fromChkpt, startTime); + result = TLCGlobals.mainChecker.modelCheck(); + } + + if ((mcOutputConsumer != null) && (mcOutputConsumer.getError() != null)) { + final SpecProcessor sp = tool.getSpecProcessor(); + final ModelConfig mc = tool.getModelConfig(); + final File sourceDirectory = mcOutputConsumer.getSourceDirectory(); + final String originalSpecName = mcOutputConsumer.getSpecName(); + + MP.printMessage(EC.GENERAL, + "The model check run produced error-states - we will generate the " + + TLAConstants.TraceExplore.ERROR_STATES_MODULE_NAME + " files now."); + mcParserResults = MCParser.generateResultsFromProcessorAndConfig(sp, mc); + final File[] files = TraceExplorer.writeSpecTEFiles(sourceDirectory, originalSpecName, + mcParserResults, mcOutputConsumer.getError()); + specTETLAFile = files[0]; } -// The following statement moved to Spec.processSpec by LL on 10 March 2011 -// MP.printMessage(EC.TLC_STARTING); - instance = mc; - mc.modelCheck(); - } + return result; } catch (Throwable e) { if (e instanceof StackOverflowError) { System.gc(); - MP.printError(EC.SYSTEM_STACK_OVERFLOW, e); + return MP.printError(EC.SYSTEM_STACK_OVERFLOW, e); } else if (e instanceof OutOfMemoryError) { System.gc(); - MP.printError(EC.SYSTEM_OUT_OF_MEMORY, e); + return MP.printError(EC.SYSTEM_OUT_OF_MEMORY, e); + } else if (e instanceof TLCRuntimeException) { + return MP.printTLCRuntimeException((TLCRuntimeException) e); } else if (e instanceof RuntimeException) { // SZ 29.07.2009 // printing the stack trace of the runtime exceptions - MP.printError(EC.GENERAL, e); + return MP.printError(EC.GENERAL, e); // e.printStackTrace(); } else { - MP.printError(EC.GENERAL, e); + return MP.printError(EC.GENERAL, e); } } finally { - modelCheckerMXWrapper.unregister(); - MP.printMessage(EC.TLC_FINISHED); - MP.flush(); + if (tlc2.module.TLC.OUTPUT != null) { + try { + tlc2.module.TLC.OUTPUT.flush(); + tlc2.module.TLC.OUTPUT.close(); + } catch (IOException e) { } + } + modelCheckerMXWrapper.unregister(); + // In tool mode print runtime in milliseconds, in non-tool mode print human + // readable runtime (days, hours, minutes, ...). + final long runtime = System.currentTimeMillis() - startTime; + MP.printMessage(EC.TLC_FINISHED, + TLCGlobals.tool ? Long.toString(runtime) + "ms" : convertRuntimeToHumanReadable(runtime)); + MP.flush(); + + waitingOnGenerationCompletion.arriveAndAwaitAdvance(); } } - @SuppressWarnings("unchecked") + private static boolean isBFS() { + return TLCGlobals.DFIDMax == -1; + } + + public static Map<String, String> getSimulationRuntime(final long seed) { + final Runtime runtime = Runtime.getRuntime(); + final long heapMemory = runtime.maxMemory() / 1024L / 1024L; + + final TLCRuntime tlcRuntime = TLCRuntime.getInstance(); + final long offHeapMemory = tlcRuntime.getNonHeapPhysicalMemory() / 1024L / 1024L; + final long pid = tlcRuntime.pid(); + + final Map<String, String> result = new LinkedHashMap<>(); + result.put("seed", String.valueOf(seed)); + result.put("workers", String.valueOf(TLCGlobals.getNumWorkers())); + result.put("plural", TLCGlobals.getNumWorkers() == 1 ? "" : "s"); + result.put("cores", Integer.toString(runtime.availableProcessors())); + result.put("osName", System.getProperty("os.name")); + result.put("osVersion", System.getProperty("os.version")); + result.put("osArch", System.getProperty("os.arch")); + result.put("jvmVendor", System.getProperty("java.vendor")); + result.put("jvmVersion", System.getProperty("java.version")); + result.put("jvmArch", tlcRuntime.getArchitecture().name()); + result.put("jvmHeapMem", Long.toString(heapMemory)); + result.put("jvmOffHeapMem", Long.toString(offHeapMemory)); + result.put("jvmPid", pid == -1 ? "" : String.valueOf(pid)); + return result; + } + + public static Map<String, String> getModelCheckingRuntime(final int fpIndex, final FPSetConfiguration fpSetConfig) { + final Runtime runtime = Runtime.getRuntime(); + final long heapMemory = runtime.maxMemory() / 1024L / 1024L; + + final TLCRuntime tlcRuntime = TLCRuntime.getInstance(); + final long offHeapMemory = tlcRuntime.getNonHeapPhysicalMemory() / 1024L / 1024L; + final long pid = tlcRuntime.pid(); + + // TODO Better to use Class#getSimpleName provided we would have access to the + // Class instance instead of just its name. However, loading the class here is + // overkill and might interfere if other parts of TLC pull off class-loading + // tricks. + final String fpSetClassSimpleName = fpSetConfig.getImplementation() + .substring(fpSetConfig.getImplementation().lastIndexOf(".") + 1); + + final String stateQueueClassSimpleName = ModelChecker.getStateQueueName(); + + // fpSetClassSimpleName and stateQueueClassSimpleName ignored in DFS mode. + final Map<String, String> result = new LinkedHashMap<>(); + result.put("workers", String.valueOf(TLCGlobals.getNumWorkers())); + result.put("plural", TLCGlobals.getNumWorkers() == 1 ? "" : "s"); + result.put("cores", Integer.toString(runtime.availableProcessors())); + result.put("osName", System.getProperty("os.name")); + result.put("osVersion", System.getProperty("os.version")); + result.put("osArch", System.getProperty("os.arch")); + result.put("jvmVendor", System.getProperty("java.vendor")); + result.put("jvmVersion", System.getProperty("java.version")); + result.put("jvmArch", tlcRuntime.getArchitecture().name()); + result.put("jvmHeapMem", Long.toString(heapMemory)); + result.put("jvmOffHeapMem", Long.toString(offHeapMemory)); + result.put("seed", Long.toString(RandomEnumerableValues.getSeed())); + result.put("fpidx", Integer.toString(fpIndex)); + result.put("jvmPid", pid == -1 ? "" : String.valueOf(pid)); + result.put("fpset", fpSetClassSimpleName); + result.put("queue", stateQueueClassSimpleName); + return result; + } + + /** + * @return The given milliseconds runtime converted into human readable form + * with SI unit and insignificant parts stripped (when runtime is + * days, nobody cares for minutes or seconds). + */ + public static String convertRuntimeToHumanReadable(long runtime) { + SimpleDateFormat df = null; + if (runtime > (60 * 60 * 24 * 1000L)) { + df = new SimpleDateFormat("D'd' HH'h'"); + runtime -= 86400000L; + } else if (runtime > (60 * 60 * 24 * 1000L)) { + df = new SimpleDateFormat("D'd' HH'h'"); + runtime -= 86400000L; + } else if (runtime > (60 * 60 * 1000L)) { + df = new SimpleDateFormat("HH'h' mm'min'"); + } else if (runtime > (60 * 1000L)) { + df = new SimpleDateFormat("mm'min' ss's'"); + } else { + df = new SimpleDateFormat("ss's'"); + } + df.setTimeZone(TimeZone.getTimeZone("UTC")); + return df.format(runtime); + } + public List<File> getModuleFiles() { final List<File> result = new ArrayList<File>(); - if (instance instanceof ModelChecker) { - ModelChecker mc = (ModelChecker) instance; - final Enumeration<ParseUnit> parseUnitContext = mc.specObj.parseUnitContext - .elements(); - while (parseUnitContext.hasMoreElements()) { - ParseUnit pu = (ParseUnit) parseUnitContext.nextElement(); - File resolve = resolver.resolve(pu.getFileName(), false); - result.add(resolve); + if (TLCGlobals.mainChecker instanceof ModelChecker) { + final ModelChecker mc = (ModelChecker) TLCGlobals.mainChecker; + result.addAll(mc.getModuleFiles(resolver)); + if (ModelInJar.hasCfg()) { + result.add(ModelInJar.getCfg()); } + // It might be desirable to include tlc.jfr - a flight recording aka profiling + // at the JVM level here. This doesn't work though as the recording get created + // after the termination of the JVM. A recording can also be several hundred + // MBs large. } return result; } @@ -802,28 +1253,6 @@ public class TLC ToolIO.setDefaultResolver(resolver); } - /** - * Set external specification object - * @param specObj spec object created external SANY run - */ - public void setSpecObject(SpecObj specObj) - { - this.specObj = specObj; - } - - /** - * Delegate cancellation request to the instance - * @param flag - */ - public void setCanceledFlag(boolean flag) - { - if (this.instance != null) - { - this.instance.setCancelFlag(flag); - DebugPrinter.print("Cancel flag set to " + flag); - } - } - /** * Print out an error message, with usage hint * @param msg, message to print @@ -843,20 +1272,237 @@ public class TLC if (!this.welcomePrinted) { this.welcomePrinted = true; - MP.printMessage(EC.TLC_VERSION, TLCGlobals.versionOfTLC); + if (TLCGlobals.getRevision() == null) { + MP.printMessage(EC.TLC_VERSION, TLCGlobals.versionOfTLC); + } else { + MP.printMessage(EC.TLC_VERSION, TLCGlobals.versionOfTLC + " (rev: " + TLCGlobals.getRevision() + ")"); + } } } + private void printStartupBanner(final int mode, final Map<String, String> parameters) { + MP.printMessage(mode, parameters.values().toArray(new String[parameters.size()])); + + final Map<String, String> udc = new LinkedHashMap<>(); + // First indicate the version (to make parsing forward compatible) + udc.put("ver", TLCGlobals.getRevisionOrDev()); + + // Simulation, DFS or BFS mode. + udc.put("mode", mode2String(mode)); + + parameters.remove("plural"); // damn hack! + // "pid", "seed", and "fpidx" have no relevance for us. + parameters.remove("jvmPid"); + parameters.remove("fpidx"); + parameters.remove("seed"); + udc.putAll(parameters); + + // True if TLC is run from within the Toolbox. Derive ide name from .tool too + // unless set explicitly. Eventually, we can probably remove the toolbox + // parameter. + udc.put("toolbox", Boolean.toString(TLCGlobals.tool)); + udc.put("ide", System.getProperty(TLC.class.getName() + ".ide", TLCGlobals.tool ? "toolbox" : "cli")); + new ExecutionStatisticsCollector().collect(udc); + } + + private static String mode2String(final int mode) { + switch (mode) { + case EC.TLC_MODE_MC: + return "bfs"; + case EC.TLC_MODE_MC_DFS: + return "dfs"; + case EC.TLC_MODE_SIMU: + return "simulation"; + default: + return "unknown"; + } + } + + /** + * This is themed on commons-io-2.6's IOUtils.copyLarge(InputStream, OutputStream, byte[]) - + * once we move to Java9+, dump this usage in favor of InputStream.transferTo(OutputStream) + * + * @param is + * @param os + * @return the count of bytes copied + * @throws IOException + */ + private static long copyStream(final InputStream is, final OutputStream os) throws IOException { + final byte[] buffer = new byte[1024 * 4]; + long byteCount = 0; + int n; + final BufferedInputStream bis = (is instanceof BufferedInputStream) ? (BufferedInputStream)is + : new BufferedInputStream(is); + final BufferedOutputStream bos = (os instanceof BufferedOutputStream) ? (BufferedOutputStream)os + : new BufferedOutputStream(os); + while ((n = bis.read(buffer)) != -1) { + bos.write(buffer, 0, n); + byteCount += n; + } + + bos.flush(); + + return byteCount; + } + /** * */ private void printUsage() { - printWelcome(); - MP.printMessage(EC.TLC_USAGE); + final List<List<UsageGenerator.Argument>> commandVariants = new ArrayList<>(); + final List<UsageGenerator.Argument> sharedArguments = new ArrayList<>(); + + // N.B. alphabetical ordering is not required by the UsageGenerator, but introduced here to more easily + // find options when reading the code + sharedArguments.add(new UsageGenerator.Argument("-checkpoint", "minutes", + "interval between check point; defaults to 30", + true)); + sharedArguments.add(new UsageGenerator.Argument("-cleanup", "clean up the states directory", true)); + sharedArguments.add(new UsageGenerator.Argument("-config", "file", + "provide the configuration file; defaults to SPEC.cfg", true)); + sharedArguments.add(new UsageGenerator.Argument("-continue", + "continue running even when an invariant is violated; default\n" + + "behavior is to halt on first violation", true)); + sharedArguments.add(new UsageGenerator.Argument("-coverage", "minutes", + "interval between the collection of coverage information;\n" + + "if not specified, no coverage will be collected", true)); + sharedArguments.add(new UsageGenerator.Argument("-deadlock", + "if specified DO NOT CHECK FOR DEADLOCK. Setting the flag is\n" + + "the same as setting CHECK_DEADLOCK to FALSE in config\n" + + "file. When -deadlock is specified, config entry is\n" + + "ignored; default behavior is to check for deadlocks", + true)); + sharedArguments.add(new UsageGenerator.Argument("-difftrace", + "show only the differences between successive states when\n" + + "printing trace information; defaults to printing\n" + + "full state descriptions", true)); + sharedArguments.add(new UsageGenerator.Argument("-debug", + "print various debugging information - not for production use\n", + true)); + sharedArguments.add(new UsageGenerator.Argument("-dump", "file", + "dump all states into the specified file; this parameter takes\n" + + "optional parameters for dot graph generation. Specifying\n" + + "'dot' allows further options, comma delimited, of zero\n" + + "or more of 'actionlabels', 'colorize', 'snapshot' to be\n" + + "specified before the '.dot'-suffixed filename", true, + "dot actionlabels,colorize,snapshot")); + sharedArguments.add(new UsageGenerator.Argument("-fp", "N", + "use the Nth irreducible polynomial from the list stored\n" + + "in the class FP64", true)); + sharedArguments.add(new UsageGenerator.Argument("-fpbits", "num", + "the number of MSB used by MultiFPSet to create nested\n" + + "FPSets; defaults to 1", true)); + sharedArguments.add(new UsageGenerator.Argument("-fpmem", "num", + "a value in (0.0,1.0) representing the ratio of total\n" + + "physical memory to devote to storing the fingerprints\n" + + "of found states; defaults to 0.25", true)); + sharedArguments.add(new UsageGenerator.Argument("-generateSpecTE", null, + "if errors are encountered during model checking, generate\n" + + "a SpecTE tla/cfg file pair which encapsulates Init-Next\n" + + "definitions to specify the state conditions of the error\n" + + "state; this enables 'tool' mode. The generated SpecTE\n" + + "will include tool output as well as all non-Standard-\n" + + "Modules dependencies embeded in the module. To prevent\n" + + "the embedding of dependencies, add the parameter\n" + + "'nomonolith' to this declaration", true, + "nomonolith")); + sharedArguments.add(new UsageGenerator.Argument("-gzip", + "control if gzip is applied to value input/output streams;\n" + + "defaults to 'off'", true)); + sharedArguments.add(new UsageGenerator.Argument("-h", "display these help instructions", true)); + sharedArguments.add(new UsageGenerator.Argument("-maxSetSize", "num", + "the size of the largest set which TLC will enumerate; defaults\n" + + "to 1000000 (10^6)", true)); + sharedArguments.add(new UsageGenerator.Argument("-metadir", "path", + "specify the directory in which to store metadata; defaults to\n" + + "SPEC-directory/states if not specified", true)); + sharedArguments.add(new UsageGenerator.Argument("-nowarning", + "disable all warnings; defaults to reporting warnings", true)); + sharedArguments.add(new UsageGenerator.Argument("-recover", "id", + "recover from the checkpoint with the specified id", true)); + sharedArguments.add(new UsageGenerator.Argument("-terse", + "do not expand values in Print statements; defaults to\n" + + "expanding values", true)); + sharedArguments.add(new UsageGenerator.Argument("-tool", + "run in 'tool' mode, surrounding output with message codes;\n" + + "if '-generateSpecTE' is specified, this is enabled\n" + + "automatically", true)); + sharedArguments.add(new UsageGenerator.Argument("-userFile", "file", + "an absolute path to a file in which to log user output (for\n" + + "example, that which is produced by Print)", true)); + sharedArguments.add(new UsageGenerator.Argument("-workers", "num", + "the number of TLC worker threads; defaults to 1. Use 'auto'\n" + + "to automatically select the number of threads based on the\n" + + "number of available cores.", true)); + + sharedArguments.add(new UsageGenerator.Argument("SPEC", null)); + + + final List<UsageGenerator.Argument> modelCheckVariant = new ArrayList<>(sharedArguments); + modelCheckVariant.add(new UsageGenerator.Argument("-dfid", "num", + "run the model check in depth-first iterative deepening\n" + + "starting with an initial depth of 'num'", true)); + modelCheckVariant.add(new UsageGenerator.Argument("-view", + "apply VIEW (if provided) when printing out states", true)); + commandVariants.add(modelCheckVariant); + + + final List<UsageGenerator.Argument> simulateVariant = new ArrayList<>(sharedArguments); + simulateVariant.add(new UsageGenerator.Argument("-depth", "num", + "specifies the depth of random simulation; defaults to 100", + true)); + simulateVariant.add(new UsageGenerator.Argument("-seed", "num", + "provide the seed for random simulation; defaults to a\n" + + "random long pulled from a pseudo-RNG", true)); + simulateVariant.add(new UsageGenerator.Argument("-aril", "num", + "adjust the seed for random simulation; defaults to 0", true)); + simulateVariant.add(new UsageGenerator.Argument("-simulate", null, + "run in simulation mode; optional parameters may be specified\n" + + "comma delimited: 'num=X' where X is the maximum number of\n" + + "total traces to generate and/or 'file=Y' where Y is the\n" + + "absolute-pathed prefix for trace file modules to be written\n" + + "by the simulation workers; for example Y='/a/b/c/tr' would\n" + + "produce, e.g, '/a/b/c/tr_1_15'", false, + "file=X,num=Y")); + commandVariants.add(simulateVariant); + + + final List<String> tips = new ArrayList<String>(); + tips.add("When using the '-generateSpecTE' you can version the generated specification by doing:\n\t" + + "./tla2tools.jar -generateSpecTE MySpec.tla && NAME=\"SpecTE-$(date +%s)\" && sed -e \"s/MODULE" + + " SpecTE/MODULE $NAME/g\" SpecTE.tla > $NAME.tla"); + tips.add("If, while checking a SpecTE created via '-generateSpecTE', you get an error message concerning\n" + + "CONSTANT declaration and you've previous used 'integers' as model values, rename your\n" + + "model values to start with a non-numeral and rerun the model check to generate a new SpecTE."); + tips.add("If, while checking a SpecTE created via '-generateSpecTE', you get a warning concerning\n" + + "duplicate operator definitions, this is likely due to the 'monolith' specification\n" + + "creation. Try re-running TLC adding the 'nomonolith' option to the '-generateSpecTE'\n" + + "parameter."); + + UsageGenerator.displayUsage(ToolIO.out, "TLC", TLCGlobals.versionOfTLC, + "provides model checking and simulation of TLA+ specifications", + Messages.getString("TLCDescription"), + commandVariants, tips, ' '); } FPSetConfiguration getFPSetConfiguration() { return fpSetConfiguration; } + + public RunMode getRunMode() { + return runMode; + } + + public String getMainFile() { + return mainFile; + } + + public String getModelName() { + return System.getProperty(MailSender.MODEL_NAME, this.mainFile); + } + + public String getSpecName() { + return System.getProperty(MailSender.SPEC_NAME, this.mainFile); + } } diff --git a/tlatools/src/tlc2/TLCGlobals.java b/tlatools/src/tlc2/TLCGlobals.java index 93f581508e6b3b8a321b2369ff87bc3ab2f14920..d88c46c664dd603e0ea539423293c045540d6ad1 100644 --- a/tlatools/src/tlc2/TLCGlobals.java +++ b/tlatools/src/tlc2/TLCGlobals.java @@ -2,8 +2,13 @@ // Portions Copyright (c) 2003 Microsoft Corporation. All rights reserved. package tlc2; -import tla2sany.semantic.FrontEnd; -import tlc2.tool.ModelChecker; +import java.net.URL; +import java.util.Enumeration; +import java.util.jar.Attributes; +import java.util.jar.Manifest; + +import tlc2.tool.AbstractChecker; +import tlc2.tool.Simulator; /** * Globals @@ -15,12 +20,14 @@ import tlc2.tool.ModelChecker; public class TLCGlobals { - // The current version of TLC - public static String versionOfTLC = "Version 2.06 of 9 May 2015"; + public static final int DEFAULT_CHECKPOINT_DURATION = (30 * 60 * 1000) + 42; + // The current version of TLC + public static String versionOfTLC = "Version 2.18 of 20 March 2023"; + // The bound for set enumeration, used for pretty printing public static int enumBound = 2000; - + // The bound for the cardinality of a set public static int setBound = 1000000; @@ -33,7 +40,26 @@ public class TLCGlobals */ public static double livenessThreshold = 0.1d; - public synchronized static void setNumWorkers(int n) + public static double livenessGraphSizeThreshold = 0.1d; + + /** + * Ratio of runtime dedicated to safety checking (80%) and liveness checking + * (20%). Some aspects of liveness are also checked during state insertion + * (see ILiveCheck#addNextState) and thus part of safety checking.. + */ + public static double livenessRatio = 0.2d; + + public static String lnCheck = "default"; + + public static boolean doLiveness() { + return !(lnCheck.equals("final") || lnCheck.equals("seqfinal")); + } + + public static boolean doSequentialLiveness() { + return lnCheck.startsWith("seq"); + } + + public synchronized static void setNumWorkers(int n) { numWorkers = n; } @@ -62,12 +88,22 @@ public class TLCGlobals incNumWorkers(-1); } - // The main model checker object - public static ModelChecker mainChecker = null; + // The main model checker object (null if simulator non-null) + public static AbstractChecker mainChecker = null; + + // The main simulator object (null if mainChecker non-null) + public static Simulator simulator = null; + // Char to indent nested coverage information. + public static final char coverageIndent = '|'; + // Enable collecting coverage information public static int coverageInterval = -1; + public static final boolean isCoverageEnabled() { + return coverageInterval >= 0; + } + // Depth for depth-first iterative deepening public static int DFIDMax = -1; @@ -85,7 +121,7 @@ public class TLCGlobals // The time interval to checkpoint. (in milliseconds) public static long chkptDuration = Integer.getInteger( - TLCGlobals.class.getName() + ".chkpt", 30 * 60 * 1000); + TLCGlobals.class.getName() + ".chkpt", DEFAULT_CHECKPOINT_DURATION); // MAK 08.2012: centralized checkpoint code and added disabling and // externally forced checkpoints @@ -94,7 +130,12 @@ public class TLCGlobals forceChkpt = true; } private static long lastChkpt = System.currentTimeMillis(); - + + public static boolean chkptExplicitlyEnabled() { + // Assumption is that a user will always select a different value. + return chkptDuration > 0 && chkptDuration != DEFAULT_CHECKPOINT_DURATION; + } + /** * IMPORTANT NOTE: The method is unsynchronized. It is the caller's * responsibility to ensure that only a single thread calls this method. @@ -130,10 +171,7 @@ public class TLCGlobals public static boolean useView = false; // The flag to control if gzip is applied to Value input/output stream. - public static boolean useGZIP = true; - - // The tool id number for TLC2. - public static int ToolId = FrontEnd.getToolId(); + public static boolean useGZIP = false; // debugging field public static boolean debug = false; @@ -147,4 +185,29 @@ public class TLCGlobals } return true; } + + public static String getRevision() { + try { + final Enumeration<URL> resources = TLCGlobals.class.getClassLoader().getResources("META-INF/MANIFEST.MF"); + while (resources.hasMoreElements()) { + final Manifest manifest = new Manifest(resources.nextElement().openStream()); + final Attributes attributes = manifest.getMainAttributes(); + if("TLA+ Tools".equals(attributes.getValue("Implementation-Title"))) { + if(attributes.getValue("X-Git-ShortRevision") != null) { + return attributes.getValue("X-Git-ShortRevision"); + } else { + return null; + } + } + } + } catch (Exception ignore) { + } + return null; + } + + public static String getRevisionOrDev() { + return TLCGlobals.getRevision() == null ? "development" : TLCGlobals.getRevision(); + } + + public static boolean expand = true; } diff --git a/tlatools/src/tlc2/TLCRunner.java b/tlatools/src/tlc2/TLCRunner.java new file mode 100644 index 0000000000000000000000000000000000000000..dcd35035ac4e1310ae3e3f5b857a1bd1ac4b1980 --- /dev/null +++ b/tlatools/src/tlc2/TLCRunner.java @@ -0,0 +1,191 @@ +package tlc2; + +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import tlc2.output.EC; +import tlc2.output.TeeOutputStream; +import tlc2.tool.fp.FPSetFactory; + +/** + * Originally i was attempting to run the model check in the same JVM in which {@link TraceExplorer} was invoked (and + * then did its parsing and generating TLA and CFG files.) The model check process would fail with a message of: + * + * @!@!@STARTMSG 2233:1 @!@!@ + * The initial predicate _TEInit cannot be a constant. + * @!@!@ENDMSG 2233 @!@!@ + * + * ... however, running TLC from command line freshly on the same TLA/CFG would not produce this error. I can only + * surmise that the parsing process is setting some static value somewhere which is not being cleared and which is + * then making the model checking process fail. + * + * In light of that, i've written this class to spin off a new JVM for the model check. + */ +class TLCRunner { + static List<String> JVM_ARGUMENTS; + private static final String TLC_CLASS = TLC.class.getName(); + + static { + JVM_ARGUMENTS = new ArrayList<>(); + JVM_ARGUMENTS.add("-XX:+UseParallelGC"); + JVM_ARGUMENTS.add("-Dfile.encoding=UTF-8"); + JVM_ARGUMENTS.add("-Dtlc2.tool.fp.FPSet.impl=" + FPSetFactory.getImplementationDefault()); + } + + + private final File outputLogfile; + private final OutputStream outputOutputStream; + private final List<String> arguments; + + private boolean silenceStdOut; + + TLCRunner(final List<String> tlcArguments, final File logfileDestination) { + outputLogfile = logfileDestination; + outputOutputStream = null; + arguments = tlcArguments; + + silenceStdOut = false; + } + + TLCRunner(final List<String> tlcArguments, final OutputStream logfileOutputStream) { + outputLogfile = null; + outputOutputStream = logfileOutputStream; + arguments = tlcArguments; + + silenceStdOut = false; + } + + /** + * @param flag if true, no output from the TLC process will be sent to System.out + */ + void setSilenceStdOut(final boolean flag) { + silenceStdOut = flag; + } + + /** + * This will not return until the process has finished. + * @return the exit value of the TLC process + * @throws IOException + */ + int run() throws IOException { + final ProcessBuilder processBuilder = createProcess(); + final Process p = processBuilder.start(); + final BufferedInputStream stdOutReader = new BufferedInputStream(p.getInputStream()); + final BufferedInputStream stdErrReader = new BufferedInputStream(p.getErrorStream()); + final BufferedOutputStream logfileOutputStream; + if (outputOutputStream != null) { + if (outputOutputStream instanceof BufferedOutputStream) { + logfileOutputStream = (BufferedOutputStream)outputOutputStream; + } else { + logfileOutputStream = new BufferedOutputStream(outputOutputStream); + } + } else { + final FileOutputStream fos = new FileOutputStream(outputLogfile); + logfileOutputStream = new BufferedOutputStream(fos); + } + final OutputStream stdOutPumpOutput; + if (silenceStdOut) { + stdOutPumpOutput = logfileOutputStream; + } else { + stdOutPumpOutput = new TeeOutputStream(System.out, logfileOutputStream); + } + final StreamPump stdOutPump = new StreamPump(stdOutReader, stdOutPumpOutput); + final StreamPump stdErrPump = new StreamPump(stdErrReader, null); + + try { + (new Thread(stdOutPump)).start(); + (new Thread(stdErrPump)).start(); + + p.waitFor(); + + return p.exitValue(); + } catch (final InterruptedException ie) { + // TODO improve logging here + System.out.println("TLC process was interrupted: " + ie.getMessage()); + } finally { + stdOutPump.stop(); + stdErrPump.stop(); + + try { + logfileOutputStream.close(); + } catch (final Exception e) { } + } + + return EC.ExitStatus.ERROR_SYSTEM; + } + + private ProcessBuilder createProcess() { + final boolean isWindows = System.getProperty("os.name").toLowerCase().startsWith("windows"); + final String jvm = System.getProperty("java.home") + + File.separator + + "bin" + + File.separator + + "java" + + (isWindows ? ".exe" : ""); + final List<String> command = new ArrayList<String>(); + command.add(jvm); + command.addAll(JVM_ARGUMENTS); + command.add(TLC_CLASS); + command.addAll(arguments); + + final ProcessBuilder processBuilder = new ProcessBuilder(command); + final Map<String, String> environment = processBuilder.environment(); + environment.put("CLASSPATH", System.getProperty("java.class.path")); + + return processBuilder; + } + + + private static class StreamPump implements Runnable { + private static final int WAIT_SLEEP = 125; + + + private final InputStream inputStream; + private final OutputStream outputStream; + + private volatile boolean shouldStop; + + /** + * @param is + * @param os pass null to simply drain the input stream + */ + StreamPump(final InputStream is, final OutputStream os) { + this.inputStream = is; + this.outputStream = os; + this.shouldStop = false; + } + + public void run() { + try { + while (!shouldStop) { + while ((inputStream.available() > 0) && !shouldStop) { + if (outputStream != null) { + outputStream.write(inputStream.read()); + } else { + inputStream.read(); + } + } + if (outputStream != null) { + outputStream.flush(); + } + + try { + Thread.sleep(WAIT_SLEEP); + } catch (final Exception e) { } + } + } catch (final Exception e) { } + } + + void stop() { + shouldStop = true; + } + } +} diff --git a/tlatools/src/tlc2/TraceExplorer.java b/tlatools/src/tlc2/TraceExplorer.java new file mode 100644 index 0000000000000000000000000000000000000000..bed61c179df1d628aee88f2ad0e0e4573146eb7a --- /dev/null +++ b/tlatools/src/tlc2/TraceExplorer.java @@ -0,0 +1,846 @@ +package tlc2; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.locks.ReentrantLock; + +import tlc2.input.MCOutputParser; +import tlc2.input.MCOutputPipeConsumer; +import tlc2.input.MCParser; +import tlc2.input.MCParserResults; +import tlc2.model.MCError; +import tlc2.model.MCState; +import tlc2.output.CFGCopier; +import tlc2.output.EC; +import tlc2.output.MP; +import tlc2.output.Messages; +import tlc2.output.SpecTraceExpressionWriter; +import tlc2.output.TLACopier; +import tlc2.util.Vect; +import tlc2.value.impl.SetEnumValue; +import tlc2.value.impl.Value; +import tlc2.value.impl.ValueEnumeration; +import util.TLAConstants; +import util.ToolIO; +import util.UsageGenerator; + +/** + * This is an application class which provides the following functionalities: + * + * . given a directory of a previously run model check (containing a .tla/.cfg/.out triplet), produce a "SpecTE" + * .tla / .cfg file pair + * . given a directory of a previously run model check (containing a .out file), dump a pretty print of the + * errors states to {@link System#out} + * . given a directory of a previously run model check (containing a .tla/.cfg/.out triplet) and a file of + * trace expressions, one per line, produce a "SpecTE" file pair, then run a model check + * evaluating the expressions, writing the triplet TE.tla, TE.cfg, TE.out + * . given a directory of a previously generated SpecTE file pair and a file of trace expressions, one per line, + * run a model check evaluating the expressions, writing the triplet TE.tla, TE.cfg, TE.out + * . given an already executed output pipe consumer, generated a "SpecTE" .tla / .cfg pair + */ +public class TraceExplorer { + private static final String GENERATE_SPEC_FUNCTION_PARAMETER_NAME = "-generateSpecTE"; + private static final String PRETTY_PRINT_FUNCTION_PARAMETER_NAME = "-prettyPrint"; + private static final String QUASI_REPL_PARAMETER_NAME = "-replBis"; + private static final String TRACE_EXPRESSIONS_FUNCTION_PARAMETER_NAME = "-traceExpressions"; + + private static final String EXPRESSIONS_FILE_PARAMETER_NAME = "-expressionsFile"; + private static final String MODEL_CHECK_JVM_ARGUMENTS_PARAMETER_NAME = "-jvmArguments"; + private static final String MODEL_CHECK_TLC_ARGUMENTS_PARAMETER_NAME = "-tlcArguments"; + + private static final String SOURCE_DIR_PARAMETER_NAME = "-source"; + private static final String GENERATE_SPEC_OVERWRITE_PARAMETER_NAME = "-overwrite"; + + static final String SPEC_TE_INIT_ID = "_SpecTEInit"; + static final String SPEC_TE_NEXT_ID = "_SpecTENext"; + private static final String SPEC_TE_ACTION_CONSTRAINT_ID = "_SpecTEActionConstraint"; + + // <parameter name, whether the parameter takes an argument> + private static final HashMap<String, Boolean> TLC_ARGUMENTS_TO_IGNORE; + + static { + TLC_ARGUMENTS_TO_IGNORE = new HashMap<>(); + + TLC_ARGUMENTS_TO_IGNORE.put("-config", Boolean.TRUE); + TLC_ARGUMENTS_TO_IGNORE.put("-metadir", Boolean.TRUE); + TLC_ARGUMENTS_TO_IGNORE.put("-tool", Boolean.FALSE); + } + + + /** + * @param sourceDirectory + * @param originalSpecName + * @param results + * @param error + * @return an array of length two; the 0-index is the location to the + * destination TLA file, and the 1-index is that of the CFG file + * @throws IOException + */ + public static File[] writeSpecTEFiles(final File sourceDirectory, final String originalSpecName, + final MCParserResults results, final MCError error) throws IOException { + final StringBuilder tlaBuffer = new StringBuilder(); + final StringBuilder cfgBuffer = new StringBuilder(); + + final Vect<?> configDeclaredConstants = results.getModelConfig().getConstants(); + final HashSet<String> constantModelValuesToDeclare = new HashSet<>(); + final int constantsCount = configDeclaredConstants.size(); + for (int i = 0; i < constantsCount; i++) { + final Vect<?> constantDeclaration = (Vect<?>)configDeclaredConstants.elementAt(i); + final Object value = constantDeclaration.elementAt(1); + if (value instanceof SetEnumValue) { + final SetEnumValue sev = (SetEnumValue)value; + final ValueEnumeration ve = sev.elements(); + Value v = ve.nextElement(); + while (v != null) { + constantModelValuesToDeclare.add(v.toString()); + v = ve.nextElement(); + } + } + } + if (constantModelValuesToDeclare.size() > 0) { + cfgBuffer.append(TLAConstants.KeyWords.CONSTANTS).append(TLAConstants.CR); + for (final String modelValue : constantModelValuesToDeclare) { + cfgBuffer.append(TLAConstants.INDENT).append(modelValue).append(TLAConstants.EQ); + cfgBuffer.append(modelValue).append(TLAConstants.CR); + } + cfgBuffer.append(TLAConstants.CR); + + tlaBuffer.append(TLAConstants.CR).append(TLAConstants.KeyWords.CONSTANTS).append(' '); + boolean firstDone = false; + for (final String modelValue : constantModelValuesToDeclare) { + if (firstDone) { + tlaBuffer.append(", "); + } else { + firstDone = true; + } + + tlaBuffer.append(modelValue); + } + tlaBuffer.append(TLAConstants.CR).append(TLAConstants.CR); + } + + final List<MCState> trace = error.getStates(); + final StringBuilder[] tlaBuffers + = SpecTraceExpressionWriter.addInitNextToBuffers(cfgBuffer, trace, null, SPEC_TE_INIT_ID, SPEC_TE_NEXT_ID, + SPEC_TE_ACTION_CONSTRAINT_ID, + results.getOriginalNextOrSpecificationName(), true); + tlaBuffer.append(tlaBuffers[0].toString()); + SpecTraceExpressionWriter.addTraceFunctionToBuffers(tlaBuffer, cfgBuffer, trace); + tlaBuffer.append(tlaBuffers[1].toString()); + + final List<String> extendedModules = results.getOriginalExtendedModules(); + final boolean specExtendsTLC = extendedModules.contains(TLAConstants.BuiltInModules.TLC); + final boolean specExtendsToolbox = extendedModules.contains(TLAConstants.BuiltInModules.TRACE_EXPRESSIONS); + final TLACopier tlaCopier = new TLACopier(originalSpecName, TLAConstants.TraceExplore.ERROR_STATES_MODULE_NAME, + sourceDirectory, tlaBuffer.toString(), specExtendsTLC, + specExtendsToolbox); + tlaCopier.copy(); + MP.printMessage(EC.GENERAL, + "The file " + tlaCopier.getDestinationFile().getAbsolutePath() + " has been created."); + + final CFGCopier cfgCopier = new CFGCopier(originalSpecName, TLAConstants.TraceExplore.ERROR_STATES_MODULE_NAME, + sourceDirectory, cfgBuffer.toString()); + cfgCopier.copy(); + MP.printMessage(EC.GENERAL, + "The file " + cfgCopier.getDestinationFile().getAbsolutePath() + " has been created."); + + return new File[] { tlaCopier.getDestinationFile(), cfgCopier.getDestinationFile() }; + } + + + private enum RunMode { + GENERATE_SPEC_TE, PRETTY_PRINT, GENERATE_FROM_TLC_RUN, QUASI_REPL, TRACE_EXPLORATION; + } + + + private File specGenerationSourceDirectory; + private String specGenerationOriginalSpecName; + private boolean expectedOutputFromStdIn; + private boolean overwriteGeneratedFiles; + + private List<String> expressions; + private List<String> tlcArguments; + + private String replSpecName; + private File temporaryREPLSpec; + + private RunMode runMode; + + /** + * @param commandLineArguments arguments, ostensibly from the command line, with which this instance will configure + * itself. + */ + public TraceExplorer(final String[] commandLineArguments) { + processArguments(commandLineArguments); + + if (runMode == null) { + printUsageAndExit(); + } + } + + protected final void processArguments(final String[] args) { + runMode = determineRunModeFromArguments(args); + if (runMode == null) { + return; + } + + switch (runMode) { + case QUASI_REPL: + case TRACE_EXPLORATION: + tlcArguments = new ArrayList<>(); + break; + default: + break; + } + + specGenerationSourceDirectory = new File(System.getProperty("user.dir")); + specGenerationOriginalSpecName = args[args.length - 1]; + expectedOutputFromStdIn = (specGenerationOriginalSpecName.charAt(0) == '-'); + if (expectedOutputFromStdIn) { + specGenerationOriginalSpecName = null; + } else if (RunMode.QUASI_REPL.equals(runMode)) { + printUsageAndExit(); + } + overwriteGeneratedFiles = false; + + String expressionsSourceFilename = null; + + boolean consumedAdditionalParameters = true; + final int upperIndex = expectedOutputFromStdIn ? args.length : (args.length - 1); + int index = 0; + while (consumedAdditionalParameters) { + if (index < upperIndex) { + final String nextArg = args[index]; + + if (getRunModeForArgument(nextArg) != null) { + index++; + continue; + } + + if (SOURCE_DIR_PARAMETER_NAME.equals(nextArg)) { + index++; + final String runDirectory = args[index++]; + final File f = new File(runDirectory); + + if (!f.exists()) { + printErrorMessage("specified source directory does not exist."); + return; + } + + if (!f.isDirectory()) { + printErrorMessage("specified source directory is not a directory."); + return; + } + specGenerationSourceDirectory = f; + + index++; + } else if (GENERATE_SPEC_OVERWRITE_PARAMETER_NAME.equals(nextArg)) { + overwriteGeneratedFiles = true; + + index++; + } else if (EXPRESSIONS_FILE_PARAMETER_NAME.equals(nextArg)) { + index++; + + expressionsSourceFilename = args[index++]; + } else if (MODEL_CHECK_JVM_ARGUMENTS_PARAMETER_NAME.equals(nextArg)) { + index++; + + final String argumentList = args[index++]; + final String[] arguments = argumentList.split(" "); + TLCRunner.JVM_ARGUMENTS.addAll(Arrays.asList(arguments)); + } else if (MODEL_CHECK_TLC_ARGUMENTS_PARAMETER_NAME.equals(nextArg)) { + index++; + final String argumentList = args[index++]; + final String[] arguments = argumentList.split(" "); + int argIndex = 0; + + while (argIndex < arguments.length) { + final String argument = arguments[argIndex]; + final Boolean ignoreAdditionalParameter = TLC_ARGUMENTS_TO_IGNORE.get(argument); + + if (ignoreAdditionalParameter == null) { + tlcArguments.add(argument); + } else { + if (ignoreAdditionalParameter.booleanValue()) { + argIndex++; + } + } + + argIndex++; + } + } else { + consumedAdditionalParameters = false; + } + } else { + consumedAdditionalParameters = false; + } + } + + if (RunMode.TRACE_EXPLORATION.equals(runMode) || RunMode.QUASI_REPL.equals(runMode)) { + if (expressionsSourceFilename == null) { + printErrorMessage("no expressions file specified."); + runMode = null; + return; + } + + final File sourceDirFile = new File(specGenerationSourceDirectory, expressionsSourceFilename); + final File absoluteFile = new File(expressionsSourceFilename); + final File f; + if (sourceDirFile.exists()) { + f = sourceDirFile; + } else if (absoluteFile.exists()) { + f = absoluteFile; + } else { + final String errorMessageSuffix; + if (sourceDirFile.getAbsolutePath().equals(absoluteFile.getAbsolutePath())) { + errorMessageSuffix = sourceDirFile.getAbsolutePath(); + } else { + errorMessageSuffix = sourceDirFile.getAbsolutePath() + + " nor " + absoluteFile.getAbsolutePath(); + } + printErrorMessage("an expressions file could not be found at " + errorMessageSuffix); + runMode = null; + return; + } + + try { + expressions = new ArrayList<>(); + try (final BufferedReader br = new BufferedReader(new FileReader(f))) { + String line; + while ((line = br.readLine()) != null) { + final String trimmed = line.trim(); + + if (trimmed.length() > 0) { + expressions.add(trimmed); + } + } + } + } catch (final IOException e) { + printErrorMessage("encountered an exception reading from expressions file " + + f.getAbsolutePath() + " :: " + e.getMessage()); + runMode = null; + return; + } + + if (RunMode.TRACE_EXPLORATION.equals(runMode)) { + tlcArguments.add("-config"); + tlcArguments.add(TLAConstants.TraceExplore.EXPLORATION_MODULE_NAME + + TLAConstants.Files.CONFIG_EXTENSION); + + tlcArguments.add("-tool"); + } else { + replSpecName = "repl"; //"REPL_" + System.currentTimeMillis(); + temporaryREPLSpec = new File(specGenerationSourceDirectory, + replSpecName + TLAConstants.Files.TLA_EXTENSION); + temporaryREPLSpec.deleteOnExit(); + } + + tlcArguments.add("-metadir"); + tlcArguments.add(specGenerationSourceDirectory.getAbsolutePath()); + + if (RunMode.TRACE_EXPLORATION.equals(runMode)) { + tlcArguments.add(TLAConstants.TraceExplore.EXPLORATION_MODULE_NAME); + } else { + tlcArguments.add(replSpecName); + } + } + } + + /** + * @return an {@link EC} defined error code representing success or failure. + */ + public int execute() throws Exception { + if (RunMode.QUASI_REPL.equals(runMode)) { + return performREPL(); + } else if (expectedOutputFromStdIn) { + return executeStreaming(); + } else { + return executeNonStreaming(); + } + } + + private RunMode determineRunModeFromArguments(final String[] args) { + for (int i = 0; i < args.length; i++) { + final RunMode rm = getRunModeForArgument(args[i]); + + if (rm != null) { + return rm; + } + } + + return null; + } + + private RunMode getRunModeForArgument(final String arg) { + if (GENERATE_SPEC_FUNCTION_PARAMETER_NAME.equals(arg)) { + return RunMode.GENERATE_SPEC_TE; + } else if (PRETTY_PRINT_FUNCTION_PARAMETER_NAME.equals(arg)) { + return RunMode.PRETTY_PRINT; + } else if (TRACE_EXPRESSIONS_FUNCTION_PARAMETER_NAME.equals(arg)) { + return RunMode.TRACE_EXPLORATION; + } else if (QUASI_REPL_PARAMETER_NAME.equals(arg)) { + return RunMode.QUASI_REPL; + } + + return null; + } + + private int executeNonStreaming() throws Exception { + if (!performPreFlightFileChecks()) { + throw new IllegalStateException("There was an issue with the input, " + + TLAConstants.TraceExplore.ERROR_STATES_MODULE_NAME + ", or " + + TLAConstants.TraceExplore.EXPLORATION_MODULE_NAME + " file."); + } + + final boolean specifiedModuleIsSpecTE + = specGenerationOriginalSpecName.equals(TLAConstants.TraceExplore.ERROR_STATES_MODULE_NAME); + final boolean needGenerateSpecTE = RunMode.GENERATE_SPEC_TE.equals(runMode) + || (!specifiedModuleIsSpecTE && RunMode.TRACE_EXPLORATION.equals(runMode)); + if (needGenerateSpecTE) { + final MCParser parser = new MCParser(specGenerationSourceDirectory, specGenerationOriginalSpecName); + final MCParserResults results = parser.parse(); + + if (results.getOutputMessages().size() == 0) { + MP.printMessage(EC.GENERAL, "The output file had no tool messages; was TLC not run with" + + " the '-tool' option when producing it?"); + + return EC.ExitStatus.ERROR; + } else if (results.getError() == null) { + final String msg; + if (RunMode.GENERATE_SPEC_TE.equals(runMode)) { + msg = "The output file contained no error-state messages, no " + + TLAConstants.TraceExplore.ERROR_STATES_MODULE_NAME + " will be produced."; + } else { + msg = "The output file contained no error-state messages, no " + + TLAConstants.TraceExplore.ERROR_STATES_MODULE_NAME + " nor " + + TLAConstants.TraceExplore.EXPLORATION_MODULE_NAME + " will be produced, and, so, " + + "no trace expressions will be evaluated."; + } + MP.printMessage(EC.GENERAL, msg); + + return EC.NO_ERROR; + } else { + try { + writeSpecTEFiles(results, results.getError()); + + if (RunMode.GENERATE_SPEC_TE.equals(runMode)) { + return EC.NO_ERROR; + } else if (RunMode.TRACE_EXPLORATION.equals(runMode)) { // currently always true + return performTraceExploration(); + } + } catch (final Exception e) { } + } + } else if (RunMode.PRETTY_PRINT.equals(runMode)) { + try { + MCOutputParser.prettyPrintToStream(System.out, specGenerationSourceDirectory, + specGenerationOriginalSpecName); + + return EC.NO_ERROR; + } catch (final Exception e) { } + } + + return EC.ExitStatus.ERROR; + } + + private int executeStreaming() throws Exception { + final AtomicBoolean mcParserCompleted = new AtomicBoolean(false); + final ReentrantLock parseLock = new ReentrantLock(); + final ArrayList<MCParser> parserList = new ArrayList<>(1); + final MCOutputPipeConsumer.ConsumerLifespanListener listener + = new MCOutputPipeConsumer.ConsumerLifespanListener() { + @Override + public void consumptionFoundSourceDirectoryAndSpecName(MCOutputPipeConsumer consumer) { + specGenerationSourceDirectory = consumer.getSourceDirectory(); + specGenerationOriginalSpecName = consumer.getSpecName(); + + final boolean specifiedModuleIsSpecTE + = specGenerationOriginalSpecName.equals(TLAConstants.TraceExplore.ERROR_STATES_MODULE_NAME); + final boolean needGenerateSpecTE + = RunMode.GENERATE_SPEC_TE.equals(runMode) + || (!specifiedModuleIsSpecTE && RunMode.TRACE_EXPLORATION.equals(runMode)); + + if (!performPreFlightFileChecks()) { + throw new IllegalStateException("There was an issue with the input, " + + TLAConstants.TraceExplore.ERROR_STATES_MODULE_NAME + ", or " + + TLAConstants.TraceExplore.EXPLORATION_MODULE_NAME + " file."); + } + + if (needGenerateSpecTE) { + MP.printMessage(EC.GENERAL, + "Have encountered the source spec in the output logging, will begin parsing of those assets now."); + + final Runnable r = () -> { + final MCParser parser + = new MCParser(specGenerationSourceDirectory, specGenerationOriginalSpecName, true); + parserList.add(parser); + + parseLock.lock(); + try { + parser.parse(); + } finally { + mcParserCompleted.set(true); + parseLock.unlock(); + } + }; + (new Thread(r)).start(); + } + } + }; + final MCOutputPipeConsumer pipeConsumer = new MCOutputPipeConsumer(System.in, listener); + + MP.printMessage(EC.GENERAL, "TraceExplorer is expecting input on stdin..."); + + pipeConsumer.consumeOutput(false); + + if (pipeConsumer.outputHadNoToolMessages()) { + MP.printMessage(EC.GENERAL, "The output had no tool messages; was TLC not run with" + + " the '-tool' option when producing it?"); + + return EC.ExitStatus.ERROR; + } + + MP.printMessage(EC.GENERAL, "Have received the final output logging message - finishing TraceExplorer work."); + + final boolean specifiedModuleIsSpecTE + = specGenerationOriginalSpecName.equals(TLAConstants.TraceExplore.ERROR_STATES_MODULE_NAME); + final boolean needGenerateSpecTE + = RunMode.GENERATE_SPEC_TE.equals(runMode) + || (!specifiedModuleIsSpecTE && RunMode.TRACE_EXPLORATION.equals(runMode)); + if (needGenerateSpecTE) { + if (pipeConsumer.getError() == null) { + final String msg; + if (RunMode.GENERATE_SPEC_TE.equals(runMode)) { + msg = "The output contained no error-state messages, no " + + TLAConstants.TraceExplore.ERROR_STATES_MODULE_NAME + " will be produced."; + } else { + msg = "The output contained no error-state messages, no " + + TLAConstants.TraceExplore.ERROR_STATES_MODULE_NAME + " nor " + + TLAConstants.TraceExplore.EXPLORATION_MODULE_NAME + " will be produced, and, so, " + + "no trace expressions will be evaluated."; + } + MP.printMessage(EC.GENERAL, msg); + + return EC.NO_ERROR; + } else { + if (!mcParserCompleted.get()) { + parseLock.lock(); + } + final MCParserResults results = parserList.get(0).getParseResults(); + + try { + writeSpecTEFiles(results, pipeConsumer.getError()); + + if (RunMode.GENERATE_SPEC_TE.equals(runMode)) { + return EC.NO_ERROR; + } else if (RunMode.TRACE_EXPLORATION.equals(runMode)) { // currently always true + return performTraceExploration(); + } + } catch (final Exception e) { } + } + } else if (RunMode.PRETTY_PRINT.equals(runMode)) { + if (pipeConsumer.getError() == null) { + MP.printMessage(EC.GENERAL, "The output contained no error-state messages; there is nothing to display."); + + return EC.NO_ERROR; + } else { + try { + MCOutputParser.prettyPrintToStream(System.out, pipeConsumer.getError()); + + return EC.NO_ERROR; + } catch (final Exception e) { } + } + } + + return EC.ExitStatus.ERROR; + } + + private int performREPL() throws IOException { + final REPLSpecWriter writer = new REPLSpecWriter(replSpecName, expressions); + final File cfgFile = new File(specGenerationSourceDirectory, replSpecName + TLAConstants.Files.CONFIG_EXTENSION); + cfgFile.deleteOnExit(); + writer.writeFiles(temporaryREPLSpec, cfgFile); + + final REPLSpecWriter.REPLLogConsumerStream outputStream = new REPLSpecWriter.REPLLogConsumerStream(); + final TLCRunner tlcRunner = new TLCRunner(tlcArguments, outputStream); + tlcRunner.setSilenceStdOut(true); + final int errorCode = tlcRunner.run(); + + System.out.println(String.join("\n", expressions)); + System.out.println("\t" + TLAConstants.EQ); + // TODO indent on multi-line + System.out.println("\t\t" + outputStream.getCollectedContent()); + + return errorCode; + } + + private int performTraceExploration() throws IOException { + final File tlaFile = new File(specGenerationSourceDirectory, + TLAConstants.TraceExplore.EXPLORATION_MODULE_NAME + TLAConstants.Files.TLA_EXTENSION); + final TraceExpressionExplorerSpecWriter writer = new TraceExpressionExplorerSpecWriter(expressions); + final String configContent = writer.getConfigBuffer().toString(); + writer.writeFiles(tlaFile, null); + + final CFGCopier cfgCopier = new CFGCopier(TLAConstants.TraceExplore.ERROR_STATES_MODULE_NAME, + TLAConstants.TraceExplore.EXPLORATION_MODULE_NAME, specGenerationSourceDirectory, configContent); + cfgCopier.copy(); + + final File outFile = new File(specGenerationSourceDirectory, + TLAConstants.TraceExplore.EXPLORATION_MODULE_NAME + TLAConstants.Files.OUTPUT_EXTENSION); + final TLCRunner tlcRunner = new TLCRunner(tlcArguments, outFile); + System.out.println("Forking TLC..."); + final int errorCode = tlcRunner.run(); + + MCOutputParser.prettyPrintToStream(System.out, specGenerationSourceDirectory, + TLAConstants.TraceExplore.EXPLORATION_MODULE_NAME); + + return errorCode; + } + + private boolean performPreFlightFileChecks() { + final boolean specifiedModuleIsSpecTE + = specGenerationOriginalSpecName.equals(TLAConstants.TraceExplore.ERROR_STATES_MODULE_NAME); + final boolean outputShouldExist = !expectedOutputFromStdIn + || (specifiedModuleIsSpecTE && RunMode.TRACE_EXPLORATION.equals(runMode)); + + String filename; + + if (outputShouldExist) { + filename = specGenerationOriginalSpecName + TLAConstants.Files.OUTPUT_EXTENSION; + final File outputFile = new File(specGenerationSourceDirectory, filename); + if (!outputFile.exists()) { + printErrorMessage("source directory (" + specGenerationSourceDirectory + ") does not contain " + + filename); + + runMode = null; + return false; + } + } + + if (RunMode.GENERATE_SPEC_TE.equals(runMode) || RunMode.TRACE_EXPLORATION.equals(runMode)) { + filename = specGenerationOriginalSpecName + TLAConstants.Files.TLA_EXTENSION; + final File tlaFile = new File(specGenerationSourceDirectory, filename); + if (!tlaFile.exists()) { + printErrorMessage("source directory (" + specGenerationSourceDirectory + ") does not contain " + + filename); + + runMode = null; + return false; + } + + filename = specGenerationOriginalSpecName + TLAConstants.Files.CONFIG_EXTENSION; + final File configFile = new File(specGenerationSourceDirectory, filename); + if (!configFile.exists()) { + printErrorMessage("source directory (" + specGenerationSourceDirectory + ") does not contain " + + filename); + + runMode = null; + return false; + } + + if (!overwriteGeneratedFiles) { + if (!specifiedModuleIsSpecTE) { + final File specTETLA = new File(specGenerationSourceDirectory, + (TLAConstants.TraceExplore.ERROR_STATES_MODULE_NAME + TLAConstants.Files.TLA_EXTENSION)); + + if (specTETLA.exists()) { + printErrorMessage("specified source directory already contains " + specTETLA.getName() + + "; specify '" + GENERATE_SPEC_OVERWRITE_PARAMETER_NAME + "' to overwrite."); + + runMode = null; + return false; + } + } + + if (RunMode.TRACE_EXPLORATION.equals(runMode)) { + final File teTLA = new File(specGenerationSourceDirectory, + (TLAConstants.TraceExplore.EXPLORATION_MODULE_NAME + TLAConstants.Files.TLA_EXTENSION)); + + if (teTLA.exists()) { + printErrorMessage("specified source directory already contains " + teTLA.getName() + + "; specify '" + GENERATE_SPEC_OVERWRITE_PARAMETER_NAME + "' to overwrite."); + + runMode = null; + return false; + } + } + } + } + + return true; + } + + private void writeSpecTEFiles(final MCParserResults results, final MCError error) throws IOException { + writeSpecTEFiles(specGenerationSourceDirectory, specGenerationOriginalSpecName, results, error); + } + + private void printErrorMessage(final String message) { + MP.printError(EC.GENERAL, message); + } + + + private static void printUsageAndExit() { + final List<List<UsageGenerator.Argument>> commandVariants = new ArrayList<>(); + final UsageGenerator.Argument expressionFile + = new UsageGenerator.Argument(EXPRESSIONS_FILE_PARAMETER_NAME, "file", + "expressions specified, one per line if being used in conjunction\n" + + "with " + TRACE_EXPRESSIONS_FUNCTION_PARAMETER_NAME + + " and just one if being used with " + + QUASI_REPL_PARAMETER_NAME + + "\nthis file must be in the source " + + "directory or specified by\nfull path", false); + final UsageGenerator.Argument jvmArguments + = new UsageGenerator.Argument(MODEL_CHECK_JVM_ARGUMENTS_PARAMETER_NAME, + "\"-Xmx3072m -Xms512m\"", + "these arguments will be passed on to the TLC JVM when performing\n" + + "the model check", + true); + final UsageGenerator.Argument overwrite + = new UsageGenerator.Argument(GENERATE_SPEC_OVERWRITE_PARAMETER_NAME, + "if specified, and if a SpecTE, or TE, file pair already exists,\n" + + "they will be overwritten; if they exist, and this has not been\n" + + "specified, the process will halt", true); + final UsageGenerator.Argument sourceDirectory + = new UsageGenerator.Argument(SOURCE_DIR_PARAMETER_NAME, "path", + "the path to the directory containing tool output from model\n" + + "checking; defaults to CWD. This will be ignored if no SpecName\n" + + "has been specified (and so tool output will be expected to\n" + + "arrive on stdin)", true); + final UsageGenerator.Argument spec + = new UsageGenerator.Argument("SpecName", + "if no spec name is specified, tool output will be expected to arrive\n" + + "via stdin and any " + SOURCE_DIR_PARAMETER_NAME + + " definition will be ignored.", false); + final UsageGenerator.Argument tlcArguments + = new UsageGenerator.Argument(MODEL_CHECK_TLC_ARGUMENTS_PARAMETER_NAME, + "\"-some -other 2 -tlc arguments\"", + "these arguments will be passed on to TLC when performing the\n" + + "model check; -config, -tool, and -metadir will be ignored,\n" + + "if specified", + true); + + final List<UsageGenerator.Argument> traceExpressionsVariant = new ArrayList<>(); + traceExpressionsVariant.add(jvmArguments); + traceExpressionsVariant.add(overwrite); + traceExpressionsVariant.add(sourceDirectory); + traceExpressionsVariant.add(tlcArguments); + traceExpressionsVariant.add(spec); + traceExpressionsVariant.add(expressionFile); + traceExpressionsVariant.add(new UsageGenerator.Argument( + TRACE_EXPRESSIONS_FUNCTION_PARAMETER_NAME, + "evaluate trace expressions in the context of an error state\n" + + "specified through a generated SpecTE file; if the 'SpecName'\n" + + "specified is anything other that 'SpecTE', the tool will\n" + + "generate the SpecTE file pair, prior to generating the TE\n" + + "file pair for the subsequently performed model checking", + false)); + commandVariants.add(traceExpressionsVariant); + + + final List<UsageGenerator.Argument> replBisVariant = new ArrayList<>(); + replBisVariant.add(jvmArguments); + replBisVariant.add(tlcArguments); + replBisVariant.add(new UsageGenerator.Argument( + QUASI_REPL_PARAMETER_NAME, + "perform a one-off evaluation of an expression", false)); + replBisVariant.add(expressionFile); + commandVariants.add(replBisVariant); + + + final List<UsageGenerator.Argument> generateSpecVariant = new ArrayList<>(); + generateSpecVariant.add(overwrite); + generateSpecVariant.add(sourceDirectory); + generateSpecVariant.add(spec); + generateSpecVariant.add(new UsageGenerator.Argument( + GENERATE_SPEC_FUNCTION_PARAMETER_NAME, + "generate a SpecTE tla/cfg pair from a model checking tool mode\n" + + "output", false)); + commandVariants.add(generateSpecVariant); + + + final List<UsageGenerator.Argument> prettyPrintVariant = new ArrayList<>(); + prettyPrintVariant.add(sourceDirectory); + prettyPrintVariant.add(spec); + prettyPrintVariant.add(new UsageGenerator.Argument( + PRETTY_PRINT_FUNCTION_PARAMETER_NAME, + "pretty print the error states of a model checking tool mode\n" + + "output", false)); + commandVariants.add(prettyPrintVariant); + + + UsageGenerator.displayUsage(ToolIO.out, "TraceExplorer", TLCGlobals.versionOfTLC, + "a tool for working with TLC tool output and exploring trace expressions", + Messages.getString("TraceExplorerDescription"), + commandVariants, null, ' '); + + System.exit(-1); + } + + /** + * Ways to run this application: + * + * 1. Evaluation of trace expressions from afrom an existing .tla/.out/.cfg triplet in which the .out contains + * one or more MP.ERROR messages - see https://github.com/tlaplus/tlaplus/issues/393 for background: + * java tlc2.TraceExplorer -traceExpressions \ + * -expressionsFile=_file_containing_expressions_one_per_line_ \ + * [-tlcArguments=_directory_containing_prior_run_output_] \ + * [-source=_directory_containing_prior_run_output_] \ + * [-overwrite] \ + * SpecName + * the source directory defaults to CWD if not defined; the expressions file must either exist in the source + * directory or be a full path; if TLC arguments are specified (all within quotes) they will be passed on to + * TLC when performing the model check; -config, -tool, and -metadir will be ignored, if specified; if no + * SpecName is specified then we will expect the output data to arrive on stdin - anything specified via + * -source will be ignore in this case as we will derive that from the output log content. + * + * 2. Evaluation of an expression, ala REPL-ese: + * java tlc2.TraceExplorer -replBis \ + * -expressionsFile=_file_containing_expressions_to_evaluate_ \ + * [-tlcArguments=_directory_containing_prior_run_output_] + * the expressions file must either exist in the source directory or be a full path; if TLC arguments are + * specified (all within quotes) they will be passed on to TLC when performing the model check; -config, + * -tool, and -metadir will be ignored, if specified + * + * 3. Generation of a 'SpecTE.tla' from an existing .tla/.out/.cfg triplet in which the .out contains + * one or more MP.ERROR messages - see https://github.com/tlaplus/tlaplus/issues/393 for background: + * java tlc2.TraceExplorer -generateSpecTE \ + * [-source=_directory_containing_prior_run_output_] \ + * [-overwrite] \ + * SpecName + * the source directory defaults to CWD if not defined; if overwrite is not specified and a SpecTE.tla + * already exists in the source directory, execution will halt; if no SpecName is specified then we + * will expect the output data to arrive on stdin - anything specified via -source will be ignore in this + * case as we will derive that from the output log content. + * + * 4. Pretty print the error states from an existing .out file to {@link System#out}: + * java tlc2.TraceExplorer -prettyPrint \ + * [-source=_directory_containing_prior_run_output_] \ + * SpecName + * the source directory defaults to CWD if not defined; if no SpecName is specified then we + * will expect the output data to arrive on stdin - anything specified via -source will be ignore in this + * case as we will derive that from the output log content. + */ + public static void main(final String[] args) { + if (args.length == 0) { + printUsageAndExit(); + } + + try { + final TraceExplorer te = new TraceExplorer(args); + final int returnCode = te.execute(); + + System.exit(returnCode); + } catch (final Exception e) { + System.err.println("Caught exception: " + e.getLocalizedMessage()); + + printUsageAndExit(); + } + } +} diff --git a/tlatools/src/tlc2/TraceExpressionExplorerSpecWriter.java b/tlatools/src/tlc2/TraceExpressionExplorerSpecWriter.java new file mode 100644 index 0000000000000000000000000000000000000000..c422a7d21ed886162846044594764c4605aa5614 --- /dev/null +++ b/tlatools/src/tlc2/TraceExpressionExplorerSpecWriter.java @@ -0,0 +1,152 @@ +package tlc2; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; + +import tlc2.output.AbstractSpecWriter; +import tlc2.output.SpecWriterUtilities; +import util.TLAConstants; + +/** + * The class name is quite wordy - but the 'more appropriate' TraceExpressionSpecWriter class name is too close to + * the already-existing SpecTraceExpressionWriter class name. + * + * This is the spec writer concrete class which has niceties that lend themselves to the creation of the "TE" tla/cfg + * pair. + */ +public class TraceExpressionExplorerSpecWriter extends AbstractSpecWriter { + private static final String EXPRESSION_VARIABLE_NAME_PREFIX = "_traceExpression_"; + private static final String EXPRESSION_COMMENT_LINE_PREFIX = TLAConstants.COMMENT + " TRACE EXPRESSION: "; + + private static final String TE_INIT_ID = "_TEInit"; + private static final String TE_INIT_ATTRIBUTE_NAME = "teBehaviorInit"; + private static final String TE_NEXT_ID = "_TENext"; + private static final String TE_NEXT_ATTRIBUTE_NAME = "teBehaviorNext"; + + /** + * If the TLA file exists, it will be examined; if it is found to have been generated by this writer, a + * map of variableName -> traceExpression will be returned. Otherwise null will be returned. + * + * @param tlaFile + * @return a map of variableName -> traceExpression if the file exists and was created by this writer, else null. + */ + public static HashMap<String, String> getVariableExpressionMapFromTLAFile(final File tlaFile) throws IOException { + if (tlaFile.exists()) { + try (final BufferedReader br = new BufferedReader(new FileReader(tlaFile))) { + final ArrayList<String> declarations = new ArrayList<>(); + boolean foundLine = true; + String line; + + while (foundLine && ((line = br.readLine()) != null)) { + if (line.startsWith(EXPRESSION_COMMENT_LINE_PREFIX)) { + declarations.add(line.substring(EXPRESSION_COMMENT_LINE_PREFIX.length())); + } else { + foundLine = false; + } + } + + if (declarations.size() > 0) { + final HashMap<String, String> variableExpressionMap = new HashMap<>(); + + for (final String declaration : declarations) { + final int index = declaration.indexOf(TLAConstants.EQ); + if (index != -1) { + final String variable = declaration.substring(0, index); + final String expression = declaration.substring(index + TLAConstants.EQ.length()); + + variableExpressionMap.put(variable, expression); + } + } + + return variableExpressionMap; + } + } + } + + return null; + } + + + private final TreeMap<String, String> variableExpressionMap; + + TraceExpressionExplorerSpecWriter(final List<String> expressions) { + super(true); + + variableExpressionMap = new TreeMap<>(); + int count = 1; + for (final String expression : expressions) { + final String key = EXPRESSION_VARIABLE_NAME_PREFIX + count; + variableExpressionMap.put(key, expression); + tlaBuffer.append(EXPRESSION_COMMENT_LINE_PREFIX).append(key).append(TLAConstants.EQ); + tlaBuffer.append(expression).append(TLAConstants.CR); + count++; + } + + addPrimer(TLAConstants.TraceExplore.EXPLORATION_MODULE_NAME, + TLAConstants.TraceExplore.ERROR_STATES_MODULE_NAME); + + declareExpressionVariables(); + createInitNextWithExpressions(); + + tlaBuffer.append(SpecWriterUtilities.getGeneratedTimeStampCommentLine()).append(TLAConstants.CR); + } + + StringBuilder getConfigBuffer() { + return cfgBuffer; + } + + private void declareExpressionVariables() { + tlaBuffer.append("VARIABLES "); + + boolean notFirst = false; + for (final String variable : variableExpressionMap.keySet()) { + if (notFirst) { + tlaBuffer.append(", "); + } else { + notFirst = true; + } + + tlaBuffer.append(variable); + } + + tlaBuffer.append(TLAConstants.CR).append(TLAConstants.CR); + } + + private void createInitNextWithExpressions() { + final StringBuilder initConjunction = new StringBuilder(TLAConstants.INDENTED_CONJUNCTIVE); + initConjunction.append(TraceExplorer.SPEC_TE_INIT_ID).append(TLAConstants.CR); + addExpressionsToBuffer(initConjunction, false); + + final List<String[]> initContent + = SpecWriterUtilities.createSourceContent(initConjunction.toString(), TE_INIT_ID, false); + addFormulaList(initContent, TLAConstants.KeyWords.INIT, TE_INIT_ATTRIBUTE_NAME); + + + final StringBuilder nextConjunction = new StringBuilder(TLAConstants.INDENTED_CONJUNCTIVE); + nextConjunction.append(TraceExplorer.SPEC_TE_NEXT_ID).append(TLAConstants.CR); + addExpressionsToBuffer(nextConjunction, true); + + final List<String[]> nextContent + = SpecWriterUtilities.createSourceContent(nextConjunction.toString(), TE_NEXT_ID, false); + addFormulaList(nextContent, TLAConstants.KeyWords.NEXT, TE_NEXT_ATTRIBUTE_NAME); + } + + private void addExpressionsToBuffer(final StringBuilder buffer, final boolean primed) { + for (final Map.Entry<String, String> me : variableExpressionMap.entrySet()) { + buffer.append(TLAConstants.INDENTED_CONJUNCTIVE).append(me.getKey()); + if (primed) { + buffer.append(TLAConstants.PRIME); + } + buffer.append(TLAConstants.EQ); + buffer.append(TLAConstants.L_PAREN).append(me.getValue()).append(TLAConstants.R_PAREN); + buffer.append(TLAConstants.CR); + } + } +} diff --git a/tlatools/src/tlc2/input/AbstractMCOutputConsumer.java b/tlatools/src/tlc2/input/AbstractMCOutputConsumer.java new file mode 100644 index 0000000000000000000000000000000000000000..439629cd36cb5c21b2c46f28e261ce03b8e6af87 --- /dev/null +++ b/tlatools/src/tlc2/input/AbstractMCOutputConsumer.java @@ -0,0 +1,150 @@ +package tlc2.input; + +import java.io.BufferedReader; +import java.io.IOException; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import tlc2.model.MCError; +import tlc2.model.MCState; +import tlc2.output.MP; + +abstract class AbstractMCOutputConsumer { + private static final int MAX_READ_RETRIES = 60; + private static final long SLEEP_BETWEEN_RETRIES = 500; + + // In the current world, the 'message class' (the second number run in the regex) is only a single digit, but i + // specify potentially two to give room for future expansion without needing to change this code. + private static final String START_MESSAGE_REGEX + = MP.DELIM + MP.STARTMSG + "([0-9]{4})" + MP.COLON + "([0-9]{1,2})" + MP.SPACE + MP.DELIM; + private static final String END_MESSAGE_REGEX = MP.DELIM + MP.ENDMSG + "[0-9]{4}" + MP.SPACE + MP.DELIM; + + protected static final Pattern START_MESSAGE_PATTERN = Pattern.compile(START_MESSAGE_REGEX); + protected static final Pattern END_MESSAGE_PATTERN = Pattern.compile(END_MESSAGE_REGEX); + + + private MCError error; + + public MCError getError() { + return error; + } + + /** + * Subclasses may override this should they wish; via this method, they will be + * handed any line which is read and does not exist in a message block. This line + * will not be written to the output file (if a non-null writer has been supplied to + * the parseChunk(BufferedReader, BufferedWriter) method) until this method returns. + * + * @param line + */ + protected void handleUnknownReadLine(final String line) throws IOException { } + + protected void consumeErrorMessageAndStates(final BufferedReader reader, final MCOutputMessage errorMessage) + throws IOException { + MCError currentError = null; + + // TODO unclear how - if - nested errors can occur; there is support for them in TLCError + // which has [therefore] been mirrored in MCError + if (error == null) { + error = new MCError(null, errorMessage.getBody()); + currentError = error; + } else { + currentError = new MCError((currentError != null) ? currentError : error, errorMessage.getBody()); + } + + MCOutputMessage message = parseChunk(reader); + if ((message == null) || (message.getType() != MP.ERROR)) { + throw new IOException("Expected a useless error message like " + + "'The behavior up to this point is...' but didn't find one after" + + "[" + currentError.getMessage() + "]"); + } + + boolean inStateTrace = true; + while (inStateTrace) { + message = parseChunk(reader); + if (message == null) { + throw new IOException("Unexpected end of the log during state consumption for " + + "[" + currentError.getMessage() + "]"); + } + + if (message.getType() == MP.STATE) { + currentError.addState(MCState.parseState(message.getBody())); + } else { + inStateTrace = false; + // TODO do we want to process this message? + } + } + } + + /** + * The reader is assumed to be parked at a line containing a start message; if + * not, lines will be consumed until one is found, and then the ensuing chunk + * is consumed. + * + * @param reader + * @return a consumed message, or null if a new chunk could not be encountered + * @throws IOException on a read error, or, if in an attempt to consume the next + * chunk, we're unable to find the end of the chunk + */ + protected MCOutputMessage parseChunk(final BufferedReader reader) throws IOException { + MCOutputMessage message = null; + String startLine = null; + while (startLine == null) { + final String line = blockingReadLine(reader, true); + + if (line == null) { + return null; + } + + final Matcher m = START_MESSAGE_PATTERN.matcher(line); + if (m.find()) { + message = new MCOutputMessage(Integer.parseInt(m.group(1)), Integer.parseInt(m.group(2))); + startLine = line; + } else { + handleUnknownReadLine(line); + } + } + + boolean chunkEndEncountered = false; + final StringBuilder sb = new StringBuilder(); + while (!chunkEndEncountered) { + final String line = blockingReadLine(reader, false); + final Matcher m = END_MESSAGE_PATTERN.matcher(line); + if (m.find()) { + message.setBody(sb); + chunkEndEncountered = true; + } else { + if (sb.length() > 0) { + sb.append(MP.CR); + } + sb.append(line); + } + } + + return message; + } + + private String blockingReadLine(final BufferedReader reader, final boolean eofOK) throws IOException { + int retry = 0; + String line = reader.readLine(); + while (line == null) { + retry++; + + if (retry == MAX_READ_RETRIES) { + if (eofOK) { + return null; + } else { + throw new IOException("We ran out of input unexpectedly."); + } + } + + try { + Thread.sleep(SLEEP_BETWEEN_RETRIES); + } catch (final Exception e) { } + + line = reader.readLine(); + } + + return line; + } +} diff --git a/tlatools/src/tlc2/input/MCOutputMessage.java b/tlatools/src/tlc2/input/MCOutputMessage.java new file mode 100644 index 0000000000000000000000000000000000000000..38b8db803b628fedf992f7dfa570fe6e38b59c5d --- /dev/null +++ b/tlatools/src/tlc2/input/MCOutputMessage.java @@ -0,0 +1,44 @@ +package tlc2.input; + +public class MCOutputMessage { + private final int code; + private final int type; + private String body; + + MCOutputMessage(final int messageCode, final int messageType) { + code = messageCode; + type = messageType; + } + + void setBody(final StringBuilder sb) { + body = sb.toString(); + } + + /** + * @return e.g EC.TLC_VERSION + */ + public int getCode() { + return code; + } + + /** + * Returns the type of the message - a.k.a message class + * @return e.g MP.ERROR + */ + public int getType() { + return type; + } + + /** + * @return the body of the message - the content of the lines found between the + * start and end message delimiters + */ + public String getBody() { + return body; + } + + @Override + public String toString() { + return "[" + code + "," + type + "]\n" + body; + } +} diff --git a/tlatools/src/tlc2/input/MCOutputParser.java b/tlatools/src/tlc2/input/MCOutputParser.java new file mode 100644 index 0000000000000000000000000000000000000000..d3b0a0bbf586df23e952e1f2d7dc7ed01e8bc728 --- /dev/null +++ b/tlatools/src/tlc2/input/MCOutputParser.java @@ -0,0 +1,125 @@ +package tlc2.input; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.io.OutputStream; +import java.io.PrintStream; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +import tlc2.TraceExpressionExplorerSpecWriter; +import tlc2.model.MCError; +import tlc2.model.MCState; +import tlc2.output.EC; +import tlc2.output.MP; +import util.TLAConstants; + +/** + * This class provides a parser to extract error & state from an MC.out file; it also provides a public static + * method which does a pretty-print dump to a provided output stream of the collected states in order. + */ +public class MCOutputParser extends AbstractMCOutputConsumer { + private final File mcOutFile; + + MCOutputParser(final File outFile) { + mcOutFile = outFile; + } + + List<MCOutputMessage> parse(final boolean returnAllMessages) throws IOException { + final ArrayList<MCOutputMessage> encounteredMessages = returnAllMessages ? new ArrayList<>() : null; + + try (final BufferedReader br = new BufferedReader(new FileReader(mcOutFile))) { + MCOutputMessage message; + + while ((message = parseChunk(br)) != null) { + if (returnAllMessages) { + encounteredMessages.add(message); + } + + if (message.getType() == MP.ERROR) { + consumeErrorMessageAndStates(br, message); + } else if (message.getCode() == EC.TLC_FINISHED) { + break; + } + } + } + + return encounteredMessages; + } + + + private static String getPrettyPrintForState(final MCState state) { + if (state.isStuttering()) { + return "<Stuttering>"; + } else if (state.isBackToState()) { + return "<Back to state " + state.getStateNumber() + ">"; + } + return state.getLabel(); + } + + /** + * @param os an output stream to send the 'pretty printed' trace to, or a + * notification if the output file contains no error-state + * messages, or if there is no output file to be found. + * @param sourceDirectory + * @param specName + * @throws IOException + */ + public static void prettyPrintToStream(final OutputStream os, final File sourceDirectory, final String specName) + throws IOException { + final File outFile = new File(sourceDirectory, specName + TLAConstants.Files.OUTPUT_EXTENSION); + final MCOutputParser parser = new MCOutputParser(outFile); + parser.parse(false); + + final File tlaFile = new File(sourceDirectory, specName + TLAConstants.Files.TLA_EXTENSION); + final HashMap<String, String> variableTraceExpressionMap + = TraceExpressionExplorerSpecWriter.getVariableExpressionMapFromTLAFile(tlaFile); + prettyPrintToStream(os, parser.getError(), variableTraceExpressionMap); + } + + /** + * @param os an output stream to send the 'pretty printed' trace to, or a + * notification if the error is null + * messages. + * @param error + * @throws IOException + */ + public static void prettyPrintToStream(final OutputStream os, final MCError error) { + prettyPrintToStream(os, error, null); + } + + /** + * @param os an output stream to send the 'pretty printed' trace to, or a + * notification if the error is null + * messages. + * @param error + * @param variableTraceExpressionMap if non-null; any keys which are variable names will have the expressions + * substituted for the display. + * @throws IOException + */ + public static void prettyPrintToStream(final OutputStream os, final MCError error, + final HashMap<String, String> variableTraceExpressionMap) { + final PrintStream ps; + if (os instanceof PrintStream) { + ps = (PrintStream)os; + } else { + ps = new PrintStream(os); + } + + if (error == null) { + ps.println("The output log contained no error-state messages; there is nothing to display."); + } else { + if (variableTraceExpressionMap != null) { + error.updateStatesForTraceExpression(variableTraceExpressionMap); + } + + for (final MCState state : error.getStates()) { + ps.println(getPrettyPrintForState(state)); + ps.println(state.getConjunctiveDescription(true, "\t", true)); + } + } + } +} diff --git a/tlatools/src/tlc2/input/MCOutputPipeConsumer.java b/tlatools/src/tlc2/input/MCOutputPipeConsumer.java new file mode 100644 index 0000000000000000000000000000000000000000..911d62febe47d6a8cd51e2c906b548ddf76cdf58 --- /dev/null +++ b/tlatools/src/tlc2/input/MCOutputPipeConsumer.java @@ -0,0 +1,137 @@ +package tlc2.input; + +import java.io.BufferedReader; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.ArrayList; +import java.util.List; + +import tlc2.output.EC; +import tlc2.output.MP; +import util.TLAConstants; + +/** + * This class exists due to a request for the TraceExplorer CL application to be able to consume the output from TLC + * piped into TraceExplorer. To that extent, this class will: + * + * . read from a stream (for piping, this is System.in) + * . ensure that it is reading tool messages (first line received will be a message of type EC.TLC_VERSION) + * and fail appropriately if not + * . look for the first TLAConstants.LoggingAtoms.PARSING_FILE line in order to derive spec name and source directory + * . continue to read and wait and read and ... until an EC.TLC_FINISHED message is received - writing the content + * to a temp file. + */ +public class MCOutputPipeConsumer extends AbstractMCOutputConsumer { + public interface ConsumerLifespanListener { + void consumptionFoundSourceDirectoryAndSpecName(final MCOutputPipeConsumer consumer); + } + + private String specName; + private File sourceDirectory; + private boolean currentlyEncounteringModuleInformation; + private final ArrayList<File> extendedModuleLocations; + + private MCOutputMessage tlcVersionMessage; + + private final InputStream sourceStream; + private final ConsumerLifespanListener listener; + + + /** + * @param inputStream the stream containing the tool output + */ + public MCOutputPipeConsumer(final InputStream inputStream, final ConsumerLifespanListener lifespanListener) { + sourceStream = inputStream; + listener = lifespanListener; + currentlyEncounteringModuleInformation = false; + extendedModuleLocations = new ArrayList<>(); + } + + /** + * This will not return until all output has been read and the output has correctly ended (or if there is a read + * error, or if the output is not proper tool message output.) + * + * @param returnAllMessages if true, all consumed messages will be returned + * @return null or a {@link List} of {@link MCOutputMessage} instances of all messages consumed + * @throws Exception + */ + public List<MCOutputMessage> consumeOutput(final boolean returnAllMessages) throws IOException { + final ArrayList<MCOutputMessage> encounteredMessages = returnAllMessages ? new ArrayList<>() : null; + + try (final BufferedReader br = new BufferedReader(new InputStreamReader(sourceStream))) { + MCOutputMessage message; + + while ((message = parseChunk(br)) != null) { + if (returnAllMessages) { + encounteredMessages.add(message); + } + + if (message.getType() == MP.ERROR) { + consumeErrorMessageAndStates(br, message); + } else if (message.getCode() == EC.TLC_VERSION) { + tlcVersionMessage = message; + } else if (message.getCode() == EC.TLC_FINISHED) { + break; + } + } + } catch (final IOException ioe) { + if (outputHadNoToolMessages()) { + // Either we threw this from handleUnknownReadLine(String), or the output was abortive. + return encounteredMessages; + } + + throw ioe; + } + + return encounteredMessages; + } + + public boolean outputHadNoToolMessages() { + return (tlcVersionMessage == null); + } + + public String getSpecName() { + return specName; + } + + public File getSourceDirectory() { + return sourceDirectory; + } + + public List<File> getExtendedModuleLocations() { + return extendedModuleLocations; + } + + @Override + protected void handleUnknownReadLine(final String line) throws IOException { + if (((specName == null) || currentlyEncounteringModuleInformation) + && line.startsWith(TLAConstants.LoggingAtoms.PARSING_FILE)) { + if (tlcVersionMessage == null) { + throw new IOException("Output does not appear to be generated by TLC run with the '-tool' flag."); + } + + final String wholePath = line.substring(TLAConstants.LoggingAtoms.PARSING_FILE.length() + 1); + final File tlaFile = new File(wholePath); + + if (specName == null) { + sourceDirectory = tlaFile.getParentFile(); + + final String tlaFilename = tlaFile.getName(); + final int extensionDotIndex = tlaFilename.lastIndexOf('.'); + specName = tlaFilename.substring(0, extensionDotIndex); + + if (listener != null) { + listener.consumptionFoundSourceDirectoryAndSpecName(this); + } + + currentlyEncounteringModuleInformation = true; + } else { + extendedModuleLocations.add(tlaFile); + } + } else if (currentlyEncounteringModuleInformation) { + currentlyEncounteringModuleInformation = false; + } + } +} diff --git a/tlatools/src/tlc2/input/MCParser.java b/tlatools/src/tlc2/input/MCParser.java new file mode 100644 index 0000000000000000000000000000000000000000..b4fe53ebe4abaef18a20bbaaae2d5eae438e51be --- /dev/null +++ b/tlatools/src/tlc2/input/MCParser.java @@ -0,0 +1,283 @@ +package tlc2.input; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Comparator; +import java.util.HashSet; +import java.util.List; + +import tla2sany.semantic.InstanceNode; +import tla2sany.semantic.ModuleNode; +import tla2sany.semantic.OpDefNode; +import tla2sany.semantic.SymbolMatcher; +import tla2sany.semantic.SymbolNode; +import tla2sany.st.Location; +import tlc2.model.MCError; +import tlc2.tool.Defns; +import tlc2.tool.impl.ModelConfig; +import tlc2.tool.impl.SpecProcessor; +import tlc2.tool.impl.SymbolNodeValueLookupProvider; +import tlc2.tool.impl.TLAClass; +import util.FilenameToStream; +import util.SimpleFilenameToStream; +import util.TLAConstants; + +/** + * This class parses the triplet of .out, .cfg, and .tla files - typically thought of as MC.out, MC.cfg, MC.tla, but + * not required to be such. + */ +public class MCParser { + private static final int TOOL_ID = 0; + + /** + * @param specProcessor + * @param modelConfig + * @return an instance of {@link MCParserResults} with neither error nor output messages set, yet + */ + public static MCParserResults generateResultsFromProcessorAndConfig(final SpecProcessor specProcessor, + final ModelConfig modelConfig) { + final String rootModuleName; + final String nextOrSpecName; + final ArrayList<Location> initNextLocationsToDelete = new ArrayList<>(); + final boolean isInitNext = !modelConfig.configDefinesSpecification(); + final ModuleNode root = specProcessor.getRootModule(); + if (isInitNext) { + final String initDefinitionName = modelConfig.getInit(); + nextOrSpecName = modelConfig.getNext(); + final Collection<SymbolNode> initNodes = root.getSymbols(new NodeNameMatcher(initDefinitionName)); + final Collection<SymbolNode> nextNodes = root.getSymbols(new NodeNameMatcher(nextOrSpecName)); + + if (initNodes.size() == 1) { + final OpDefNode initNode = (OpDefNode) initNodes.iterator().next(); + + if (initNode.getOriginallyDefinedInModuleNode().equals(root)) { + initNextLocationsToDelete.add(initNode.getLocation()); + } + // else it is defined in a module which the original spec is extending.. nothing to document + } + + if (nextNodes.size() == 1) { + final OpDefNode nextNode = (OpDefNode) nextNodes.iterator().next(); + + if (nextNode.getOriginallyDefinedInModuleNode().equals(root)) { + initNextLocationsToDelete.add(nextNode.getLocation()); + } + // else it is defined in a module which the original spec is extending.. nothing to document + } + } else { + nextOrSpecName = modelConfig.getSpec(); + } + initNextLocationsToDelete.sort(new LocationComparator()); + + final ArrayList<String> extendees = new ArrayList<>(); + root.getExtendedModuleSet(false).stream().forEach(moduleNode -> extendees.add(moduleNode.getName().toString())); + + final HashSet<String> allExtendees = new HashSet<>(); + root.getExtendedModuleSet(true).stream().forEach(moduleNode -> allExtendees.add(moduleNode.getName().toString())); + + rootModuleName = root.getName().toString(); + + final HashSet<String> instantiatedModules = new HashSet<>(); + collectionInstantiatedModules(root, instantiatedModules, allExtendees); + + return new MCParserResults(rootModuleName, extendees, allExtendees, instantiatedModules, + initNextLocationsToDelete, isInitNext, nextOrSpecName, modelConfig); + } + + private static void collectionInstantiatedModules(final ModuleNode node, final HashSet<String> modulesList, + final HashSet<String> allExtendees) { + final InstanceNode[] instances = node.getInstances(); + for (final InstanceNode instance : instances) { + final ModuleNode instanceModule = instance.getModule(); + instanceModule.getExtendedModuleSet(true) + .stream().forEach(moduleNode -> allExtendees.add(moduleNode.getName().toString())); + modulesList.add(instanceModule.getName().toString()); + collectionInstantiatedModules(instanceModule, modulesList, allExtendees); + } + } + + + private final ModelConfig configParser; + + private MCOutputParser outputParser; + + private final TLAClass tlaClass; + private final Defns defns; + + private final FilenameToStream resolver; + + private MCParserResults parserResults; + + private final String specBaseName; + + /** + * @param sourceDirectory + * @param specName + * @param specialCaseOutputFile if non-null, this will be used as the source for the output parser + */ + public MCParser(final File sourceDirectory, final String specName) { + this(sourceDirectory, specName, null); + + File f= new File(sourceDirectory, (specBaseName + TLAConstants.Files.OUTPUT_EXTENSION)); + if (!f.exists()) { + throw new IllegalArgumentException("No readable output file could be found at " + f.getAbsolutePath()); + } + + outputParser = new MCOutputParser(f); + + f = new File(sourceDirectory, (specBaseName + TLAConstants.Files.TLA_EXTENSION)); + if (!f.exists()) { + throw new IllegalArgumentException("No readable TLA file could be found at " + f.getAbsolutePath()); + } + } + + /** + * @param sourceDirectory + * @param specName + * @param ignoreOutputParse if this is true, no output parsing will be done by this instance; the results returned + * by {@link #parse()} will contain no output messages nor any potential {@code MCError} + */ + public MCParser(final File sourceDirectory, final String specName, final boolean ignoreOutputParse) { + this(sourceDirectory, specName, null); + + if (!ignoreOutputParse) { + final File f = new File(sourceDirectory, (specBaseName + TLAConstants.Files.OUTPUT_EXTENSION)); + if (!f.exists()) { + throw new IllegalArgumentException("No readable output file could be found at " + f.getAbsolutePath()); + } + + outputParser = new MCOutputParser(f); + } + + final File f = new File(sourceDirectory, (specBaseName + TLAConstants.Files.TLA_EXTENSION)); + if (!f.exists()) { + throw new IllegalArgumentException("No readable TLA file could be found at " + f.getAbsolutePath()); + } + } + + private MCParser(final File sourceDirectory, final String specName, final Object signatureDifferentiator) { + resolver = new SimpleFilenameToStream(sourceDirectory.getAbsolutePath()); + specBaseName = specName; + + final File f = new File(sourceDirectory, (specBaseName + TLAConstants.Files.CONFIG_EXTENSION)); + if (!f.exists()) { + throw new IllegalArgumentException("No readable config file could be found at " + f.getAbsolutePath()); + } + configParser = new ModelConfig(f.getAbsolutePath(), resolver); + + tlaClass = new TLAClass("tlc2.module", resolver); + defns = new Defns(); + } + + /** + * @return will return null until {@link #parse()} has been run successfully + */ + public MCParserResults getParseResults() { + return parserResults; + } + + /** + * @return an instance of {@link MCParserResults} representing the results. If + * this instance was constructed via + * {@link #MCParser(File, String, boolean)} with the third argument + * {@code true} then the results returned will contain no output + * messages nor any potential {@code MCError} + * @throws IllegalStateException if parse has already been called on this + * instance + */ + public MCParserResults parse() { + if (parserResults != null) { + throw new IllegalStateException("Parse has already been called."); + } + + try { + final List<MCOutputMessage> encounteredMessages = (outputParser != null) ? outputParser.parse(true) : null; + + configParser.parse(); + + // No reason to start-up SANY if we're not going to generate something because the output file is unusable + if ((encounteredMessages == null) || (encounteredMessages.size() > 0)) { + final SymbolNodeValueLookupProvider defaultLookup = new SymbolNodeValueLookupProvider() {}; + final SpecProcessor specProcessor = new SpecProcessor(specBaseName, resolver, TOOL_ID, defns, + configParser, defaultLookup, null, tlaClass); + parserResults = generateResultsFromProcessorAndConfig(specProcessor, configParser); + + if (outputParser != null) { + parserResults.setError(outputParser.getError()); + parserResults.setOutputMessages(encounteredMessages); + } + } else { + // we'll have a zero size if the output generated came from a TLC run that did not have the '-tool' flag + parserResults = new MCParserResults(null, ((outputParser != null) ? outputParser.getError() : null), + encounteredMessages, new ArrayList<>(), new HashSet<>(), + new HashSet<>(), new ArrayList<>(), true, null, configParser); + } + + return parserResults; + } catch (final IOException e) { + System.out.println("Caught exception while performing MC parsing: " + e.getMessage()); + e.printStackTrace(); + } + + return null; + } + + + public static void main(final String[] args) throws Exception { + final MCParser parser = new MCParser(new File(args[0]), TLAConstants.Files.MODEL_CHECK_FILE_BASENAME, null); + final MCParserResults results = parser.parse(); + + System.out.println("Parse encountered " + results.getOutputMessages().size() + " messages."); + final MCError error = results.getError(); + if (error != null) { + System.out.println("Encountered error: "); + System.out.println(error.toSequenceOfRecords(true)); + } + + final List<String> extendedModules = results.getOriginalExtendedModules(); + System.out.println("Found " + extendedModules.size() + " module(s) being extended explicitly by the root spec:"); + for (final String module : extendedModules) { + System.out.println("\t" + module); + } + } + + + private static class NodeNameMatcher implements SymbolMatcher { + private final String name; + + NodeNameMatcher(final String nameToMatch) { + name = nameToMatch; + } + + @Override + public boolean matches(final SymbolNode aSymbol) { + if (aSymbol.getName() != null) { + return name.equals(aSymbol.getName().toString()); + } + return false; + } + } + + + // orders from location first in document to that which is last in document + private static class LocationComparator implements Comparator<Location> { + @Override + public int compare(final Location l2, final Location l1) { + if (l1.beginLine() == l2.beginLine()) { + if (l1.beginColumn() != l2.beginColumn()) { + return l1.beginColumn() - l2.beginColumn(); + } + + if (l1.endLine() != l2.endLine()) { + return l1.endLine() - l2.endLine(); + } + + return l1.endColumn() - l2.endColumn(); + } + + return l1.beginLine() - l2.beginLine(); + } + } +} diff --git a/tlatools/src/tlc2/input/MCParserResults.java b/tlatools/src/tlc2/input/MCParserResults.java new file mode 100644 index 0000000000000000000000000000000000000000..b3f57e89acfc96ddf63dbb780e7c88098551954c --- /dev/null +++ b/tlatools/src/tlc2/input/MCParserResults.java @@ -0,0 +1,122 @@ +package tlc2.input; + +import java.util.List; +import java.util.Set; + +import tla2sany.st.Location; +import tlc2.model.MCError; +import tlc2.tool.impl.ModelConfig; + +public class MCParserResults { + private final String moduleName; + + private MCError error; + private List<MCOutputMessage> outputMessages; + + private final List<String> immediateExtendedModules; + private final Set<String> allExtendedModules; + private final Set<String> modulesInstantiated; + + private final List<Location> initNextLocationsToDelete; + + private final boolean behaviorIsInitNext; + + private final String originalNextOrSpecificationName; + + private final ModelConfig modelConfig; + + MCParserResults(final String rootModuleName, final List<String> immediateExtendeds, final Set<String> allExtendeds, + final Set<String> allInstantiated, final List<Location> initNextLocations, + final boolean wasInitNext, final String nextOrSpecName, final ModelConfig config) { + moduleName = rootModuleName; + + immediateExtendedModules = immediateExtendeds; + allExtendedModules = allExtendeds; + + modulesInstantiated = allInstantiated; + + initNextLocationsToDelete = initNextLocations; + + behaviorIsInitNext = wasInitNext; + + originalNextOrSpecificationName = nextOrSpecName; + + modelConfig = config; + } + + MCParserResults(final String rootModuleName, final MCError mcError, final List<MCOutputMessage> messages, + final List<String> immediateExtendeds, final Set<String> allExtendeds, + final Set<String> allInstantiated, final List<Location> initNextLocations, + final boolean wasInitNext, final String nextOrSpecName, final ModelConfig config) { + this(rootModuleName, immediateExtendeds, allExtendeds, allInstantiated, initNextLocations, wasInitNext, + nextOrSpecName, config); + + error = mcError; + outputMessages = messages; + } + + public ModelConfig getModelConfig() { + return modelConfig; + } + + public String getModuleName() { + return moduleName; + } + + public String getOriginalNextOrSpecificationName() { + return originalNextOrSpecificationName; + } + + public MCError getError() { + return error; + } + + void setError(final MCError mcError) { + error = mcError; + } + + public List<MCOutputMessage> getOutputMessages() { + return outputMessages; + } + + void setOutputMessages(final List<MCOutputMessage> messages) { + outputMessages = messages; + } + + /** + * @return the {@link List} of all modules extended by the root spec explicitly - in other words, for example, + * the X, Y, Z cited by a root spec's "EXTENDS X, Y, Z" + */ + public List<String> getOriginalExtendedModules() { + return immediateExtendedModules; + } + + /** + * @return the {@link Set} of all modules extended - in other words, the modules extended by all modules extended + * by all modules extended by ... the root spec. + */ + public Set<String> getAllExtendedModules() { + return allExtendedModules; + } + + /** + * @return this returns a {@link Set} of all instantiated modules - in other words, all modules, X, found in any + * module of the entire module tree that is instantiated in TLA+ code like {@code ... = INSTANCE X} + * <b>Note:</b> the is the possibility of a non-null intersection between this set and the set of + * extended modules. + */ + public Set<String> getAllInstantiatedModules() { + return modulesInstantiated; + } + + /** + * @return ordered from location first in document to that which is last in document + */ + public List<Location> getInitNextLocationsToDelete() { + return initNextLocationsToDelete; + } + + public boolean isBehaviorInitNext() { + return behaviorIsInitNext; + } +} diff --git a/tlatools/src/tlc2/model/Assignment.java b/tlatools/src/tlc2/model/Assignment.java new file mode 100644 index 0000000000000000000000000000000000000000..29e9e4a425810fa6164e44aa389732f0025d52d6 --- /dev/null +++ b/tlatools/src/tlc2/model/Assignment.java @@ -0,0 +1,345 @@ +package tlc2.model; + +/** + * An Assignment consists of a label, a list of parameters and the right side. + * e.G. <code>F(_, _, _,) <- foo</code>. <code>F</code> is the label, <code>foo</code> is the + * right side, and there are three parameters. Because of the fact, that usually the user + * wants to specify <code>foo</code> as a function of parameters, the parameters can be named. + * So <code>F(a, b, c) <- foo(a, b) + c<code> the three parameters has names <code>a, b, c</code>. + * <br><br> + * The label of an assignment is immutable. The parameters and the right side can be changed. + * <br><br> + * The right side of assignments with no parameters has different meanings depending on the {@link Assignment#modelValue}. + * If the value of {@link Assignment#modelValue} is <code>false</code>, the right side is treated as an ordinary assignment. + * <br> + * If the value of {@link Assignment#modelValue} is <code>true</code>, the assignment is treated as a assignments of + * model value(s) to a constant. If the right side is equals to the label of the assignment, it is considered, that the + * assignment of type <code>F = F</code> is used. Otherwise, the right side is interpreted as a set of model values and the + * assignment of type <code>F = {f_1, f_2, f_3}</code> with <code>f_1 = f_1, f_2 = f_2, f_3 = f_3</code>. + * <br> + * Especially, the right side is parsed using {@link TypedSet#parseSet(String)}. The information whether the set of model values + * is typed or not is not saved explicitly, but is based on the syntax of the set. The set <code>{p_1, p_2, p_3}</code> is typed + * (type p), the set <code>{k_1, i_2, 3}</code> is untyped. + * <br> + * For assignments with at least one parameter, the value of the {@link Assignment#modelValue} is ignored. + * <br> + * <br> + * @author Simon Zambrovski + * @version $Id$ + */ +public class Assignment extends Formula { + public static final String ASSIGNMENT_SIGN = " <- "; + public static final String IS_MV = " [ model value ] "; + public static final String SYMMETRICAL = " <symmetrical> "; + + + private String label; + private String[] params = new String[0]; + private boolean modelValue = false; + private boolean symmetry = false; + private TypedSet setOfModelValues = null; + + /** + * Constructs the assignment + * if the right side is equals to the label, the assignment is treated as a model value assignment + */ + public Assignment(String label, String[] params, String right) { + super(right); + this.label = label; + this.setParams(params); + if ((this.label != null) && (right != null) && this.label.equals(right)) { + // right side equals label => model value + setModelValue(true); + } + } + + public String getFormula() { + return getFormula(""); + } + + public String getFormula(String tab) + { + StringBuffer buffer = new StringBuffer(getLeft()); + buffer.append(tab); + buffer.append(ASSIGNMENT_SIGN); + + if (this.modelValue) + { + buffer.append(IS_MV); + if (isSetOfModelValues()) + { + if (this.isSymmetricalSet()) + { + buffer.append(SYMMETRICAL); + buffer.append(getFormattedRight()); + } else { + // print out the set + buffer.append(getFormattedRight()); + } + } else + { + // a single value, nothing to print + } + } else + { + buffer.append(getFormattedRight()); + } + + return buffer.toString(); + } + + /** + * Retrieves the left part (label with parameter list) + * @return + */ + public String getLeft() + { + return getParametrizedLabel(this.label); + } + + /** + * Appends parameters to the label + * @param id + * @return + */ + public String getParametrizedLabel(String id) + { + return id + listParams(); + } + + /** + * @return a string representation of parameter list + */ + private String listParams() + { + if (params.length == 0) + { + return ""; + } + final StringBuilder buffer = new StringBuilder(); + buffer.append('('); + for (int i = 0; i < params.length; i++) + { + buffer.append(params[i]); + buffer.append((i != params.length - 1) ? ", " : ""); + } + buffer.append(')'); + return buffer.toString(); + } + + public void setFormula(String formula) + { + throw new UnsupportedOperationException("Not implemented yet"); + } + + /** + * @return the label + */ + public String getLabel() + { + return label; + } + + /** + * @return "bar" for a assignment "frob!bar" + */ + public String getLocalLabel() { + final int beginIndex = label.lastIndexOf("!") + 1; + return label.substring(beginIndex); + } + + /** + * Retrieve the right part + * @return the right side of the assignment, can be <code>null</code> + */ + public String getRight() + { + return super.getFormula(); + } + + /** + * Retrieves a formatted version of the right side + * @return right side ending with ... iff \n is contained in the right side + */ + public String getFormattedRight() + { + String tempRight = getRight(); + if (tempRight == null) + { + return null; + } + int i = -1; + if ( (i = tempRight.indexOf("\n")) != -1) + { + tempRight = tempRight.substring(0, i + 1) + " ..."; + } + return tempRight; + } + + + public String[] getParams() + { + return params; + } + + public String toString() + { + return getFormula(); + } + + /** + * Sets the right part + * @param right + */ + public synchronized void setRight(String right) { + super.setFormula(right); + setOfModelValues = null; + } + + /** + * Set parameters, the left part depends on + * @param params + * + * Modified by LL on 11 Nov 2009 to trim spaces off the ends of + * the parameters. + */ + public void setParams(String[] params) + { + if (params != null) + { + this.params = params; + for (int i = 0; i < this.params.length; i++) { + this.params[i] = this.params[i].trim(); + } + } else + { + this.params = new String[0]; + } + } + + /** + * @return if this assignment is to be set to the model value + */ + public boolean isModelValue() + { + return modelValue; + } + + /** + * @return true iff model value but not a set of values + */ + public boolean isSimpleModelValue() { + return isModelValue() && !isSetOfModelValues(); + } + + /** + * @return true, iff the assignment is a set of model values + */ + public boolean isSetOfModelValues() { + return modelValue && !getLabel().equals(getRight()); + } + + /** + * @return the TypedSet for model values if {@link #isSetOfModelValues()} returns true, otherwise null + */ + public synchronized TypedSet getSetOfModelValues() { + if (isSetOfModelValues()) { + if (setOfModelValues == null) { + setOfModelValues = TypedSet.parseSet(getRight()); + } + + return setOfModelValues; + } + + return null; + } + + /** + * @return true, if the set of model values is symmetrical + */ + public boolean isSymmetricalSet() { + return symmetry; + } + + /** + * Sets the symmetry property for a set of model values + * @param isSymmetric + */ + public void setSymmetric(boolean isSymmetric) + { + if (isSymmetric && !modelValue) + { + throw new IllegalArgumentException("Current assignment is not a set of model values"); + } + this.symmetry = isSymmetric; + } + + /** + * Set the constant assignment to be a model value + * @param modelValue + */ + public void setModelValue(boolean modelValue) + { + if (modelValue && this.params.length != 0) + { + throw new IllegalArgumentException("Operators can not be instantiated with model values"); + } + this.modelValue = modelValue; + } + + public static String[] getArrayOfEmptyStrings(int number) + { + String[] array = new String[number]; + String EMPTY = new String(""); + for (int i = 0; i < number; i++) + { + array[i] = EMPTY; + } + return array; + } + + + /** + * compares if the signature (label and the number of parameters matches) + */ + public boolean equalSignature(Assignment obj) + { + if (this == obj) + return true; + if (obj == null) + return false; + if (label == null) + { + if (obj.label != null) + return false; + } else if (!label.equals(obj.label)) + return false; + + return (params.length == obj.params.length); + } + + public String prettyPrint() { + return prettyPrint(""); + } + + public String prettyPrint(final String delim) { + final StringBuffer buf = new StringBuffer(); + if (!isModelValue()) { + return getFormula(delim); + } else if (isSetOfModelValues()) { + buf.append(getLeft()); + buf.append(delim); + buf.append(ASSIGNMENT_SIGN); + if (isSymmetricalSet()) { + buf.append("s"); + } + buf.append(getFormattedRight()); + } else { + // Ordinary model value, just skip the value (no point showing "X <- + // X"). + buf.append(getLeft()); + } + return buf.toString(); + } + +} diff --git a/tlatools/src/tlc2/model/Formula.java b/tlatools/src/tlc2/model/Formula.java new file mode 100644 index 0000000000000000000000000000000000000000..aa429c18a6269444679fa42764df77b12239b62e --- /dev/null +++ b/tlatools/src/tlc2/model/Formula.java @@ -0,0 +1,86 @@ +package tlc2.model; + +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Representation of a formula. + * @author Simon Zambrovski + */ +public class Formula { + /** + * De-serialize formula list, to a list of formulas, that are selected (have a leading "1") + * + * The first character of the formula is used to determine if the formula is enabled in the model + * editor or not. This allows the user to persist formulas, which are not used in the current model + */ + public static List<Formula> deserializeFormulaList(final List<String> serializedList) { + final ArrayList<Formula> result = new ArrayList<>(serializedList.size()); + for (final String entry : serializedList) { + if ("1".equals(entry.substring(0, 1))) { + result.add(new Formula(entry.substring(1))); + } + } + return result; + } + + // DOTALL to match beyond line endings. + private static final Pattern PATTERN = Pattern.compile("^\\s*(\\w+)\\s*==(.*)$", Pattern.DOTALL); + + + private String formula; + + /** + * Constructs a formula representation + * @param formula + */ + public Formula(String formulaString) { + formula = formulaString; + } + + /** + * Retrives formula + * + * @return + */ + public String getFormula() { + return formula; + } + + @Override + public String toString() { + return formula; + } + + /** + * @param formulaString + */ + public void setFormula(String formulaString) { + formula = formulaString; + } + + // MAK 03/2019: This methods below appear redundant to the Assignment subclass + // but I don't have time to look into it. + + public boolean isNamed() { + return !getLeftHandSide().equals(getFormula()); + } + + public String getLeftHandSide() { + final Matcher matcher = PATTERN.matcher(this.formula); + if (matcher.find()) { + return matcher.group(1).trim(); + } + return getFormula(); + } + + public String getRightHandSide() { + final Matcher matcher = PATTERN.matcher(this.formula); + if (matcher.find()) { + return matcher.group(2).trim(); + } + return getFormula(); + } +} diff --git a/tlatools/src/tlc2/model/MCError.java b/tlatools/src/tlc2/model/MCError.java new file mode 100644 index 0000000000000000000000000000000000000000..69b60a9e894d84394628760bc6c5bc633892bdf1 --- /dev/null +++ b/tlatools/src/tlc2/model/MCError.java @@ -0,0 +1,79 @@ +package tlc2.model; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +import util.TLAConstants; + +public class MCError { + private final String message; + private final MCError cause; + private final ArrayList<MCState> states; + + public MCError(final MCError errorCause, final String errorMessage) { + cause = errorCause; + message = errorMessage; + states = new ArrayList<>(); + } + + public void addState(final MCState state) { + states.add(state); + } + + public List<MCState> getStates() { + return states; + } + + public String getMessage() { + return message; + } + + public MCError getCause() { + return cause; + } + + public void updateStatesForTraceExpression(final HashMap<String, String> variableExpressionMap) { + for (final MCState state : states) { + for (final MCVariable variable : state.getVariables()) { + final String expression = variableExpressionMap.get(variable.getName()); + + if (expression != null) { + variable.setTraceExpression(expression); + } + } + } + } + + public String toSequenceOfRecords(final boolean includeHeaders) { + final StringBuilder buf = new StringBuilder(); + buf.append(TLAConstants.BEGIN_TUPLE); + buf.append(TLAConstants.CR); + + for (int i = 0; i < states.size(); i++) { + final MCState tlcState = states.get(i); + + if (tlcState.isBackToState() || tlcState.isStuttering()) { + //TODO How to represent these two types of states? + continue; + } + + if ((tlcState.getVariables().length == 0) && !includeHeaders) { + // When an user has used filtering to hide all variables, the error trace here + // has no variables. In this case just return empty sequence <<>> by breaking + // from the loop. + break; + } + + if (i > 0) { + // Append a comma if a record is going to be added below. + buf.append(TLAConstants.COMMA).append(TLAConstants.CR); + } + buf.append(tlcState.asRecord(includeHeaders)); + } + + buf.append(TLAConstants.CR); + buf.append(TLAConstants.END_TUPLE); + return buf.toString(); + } +} diff --git a/tlatools/src/tlc2/model/MCState.java b/tlatools/src/tlc2/model/MCState.java new file mode 100644 index 0000000000000000000000000000000000000000..aa1af885eab94d9126af0437d2c2fc68812ef216 --- /dev/null +++ b/tlatools/src/tlc2/model/MCState.java @@ -0,0 +1,303 @@ +package tlc2.model; + +import java.util.ArrayList; + +import tla2sany.st.Location; +import util.TLAConstants; + +public class MCState { + private static final String BACK_TO_STATE = " " + TLAConstants.BACK_TO_STATE; + + public static MCState parseState(final String stateInputString) { + // state number + final int index = stateInputString.indexOf(TLAConstants.COLON); + // multi line + int index2 = stateInputString.indexOf(TLAConstants.CR, index); + if (index2 == -1) { + index2 = stateInputString.length(); + } + + final int stateNumber = Integer.parseInt(stateInputString.substring(0, index).trim()); + final String label = stateInputString.substring((index + 1), index2); + final boolean isStuttering = (label.indexOf(TLAConstants.STUTTERING) == 0); + final boolean isBackToState = (label.indexOf(BACK_TO_STATE) == 0); + + MCVariable[] vars = null; + final String name; + final Location location; + if (!isBackToState && !isStuttering) { + // string from which the variables can be parsed + final String variableInputString = stateInputString.substring(index2 + 1); + vars = parseVariables(variableInputString); + + final String sublabel = label.substring(2, (label.length() - 1)); + final int lineIndex = sublabel.indexOf(TLAConstants.LINE); + if (lineIndex != -1) { + name = sublabel.substring(0, (lineIndex - 1)); + location = Location.parseLocation(sublabel.substring(lineIndex)); + } else { + name = sublabel; + location = null; + } + } else { + name = null; + location = null; + } + + return new MCState(vars, name, label, location, isStuttering, isBackToState, stateNumber); + } + + + private final MCVariable[] variables; + private final String name; + private final String label; + private final Location location; + private final boolean isStuttering; + private final boolean isBackToState; + private final int stateNumber; + + /** + * @param vars variables in this state. + * @param stateName the name for this state + * @param stateLabel the display label, usually including line location and module + * @param moduleLocation the name of this module whose checking this state is from + * @param stuttering whether this is a stuttering state or not + * @param backToState whether this is a back to state or not + * @param ordinal number of the state in the trace + */ + public MCState(final MCVariable[] vars, final String stateName, final String stateLabel, + final Location moduleLocation, final boolean stuttering, final boolean backToState, final int ordinal) { + variables = vars; + name = stateName; + label = stateLabel; + location = moduleLocation; + isStuttering = stuttering; + isBackToState = backToState; + stateNumber = ordinal; + } + + public MCVariable[] getVariables() { + return variables; + } + + public String getLabel() { + return label; + } + + public boolean isStuttering() { + return isStuttering; + } + + public boolean isBackToState() { + return isBackToState; + } + + public int getStateNumber() { + return stateNumber; + } + + public String asRecord(final boolean includeHeader) { + final StringBuilder result = new StringBuilder(); + result.append(TLAConstants.L_SQUARE_BRACKET); + result.append(TLAConstants.CR); + + if (includeHeader) { + result.append(TLAConstants.SPACE); + result.append(TLAConstants.TraceExplore.ACTION); + result.append(TLAConstants.RECORD_ARROW); + + result.append(TLAConstants.L_SQUARE_BRACKET); + result.append(TLAConstants.CR); + result.append(TLAConstants.SPACE).append(TLAConstants.SPACE).append(TLAConstants.SPACE); + result.append("position"); + result.append(TLAConstants.RECORD_ARROW); + result.append(getStateNumber()); + result.append(TLAConstants.COMMA).append(TLAConstants.CR); + + result.append(TLAConstants.SPACE).append(TLAConstants.SPACE).append(TLAConstants.SPACE); + result.append("name"); + result.append(TLAConstants.RECORD_ARROW); + result.append(TLAConstants.QUOTE); + result.append(name); + result.append(TLAConstants.QUOTE); + result.append(TLAConstants.COMMA).append(TLAConstants.CR); + + result.append(TLAConstants.SPACE).append(TLAConstants.SPACE).append(TLAConstants.SPACE); + result.append("location"); + result.append(TLAConstants.RECORD_ARROW); + result.append(TLAConstants.QUOTE); + result.append(location); + result.append(TLAConstants.QUOTE); + + result.append(TLAConstants.CR); + result.append(TLAConstants.SPACE).append(TLAConstants.R_SQUARE_BRACKET); + if (variables.length != 0) { + // only append comma for additional records iff there are any variables to + // append. + result.append(TLAConstants.COMMA).append(TLAConstants.CR); + } + } + + for (int i = 0; i < variables.length; i++) { + final MCVariable variable = variables[i]; + if (variable.isTraceExplorerExpression()) { + result.append(variable.getSingleLineDisplayName()); + } else { + result.append(variable.getName()); + } + + result.append(TLAConstants.RECORD_ARROW); + + result.append(variable.getValueAsString()); + + if (i < (variables.length - 1)) { + result.append(TLAConstants.COMMA).append(TLAConstants.CR); + } + } + + result.append(TLAConstants.CR).append(TLAConstants.R_SQUARE_BRACKET); + return result.toString(); + } + + public String asSimpleRecord() { + final StringBuilder buf = new StringBuilder(); + buf.append(TLAConstants.L_SQUARE_BRACKET); + for (int i = 0; i < variables.length; i++) { + final MCVariable var = variables[i]; + + buf.append(var.getName()); + buf.append(TLAConstants.RECORD_ARROW); + buf.append(var.getValueAsString()); + + if (i < variables.length - 1) { + buf.append(TLAConstants.COMMA); + } + } + buf.append(TLAConstants.R_SQUARE_BRACKET); + return buf.toString(); + } + + /** + * The returns a conjunction list of variables. + * + * For variables representing trace explorer expressions, if {@code includeTraceExpressions} is true, + * the returned string has: + * + * /\ expr = value + * + * where expr is the single line form of the trace explorer expression as shown in the Name column of + * the trace viewer. + * + * For all other variables, this method attempts to display them as TLC does. + * + * @param includeTraceExpressions whether trace expressions should be included. + * @param indent if non-null, this will be prepended to each line + * @return + */ + public String getConjunctiveDescription(final boolean includeTraceExpressions, final String indent) { + return getConjunctiveDescription(includeTraceExpressions, indent, false); + } + + /** + * The returns a conjunction list of variables. + * + * For variables representing trace explorer expressions, if {@code includeTraceExpressions} is true, + * the returned string has: + * + * /\ expr = value + * + * where expr is the single line form of the trace explorer expression as shown in the Name column of + * the trace viewer. + * + * For all other variables, this method attempts to display them as TLC does. + * + * @param includeTraceExpressions whether trace expressions should be included. + * @param indent if non-null, this will be prepended to each line + * @param ansiMarkup if true, the String will include ANSI markup for trace expressions; this is currently ignored + * if includeTraceExpressions is false + * @return + */ + public String getConjunctiveDescription(final boolean includeTraceExpressions, final String indent, + final boolean ansiMarkup) { + final StringBuilder result = new StringBuilder(); + + for (int i = 0; i < variables.length; i++) { + final MCVariable var = variables[i]; + + if (var.isTraceExplorerExpression() && !includeTraceExpressions) { + continue; + } + + if (indent != null) { + result.append(indent); + } + + result.append("/\\ "); + if (var.isTraceExplorerExpression()) { + if (ansiMarkup) { + result.append(TLAConstants.ANSI.BOLD_CODE); + } + + result.append(var.getSingleLineDisplayName()); + } else { + result.append(var.getName()); + } + + result.append(" = ").append(var.getValueAsString()); + + if (var.isTraceExplorerExpression() && ansiMarkup) { + result.append(TLAConstants.ANSI.RESET_CODE); + } + + result.append('\n'); + } + + return result.toString(); + } + + + private static MCVariable[] parseVariables(final String variableInputString) { + String[] lines = variableInputString.split(TLAConstants.CR); + ArrayList<MCVariable> vars = new ArrayList<>(); + int index; + + // buffer for accumulating the state variable + String[] stateVarString = null; + + // iterate line-wise + for (int j = 0; j < lines.length; j++) { + // find the index of the first /\ in the line + index = lines[j].indexOf(TLAConstants.TLA_AND); + // adding the current line to the previous lines + if (index != -1) { + // there was something in the buffer for the state variable + // found an empty line, which means that this is the end of the current state + if (stateVarString != null) { + final MCVariable var = new MCVariable(stateVarString[0], stateVarString[1]); + vars.add(var); + } + + stateVarString = lines[j].substring(index + TLAConstants.TLA_AND.length() + 1).split(TLAConstants.EQ); + } else { + // no index + + if (stateVarString != null) { + // either an empty line + stateVarString[1] += TLAConstants.CR; + stateVarString[1] += lines[j]; + } else { + // the state has one variable only + stateVarString = lines[j].split(TLAConstants.EQ); + } + } + } + + // write the last one + if (stateVarString != null) { + final MCVariable var = new MCVariable(stateVarString[0], stateVarString[1]); + vars.add(var); + } + + return (MCVariable[]) vars.toArray(new MCVariable[vars.size()]); + } +} diff --git a/tlatools/src/tlc2/model/MCVariable.java b/tlatools/src/tlc2/model/MCVariable.java new file mode 100644 index 0000000000000000000000000000000000000000..ff057303a0cbd1cd270cf2935cea9d07e652382f --- /dev/null +++ b/tlatools/src/tlc2/model/MCVariable.java @@ -0,0 +1,62 @@ +package tlc2.model; + +public class MCVariable { + private final String name; + private final String valueAsString; + private String traceExpression; + + /** + * @param varName name of the variable + * @param value TLC string representation of the variable value + */ + public MCVariable(final String varName, final String value) { + name = varName; + valueAsString = value; + traceExpression = null; + } + + public String getName() { + return name; + } + + /** + * @return the name, or the trace expression if it is defined, for this variable + * in a single line String; the name could be multiple lines if this + * represents a trace explorer expression. + */ + public String getSingleLineDisplayName() { + final String s = isTraceExplorerExpression() ? traceExpression : name; + + return s.replaceAll("\\n", "").replaceAll("\\r", ""); + } + + public String getValueAsString() { + return valueAsString; + } + + public String getValueAsStringReIndentedAs(final String indent) { + final String[] split = valueAsString.split("(\\r\\n|\\r|\\n)"); + final StringBuilder sb = new StringBuilder(); + + for (int i = 0; i < split.length; i++) { + sb.append(indent).append(split[i]); + if (i < (split.length - 1)) { + sb.append("\n"); + } + } + + return sb.toString(); + } + + public boolean isTraceExplorerExpression() { + return (traceExpression != null); + } + + public void setTraceExpression(final String expression) { + traceExpression = expression; + } + + public String getTraceExpression() { + return traceExpression; + } +} diff --git a/tlatools/src/tlc2/model/TraceExpressionInformationHolder.java b/tlatools/src/tlc2/model/TraceExpressionInformationHolder.java new file mode 100644 index 0000000000000000000000000000000000000000..a771e76a13d090112b7002deaa062f914b1e42cc --- /dev/null +++ b/tlatools/src/tlc2/model/TraceExpressionInformationHolder.java @@ -0,0 +1,115 @@ +package tlc2.model; + +import java.util.List; + +import tlc2.output.SpecWriterUtilities; +import util.TLAConstants; + +/** + * A container class for the relevant information about a + * trace explorer expression. This class contains the expression, + * the identifier which is defined to be the expression, the variable + * that is declared for this expression, and the level of the expression. + * + * @author Daniel Ricketts + * + */ +public class TraceExpressionInformationHolder { + /** + * @param expressions + * @param attributeName + * @return expressions.size() instances of {@code TraceExpressionInformationHolder} + */ + public static TraceExpressionInformationHolder[] createHolders(final List<Formula> expressions, final String attributeName) { + final TraceExpressionInformationHolder[] holders = new TraceExpressionInformationHolder[expressions.size()]; + int position = 0; + for (final Formula formula : expressions) { + final String expression = formula.getFormula(); + + if ((expression != null) && (expression.length() > 0)) { + final String identifier + = SpecWriterUtilities.getValidIdentifier(TLAConstants.Schemes.TRACE_EXPR_DEF_SCHEME); + if (formula.isNamed()) { + final String varname = formula.getLeftHandSide(); + final String rightHandSide = formula.getRightHandSide(); + holders[position] = new TraceExpressionInformationHolder(rightHandSide, identifier, varname); + } else { + final String varname + = SpecWriterUtilities.getValidIdentifier(TLAConstants.Schemes.TRACE_EXPR_VAR_SCHEME); + holders[position] = new TraceExpressionInformationHolder(expression, identifier, varname); + } + } + + position++; + } + + return holders; + } + + + /* + * The expression that the user wants to be evaluated at every + * state of the trace. + */ + private String expression; + /* + * The identifier that is defined to be the expression. + */ + private String identifier; + /* + * The variable name that is declared for this expression. + */ + private String variableName; + /* + * The level of the trace expression + */ + private int level; + + public void setExpression(String expression) + { + this.expression = expression; + } + + public void setIdentifier(String identifier) + { + this.identifier = identifier; + } + + public void setVariableName(String variableName) + { + this.variableName = variableName; + } + + public void setLevel(int level) + { + this.level = level; + } + + public TraceExpressionInformationHolder(String expression, String identifier, String variableName) + { + this.expression = expression; + this.identifier = identifier; + this.variableName = variableName; + } + + public String getExpression() + { + return expression; + } + + public String getIdentifier() + { + return identifier; + } + + public String getVariableName() + { + return variableName; + } + + public int getLevel() + { + return level; + } + +} diff --git a/tlatools/src/tlc2/model/TypedSet.java b/tlatools/src/tlc2/model/TypedSet.java new file mode 100644 index 0000000000000000000000000000000000000000..e890e7e486bc9c699d998453bbd36bef4fc8edf6 --- /dev/null +++ b/tlatools/src/tlc2/model/TypedSet.java @@ -0,0 +1,374 @@ +package tlc2.model; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * Representation of a typed set. + * <br> + * This class is used for typed model values. To create an instance of this class + * you probably want to parse it out of a string, using {@link TypedSet#parseSet(String)} method. + * + * A TypedSet object appears to be used to represent a TLA+ set of the form + * + * { a1 , a2, ... , aN } + * + * for N >= 0, where each ai is a TLA+ identifier. If each of the ai begins with "C_" + * for the same character C, then it is represented by an object having type = "C_" and + * values[i] equal to ai with the prefix "C_" removed, for each i. Otherwise, it is represented + * by an object having type = null and values[i] = ai, for each i. + * + * @author Simon Zambrovski + * @version $Id$ + */ +public class TypedSet { + public static final TypedSet EMPTY_SET = new TypedSet(); + + private static final String SEPARATOR = "_"; + private static final String PATTERN = "[\\s]*,[\\s]*"; + + + private String[] values = new String[0]; + private String type = null; + + /** + * Factory method + * @param set String representation of the set + * @return a typed set + */ + public static TypedSet parseSet(String set) + { + // This code did not work properly because it could leave spaces at the + // beginning of values[0] or end of values[value.length-1] if there + // were spaces at the beginning or end of set or the curly braces were + // preceded or followed by spaces. This was fixed by adding the + // two set = set.trim(); statements and rewriting the + // tests for an empty or null set. Changes made by LL on 11 Nov 2009. + TypedSet result = new TypedSet(); + if (set == null) + { + return result; + } + set = set.trim(); + // if the curly braces are provided, cut them + if (set.length() > 0 && set.charAt(0) == '{' && set.charAt(set.length() - 1) == '}') + { + set = set.substring(1, set.length() - 1); + set = set.trim(); + } + + if ("".equals(set)) + { + return result; + } + + String[] parsedSet = null; + + // split by comma surrounded by any kind of spaces/tabs/new lines + parsedSet = set.split(PATTERN); + + if (parsedSet.length > 0) + { + // pre-fetch first element (if no type it will store -1 there) + int typeSeparatorPosition = parsedSet[0].indexOf("_"); + if (typeSeparatorPosition == -1 || typeSeparatorPosition == 0) + { + // no type, provided + result.setValues(parsedSet); + return result; + } else + { + // type is provided + result.setType(parsedSet[0].substring(0, typeSeparatorPosition)); + parsedSet[0] = parsedSet[0].substring(typeSeparatorPosition + 1); + + // assume that all strings have same structure + // and set type violated to false + // this also checks that strings like e.G. "P_", "x_" are not valid + // because they miss the untyped part + boolean typePatternViolated = parsedSet[0].length() == 0; + + // run through the strings and compare the position of the first "_" + // if it changes the type patter is violated: e.G. "p_1" "pi_2". + // also compare for the type: e.G. "p_1" "i_2" + for (int i = 1; i < parsedSet.length; i++) + { + if (parsedSet[i].startsWith(result.getType() + "_")) + { + parsedSet[i] = parsedSet[i].substring(typeSeparatorPosition + 1); + if (parsedSet[i].length() == 0) + { + typePatternViolated = true; + } + } else + { + typePatternViolated = true; + } + // exit if type pattern is violated + if (typePatternViolated) + { + break; + } + } + + if (typePatternViolated) + { + // violated type, restore the split + result.setValues(set.split(PATTERN)); + result.setType(null); + } else + { + // type recognized + result.setValues(parsedSet); + } + } + + } else + { + // no values in the set... + } + + return result; + } + + public boolean hasType() + { + return (type != null); + } + + public String getType() + { + return type; + } + + // If id is a "typed" identifier, then it returns + // the (one-character) type as a string. Else, it + // returns null. Note that getTypeOfId("1_xyz") = "1", + // and getTypteOfId("__z") = "z". + public static String getTypeOfId(String id) + { + if (id == null || id.length() < 2 || !id.substring(1, 2).equals("_")) + { + return null; + } + return id.substring(0, 1); + } + + public void setType(String type) + { + this.type = type; + } + + public void unsetType() { + setType(null); + } + + /** + * Not remotely efficient. + * + * @param value + * @return true if the parameter value is one of this set's values. + */ + public boolean contains(final String value) { + if (value != null) { + for (final String aValue : values) { + if (value.equals(aValue)) { + return true; + } + } + } + + return false; + } + + public String[] getValues() + { + return values; + } + + /** + * Convenience interface for iteration over the values + * This method disconnects the actual typed set from the collection of values + * @return a list containing the values + */ + public List<String> getValuesAsList() + { + if (!hasType()) + { + return Arrays.asList(values); + } else + { + List<String> typedList = new ArrayList<String>(values.length); + // add type to the list + for (int i = 0; i < values.length; i++) + { + String value = type + SEPARATOR + values[i]; + typedList.add(value); + } + return typedList; + } + } + + /** + * retrieves the number of values in the set + * @return number of values in the set, or null if none + */ + public int getValueCount() + { + if (values == null) + { + return 0; + } else + { + return values.length; + } + } + + /** + * Retrieves a value by index + * @param index, index of the value, should be smaller then the value of {@link TypedSet#getValueCount()} + * @return value (with type, if any) or null if index out of range + */ + public String getValue(int index) + { + if (index >= getValueCount()) + { + return null; + } else + { + return (hasType() ? getType() + SEPARATOR : "") + values[index]; + } + } + + public void setValues(String[] values) + { + if (values == null) + { + this.values = new String[0]; + } else + { + this.values = values; + } + } + + public int hashCode() + { + final int prime = 31; + int result = 1; + result = prime * result + ((type == null) ? 0 : type.hashCode()); + result = prime * result + TypedSet.hashCode(values); + return result; + } + + private static int hashCode(Object[] array) + { + int prime = 31; + if (array == null) + return 0; + int result = 1; + for (int index = 0; index < array.length; index++) + { + result = prime * result + (array[index] == null ? 0 : array[index].hashCode()); + } + return result; + } + + /** + * Two typed sets are equals if they have + * the same type and the elements of the set are the same + * (and appear in the same order) + */ + public boolean equals(Object obj) + { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + TypedSet other = (TypedSet) obj; + if (type == null) + { + if (other.type != null) + return false; + } else if (!type.equals(other.type)) + return false; + if (!Arrays.equals(values, other.values)) + return false; + return true; + } + + /** + * The string implementation of the typed set + * Is used to be set in the right side of assignment ({@link Assignment#setRight(String)}) + * <br><b>Note:</b> {@link TypedSet#toString()} should not be used for comparison + * @see TypedSet#equals(Object) + */ + public String toString() + { + StringBuffer buffer = new StringBuffer("{"); + for (int i = 0; i < this.values.length; i++) + { + if (this.type != null) + { + buffer.append(this.type).append(SEPARATOR); + } + buffer.append(this.values[i]); + if (i != this.values.length - 1) + { + buffer.append(", "); + } + } + buffer.append("}"); + return buffer.toString(); + } + + /** + * Same as toString, but without curly braces + * @return + */ + public String toStringWithoutBraces() + { + String set = toString(); + return set.substring(1, set.length() - 1); + } + + /** + * This test functions checks whether the type has at least one value + * that contain only of digits + * @return true if on of the values (taking the type into account) is a digit + * @deprecated + */ + public boolean hasANumberOnlyValue() + { + if (hasType()) + { + return !hasValidType(); + } else + { + for (int i = 0; i < values.length; i++) + { + if (values[i].matches("[0-9]*")) + { + // a digit sequence found + return true; + } + } + return false; + } + } + + public boolean hasValidType() + { + if (type != null) + { + if (!type.matches("[A-Za-z]{1}[A-Za-z0-9]*")) + { + // the type must be a valid identifier + return false; + } + } + return true; + } +} diff --git a/tlatools/src/tlc2/module/AnySet.java b/tlatools/src/tlc2/module/AnySet.java index a9d789f483058861a06b8c07cab01cd43cf5ddb2..90785d9d1363bf79079d950fea09ff4f307c23d4 100644 --- a/tlatools/src/tlc2/module/AnySet.java +++ b/tlatools/src/tlc2/module/AnySet.java @@ -7,36 +7,42 @@ package tlc2.module; import tlc2.output.EC; import tlc2.tool.EvalException; -import tlc2.value.UserObj; -import tlc2.value.UserValue; -import tlc2.value.Value; +import tlc2.value.Values; +import tlc2.value.impl.UserObj; +import tlc2.value.impl.UserValue; +import tlc2.value.impl.Value; public class AnySet extends UserObj { + public static final long serialVersionUID = 20160822L; - private static Value AnySet = new UserValue(new AnySet()); + private final static Value AnySet = new UserValue(new AnySet()); public static Value ANY() { return AnySet; } + @Override public final int compareTo(Value val) { - throw new EvalException(EC.TLC_MODULE_COMPARE_VALUE, new String[] { "ANY", Value.ppr(val.toString()) }); + throw new EvalException(EC.TLC_MODULE_COMPARE_VALUE, new String[] { "ANY", Values.ppr(val.toString()) }); } + @Override public final boolean member(Value val) { return true; } + @Override public final boolean isFinite() { return false; } - public final StringBuffer toString(StringBuffer sb, int offset) + @Override + public final StringBuffer toString(StringBuffer sb, int offset, boolean swallow) { return sb.append("ANY"); } diff --git a/tlatools/src/tlc2/module/Bags.java b/tlatools/src/tlc2/module/Bags.java index 73973d05082404ea78e151b1f8386664827d4a71..d6651aade02556e11c05bb1058063f0c8fd2cfed 100644 --- a/tlatools/src/tlc2/module/Bags.java +++ b/tlatools/src/tlc2/module/Bags.java @@ -8,58 +8,68 @@ package tlc2.module; import tlc2.output.EC; import tlc2.tool.EvalControl; import tlc2.tool.EvalException; -import tlc2.tool.TLARegistry; +import tlc2.tool.impl.TLARegistry; import tlc2.util.Vect; -import tlc2.value.Applicable; -import tlc2.value.BoolValue; -import tlc2.value.FcnRcdValue; -import tlc2.value.IntValue; -import tlc2.value.SetEnumValue; -import tlc2.value.Value; +import tlc2.value.IBoolValue; import tlc2.value.ValueConstants; -import tlc2.value.ValueVec; +import tlc2.value.Values; +import tlc2.value.impl.Applicable; +import tlc2.value.impl.BoolValue; +import tlc2.value.impl.FcnRcdValue; +import tlc2.value.impl.IntValue; +import tlc2.value.impl.SetEnumValue; +import tlc2.value.impl.Value; +import tlc2.value.impl.ValueVec; +import util.Assert; public class Bags implements ValueConstants { + public static final long serialVersionUID = 20160822L; static { - TLARegistry.put("BagCup", "\\oplus"); - TLARegistry.put("BagDiff", "\\ominus"); - TLARegistry.put("SqSubseteq", "\\sqsubseteq"); + // The following entries in TLARegistry each defines a mapping from a TLA+ infix + // operator to a Java method, e.g. the TLA+ infix operator \\oplus (which is the + // same as (+) ) is mapped to and thus implemented by the Java method + // tlc2.module.Bags.BagCup(Value, Value) below. + Assert.check(TLARegistry.put("BagCup", "\\oplus") == null, EC.TLC_REGISTRY_INIT_ERROR, "BagCup"); + Assert.check(TLARegistry.put("BagDiff", "\\ominus") == null, EC.TLC_REGISTRY_INIT_ERROR, "BagDiff"); + Assert.check(TLARegistry.put("SqSubseteq", "\\sqsubseteq") == null, EC.TLC_REGISTRY_INIT_ERROR, "SqSubseteq"); } public static Value EmptyBag() { - return EmptyFcn; + return FcnRcdValue.EmptyFcn; } - public static BoolValue IsABag(Value b) + public static IBoolValue IsABag(final Value b) { - FcnRcdValue fcn = FcnRcdValue.convert(b); + final FcnRcdValue fcn = (FcnRcdValue) b.toFcnRcd(); if (fcn == null) { - throw new EvalException(EC.TLC_MODULE_APPLYING_TO_WRONG_VALUE, new String[] { "IsBag", - "a function with a finite domain", Value.ppr(b.toString()) }); + // MAK 02/23/2018 Changed to return ValFalse instead of exception when Value is not a bag. + //throw new EvalException(EC.TLC_MODULE_APPLYING_TO_WRONG_VALUE, new String[] { "IsBag", + // "a function with a finite domain", Value.ppr(b.toString()) }); + return BoolValue.ValFalse; } - Value[] vals = fcn.values; + final Value[] vals = fcn.values; for (int i = 0; i < vals.length; i++) { - if (!(vals[i] instanceof IntValue) || ((IntValue) vals[i]).val < 0) + if (!(vals[i] instanceof IntValue) || ((IntValue) vals[i]).val <= 0) { - return ValFalse; + return BoolValue.ValFalse; } } - return ValTrue; + return BoolValue.ValTrue; } public static IntValue BagCardinality(Value b) { - FcnRcdValue fcn = FcnRcdValue.convert(b); + FcnRcdValue fcn = (FcnRcdValue) b.toFcnRcd(); if (fcn == null) { throw new EvalException(EC.TLC_MODULE_APPLYING_TO_WRONG_VALUE, new String[] { "BagCardinality", - "a function with a finite domain", Value.ppr(b.toString()) }); + "a function with a finite domain", Values.ppr(b.toString()) }); } int num = 0; Value[] vals = fcn.values; @@ -74,44 +84,42 @@ public class Bags implements ValueConstants } else { throw new EvalException(EC.TLC_MODULE_APPLYING_TO_WRONG_VALUE, new String[] { "BagCardinality", "a bag", - Value.ppr(b.toString()) }); + Values.ppr(b.toString()) }); } } else { throw new EvalException(EC.TLC_MODULE_APPLYING_TO_WRONG_VALUE, new String[] { "BagCardinality", "a bag", - Value.ppr(b.toString()) }); + Values.ppr(b.toString()) }); } } return IntValue.gen(num); } - public static BoolValue BagIn(Value e, Value b) + public static IBoolValue BagIn(final Value e, final Value b) { - FcnRcdValue fcn = FcnRcdValue.convert(b); - Value[] domain = fcn.domain; - Value[] values = fcn.values; - // Value val; // SZ: variable never read locally + final FcnRcdValue fcn = (FcnRcdValue) b.toFcnRcd(); + final Value[] values = fcn.values; + final Value[] domain = fcn.getDomainAsValues(); for (int i = 0; i < domain.length; i++) { if (e.equals(domain[i])) { if (values[i] instanceof IntValue) { - return (((IntValue) values[i]).val > 0) ? ValTrue : ValFalse; + return (((IntValue) values[i]).val > 0) ? BoolValue.ValTrue : BoolValue.ValFalse; } throw new EvalException(EC.TLC_MODULE_ARGUMENT_ERROR, new String[] { "second", "BagIn", "bag", - Value.ppr(b.toString()) }); + Values.ppr(b.toString()) }); } } - return ValFalse; + return BoolValue.ValFalse; } - public static IntValue CopiesIn(Value e, Value b) + public static IntValue CopiesIn(final Value e, final Value b) { - FcnRcdValue fcn = FcnRcdValue.convert(b); - Value[] domain = fcn.domain; - Value[] values = fcn.values; - // Value val; // SZ: variable never read locally + final FcnRcdValue fcn = (FcnRcdValue) b.toFcnRcd(); + final Value[] values = fcn.values; + final Value[] domain = fcn.getDomainAsValues(); for (int i = 0; i < domain.length; i++) { if (e.equals(domain[i])) @@ -121,32 +129,32 @@ public class Bags implements ValueConstants return (IntValue) values[i]; } throw new EvalException(EC.TLC_MODULE_ARGUMENT_ERROR, new String[] { "second", "CopiesIn", "bag", - Value.ppr(b.toString()) }); + Values.ppr(b.toString()) }); } } - return ValZero; + return IntValue.ValZero; } public static Value BagCup(Value b1, Value b2) { - FcnRcdValue fcn1 = FcnRcdValue.convert(b1); - FcnRcdValue fcn2 = FcnRcdValue.convert(b2); - if (!IsABag(fcn1).val) + FcnRcdValue fcn1 = (FcnRcdValue) b1.toFcnRcd(); + FcnRcdValue fcn2 = (FcnRcdValue) b2.toFcnRcd(); + if (!IsABag(fcn1).getVal()) { throw new EvalException(EC.TLC_MODULE_ARGUMENT_ERROR, new String[] { "first", "(+)", "bag", - Value.ppr(b1.toString()) }); + Values.ppr(b1.toString()) }); } - if (!IsABag(fcn2).val) + if (!IsABag(fcn2).getVal()) { throw new EvalException(EC.TLC_MODULE_ARGUMENT_ERROR, new String[] { "second", "(+)", "bag", - Value.ppr(b2.toString()) }); + Values.ppr(b2.toString()) }); } - Value[] domain1 = fcn1.domain; + Value[] domain1 = fcn1.getDomainAsValues(); Value[] values1 = fcn1.values; - Value[] domain2 = fcn2.domain; + Value[] domain2 = fcn2.getDomainAsValues(); Value[] values2 = fcn2.values; - Vect dVec = new Vect(domain1.length); - Vect vVec = new Vect(domain1.length); + Vect<Value> dVec = new Vect<>(domain1.length); + Vect<Value> vVec = new Vect<>(domain1.length); for (int i = 0; i < domain1.length; i++) { dVec.addElement(domain1[i]); @@ -176,32 +184,32 @@ public class Bags implements ValueConstants Value[] values = new Value[dVec.size()]; for (int i = 0; i < domain.length; i++) { - domain[i] = (Value) dVec.elementAt(i); - values[i] = (Value) vVec.elementAt(i); + domain[i] = dVec.elementAt(i); + values[i] = vVec.elementAt(i); } return new FcnRcdValue(domain, values, false); } public static Value BagDiff(Value b1, Value b2) { - FcnRcdValue fcn1 = FcnRcdValue.convert(b1); - FcnRcdValue fcn2 = FcnRcdValue.convert(b2); + FcnRcdValue fcn1 = (FcnRcdValue) b1.toFcnRcd(); + FcnRcdValue fcn2 = (FcnRcdValue) b2.toFcnRcd(); if (fcn1 == null) { throw new EvalException(EC.TLC_MODULE_ARGUMENT_ERROR, new String[] { "first", "(-)", "bag", - Value.ppr(b1.toString()) }); + Values.ppr(b1.toString()) }); } if (fcn2 == null) { throw new EvalException(EC.TLC_MODULE_ARGUMENT_ERROR, new String[] { "second", "(-)", "bag", - Value.ppr(b2.toString()) }); + Values.ppr(b2.toString()) }); } - Value[] domain1 = fcn1.domain; + Value[] domain1 = fcn1.getDomainAsValues(); Value[] values1 = fcn1.values; - Value[] domain2 = fcn2.domain; + Value[] domain2 = fcn2.getDomainAsValues(); Value[] values2 = fcn2.values; - Vect dVec = new Vect(domain1.length); - Vect vVec = new Vect(domain1.length); + Vect<Value> dVec = new Vect<>(domain1.length); + Vect<Value> vVec = new Vect<>(domain1.length); for (int i = 0; i < domain1.length; i++) { int v1 = ((IntValue) values1[i]).val; @@ -224,34 +232,43 @@ public class Bags implements ValueConstants Value[] values = new Value[vVec.size()]; for (int i = 0; i < domain.length; i++) { - domain[i] = (Value) dVec.elementAt(i); - values[i] = (Value) vVec.elementAt(i); + domain[i] = dVec.elementAt(i); + values[i] = vVec.elementAt(i); } return new FcnRcdValue(domain, values, fcn1.isNormalized()); } - public static Value BagUnion(Value s) + public static Value BagUnion(final Value s) { - SetEnumValue s1 = SetEnumValue.convert(s); + final SetEnumValue s1 = (SetEnumValue) s.toSetEnum(); if (s1 == null) { throw new EvalException(EC.TLC_MODULE_APPLYING_TO_WRONG_VALUE, new String[] { "BagUnion", - "a finite enumerable set", Value.ppr(s.toString()) }); + "a finite enumerable set", Values.ppr(s.toString()) }); } + // MAK 02/20/2018: + // Need to normalize s in cases where it is an unnormalized set of identical + // bags, such as h == [ i \in 1..3 |-> 1 ] and BagUnion({h, h}). In other + // words, let b be a bag, BagUnion({b,b}) = b and not b (+) b. This + // unfortunately degrades performance due to sorting s1's elements. + s1.normalize(); + ValueVec elems = s1.elems; int sz = elems.size(); - if (sz == 0) - return EmptyFcn; - if (sz == 1) - return elems.elementAt(0); + if (sz == 0) { + return FcnRcdValue.EmptyFcn; + } + if (sz == 1) { + return elems.elementAt(0); + } ValueVec dVec = new ValueVec(); ValueVec vVec = new ValueVec(); - FcnRcdValue fcn = FcnRcdValue.convert(elems.elementAt(0)); + FcnRcdValue fcn = (FcnRcdValue) elems.elementAt(0).toFcnRcd(); if (fcn == null) { - throw new EvalException(EC.TLC_MODULE_BAG_UNION1, Value.ppr(s.toString())); + throw new EvalException(EC.TLC_MODULE_BAG_UNION1, Values.ppr(s.toString())); } - Value[] domain = fcn.domain; + Value[] domain = fcn.getDomainAsValues(); Value[] values = fcn.values; for (int i = 0; i < domain.length; i++) { @@ -260,13 +277,13 @@ public class Bags implements ValueConstants } for (int i = 1; i < sz; i++) { - fcn = FcnRcdValue.convert(elems.elementAt(i)); + fcn = (FcnRcdValue) elems.elementAt(i).toFcnRcd(); if (fcn == null) { - throw new EvalException(EC.TLC_MODULE_BAG_UNION1, Value.ppr(s.toString())); + throw new EvalException(EC.TLC_MODULE_BAG_UNION1, Values.ppr(s.toString())); } - domain = fcn.domain; + domain = fcn.getDomainAsValues(); values = fcn.values; for (int j = 0; j < domain.length; j++) { @@ -299,24 +316,24 @@ public class Bags implements ValueConstants return new FcnRcdValue(dom, vals, false); } - public static BoolValue SqSubseteq(Value b1, Value b2) + public static IBoolValue SqSubseteq(Value b1, Value b2) { - FcnRcdValue fcn1 = FcnRcdValue.convert(b1); - FcnRcdValue fcn2 = FcnRcdValue.convert(b2); + FcnRcdValue fcn1 = (FcnRcdValue) b1.toFcnRcd(); + FcnRcdValue fcn2 = (FcnRcdValue) b2.toFcnRcd(); if (fcn1 == null) { throw new EvalException(EC.TLC_MODULE_APPLYING_TO_WRONG_VALUE, new String[] { "\\sqsubseteq", - "a function with a finite domain", Value.ppr(b1.toString()) }); + "a function with a finite domain", Values.ppr(b1.toString()) }); } if (fcn2 == null) { throw new EvalException(EC.TLC_MODULE_APPLYING_TO_WRONG_VALUE, new String[] { "\\sqsubseteq", - "a function with a finite domain", Value.ppr(b2.toString()) }); + "a function with a finite domain", Values.ppr(b2.toString()) }); } - Value[] domain1 = fcn1.domain; + Value[] domain1 = fcn1.getDomainAsValues(); Value[] values1 = fcn1.values; - Value[] domain2 = fcn2.domain; + Value[] domain2 = fcn2.getDomainAsValues(); Value[] values2 = fcn2.values; for (int i = 0; i < domain1.length; i++) { @@ -331,9 +348,9 @@ public class Bags implements ValueConstants } } if (v1 > 0) - return ValFalse; + return BoolValue.ValFalse; } - return ValTrue; + return BoolValue.ValTrue; } public static Value BagOfAll(Value f, Value b) @@ -341,18 +358,18 @@ public class Bags implements ValueConstants if (!(f instanceof Applicable)) { throw new EvalException(EC.TLC_MODULE_ARGUMENT_ERROR_AN, new String[] { "first", "BagOfAll", "operator", - Value.ppr(f.toString()) }); + Values.ppr(f.toString()) }); } - FcnRcdValue fcn = FcnRcdValue.convert(b); + FcnRcdValue fcn = (FcnRcdValue) b.toFcnRcd(); if (fcn == null) { throw new EvalException(EC.TLC_MODULE_ARGUMENT_ERROR, new String[] { "second", "BagOfAll", - "function with a finite domain", Value.ppr(b.toString()) }); + "function with a finite domain", Values.ppr(b.toString()) }); } Applicable ff = (Applicable) f; ValueVec dVec = new ValueVec(); ValueVec vVec = new ValueVec(); - Value[] domain = fcn.domain; + Value[] domain = fcn.getDomainAsValues(); Value[] values = fcn.values; Value[] args = new Value[1]; for (int i = 0; i < domain.length; i++) @@ -390,7 +407,7 @@ public class Bags implements ValueConstants /****** // For now, we do not override SubBag. So, We are using the TLA+ definition. public static Value SubBag(Value b) { - FcnRcdValue fcn = FcnRcdValue.convert(b); + FcnRcdValue fcn = b.toFcnRcd(); if (fcn == null) { String msg = "Applying SubBag to the following value, which is\n" + "not a function with a finite domain:\n" + Value.ppr(b.toString()); @@ -402,22 +419,22 @@ public class Bags implements ValueConstants public static Value BagToSet(Value b) { - FcnRcdValue fcn = FcnRcdValue.convert(b); + FcnRcdValue fcn = (FcnRcdValue) b.toFcnRcd(); if (fcn == null) { throw new EvalException(EC.TLC_MODULE_APPLYING_TO_WRONG_VALUE, new String[] { "BagToSet", - "a function with a finite domain", Value.ppr(b.toString()) }); + "a function with a finite domain", Values.ppr(b.toString()) }); } return fcn.getDomain(); } public static Value SetToBag(Value b) { - SetEnumValue s1 = SetEnumValue.convert(b); + SetEnumValue s1 = (SetEnumValue) b.toSetEnum(); if (s1 == null) { throw new EvalException(EC.TLC_MODULE_APPLYING_TO_WRONG_VALUE, new String[] { "BagToSet", - "a function with a finite domain", Value.ppr(b.toString()) }); + "a function with a finite domain", Values.ppr(b.toString()) }); } // The following `if' added by LL on 5 Mar 2012 to correct a bug found by Tom Rodeheffer, // in which SetToBag creates a function with multiple copies of the elements in its @@ -431,7 +448,7 @@ public class Bags implements ValueConstants for (int i = 0; i < elems.size(); i++) { domain[i] = elems.elementAt(i); - values[i] = ValOne; + values[i] = IntValue.ValOne; } return new FcnRcdValue(domain, values, s1.isNormalized()); } diff --git a/tlatools/src/tlc2/module/BuiltInModuleHelper.java b/tlatools/src/tlc2/module/BuiltInModuleHelper.java new file mode 100644 index 0000000000000000000000000000000000000000..8b62e2b08e4e23e57442eb55aed1d1ffebfdeef7 --- /dev/null +++ b/tlatools/src/tlc2/module/BuiltInModuleHelper.java @@ -0,0 +1,84 @@ +/******************************************************************************* + * Copyright (c) 2016 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ + +package tlc2.module; + +import java.io.File; +import java.lang.reflect.Field; + +public class BuiltInModuleHelper { + + public static final String BUNDLE_ID = "org.lamport.tlatools"; + + public static final String STANDARD_MODULES = "StandardModules"; + public static final String STANDARD_MODULES_PATH = File.separator + "tla2sany" + File.separator; + + private BuiltInModuleHelper() { + // no instantiation + } + + public static boolean isBuiltInModule(Class<?> clazz) { + try { + // Compare serialVersionUID because a user is allowed to override a + // built-in module. Thus, the name alone does not uniquely identify + // a built-in class. + final Field field = clazz.getField("serialVersionUID"); + if (field != null) { + final long value = field.getLong(null); + if (clazz == AnySet.class && value == AnySet.serialVersionUID) { + return true; + } else if (clazz == Bags.class && value == Bags.serialVersionUID) { + return true; + } else if (clazz == FiniteSets.class && value == FiniteSets.serialVersionUID) { + return true; + } else if (clazz == Integers.class && value == Integers.serialVersionUID) { + return true; + } else if (clazz == Naturals.class && value == Naturals.serialVersionUID) { + return true; + } else if (clazz == Sequences.class && value == Sequences.serialVersionUID) { + return true; + } else if (clazz == Strings.class && value == Strings.serialVersionUID) { + return true; + } else if (clazz == TLC.class && value == TLC.serialVersionUID) { + return true; + } else if (clazz == TransitiveClosure.class && value == TransitiveClosure.serialVersionUID) { + return true; + } else if (clazz == Randomization.class && value == Randomization.serialVersionUID) { + return true; + } + } + } catch (SecurityException e) { + return false; + } catch (NoSuchFieldException e) { + return false; + } catch (IllegalArgumentException e) { + return false; + } catch (IllegalAccessException e) { + return false; + } + return false; + } +} diff --git a/tlatools/src/tlc2/module/FiniteSets.java b/tlatools/src/tlc2/module/FiniteSets.java index 751b9c0566a33f194a582c84ef427d0cac3cde03..2c4d4314093637c35696b2d5d5b0f2421ba3a00e 100644 --- a/tlatools/src/tlc2/module/FiniteSets.java +++ b/tlatools/src/tlc2/module/FiniteSets.java @@ -7,27 +7,30 @@ package tlc2.module; import tlc2.output.EC; import tlc2.tool.EvalException; -import tlc2.value.BoolValue; -import tlc2.value.Enumerable; -import tlc2.value.IntValue; -import tlc2.value.Value; +import tlc2.value.IBoolValue; import tlc2.value.ValueConstants; +import tlc2.value.Values; +import tlc2.value.impl.BoolValue; +import tlc2.value.impl.Enumerable; +import tlc2.value.impl.IntValue; +import tlc2.value.impl.Value; public class FiniteSets implements ValueConstants { + public static final long serialVersionUID = 20160822L; - public static BoolValue IsFiniteSet(Value val) + public static IBoolValue IsFiniteSet(Value val) { - return val.isFinite() ? ValTrue : ValFalse; + return val.isFinite() ? BoolValue.ValTrue : BoolValue.ValFalse; } public static IntValue Cardinality(Value val) { if (val instanceof Enumerable) { - return IntValue.gen(((Enumerable) val).size()); + return IntValue.gen(val.size()); } - throw new EvalException(EC.TLC_MODULE_COMPUTING_CARDINALITY, Value.ppr(val.toString())); + throw new EvalException(EC.TLC_MODULE_COMPUTING_CARDINALITY, Values.ppr(val.toString())); } // SZ 16.07.2009: commented the following code out, since it is not a part of FiniteSets @@ -48,7 +51,7 @@ public class FiniteSets implements ValueConstants } public static Value listToSet(Value list) { - TupleValue tv = TupleValue.convert(list); + TupleValue tv = list.toTuple() if (tv == null) { throw new EvalException("listToSet"); } @@ -60,7 +63,7 @@ public class FiniteSets implements ValueConstants } public static Value appendSetToList(Value list, Value set) { - TupleValue tv = TupleValue.convert(list); + TupleValue tv = list.toTuple(); if (tv == null || IsFiniteSet(set) == ValFalse) { throw new EvalException("appendSetToList"); } @@ -80,7 +83,7 @@ public class FiniteSets implements ValueConstants } public static Value deleteSetFromList(Value set, Value list) { - TupleValue tv = TupleValue.convert(list); + TupleValue tv = list.toTuple(); if (tv == null) { throw new EvalException("deleteSetFromList"); } @@ -98,7 +101,7 @@ public class FiniteSets implements ValueConstants } public static Value keepSetFromList(Value set, Value list) { - TupleValue tv = TupleValue.convert(list); + TupleValue tv = list.toTuple() if (tv == null) { throw new EvalException("keepSetFromList"); } diff --git a/tlatools/src/tlc2/module/Integers.java b/tlatools/src/tlc2/module/Integers.java index 0ecd844f8db3fc0b9e54905ce87bb08298f7d08a..bb99dc59d5b8b3f8ef7330f315afb6bbc74b2cbf 100644 --- a/tlatools/src/tlc2/module/Integers.java +++ b/tlatools/src/tlc2/module/Integers.java @@ -7,21 +7,29 @@ package tlc2.module; import tlc2.output.EC; import tlc2.tool.EvalException; -import tlc2.tool.TLARegistry; -import tlc2.value.BoolValue; -import tlc2.value.IntValue; -import tlc2.value.IntervalValue; -import tlc2.value.ModelValue; -import tlc2.value.UserObj; -import tlc2.value.UserValue; -import tlc2.value.Value; +import tlc2.tool.impl.TLARegistry; +import tlc2.value.IBoolValue; import tlc2.value.ValueConstants; +import tlc2.value.Values; +import tlc2.value.impl.BoolValue; +import tlc2.value.impl.IntValue; +import tlc2.value.impl.IntervalValue; +import tlc2.value.impl.ModelValue; +import tlc2.value.impl.UserObj; +import tlc2.value.impl.UserValue; +import tlc2.value.impl.Value; public class Integers extends UserObj implements ValueConstants { + public static final long serialVersionUID = 20160822L; static { + // The following entries in TLARegistry each define a mapping from a TLA+ infix + // operator to a Java method, e.g. the TLA+ infix operator "+" is mapped to and + // thus implemented by the Java method tlc2.module.Integers.Plus(IntValue, + // IntValue) below. + //TODO Why does tlc2.module.Naturals define identical mappings? TLARegistry.put("Plus", "+"); TLARegistry.put("Minus", "-"); TLARegistry.put("Times", "*"); @@ -36,7 +44,7 @@ public class Integers extends UserObj implements ValueConstants TLARegistry.put("Expt", "^"); } - private static Value SetInt = new UserValue(new Integers()); + private static final Value SetInt = new UserValue(new Integers()); public static Value Int() { @@ -63,36 +71,36 @@ public class Integers extends UserObj implements ValueConstants return Naturals.Times(x, y); } - public static BoolValue LT(Value x, Value y) + public static IBoolValue LT(Value x, Value y) { if (!(x instanceof IntValue)) { throw new EvalException(EC.TLC_MODULE_ARGUMENT_ERROR_AN, new String[] { "first", "<", "integer", - Value.ppr(x.toString()) }); + Values.ppr(x.toString()) }); } if (!(y instanceof IntValue)) { throw new EvalException(EC.TLC_MODULE_ARGUMENT_ERROR_AN, new String[] { "second", "<", "integer", - Value.ppr(y.toString()) }); + Values.ppr(y.toString()) }); } - return (((IntValue) x).val < ((IntValue) y).val) ? ValTrue : ValFalse; + return (((IntValue) x).val < ((IntValue) y).val) ? BoolValue.ValTrue : BoolValue.ValFalse; } - public static BoolValue LE(Value x, Value y) + public static IBoolValue LE(Value x, Value y) { if (!(x instanceof IntValue)) { throw new EvalException(EC.TLC_MODULE_ARGUMENT_ERROR_AN, new String[] { "first", "<=", "integer", - Value.ppr(x.toString()) }); + Values.ppr(x.toString()) }); } if (!(y instanceof IntValue)) { throw new EvalException(EC.TLC_MODULE_ARGUMENT_ERROR_AN, new String[] { "second", "<=", "integer", - Value.ppr(x.toString()) }); + Values.ppr(y.toString()) }); } - return (((IntValue) x).val <= ((IntValue) y).val) ? ValTrue : ValFalse; + return (((IntValue) x).val <= ((IntValue) y).val) ? BoolValue.ValTrue : BoolValue.ValFalse; } public static BoolValue GT(Value x, Value y) @@ -100,31 +108,31 @@ public class Integers extends UserObj implements ValueConstants if (!(x instanceof IntValue)) { throw new EvalException(EC.TLC_MODULE_ARGUMENT_ERROR_AN, new String[] { "first", ">", "integer", - Value.ppr(x.toString()) }); + Values.ppr(x.toString()) }); } if (!(y instanceof IntValue)) { throw new EvalException(EC.TLC_MODULE_ARGUMENT_ERROR_AN, new String[] { "second", ">", "integer", - Value.ppr(x.toString()) }); + Values.ppr(y.toString()) }); } - return (((IntValue) x).val > ((IntValue) y).val) ? ValTrue : ValFalse; + return (((IntValue) x).val > ((IntValue) y).val) ? BoolValue.ValTrue : BoolValue.ValFalse; } - public static BoolValue GEQ(Value x, Value y) + public static IBoolValue GEQ(Value x, Value y) { if (!(x instanceof IntValue)) { throw new EvalException(EC.TLC_MODULE_ARGUMENT_ERROR_AN, new String[] { "first", ">=", "integer", - Value.ppr(x.toString()) }); + Values.ppr(x.toString()) }); } if (!(y instanceof IntValue)) { throw new EvalException(EC.TLC_MODULE_ARGUMENT_ERROR_AN, new String[] { "second", ">=", "integer", - Value.ppr(x.toString()) }); + Values.ppr(y.toString()) }); } - return (((IntValue) x).val >= ((IntValue) y).val) ? ValTrue : ValFalse; + return (((IntValue) x).val >= ((IntValue) y).val) ? BoolValue.ValTrue : BoolValue.ValFalse; } public static IntervalValue DotDot(IntValue x, IntValue y) @@ -186,7 +194,7 @@ public class Integers extends UserObj implements ValueConstants { throw new EvalException(EC.TLC_MODULE_NULL_POWER_NULL); } - return ValOne; + return IntValue.ValOne; } long res = x.val; for (int i = 1; i < y.val; i++) @@ -201,6 +209,7 @@ public class Integers extends UserObj implements ValueConstants return IntValue.gen((int) res); } + @Override public final int compareTo(Value val) { if (val instanceof UserValue) @@ -216,9 +225,10 @@ public class Integers extends UserObj implements ValueConstants } if (val instanceof ModelValue) return 1; - throw new EvalException(EC.TLC_MODULE_COMPARE_VALUE, new String[] { "Int", Value.ppr(val.toString()) }); + throw new EvalException(EC.TLC_MODULE_COMPARE_VALUE, new String[] { "Int", Values.ppr(val.toString()) }); } + @Override public final boolean member(Value val) { if (val instanceof IntValue) @@ -227,15 +237,17 @@ public class Integers extends UserObj implements ValueConstants { return ((ModelValue) val).modelValueMember(this); } - throw new EvalException(EC.TLC_MODULE_CHECK_MEMBER_OF, new String[] { Value.ppr(val.toString()), "Int" }); + throw new EvalException(EC.TLC_MODULE_CHECK_MEMBER_OF, new String[] { Values.ppr(val.toString()), "Int" }); } + @Override public final boolean isFinite() { return false; } - public final StringBuffer toString(StringBuffer sb, int offset) + @Override + public final StringBuffer toString(StringBuffer sb, int offset, boolean swallow) { return sb.append("Int"); } diff --git a/tlatools/src/tlc2/module/Naturals.java b/tlatools/src/tlc2/module/Naturals.java index a1771f38b3a4ebc82f49c5b2520e74a9a000d132..ac1421b7f0565ed22442012ea9fbf22e590cf144 100644 --- a/tlatools/src/tlc2/module/Naturals.java +++ b/tlatools/src/tlc2/module/Naturals.java @@ -7,21 +7,29 @@ package tlc2.module; import tlc2.output.EC; import tlc2.tool.EvalException; -import tlc2.tool.TLARegistry; -import tlc2.value.BoolValue; -import tlc2.value.IntValue; -import tlc2.value.IntervalValue; -import tlc2.value.ModelValue; -import tlc2.value.UserObj; -import tlc2.value.UserValue; -import tlc2.value.Value; +import tlc2.tool.impl.TLARegistry; +import tlc2.value.IBoolValue; import tlc2.value.ValueConstants; +import tlc2.value.Values; +import tlc2.value.impl.BoolValue; +import tlc2.value.impl.IntValue; +import tlc2.value.impl.IntervalValue; +import tlc2.value.impl.ModelValue; +import tlc2.value.impl.UserObj; +import tlc2.value.impl.UserValue; +import tlc2.value.impl.Value; public class Naturals extends UserObj implements ValueConstants { - + public static final long serialVersionUID = 20160822L; + static { + // The following entries in TLARegistry each define a mapping from a TLA+ infix + // operator to a Java method, e.g. the TLA+ infix operator "+" is mapped to and + // thus implemented by the Java method tlc2.module.Naturals.Plus(IntValue, + // IntValue) below. + //TODO Why does tlc2.module.Integers define identical mappings? TLARegistry.put("Plus", "+"); TLARegistry.put("Minus", "-"); TLARegistry.put("Times", "*"); @@ -84,70 +92,70 @@ public class Naturals extends UserObj implements ValueConstants return IntValue.gen((int) res); } - public static BoolValue LT(Value x, Value y) + public static IBoolValue LT(Value x, Value y) { if (!(x instanceof IntValue)) { throw new EvalException(EC.TLC_MODULE_ARGUMENT_ERROR_AN, new String[] { "first", "<", "integer", - Value.ppr(x.toString()) }); + Values.ppr(x.toString()) }); } if (!(y instanceof IntValue)) { throw new EvalException(EC.TLC_MODULE_ARGUMENT_ERROR_AN, new String[] { "second", "<", "integer", - Value.ppr(x.toString()) }); + Values.ppr(y.toString()) }); } - return (((IntValue) x).val < ((IntValue) y).val) ? ValTrue : ValFalse; + return (((IntValue) x).val < ((IntValue) y).val) ? BoolValue.ValTrue : BoolValue.ValFalse; } - public static BoolValue LE(Value x, Value y) + public static IBoolValue LE(Value x, Value y) { if (!(x instanceof IntValue)) { throw new EvalException(EC.TLC_MODULE_ARGUMENT_ERROR_AN, new String[] { "first", "<=", "integer", - Value.ppr(x.toString()) }); + Values.ppr(x.toString()) }); } if (!(y instanceof IntValue)) { throw new EvalException(EC.TLC_MODULE_ARGUMENT_ERROR_AN, new String[] { "second", "<=", "integer", - Value.ppr(x.toString()) }); + Values.ppr(y.toString()) }); } - return (((IntValue) x).val <= ((IntValue) y).val) ? ValTrue : ValFalse; + return (((IntValue) x).val <= ((IntValue) y).val) ? BoolValue.ValTrue : BoolValue.ValFalse; } - public static BoolValue GT(Value x, Value y) + public static IBoolValue GT(Value x, Value y) { if (!(x instanceof IntValue)) { throw new EvalException(EC.TLC_MODULE_ARGUMENT_ERROR_AN, new String[] { "first", ">", "integer", - Value.ppr(x.toString()) }); + Values.ppr(x.toString()) }); } if (!(y instanceof IntValue)) { // On 21 May 2012 LL corrected following call, which was reporting the first argument. throw new EvalException(EC.TLC_MODULE_ARGUMENT_ERROR_AN, new String[] { "second", ">", "integer", - Value.ppr(y.toString()) }); + Values.ppr(y.toString()) }); } - return (((IntValue) x).val > ((IntValue) y).val) ? ValTrue : ValFalse; + return (((IntValue) x).val > ((IntValue) y).val) ? BoolValue.ValTrue : BoolValue.ValFalse; } - public static BoolValue GEQ(Value x, Value y) + public static IBoolValue GEQ(Value x, Value y) { if (!(x instanceof IntValue)) { throw new EvalException(EC.TLC_MODULE_ARGUMENT_ERROR_AN, new String[] { "first", ">", "integer", - Value.ppr(x.toString()) }); + Values.ppr(x.toString()) }); } if (!(y instanceof IntValue)) { // On 21 May 2012 LL corrected following call, which was reporting the first argument. throw new EvalException(EC.TLC_MODULE_ARGUMENT_ERROR_AN, new String[] { "second", ">", "integer", - Value.ppr(y.toString()) }); + Values.ppr(y.toString()) }); } - return (((IntValue) x).val >= ((IntValue) y).val) ? ValTrue : ValFalse; + return (((IntValue) x).val >= ((IntValue) y).val) ? BoolValue.ValTrue : BoolValue.ValFalse; } public static IntervalValue DotDot(IntValue x, IntValue y) @@ -197,7 +205,7 @@ public class Naturals extends UserObj implements ValueConstants { throw new EvalException(EC.TLC_MODULE_NULL_POWER_NULL); } - return ValOne; + return IntValue.ValOne; } long res = n1; for (int i = 1; i < n2; i++) @@ -211,6 +219,7 @@ public class Naturals extends UserObj implements ValueConstants return IntValue.gen((int) res); } + @Override public final int compareTo(Value val) { if (val instanceof UserValue) @@ -226,9 +235,10 @@ public class Naturals extends UserObj implements ValueConstants } if (val instanceof ModelValue) return 1; - throw new EvalException(EC.TLC_MODULE_COMPARE_VALUE, new String[] { "Nat", Value.ppr(val.toString()) }); + throw new EvalException(EC.TLC_MODULE_COMPARE_VALUE, new String[] { "Nat", Values.ppr(val.toString()) }); } + @Override public final boolean member(Value val) { if (val instanceof IntValue) @@ -236,15 +246,17 @@ public class Naturals extends UserObj implements ValueConstants if (val instanceof ModelValue) return ((ModelValue) val).modelValueMember(this); - throw new EvalException(EC.TLC_MODULE_CHECK_MEMBER_OF, new String[] { Value.ppr(val.toString()), "Nat" }); + throw new EvalException(EC.TLC_MODULE_CHECK_MEMBER_OF, new String[] { Values.ppr(val.toString()), "Nat" }); } + @Override public final boolean isFinite() { return false; } - public final StringBuffer toString(StringBuffer sb, int offset) + @Override + public final StringBuffer toString(StringBuffer sb, int offset, boolean swallow) { return sb.append("Nat"); } diff --git a/tlatools/src/tlc2/module/Randomization.java b/tlatools/src/tlc2/module/Randomization.java new file mode 100644 index 0000000000000000000000000000000000000000..7916353238a52cdd884dcfd2272dac16068a3f64 --- /dev/null +++ b/tlatools/src/tlc2/module/Randomization.java @@ -0,0 +1,147 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.module; + +import tlc2.output.EC; +import tlc2.tool.EvalException; +import tlc2.value.ValueConstants; +import tlc2.value.Values; +import tlc2.value.impl.EnumerableValue; +import tlc2.value.impl.IntValue; +import tlc2.value.impl.StringValue; +import tlc2.value.impl.SubsetValue; +import tlc2.value.impl.Value; + +public class Randomization implements ValueConstants { + + public static final long serialVersionUID = 20180618L; + + public static Value RandomSubset(final Value v1, final Value v2) { + if (!(v1 instanceof IntValue)) { + throw new EvalException(EC.TLC_MODULE_ARGUMENT_ERROR, + new String[] { "first", "RandomSubset", "nonnegative integer", Values.ppr(v1.toString()) }); + } + if (!(v2 instanceof EnumerableValue)) { + throw new EvalException(EC.TLC_MODULE_ARGUMENT_ERROR, + new String[] { "second", "RandomSubset", "a finite set", Values.ppr(v2.toString()) }); + } + return ((EnumerableValue) v2).getRandomSubset(((IntValue) v1).val); + } + + public static Value RandomSetOfSubsets(final Value v1, final Value v2, final Value v3) { + // first parameter + if (!(v1 instanceof IntValue)) { + throw new EvalException(EC.TLC_MODULE_ARGUMENT_ERROR, + new String[] { "first", "RandomSetOfSubsets", "nonnegative integer", Values.ppr(v1.toString()) }); + } + final int numberOfPicks = ((IntValue) v1).val; + if (numberOfPicks < 0) { + throw new EvalException(EC.TLC_MODULE_ARGUMENT_ERROR, + new String[] { "first", "RandomSetOfSubsets", "nonnegative integer", Values.ppr(v1.toString()) }); + } + // second parameter + if (!(v2 instanceof IntValue)) { + throw new EvalException(EC.TLC_MODULE_ARGUMENT_ERROR, + new String[] { "second", "RandomSetOfSubsets", "nonnegative integer", Values.ppr(v2.toString()) }); + } + final int n = ((IntValue) v2).val; + if (n < 0) { + throw new EvalException(EC.TLC_MODULE_ARGUMENT_ERROR, + new String[] { "second", "RandomSetOfSubsets", "nonnegative integer", Values.ppr(v2.toString()) }); + } + // third parameter + if (!(v3 instanceof EnumerableValue)) { + throw new EvalException(EC.TLC_MODULE_ARGUMENT_ERROR, + new String[] { "third", "RandomSetOfSubsets", "finite set", Values.ppr(v3.toString()) }); + } + final EnumerableValue ev = (EnumerableValue) v3; + if (31 - Integer.numberOfLeadingZeros(numberOfPicks) + 1 > ev.size() && numberOfPicks > (1 << ev.size())) { + // First compare exponents before explicit calculating size of subset. The + // calculated value which is the subset's size then won't overflow. + throw new EvalException(EC.TLC_MODULE_ARGUMENT_ERROR, + new String[] { "first", "RandomSetOfSubsets", + "nonnegative integer that is smaller than the subset's size of 2^" + ev.size(), + Integer.toString(numberOfPicks) }); + } + // second parameter (now that we know third is enumerable) + if (ev.size() < n) { + throw new EvalException(EC.TLC_MODULE_ARGUMENT_ERROR, + new String[] { "second", "RandomSetOfSubsets", "nonnegative integer in range 0..Cardinality(S)", Values.ppr(v2.toString()) }); + } + final double probability = (1d * n) / ev.size(); + if (probability < 0d || 1d < probability) { + throw new EvalException(EC.TLC_MODULE_ARGUMENT_ERROR, + new String[] { "second", "RandomSetOfSubsets", "nonnegative integer in range 0..Cardinality(S)", Values.ppr(v2.toString()) }); + } + return new SubsetValue(ev).getRandomSetOfSubsets(numberOfPicks, probability); + } + + public static Value RandomSubsetSet(final Value v1, final Value v2, final Value v3) { + // first parameter + if (!(v1 instanceof IntValue)) { + throw new EvalException(EC.TLC_MODULE_ARGUMENT_ERROR, + new String[] { "first", "RandomSubsetSetProbability", "nonnegative integer", Values.ppr(v1.toString()) }); + } + final int numberOfPicks = ((IntValue) v1).val; + if (numberOfPicks < 0) { + throw new EvalException(EC.TLC_MODULE_ARGUMENT_ERROR, + new String[] { "first", "RandomSubsetSetProbability", "nonnegative integer", Values.ppr(v1.toString()) }); + } + // second parameter + if (!(v2 instanceof StringValue)) { + throw new EvalException(EC.TLC_MODULE_ARGUMENT_ERROR, + new String[] { "second", "RandomSubsetSetProbability", "string literal representing a probability", Values.ppr(v2.toString()) }); + + } + double probability; + try { + probability = Double.valueOf(((StringValue) v2).getVal().toString()); + } catch (NumberFormatException nfe) { + throw new EvalException(EC.TLC_MODULE_ARGUMENT_ERROR, + new String[] { "second", "RandomSubsetSetProbability", "string literal does not represent a parsable probability", Values.ppr(v2.toString()) }); + } + if (probability < 0d || 1d < probability) { + throw new EvalException(EC.TLC_MODULE_ARGUMENT_ERROR, + new String[] { "second", "RandomSubsetSetProbability", "string literal does not represent a parsable probability", Values.ppr(v2.toString()) }); + } + // third parameter + if (!(v3 instanceof EnumerableValue)) { + throw new EvalException(EC.TLC_MODULE_ARGUMENT_ERROR, + new String[] { "third", "RandomSubsetSetProbability", "finite set", Values.ppr(v3.toString()) }); + } + final EnumerableValue ev = (EnumerableValue) v3; + if (31 - Integer.numberOfLeadingZeros(numberOfPicks) + 1 > ev.size() && numberOfPicks > (1 << ev.size())) { + // First compare exponents before explicit calculating size of subset. The + // calculated value which is the subset's size then won't overflow. + throw new EvalException(EC.TLC_MODULE_ARGUMENT_ERROR, + new String[] { "first", "RandomSubsetSetProbability", + "nonnegative integer that is smaller than the subset's size of 2^" + ev.size(), + Integer.toString(numberOfPicks) }); + } + + return new SubsetValue(ev).getRandomSetOfSubsets(numberOfPicks, probability); + } +} diff --git a/tlatools/src/tlc2/module/Sequences.java b/tlatools/src/tlc2/module/Sequences.java index ecd4b58c68191cb7726b3c4fbe720d22dcfac7e4..5d4dc6c4c42e0d31c7cfe79052d0b683f7ef4d4a 100644 --- a/tlatools/src/tlc2/module/Sequences.java +++ b/tlatools/src/tlc2/module/Sequences.java @@ -8,25 +8,29 @@ package tlc2.module; import tlc2.output.EC; import tlc2.tool.EvalControl; import tlc2.tool.EvalException; -import tlc2.tool.TLARegistry; -import tlc2.value.Applicable; -import tlc2.value.BoolValue; -import tlc2.value.IntValue; -import tlc2.value.ModelValue; -import tlc2.value.OpLambdaValue; -import tlc2.value.OpRcdValue; -import tlc2.value.StringValue; -import tlc2.value.TupleValue; -import tlc2.value.UserObj; -import tlc2.value.UserValue; -import tlc2.value.Value; +import tlc2.tool.impl.TLARegistry; +import tlc2.value.IBoolValue; import tlc2.value.ValueConstants; -import tlc2.value.ValueVec; +import tlc2.value.Values; +import tlc2.value.impl.Applicable; +import tlc2.value.impl.BoolValue; +import tlc2.value.impl.IntValue; +import tlc2.value.impl.ModelValue; +import tlc2.value.impl.OpLambdaValue; +import tlc2.value.impl.OpRcdValue; +import tlc2.value.impl.StringValue; +import tlc2.value.impl.TupleValue; +import tlc2.value.impl.UserObj; +import tlc2.value.impl.UserValue; +import tlc2.value.impl.Value; +import tlc2.value.impl.ValueVec; import util.Assert; import util.UniqueString; public class Sequences extends UserObj implements ValueConstants { + public static final long serialVersionUID = 20160822L; + private Value range; private int size; @@ -36,9 +40,11 @@ public class Sequences extends UserObj implements ValueConstants this.size = size; } - static + static { - // SZ Jul 13, 2009: added message for initialization assertion + // This entry in TLARegistry defines a mapping from TLA+' infix + // operator \o to the Java method tlc2.module.Sequences.Concat(Value, Value) + // below. Assert.check(TLARegistry.put("Concat", "\\o") == null, EC.TLC_REGISTRY_INIT_ERROR, "Concat"); } @@ -56,18 +62,27 @@ public class Sequences extends UserObj implements ValueConstants return IntValue.gen(((StringValue) s).length()); } - TupleValue seq = TupleValue.convert(s); + TupleValue seq = (TupleValue) s.toTuple(); if (seq != null) { return IntValue.gen(seq.size()); } - throw new EvalException(EC.TLC_MODULE_ARGUMENT_ERROR, new String[] { "\b", "Len", "sequence", - Value.ppr(s.toString()) }); + throw new EvalException(EC.TLC_MODULE_ONE_ARGUMENT_ERROR, new String[] { "Len", "sequence", + Values.ppr(s.toString()) }); } public static Value Head(Value s) { - TupleValue seq = TupleValue.convert(s); + // Implementation of Head(string) by MAK on 4 Dec 2019 + if (s instanceof StringValue) { + String str = ((StringValue) s).val.toString(); + if (str.equals("")) { + throw new EvalException(EC.TLC_MODULE_APPLY_EMPTY_SEQ, "Head"); + } + return new StringValue(str.substring(0,1)); + } + + TupleValue seq = (TupleValue) s.toTuple(); if (seq != null) { if (seq.size() == 0) @@ -76,8 +91,8 @@ public class Sequences extends UserObj implements ValueConstants } return seq.elems[0]; } - throw new EvalException(EC.TLC_MODULE_ARGUMENT_ERROR, new String[] { "\b", "Head", "sequence", - Value.ppr(s.toString()) }); + throw new EvalException(EC.TLC_MODULE_ONE_ARGUMENT_ERROR, new String[] { "Head", "sequence", + Values.ppr(s.toString()) }); } public static Value Tail(Value s) @@ -91,7 +106,7 @@ public class Sequences extends UserObj implements ValueConstants return new StringValue(str.substring(1)); } - TupleValue seq = TupleValue.convert(s); + TupleValue seq = (TupleValue) s.toTuple(); if (seq != null) { if (seq.size() == 0) @@ -103,17 +118,17 @@ public class Sequences extends UserObj implements ValueConstants System.arraycopy(seq.elems, 1, vals, 0, vals.length); return new TupleValue(vals); } - throw new EvalException(EC.TLC_MODULE_ARGUMENT_ERROR, new String[] { "\b", "Tail", "sequence", - Value.ppr(s.toString()) }); + throw new EvalException(EC.TLC_MODULE_ONE_ARGUMENT_ERROR, new String[] { "Tail", "sequence", + Values.ppr(s.toString()) }); } public static Value Cons(Value v, Value s) { - TupleValue seq = TupleValue.convert(s); + TupleValue seq = (TupleValue) s.toTuple(); if (seq == null) { throw new EvalException(EC.TLC_MODULE_EVALUATING, new String[] { "Cons(v, s)", "sequence", - Value.ppr(s.toString()) }); + Values.ppr(s.toString()) }); } int len = seq.size(); Value[] values = new Value[len + 1]; @@ -124,11 +139,24 @@ public class Sequences extends UserObj implements ValueConstants public static Value Append(Value s, Value v) { - TupleValue seq = TupleValue.convert(s); + // Implementation of Append(string, string) by MAK on 4 Dec 2019 + if (s instanceof StringValue) + { + if (!(v instanceof StringValue)) + { + throw new EvalException(EC.TLC_MODULE_EVALUATING, new String[] { "t \\o s", "string", + Values.ppr(v.toString()) }); + } + UniqueString u1 = ((StringValue) s).val; + UniqueString u2 = ((StringValue) v).val; + return new StringValue(u1.concat(u2)); + } + + TupleValue seq = (TupleValue) s.toTuple(); if (seq == null) { throw new EvalException(EC.TLC_MODULE_EVALUATING, new String[] { "Append(v, s)", "sequence", - Value.ppr(s.toString()) }); + Values.ppr(s.toString()) }); } int len = seq.size(); Value[] values = new Value[len + 1]; @@ -144,24 +172,24 @@ public class Sequences extends UserObj implements ValueConstants if (!(s2 instanceof StringValue)) { throw new EvalException(EC.TLC_MODULE_EVALUATING, new String[] { "t \\o s", "string", - Value.ppr(s2.toString()) }); + Values.ppr(s2.toString()) }); } UniqueString u1 = ((StringValue) s1).val; UniqueString u2 = ((StringValue) s2).val; return new StringValue(u1.concat(u2)); } - TupleValue seq1 = TupleValue.convert(s1); + TupleValue seq1 = (TupleValue) s1.toTuple(); if (seq1 == null) { throw new EvalException(EC.TLC_MODULE_EVALUATING, new String[] { "s \\o t", "sequence", - Value.ppr(s1.toString()) }); + Values.ppr(s1.toString()) }); } - TupleValue seq2 = TupleValue.convert(s2); + TupleValue seq2 = (TupleValue) s2.toTuple(); if (seq2 == null) { throw new EvalException(EC.TLC_MODULE_EVALUATING, new String[] { "t \\o s", "sequence", - Value.ppr(s2.toString()) }); + Values.ppr(s2.toString()) }); } int len1 = seq1.size(); int len2 = seq2.size(); @@ -187,16 +215,16 @@ public class Sequences extends UserObj implements ValueConstants */ public static Value SelectInSeq(Value s, Value test) { - TupleValue seq = TupleValue.convert(s); + TupleValue seq = (TupleValue) s.toTuple(); if (seq == null) { throw new EvalException(EC.TLC_MODULE_ARGUMENT_ERROR, new String[] { "first", "SelectInSeq", "sequence", - Value.ppr(s.toString()) }); + Values.ppr(s.toString()) }); } if (!(test instanceof Applicable)) { throw new EvalException(EC.TLC_MODULE_ARGUMENT_ERROR, new String[] { "second", "SelectInSeq", "function", - Value.ppr(test.toString()) }); + Values.ppr(test.toString()) }); } int len = seq.size(); Applicable ftest = (Applicable) test; @@ -205,20 +233,20 @@ public class Sequences extends UserObj implements ValueConstants { args[0] = seq.elems[i]; Value val = ftest.apply(args, EvalControl.Clear); - if (!(val instanceof BoolValue)) + if (!(val instanceof IBoolValue)) { throw new EvalException(EC.TLC_MODULE_ARGUMENT_ERROR, new String[] { "second", "SelectInSeq", - "boolean-valued function", Value.ppr(test.toString()) }); + "boolean-valued function", Values.ppr(test.toString()) }); } if (((BoolValue) val).val) return IntValue.gen(i + 1); } - return ValZero; + return IntValue.ValZero; } /** Not in the standard interface. public static Value Remove(Value s, Value index) { - TupleValue seq = TupleValue.convert(s); + TupleValue seq = s.toTuple() if (seq != null) { if (index instanceof IntValue) { int ridx = ((IntValue)index).val; @@ -264,23 +292,23 @@ public class Sequences extends UserObj implements ValueConstants } if (! isString) { - seq = TupleValue.convert(s); + seq = (TupleValue) s.toTuple(); if (seq == null) { throw new EvalException(EC.TLC_MODULE_ARGUMENT_ERROR, new String[] { "first", "SubSeq", "sequence", - Value.ppr(s.toString()) }); + Values.ppr(s.toString()) }); } } if (!(m instanceof IntValue)) { throw new EvalException(EC.TLC_MODULE_ARGUMENT_ERROR, new String[] { "second", "SubSeq", "natural number", - Value.ppr(m.toString()) }); + Values.ppr(m.toString()) }); } if (!(n instanceof IntValue)) { throw new EvalException(EC.TLC_MODULE_ARGUMENT_ERROR, new String[] { "third", "SubSeq", "natural number", - Value.ppr(n.toString()) }); + Values.ppr(n.toString()) }); } int beg = ((IntValue) m).val; int end = ((IntValue) n).val; @@ -289,7 +317,7 @@ public class Sequences extends UserObj implements ValueConstants return new StringValue("") ; } else { - return EmptyTuple; + return TupleValue.EmptyTuple; } } @@ -298,13 +326,13 @@ public class Sequences extends UserObj implements ValueConstants if (beg < 1 || beg > len) { - throw new EvalException(EC.TLC_MODULE_ARGUMENT_NOT_IN_DOMAIN, new String[] { "second", "SubSeq", - Value.ppr(s.toString()), Value.ppr(m.toString()) }); + throw new EvalException(EC.TLC_MODULE_ARGUMENT_NOT_IN_DOMAIN, new String[] { "second", "SubSeq", "first", + Values.ppr(s.toString()), Values.ppr(m.toString()) }); } if (end < 1 || end > len) { - throw new EvalException(EC.TLC_MODULE_ARGUMENT_NOT_IN_DOMAIN, new String[] { "third", "SubSeq", - Value.ppr(s.toString()), Value.ppr(n.toString()) }); + throw new EvalException(EC.TLC_MODULE_ARGUMENT_NOT_IN_DOMAIN, new String[] { "third", "SubSeq", "first", + Values.ppr(s.toString()), Values.ppr(n.toString()) }); } if (isString) { @@ -320,19 +348,19 @@ public class Sequences extends UserObj implements ValueConstants public static Value SelectSeq(Value s, Value test) { - TupleValue seq = TupleValue.convert(s); + TupleValue seq = (TupleValue) s.toTuple(); if (seq == null) { throw new EvalException(EC.TLC_MODULE_ARGUMENT_ERROR, new String[] { "first", "SelectSeq", "sequence", - Value.ppr(s.toString()) }); + Values.ppr(s.toString()) }); } int len = seq.size(); if (len == 0) - return EmptyTuple; + return TupleValue.EmptyTuple; if (!(test instanceof OpLambdaValue) && !(test instanceof OpRcdValue)) { throw new EvalException(EC.TLC_MODULE_ARGUMENT_ERROR, new String[] { "second", "SelectSeq", "operator", - Value.ppr(test.toString()) }); + Values.ppr(test.toString()) }); } ValueVec vals = new ValueVec(); Applicable ftest = (Applicable) test; @@ -341,14 +369,14 @@ public class Sequences extends UserObj implements ValueConstants { args[0] = seq.elems[i]; Value val = ftest.apply(args, EvalControl.Clear); - if (val instanceof BoolValue) + if (val instanceof IBoolValue) { if (((BoolValue) val).val) vals.addElement(args[0]); } else { throw new EvalException(EC.TLC_MODULE_ARGUMENT_ERROR, new String[] { "second", "SelectSeq", - "boolean-valued operator", Value.ppr(test.toString()) }); + "boolean-valued operator", Values.ppr(test.toString()) }); } } Value[] elems = new Value[vals.size()]; @@ -359,6 +387,7 @@ public class Sequences extends UserObj implements ValueConstants return new TupleValue(elems); } + @Override public final int compareTo(Value s) { if ((s instanceof UserValue) && (((UserValue) s).userObj instanceof Sequences)) @@ -377,19 +406,20 @@ public class Sequences extends UserObj implements ValueConstants } // SZ Jul 14, 2009: // replaced the message with a standard one, thrown by mismatch of compared elements - throw new EvalException(EC.TLC_MODULE_COMPARE_VALUE, new String[] { Value.ppr(this.toString()), - Value.ppr(s.toString()) }); + throw new EvalException(EC.TLC_MODULE_COMPARE_VALUE, new String[] { Values.ppr(this.toString()), + Values.ppr(s.toString()) }); } + @Override public final boolean member(Value s) { - TupleValue seq = TupleValue.convert(s); + TupleValue seq = (TupleValue) s.toTuple(); if (seq == null) { if (s instanceof ModelValue) return ((ModelValue) s).modelValueMember(this); - throw new EvalException(EC.TLC_MODULE_CHECK_MEMBER_OF, new String[] { Value.ppr(s.toString()), - Value.ppr(this.toString()) }); + throw new EvalException(EC.TLC_MODULE_CHECK_MEMBER_OF, new String[] { Values.ppr(s.toString()), + Values.ppr(this.toString()) }); } int len = seq.size(); if (len > this.size) @@ -402,22 +432,24 @@ public class Sequences extends UserObj implements ValueConstants return true; } + @Override public final boolean isFinite() { return this.size != Integer.MAX_VALUE; } - public final StringBuffer toString(StringBuffer sb, int offset) + @Override + public final StringBuffer toString(StringBuffer sb, int offset, boolean swallow) { if (this.size == Integer.MAX_VALUE) { sb = sb.append("Seq("); - sb = this.range.toString(sb, offset); + sb = this.range.toString(sb, offset, swallow); sb = sb.append(")"); } else { sb = sb.append("BSeq("); - sb = this.range.toString(sb, offset); + sb = this.range.toString(sb, offset, swallow); sb = sb.append(", "); sb = sb.append(this.size); sb = sb.append(")"); @@ -427,16 +459,16 @@ public class Sequences extends UserObj implements ValueConstants public static Value Insert(Value s, Value v, Value test) { - TupleValue seq = TupleValue.convert(s); + TupleValue seq = (TupleValue) s.toTuple(); if (seq == null) { throw new EvalException(EC.TLC_MODULE_ARGUMENT_ERROR, new String[] { "first", "Insert", "sequence", - Value.ppr(s.toString()) }); + Values.ppr(s.toString()) }); } if (!(test instanceof Applicable)) { throw new EvalException(EC.TLC_MODULE_ARGUMENT_ERROR, new String[] { "second", "SubSeq", "function", - Value.ppr(test.toString()) }); + Values.ppr(test.toString()) }); } int len = seq.size(); Applicable ftest = (Applicable) test; @@ -448,10 +480,10 @@ public class Sequences extends UserObj implements ValueConstants { args[1] = seq.elems[idx - 1]; Value val = ftest.apply(args, EvalControl.Clear); - if (!(val instanceof BoolValue)) + if (!(val instanceof IBoolValue)) { throw new EvalException(EC.TLC_MODULE_ARGUMENT_ERROR, new String[] { "third", "Insert", - "boolean-valued operator", Value.ppr(test.toString()) }); + "boolean-valued operator", Values.ppr(test.toString()) }); } if (((BoolValue) val).val && v.compareTo(args[1]) < 0) { diff --git a/tlatools/src/tlc2/module/Strings.java b/tlatools/src/tlc2/module/Strings.java index f9fb934198201afa670c3d3aca861ce65d1a0617..2be9f901b8539d66fcb40832c7459a27c8569cfe 100644 --- a/tlatools/src/tlc2/module/Strings.java +++ b/tlatools/src/tlc2/module/Strings.java @@ -7,14 +7,16 @@ package tlc2.module; import tlc2.output.EC; import tlc2.tool.EvalException; -import tlc2.value.ModelValue; -import tlc2.value.StringValue; -import tlc2.value.UserObj; -import tlc2.value.UserValue; -import tlc2.value.Value; +import tlc2.value.Values; +import tlc2.value.impl.ModelValue; +import tlc2.value.impl.StringValue; +import tlc2.value.impl.UserObj; +import tlc2.value.impl.UserValue; +import tlc2.value.impl.Value; public class Strings extends UserObj { + public static final long serialVersionUID = 20160822L; private static Value SetString = new UserValue(new Strings()); @@ -23,6 +25,7 @@ public class Strings extends UserObj return SetString; } + @Override public final int compareTo(Value val) { if ((val instanceof UserValue) && (((UserValue) val).userObj instanceof Strings)) @@ -31,24 +34,27 @@ public class Strings extends UserObj } if (val instanceof ModelValue) return 1; - throw new EvalException(EC.TLC_MODULE_COMPARE_VALUE, new String[] { "STRING", Value.ppr(val.toString()) }); + throw new EvalException(EC.TLC_MODULE_COMPARE_VALUE, new String[] { "STRING", Values.ppr(val.toString()) }); } + @Override public final boolean member(Value val) { if (val instanceof StringValue) return true; if (val instanceof ModelValue) return ((ModelValue) val).modelValueMember(this); - throw new EvalException(EC.TLC_MODULE_CHECK_MEMBER_OF, new String[] { Value.ppr(val.toString()), "STRING" }); + throw new EvalException(EC.TLC_MODULE_CHECK_MEMBER_OF, new String[] { Values.ppr(val.toString()), "STRING" }); } + @Override public final boolean isFinite() { return false; } - public final StringBuffer toString(StringBuffer sb, int offset) + @Override + public final StringBuffer toString(StringBuffer sb, int offset, boolean swallow) { return sb.append("STRING"); } diff --git a/tlatools/src/tlc2/module/TLC.java b/tlatools/src/tlc2/module/TLC.java index dd0969ba415ff9c6b6e031a78d40690470b5dd38..b1a5277f1f056ec7be82f9ff2887ce7ddb87e2d2 100644 --- a/tlatools/src/tlc2/module/TLC.java +++ b/tlatools/src/tlc2/module/TLC.java @@ -5,41 +5,63 @@ package tlc2.module; +import java.io.BufferedWriter; +import java.io.IOException; + import tlc2.TLCGlobals; import tlc2.output.EC; +import tlc2.output.MP; import tlc2.tool.EvalControl; import tlc2.tool.EvalException; -import tlc2.tool.TLARegistry; -import tlc2.tool.Worker; -import tlc2.util.RandomGenerator; -import tlc2.value.Applicable; -import tlc2.value.BoolValue; -import tlc2.value.FcnRcdValue; -import tlc2.value.IntValue; -import tlc2.value.IntervalValue; -import tlc2.value.RecordValue; -import tlc2.value.SetEnumValue; -import tlc2.value.SetOfFcnsValue; -import tlc2.value.SetOfRcdsValue; -import tlc2.value.SetOfTuplesValue; -import tlc2.value.StringValue; -import tlc2.value.TupleValue; -import tlc2.value.Value; +import tlc2.tool.ModelChecker; +import tlc2.tool.TLCState; +import tlc2.tool.impl.TLARegistry; +import tlc2.util.IdThread; +import tlc2.value.IBoolValue; import tlc2.value.ValueConstants; -import tlc2.value.ValueVec; +import tlc2.value.Values; +import tlc2.value.impl.Applicable; +import tlc2.value.impl.BoolValue; +import tlc2.value.impl.FcnRcdValue; +import tlc2.value.impl.IntValue; +import tlc2.value.impl.IntervalValue; +import tlc2.value.impl.RecordValue; +import tlc2.value.impl.SetEnumValue; +import tlc2.value.impl.SetOfFcnsValue; +import tlc2.value.impl.SetOfRcdsValue; +import tlc2.value.impl.SetOfTuplesValue; +import tlc2.value.impl.StringValue; +import tlc2.value.impl.TupleValue; +import tlc2.value.impl.Value; +import tlc2.value.impl.ValueVec; import util.Assert; import util.ToolIO; +import util.UniqueString; public class TLC implements ValueConstants { + private static final UniqueString LEVEL = UniqueString.uniqueStringOf("level"); + private static final UniqueString DURATION = UniqueString.uniqueStringOf("duration"); + private static final UniqueString QUEUE = UniqueString.uniqueStringOf("queue"); + private static final UniqueString DISTINCT = UniqueString.uniqueStringOf("distinct"); + private static final UniqueString DIAMETER = UniqueString.uniqueStringOf("diameter"); + private static final UniqueString EXIT = UniqueString.uniqueStringOf("exit"); + private static final UniqueString PAUSE = UniqueString.uniqueStringOf("pause"); + + public static final long serialVersionUID = 20160822L; + + private static final long startTime = System.currentTimeMillis(); - private static RandomGenerator rng; + public static BufferedWriter OUTPUT; static { + // The following two entries in TLARegistry define a mapping from a TLA+ infix + // operator to a Java method, e.g. the TLA+ infix operator "@@" is mapped to and + // thus implemented by the Java method tlc2.module.TLC.CombineFcn(Value, Value) + // below. Assert.check(TLARegistry.put("MakeFcn", ":>") == null, EC.TLC_REGISTRY_INIT_ERROR, "MakeFcn"); Assert.check(TLARegistry.put("CombineFcn", "@@") == null, EC.TLC_REGISTRY_INIT_ERROR, "CombineFcn"); - rng = new RandomGenerator(); } /** @@ -53,11 +75,19 @@ public class TLC implements ValueConstants */ public static Value Print(Value v1, Value v2) { - Value v1c = v1.deepCopy(); - Value v2c = v2.deepCopy(); + Value v1c = (Value) v1.deepCopy(); + Value v2c = (Value) v2.deepCopy(); v1c.deepNormalize(); v2c.deepNormalize(); - ToolIO.out.println(Value.ppr(v1c.toString()) + " " + Value.ppr(v2c.toString())); + if (OUTPUT == null) { + ToolIO.out.println(Values.ppr(v1c.toStringUnchecked()) + " " + Values.ppr(v2c.toStringUnchecked())); + } else { + try { + OUTPUT.write(Values.ppr(v1c.toStringUnchecked()) + " " + Values.ppr(v2c.toStringUnchecked()) + "\n"); + } catch (IOException e) { + MP.printError(EC.GENERAL, e); + } + } return v2; } @@ -66,18 +96,27 @@ public class TLC implements ValueConstants * * Modified on 22 June 2011 by LL. See comment on the Print method */ - public static Value PrintT(Value v1) + public static Value PrintT(Value v1) { - Value v1c = v1.deepCopy(); + Value v1c = (Value) v1.deepCopy(); v1c.deepNormalize(); - ToolIO.out.println(Value.ppr(v1c.toString())); - return ValTrue; + if (OUTPUT == null) { + String ppr = Values.ppr(v1c.toStringUnchecked()); + ToolIO.out.println(ppr); + } else { + try { + OUTPUT.write(Values.ppr(v1c.toStringUnchecked("\n"))); + } catch (IOException e) { + MP.printError(EC.GENERAL, e); + } + } + return BoolValue.ValTrue; } /* Returns the string value of the string representation of v. */ public static Value ToString(Value v) { - return new StringValue(v.toString()); + return new StringValue(v.toStringUnchecked()); } /** @@ -86,11 +125,11 @@ public class TLC implements ValueConstants */ public static Value Assert(Value v1, Value v2) { - if ((v1 instanceof BoolValue) && ((BoolValue) v1).val) + if ((v1 instanceof IBoolValue) && ((BoolValue) v1).val) { return v1; } - throw new EvalException(EC.TLC_VALUE_ASSERT_FAILED, Value.ppr(v2.toString())); + throw new EvalException(EC.TLC_VALUE_ASSERT_FAILED, Values.ppr(v2.toString())); } /** @@ -112,12 +151,15 @@ public class TLC implements ValueConstants { Thread th = Thread.currentThread(); Value res = null; - if (th instanceof Worker) + if (th instanceof IdThread) { - res = ((Worker) th).getLocalValue(idx); - } else + res = (Value) ((IdThread) th).getLocalValue(idx); + } else if (TLCGlobals.mainChecker != null) { - res = tlc2.TLCGlobals.mainChecker.getValue(0, idx); + res = (Value) tlc2.TLCGlobals.mainChecker.getValue(0, idx); + } else + { + res = (Value) tlc2.TLCGlobals.simulator.getLocalValue(idx); } if (res == null) { @@ -125,12 +167,76 @@ public class TLC implements ValueConstants } return res; } + } else if (vidx instanceof StringValue) { + return TLCGetStringValue(vidx); } - throw new EvalException(EC.TLC_MODULE_ARGUMENT_ERROR, new String[] { "\b" /* delete the space*/, "TLCGet", - "nonnegative integer", Value.ppr(vidx.toString()) }); + throw new EvalException(EC.TLC_MODULE_ONE_ARGUMENT_ERROR, new String[] { "TLCGet", + "nonnegative integer", Values.ppr(vidx.toString()) }); } - public static Value TLCSet(Value vidx, Value val) + private static final Value TLCGetStringValue(final Value vidx) { + final StringValue sv = (StringValue) vidx; + if (DIAMETER == sv.val) { + try { + return IntValue.gen(TLCGlobals.mainChecker.getProgress()); + } catch (ArithmeticException e) { + throw new EvalException(EC.TLC_MODULE_OVERFLOW, + Long.toString(TLCGlobals.mainChecker.getProgress())); + } catch (NullPointerException npe) { + // TLCGlobals.mainChecker is null while the spec is parsed. A constant + // expression referencing one of the named values here would thus result in an + // NPE. + throw new EvalException(EC.TLC_MODULE_TLCGET_UNDEFINED, String.valueOf(sv.val)); + } + } else if (DISTINCT == sv.val) { + try { + return IntValue.gen(Math.toIntExact(TLCGlobals.mainChecker.getDistinctStatesGenerated())); + } catch (ArithmeticException e) { + throw new EvalException(EC.TLC_MODULE_OVERFLOW, + Long.toString(TLCGlobals.mainChecker.getDistinctStatesGenerated())); + } catch (NullPointerException npe) { + throw new EvalException(EC.TLC_MODULE_TLCGET_UNDEFINED, String.valueOf(sv.val)); + } + } else if (QUEUE == sv.val) { + try { + return IntValue.gen(Math.toIntExact(TLCGlobals.mainChecker.getStateQueueSize())); + } catch (ArithmeticException e) { + throw new EvalException(EC.TLC_MODULE_OVERFLOW, + Long.toString(TLCGlobals.mainChecker.getStateQueueSize())); + } catch (NullPointerException npe) { + throw new EvalException(EC.TLC_MODULE_TLCGET_UNDEFINED, String.valueOf(sv.val)); + } + } else if (DURATION == sv.val) { + try { + final int duration = (int) ((System.currentTimeMillis() - startTime) / 1000L); + return IntValue.gen(Math.toIntExact(duration)); + } catch (ArithmeticException e) { + throw new EvalException(EC.TLC_MODULE_OVERFLOW, + Long.toString(((System.currentTimeMillis() - startTime) / 1000L))); + } + } else if (LEVEL == sv.val) { + // Contrary to "diameter", "level" is not monotonically increasing. "diameter" is + // because it calls tlc2.tool.TLCTrace.getLevelForReporting(). "level" is the height + // stores as part of the state that is currently explored. + final TLCState currentState = IdThread.getCurrentState(); + if (currentState != null) { + return IntValue.gen(currentState.getLevel()); + } else { + if (TLCGlobals.mainChecker == null && TLCGlobals.simulator == null) { + // Be consistent with TLCGet("diameter") when TLCGet("level") appears as + // constant substitution. + throw new EvalException(EC.TLC_MODULE_TLCGET_UNDEFINED, String.valueOf(sv.val)); + } + // Not an IdThread (hence currentState is null) implies that TLCGet("level") is + // evaluated as part of the initial predicate where the level - by definition - + // is 0 (see TLCState#level). + return IntValue.gen(TLCState.INIT_LEVEL - 1); + } + } + throw new EvalException(EC.TLC_MODULE_TLCGET_UNDEFINED, String.valueOf(sv.val)); + } + + public static Value TLCSet(Value vidx, Value val) { if (vidx instanceof IntValue) { @@ -138,50 +244,83 @@ public class TLC implements ValueConstants if (idx >= 0) { Thread th = Thread.currentThread(); - if (th instanceof Worker) + if (th instanceof IdThread) { - ((Worker) th).setLocalValue(idx, val); - } else + ((IdThread) th).setLocalValue(idx, val); + } else if (TLCGlobals.mainChecker != null) { TLCGlobals.mainChecker.setAllValues(idx, val); + } else + { + tlc2.TLCGlobals.simulator.setAllValues(idx, val); } - return ValTrue; + return BoolValue.ValTrue; } + } else if (vidx instanceof StringValue) { + final StringValue sv = (StringValue) vidx; + if (EXIT == sv.val) { + if (val == BoolValue.ValTrue) { + TLCGlobals.mainChecker.stop(); + } + return BoolValue.ValTrue; + } else if (PAUSE == sv.val) { + // Provisional TLCSet("pause", TRUE) implementation that suspends BFS model + // checking until enter is pressed on system.in. Either use in spec as: + // TLCSet("pause", guard) + // but it might be better guarded by IfThenElse for performance reasons: + // IF guard THEN TLCSet("pause", TRUE) ELSE TRUE + if (val == BoolValue.ValTrue && TLCGlobals.mainChecker instanceof ModelChecker) { + final ModelChecker mc = (ModelChecker) TLCGlobals.mainChecker; + synchronized (mc.theStateQueue) { + ToolIO.out.println("Press enter to resume model checking."); + ToolIO.out.flush(); + try { + System.in.read(); + } catch (IOException e) { + throw new EvalException(EC.GENERAL, e.getMessage()); + } + } + } + return BoolValue.ValTrue; + } } - throw new EvalException(EC.TLC_MODULE_ARGUMENT_ERROR, new String[] { "first", "TLCSet", "nonnegative integer", - Value.ppr(vidx.toString()) }); + Values.ppr(vidx.toString()) }); } public static Value MakeFcn(Value d, Value e) { - Value[] dom = new Value[1]; - Value[] vals = new Value[1]; + Value[] dom = new Value[1]; + Value[] vals = new Value[1]; dom[0] = d; vals[0] = e; return new FcnRcdValue(dom, vals, true); } + /** + * f @@ g == [x \in (DOMAIN f) \cup (DOMAIN g) |-> + * IF x \in DOMAIN f THEN f[x] ELSE g[x]] + */ public static Value CombineFcn(Value f1, Value f2) { - FcnRcdValue fcn1 = FcnRcdValue.convert(f1); - FcnRcdValue fcn2 = FcnRcdValue.convert(f2); + FcnRcdValue fcn1 = (FcnRcdValue) f1.toFcnRcd(); + FcnRcdValue fcn2 = (FcnRcdValue) f2.toFcnRcd(); if (fcn1 == null) { throw new EvalException(EC.TLC_MODULE_ARGUMENT_ERROR, new String[] { "first", "@@", "function", - Value.ppr(f1.toString()) }); + Values.ppr(f1.toString()) }); } if (fcn2 == null) { throw new EvalException(EC.TLC_MODULE_ARGUMENT_ERROR, new String[] { "second", "@@", "function", - Value.ppr(f2.toString()) }); + Values.ppr(f2.toString()) }); } ValueVec dom = new ValueVec(); ValueVec vals = new ValueVec(); - Value[] vals1 = fcn1.values; - Value[] vals2 = fcn2.values; + Value [] vals1 = fcn1.values; + Value [] vals2 = fcn2.values; - Value[] dom1 = fcn1.domain; + Value [] dom1 = fcn1.domain; if (dom1 == null) { IntervalValue intv1 = fcn1.intv; @@ -200,13 +339,13 @@ public class TLC implements ValueConstants } int len1 = dom.size(); - Value[] dom2 = fcn2.domain; + Value [] dom2 = fcn2.domain; if (dom2 == null) { IntervalValue intv2 = fcn2.intv; for (int i = intv2.low; i <= intv2.high; i++) { - Value val = IntValue.gen(i); + Value val = IntValue.gen(i); boolean found = false; for (int j = 0; j < len1; j++) { @@ -219,14 +358,14 @@ public class TLC implements ValueConstants if (!found) { dom.addElement(val); - vals.addElement(vals2[i]); + vals.addElement(vals2[i - intv2.low]); } } } else { for (int i = 0; i < dom2.length; i++) { - Value val = dom2[i]; + Value val = dom2[i]; boolean found = false; for (int j = 0; j < len1; j++) { @@ -244,8 +383,8 @@ public class TLC implements ValueConstants } } - Value[] domain = new Value[dom.size()]; - Value[] values = new Value[dom.size()]; + Value [] domain = new Value[dom.size()]; + Value [] values = new Value[dom.size()]; for (int i = 0; i < domain.length; i++) { domain[i] = dom.elementAt(i); @@ -256,24 +395,24 @@ public class TLC implements ValueConstants public static Value SortSeq(Value s, Value cmp) { - TupleValue seq = TupleValue.convert(s); + TupleValue seq = (TupleValue) s.toTuple(); if (seq == null) { throw new EvalException(EC.TLC_MODULE_ARGUMENT_ERROR, new String[] { "first", "SortSeq", "natural number", - Value.ppr(s.toString()) }); + Values.ppr(s.toString()) }); } if (!(cmp instanceof Applicable)) { throw new EvalException(EC.TLC_MODULE_ARGUMENT_ERROR, new String[] { "second", "SortSeq", "function", - Value.ppr(cmp.toString()) }); + Values.ppr(cmp.toString()) }); } Applicable fcmp = (Applicable) cmp; - Value[] elems = seq.elems; + Value [] elems = seq.elems; int len = elems.length; if (len == 0) return seq; - Value[] args = new Value[2]; - Value[] newElems = new Value[len]; + Value [] args = new Value[2]; + Value [] newElems = new Value[len]; newElems[0] = elems[0]; for (int i = 1; i < len; i++) { @@ -293,51 +432,51 @@ public class TLC implements ValueConstants return new TupleValue(newElems); } - private static boolean compare(Applicable fcmp, Value[] args) + private static boolean compare(Applicable fcmp, Value [] args) { - Value res = fcmp.apply(args, EvalControl.Clear); - if (res instanceof BoolValue) + Value res = fcmp.apply(args, EvalControl.Clear); + if (res instanceof IBoolValue) { return ((BoolValue) res).val; } - throw new EvalException(EC.TLC_MODULE_ARGUMENT_ERROR, new String[] { "second", "SortSeq", "noolean function", - Value.ppr(res.toString()) }); + throw new EvalException(EC.TLC_MODULE_ARGUMENT_ERROR, new String[] { "second", "SortSeq", "boolean function", + Values.ppr(res.toString()) }); } + // Returns a set of size n! where n = |s|. public static Value Permutations(Value s) { - SetEnumValue s1 = SetEnumValue.convert(s); + SetEnumValue s1 = (SetEnumValue) s.toSetEnum(); if (s1 == null) { throw new EvalException(EC.TLC_MODULE_APPLYING_TO_WRONG_VALUE, new String[] { "Permutations", - "a finite set", Value.ppr(s.toString()) }); + "a finite set", Values.ppr(s.toString()) }); } s1.normalize(); ValueVec elems = s1.elems; int len = elems.size(); if (len == 0) { - Value[] elems1 = { EmptyFcn }; + Value[] elems1 = { FcnRcdValue.EmptyFcn }; return new SetEnumValue(elems1, true); } - Value[] domain = new Value[len]; - for (int i = 0; i < len; i++) - { - domain[i] = elems.elementAt(i); - } + int factorial = 1; + Value [] domain = new Value[len]; int[] idxArray = new int[len]; boolean[] inUse = new boolean[len]; for (int i = 0; i < len; i++) { + domain[i] = elems.elementAt(i); idxArray[i] = i; inUse[i] = true; + factorial = factorial * (i + 1); } - ValueVec fcns = new ValueVec(); + ValueVec fcns = new ValueVec(factorial); _done: while (true) { - Value[] vals = new Value[len]; + Value [] vals = new Value[len]; for (int i = 0; i < len; i++) { vals[i] = domain[idxArray[i]]; @@ -358,10 +497,12 @@ public class TLC implements ValueConstants break; } } - if (found) + if (found) { break; - if (i == 0) + } + if (i == 0) { break _done; + } inUse[idxArray[i]] = false; } for (int j = i + 1; j < len; j++) @@ -380,22 +521,22 @@ public class TLC implements ValueConstants return new SetEnumValue(fcns, false); } - public static Value RandomElement(Value val) + public static Value RandomElement(Value val) { switch (val.getKind()) { case SETOFFCNSVALUE: { SetOfFcnsValue sfv = (SetOfFcnsValue) val; sfv.normalize(); - SetEnumValue domSet = SetEnumValue.convert(sfv.domain); + SetEnumValue domSet = (SetEnumValue) sfv.domain.toSetEnum(); if (domSet == null) { throw new EvalException(EC.TLC_MODULE_APPLYING_TO_WRONG_VALUE, new String[] { "RandomElement", - "a finite set", Value.ppr(val.toString()) }); + "a finite set", Values.ppr(val.toString()) }); } domSet.normalize(); ValueVec elems = domSet.elems; - Value[] dom = new Value[elems.size()]; - Value[] vals = new Value[elems.size()]; + Value [] dom = new Value[elems.size()]; + Value [] vals = new Value[elems.size()]; for (int i = 0; i < dom.length; i++) { dom[i] = elems.elementAt(i); @@ -406,7 +547,7 @@ public class TLC implements ValueConstants case SETOFRCDSVALUE: { SetOfRcdsValue srv = (SetOfRcdsValue) val; srv.normalize(); - Value[] vals = new Value[srv.names.length]; + Value [] vals = new Value[srv.names.length]; for (int i = 0; i < vals.length; i++) { vals[i] = RandomElement(srv.values[i]); @@ -416,7 +557,7 @@ public class TLC implements ValueConstants case SETOFTUPLESVALUE: { SetOfTuplesValue stv = (SetOfTuplesValue) val; stv.normalize(); - Value[] vals = new Value[stv.sets.length]; + Value [] vals = new Value[stv.sets.length]; for (int i = 0; i < vals.length; i++) { vals[i] = RandomElement(stv.sets[i]); @@ -424,15 +565,13 @@ public class TLC implements ValueConstants return new TupleValue(vals); } default: { - SetEnumValue enumVal = SetEnumValue.convert(val); + SetEnumValue enumVal = (SetEnumValue) val.toSetEnum(); if (enumVal == null) { throw new EvalException(EC.TLC_MODULE_APPLYING_TO_WRONG_VALUE, new String[] { "RandomElement", - "a finite set", Value.ppr(val.toString()) }); + "a finite set", Values.ppr(val.toString()) }); } - int sz = enumVal.size(); - int index = (int) Math.floor(rng.nextDouble() * sz); - return enumVal.elems.elementAt(index); + return enumVal.randomElement(); } } } @@ -450,12 +589,12 @@ public class TLC implements ValueConstants * @param val * @return */ - public static Value TLCEval(Value val) { - Value evalVal = SetEnumValue.convert(val); + public static Value TLCEval(Value val) { + Value evalVal = val.toSetEnum(); if (evalVal != null) { return evalVal; } - evalVal = FcnRcdValue.convert(val); + evalVal = val.toFcnRcd(); if (evalVal != null) { return evalVal; } @@ -464,7 +603,7 @@ public class TLC implements ValueConstants } /* public static Value FApply(Value f, Value op, Value base) { - FcnRcdValue fcn = FcnRcdValue.convert(f); + FcnRcdValue fcn = f.toFcnRcd(); if (fcn == null) { String msg = "The first argument of FApply must be a " + "function with finite domain, but instead it is\n" + @@ -487,7 +626,7 @@ public class TLC implements ValueConstants } public static Value FSum(Value f) { - FcnRcdValue fcn = FcnRcdValue.convert(f); + FcnRcdValue fcn = f.toFcnRcd(); if (fcn == null) { String msg = "The argument of FSum should be a function; " + "but instead it is:\n" + Value.ppr(f.toString()); diff --git a/tlatools/src/tlc2/module/TransitiveClosure.java b/tlatools/src/tlc2/module/TransitiveClosure.java index cd10f0cf0f99a36b6022931ff39b7f3d91cb0749..49e53efa03be6c388127e369e5c27dffb8490dac 100644 --- a/tlatools/src/tlc2/module/TransitiveClosure.java +++ b/tlatools/src/tlc2/module/TransitiveClosure.java @@ -10,16 +10,18 @@ import java.util.Hashtable; import tlc2.output.EC; import tlc2.tool.EvalException; import tlc2.util.Vect; -import tlc2.value.Enumerable; -import tlc2.value.SetEnumValue; -import tlc2.value.TupleValue; -import tlc2.value.Value; import tlc2.value.ValueConstants; -import tlc2.value.ValueEnumeration; -import tlc2.value.ValueVec; +import tlc2.value.Values; +import tlc2.value.impl.Enumerable; +import tlc2.value.impl.SetEnumValue; +import tlc2.value.impl.TupleValue; +import tlc2.value.impl.Value; +import tlc2.value.impl.ValueEnumeration; +import tlc2.value.impl.ValueVec; public class TransitiveClosure implements ValueConstants { + public static final long serialVersionUID = 20160822L; /* Implement the Warshall algorithm for transitive closure. */ public static Value Warshall(Value rel) @@ -27,26 +29,26 @@ public class TransitiveClosure implements ValueConstants if (!(rel instanceof Enumerable)) { throw new EvalException(EC.TLC_MODULE_APPLYING_TO_WRONG_VALUE, new String[] { "TransitiveClosure", - "an enumerable set", Value.ppr(rel.toString()) }); + "an enumerable set", Values.ppr(rel.toString()) }); } int maxLen = 2 * rel.size(); boolean[][] matrix = new boolean[maxLen][maxLen]; ValueEnumeration elems = ((Enumerable) rel).elements(); - Vect elemList = new Vect(); - Hashtable fps = new Hashtable(); + Vect<Value> elemList = new Vect<>(); + Hashtable<Value, Integer> fps = new Hashtable<>(); int cnt = 0; Value elem = null; while ((elem = elems.nextElement()) != null) { - TupleValue tv = TupleValue.convert(elem); + TupleValue tv = (TupleValue) elem.toTuple(); if (tv == null || tv.size() != 2) { - throw new EvalException(EC.TLC_MODULE_TRANSITIVE_CLOSURE, Value.ppr(elem.toString())); + throw new EvalException(EC.TLC_MODULE_TRANSITIVE_CLOSURE, Values.ppr(elem.toString())); } Value elem1 = tv.elems[0]; Value elem2 = tv.elems[1]; int num1 = cnt; - Integer num = (Integer) fps.get(elem1); + Integer num = fps.get(elem1); if (num == null) { fps.put(elem1, new Integer(cnt)); @@ -57,7 +59,7 @@ public class TransitiveClosure implements ValueConstants num1 = num.intValue(); } int num2 = cnt; - num = (Integer) fps.get(elem2); + num = fps.get(elem2); if (num == null) { fps.put(elem2, new Integer(cnt)); @@ -92,9 +94,9 @@ public class TransitiveClosure implements ValueConstants { if (matrix[i][j]) { - Value elem1 = (Value) elemList.elementAt(i); - Value elem2 = (Value) elemList.elementAt(j); - Value newElem = new TupleValue(elem1, elem2); + Value elem1 = elemList.elementAt(i); + Value elem2 = elemList.elementAt(j); + Value newElem = new TupleValue(elem1, elem2); newElems.addElement(newElem); } } diff --git a/tlatools/src/tlc2/output/AbstractCopier.java b/tlatools/src/tlc2/output/AbstractCopier.java new file mode 100644 index 0000000000000000000000000000000000000000..2289cb54b1dc1c9c6858f86b99442716e7623a16 --- /dev/null +++ b/tlatools/src/tlc2/output/AbstractCopier.java @@ -0,0 +1,93 @@ +package tlc2.output; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; +import java.io.Reader; +import java.io.Writer; + +/** + * This is the abstract copying class for the generation of SpecTE file assets. + */ +abstract class AbstractCopier { + protected final String originalModuleName; + protected final String newModuleName; + + protected final File sourceDirectory; + + protected File sourceFile; + protected File destinationFile; + + AbstractCopier(final String originalName, final String newName, final File sourceLocation) { + originalModuleName = originalName; + newModuleName = newName; + + sourceDirectory = sourceLocation; + } + + /** + * @return the extension including the prefixed '.' + */ + protected abstract String getFileExtension(); + + /** + * @param writer + * @param originalLine + * @param lineNumber this is 1-based (e.g the first line of the file read will be lineNumber == 1) + * @throws IOException + */ + protected abstract void copyLine(final BufferedWriter writer, final String originalLine, final int lineNumber) + throws IOException; + + /** + * Overriders will receive this notification after the final line of input has + * been consumed and {@link #copyLine(BufferedWriter, String, int)} has been + * invoked on that line. + */ + protected void allInputHasBeenConsumed(final BufferedWriter writer) throws IOException { } + + /** + * Overriders will receive this notification after the reader and writer + * instances employed in the copy haven been closed but before + * {@link #copy(Reader, Writer)} exits. + */ + protected void copyHasFinished() throws IOException { } + + /** + * @return null until {@link #copy()} has been invoked, thereafter the location of the destination file. + */ + public File getDestinationFile() { + return destinationFile; + } + + public final void copy() throws IOException { + final String extension = getFileExtension(); + + sourceFile = new File(sourceDirectory, (originalModuleName + extension)); + destinationFile = new File(sourceDirectory, (newModuleName + extension)); + + copy(new FileReader(sourceFile), new FileWriter(destinationFile)); + } + + // This extra level of abstraction is done for unit tests + protected void copy(final Reader reader, final Writer writer) throws IOException { + try (final BufferedReader br = new BufferedReader(reader)) { + try (final BufferedWriter bw = new BufferedWriter(writer)) { + String line; + int lineCount = 1; // staying 1-based since Location is as well and our subclasses use Location instances + while ((line = br.readLine()) != null) { + copyLine(bw, line, lineCount); + + lineCount++; + } + + allInputHasBeenConsumed(bw); + } + } + + copyHasFinished(); + } +} diff --git a/tlatools/src/tlc2/output/AbstractSpecWriter.java b/tlatools/src/tlc2/output/AbstractSpecWriter.java new file mode 100644 index 0000000000000000000000000000000000000000..bb6e81936a8fe2470934743f536ddcb55b3d24af --- /dev/null +++ b/tlatools/src/tlc2/output/AbstractSpecWriter.java @@ -0,0 +1,499 @@ +package tlc2.output; + +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.CopyOption; +import java.nio.file.Files; +import java.nio.file.StandardCopyOption; +import java.util.ArrayList; +import java.util.List; + +import tlc2.model.Assignment; +import tlc2.model.TypedSet; +import util.TLAConstants; + +/** + * This is the abstract class of spec writers; there's no reason this need be abstract, it is more a semantic + * denoting that within our code base, it acts as only a superclass to other employed classes. + */ +public abstract class AbstractSpecWriter { + /** + * Assigns a right side to a label using an id generated from given schema + * @param tlaBuffer the buffer into which the TLA code will be placed + * @param cfgBuffer if non-null, the buffer into which the CFG code will be placed + * @param constant, constant containing the values + * @param schema schema to generate the Id + * @return generated id + */ + public static String addArrowAssignmentToBuffers(final StringBuilder tlaBuffer, final StringBuilder cfgBuffer, + final Assignment constant, final String schema) { + // constant instantiation + // to .cfg : foo <- <id> + // to _MC.tla : <id>(a, b, c)== + // <expression> + final String id = SpecWriterUtilities.getValidIdentifier(schema); + tlaBuffer.append(constant.getParametrizedLabel(id)).append(TLAConstants.DEFINES).append(TLAConstants.CR); + tlaBuffer.append(constant.getRight()).append(TLAConstants.CR); + + if (cfgBuffer != null) { + cfgBuffer.append(TLAConstants.KeyWords.CONSTANT).append(TLAConstants.CR); + cfgBuffer.append(constant.getLabel()).append(TLAConstants.ARROW).append(id).append(TLAConstants.CR); + } + + return id; + } + + + /** + * Subclasses may implement this interface in order to invoke {@link #writeFiles(ContentWriter)} for cases + * in which the subclass is has file handles which are not suitable to being converted and used in + * {@link #writeStreamToFile(InputStream, boolean)} + */ + protected interface ContentWriter { + void writeStreamToFile(final InputStream inputStream, final boolean forTLAFile) throws IOException; + } + + + protected static final String CLOSING_SEP = TLAConstants.CR + TLAConstants.SEP + TLAConstants.CR; + + + protected final StringBuilder tlaBuffer; + protected final StringBuilder cfgBuffer; + + /** + * @param generateConfigurationContent if true, configuration file (.cfg) + * accompanying the TLA module content will + * be generated + */ + protected AbstractSpecWriter(final boolean generateConfigurationContent) { + tlaBuffer = new StringBuilder(); + cfgBuffer = generateConfigurationContent ? new StringBuilder() : null; + } + + /** + * Subclasses may override this to set their own spin on a closing tag. + * + * @return the String content of a module closing tag. + */ + protected String getTLAModuleClosingTag() { + final StringBuilder sb = SpecWriterUtilities.getModuleClosingTag(77, false); + + return sb.toString(); + } + + /** + * Provided for test code + * @param tla if non-null the content will be appended to the tlaBuffer + * @param cfg if non-null the content will be appended to the cfgBuffer + */ + void appendContentToBuffers(final String tla, final String cfg) { + if (tla != null) { + tlaBuffer.append(tla); + } + + if (cfg != null) { + cfgBuffer.append(cfg); + } + } + + /** + * Write the buffers to files. + * + * @param tlaFile if null, nothing is written + * @param cfgFile if null, nothing is written + * @throws IOException + */ + public void writeFiles(final File tlaFile, final File cfgFile) throws IOException { + final ContentWriter cw = (inputStream, forTLAFile) -> { + final File f = (forTLAFile ? tlaFile: cfgFile); + + if (f != null) { + Files.copy(inputStream, f.toPath(), StandardCopyOption.REPLACE_EXISTING); + } + }; + + writeFiles(cw); + } + + /** + * Subclasses may use to this to provide a content writer which deals with output other than using {@code java.io} + * directly. + * + * @param contentWriter + * @throws IOException + */ + protected void writeFiles(final ContentWriter contentWriter) throws IOException { + tlaBuffer.append(getTLAModuleClosingTag()); + final ByteArrayInputStream tlaBAIS = new ByteArrayInputStream(tlaBuffer.toString().getBytes()); + contentWriter.writeStreamToFile(tlaBAIS, true); + + if (cfgBuffer != null) { + cfgBuffer.append(SpecWriterUtilities.getGeneratedTimeStampCommentLine()); + final ByteArrayInputStream configurationBAIS = new ByteArrayInputStream(cfgBuffer.toString().getBytes()); + contentWriter.writeStreamToFile(configurationBAIS, false); + } + } + + /** + * Add file header, which consists of the module-beginning ----- MODULE ... ---- + * line and the EXTENDS statement. + * + * @param moduleFilename + * @param extendedModuleName + */ + public void addPrimer(final String moduleFilename, final String extendedModuleName) { + tlaBuffer.append(SpecWriterUtilities.getExtendingModuleContent(moduleFilename, + new String[] { extendedModuleName, "TLC" })); + } + + /** + * Add spec definition + * + * @param specDefinition + * @param attributeName + */ + public void addSpecDefinition(final String[] specDefinition, final String attributeName) { + if (cfgBuffer != null) { + cfgBuffer.append(TLAConstants.KeyWords.SPECIFICATION).append(TLAConstants.SPACE); + cfgBuffer.append(specDefinition[0]).append(TLAConstants.CR); + } + + tlaBuffer.append(TLAConstants.COMMENT).append(TLAConstants.KeyWords.SPECIFICATION).append(TLAConstants.SPACE); + tlaBuffer.append(TLAConstants.ATTRIBUTE).append(attributeName).append(TLAConstants.CR); + tlaBuffer.append(specDefinition[1]).append(CLOSING_SEP); + } + + /** + * Add an init-next pair of definitions. + * + * @param initDefinition the 0th element is the init definition name, the 1rst is the entire definition + * @param nextDefinition the 0th element is the next definition name, the 1rst is the entire definition + * @param initAttributeName + * @param nextAttributeName + */ + public void addInitNextDefinitions(final String[] initDefinition, final String[] nextDefinition, + final String initAttributeName, final String nextAttributeName) { + if (cfgBuffer != null) { + cfgBuffer.append(TLAConstants.COMMENT).append(TLAConstants.KeyWords.INIT).append(" definition"); + cfgBuffer.append(TLAConstants.CR).append(TLAConstants.KeyWords.INIT).append(TLAConstants.CR); + cfgBuffer.append(initDefinition[0]).append(TLAConstants.CR); + } + + tlaBuffer.append(TLAConstants.COMMENT).append(TLAConstants.KeyWords.INIT).append(" definition "); + tlaBuffer.append(TLAConstants.ATTRIBUTE).append(initAttributeName).append(TLAConstants.CR); + tlaBuffer.append(initDefinition[1]).append(TLAConstants.CR); + + + if (cfgBuffer != null) { + cfgBuffer.append(TLAConstants.COMMENT).append(TLAConstants.KeyWords.NEXT).append(" definition"); + cfgBuffer.append(TLAConstants.CR).append(TLAConstants.KeyWords.NEXT).append(TLAConstants.CR); + cfgBuffer.append(nextDefinition[0]).append(TLAConstants.CR); + } + + tlaBuffer.append(TLAConstants.COMMENT).append(TLAConstants.KeyWords.NEXT).append(" definition "); + tlaBuffer.append(TLAConstants.ATTRIBUTE).append(nextAttributeName).append(TLAConstants.CR); + tlaBuffer.append(nextDefinition[1]).append(TLAConstants.CR); + } + + /** + * Documentation by SZ: Add constants declarations. + * + * On 17 March 2012, LL split the original addConstants method + * into the current one plus the addConstantsBis method. As explained in Bugzilla Bug 280, + * this was to allow the user definitions added on the Advanced Model page to appear between + * the CONSTANT declarations for model values and the definitions of the expressions that + * instantiate CONSTANT parameters. (This allows symbols defined in those user definitions to + * appear in the expressions instantiated for CONSTANT parameters.) + * + * See the use of these two methods in TLCModelLaunchDelegate.buildForLaunch for a description + * of what these methods do. + * + * @param constants + * @param modelValues + * @param attributeConstants + * @param attributeMVs + */ + public void addConstants(final List<Assignment> constants, final TypedSet modelValues, final String attributeConstants, + final String attributeMVs) { + // add declarations for model values introduced on Advanced Model page. + addMVTypedSet(modelValues, "MV CONSTANT declarations ", attributeMVs); + + final ArrayList<String> symmetrySets = new ArrayList<>(); + + Assignment constant; + // first run for all the declarations + for (int i = 0; i < constants.size(); i++) { + constant = constants.get(i); + if (constant.isModelValue()) { + if (constant.isSetOfModelValues()) { + // set model values + addMVTypedSet(constant.getSetOfModelValues(), "MV CONSTANT declarations", attributeConstants); + } + } + } + + // now all the definitions + for (int i = 0; i < constants.size(); i++) { + constant = constants.get(i); + if (constant.isModelValue()) { + if (constant.isSetOfModelValues()) { + // set model values + if (cfgBuffer != null) { + cfgBuffer.append(TLAConstants.COMMENT).append("MV CONSTANT definitions").append(TLAConstants.CR); + } + tlaBuffer.append(TLAConstants.COMMENT).append("MV CONSTANT definitions " + constant.getLeft()); + tlaBuffer.append(TLAConstants.CR); + + final String id = addArrowAssignment(constant, TLAConstants.Schemes.CONSTANT_SCHEME); + if (constant.isSymmetricalSet()) { + symmetrySets.add(id); + } + tlaBuffer.append(TLAConstants.SEP).append(TLAConstants.CR).append(TLAConstants.CR); + } else if (cfgBuffer != null) { + cfgBuffer.append(TLAConstants.COMMENT).append(TLAConstants.KeyWords.CONSTANT) + .append(" declarations").append(TLAConstants.CR); + // model value assignment + // to .cfg : foo = foo + // to _MC.tla : <nothing>, since the constant is already defined in one of the + // spec modules + cfgBuffer.append(TLAConstants.KeyWords.CONSTANT).append(TLAConstants.SPACE).append(constant.getLabel()); + cfgBuffer.append(TLAConstants.EQ).append(constant.getRight()).append(TLAConstants.CR); + } + } else { +// // simple constant value assignment +// cfgBuffer.append(COMMENT).append("CONSTANT definitions").append(CR); +// +// tlaBuffer.append(COMMENT).append("CONSTANT definitions ").append(ATTRIBUTE).append(attributeConstants) +// .append(INDEX).append(i).append(constant.getLeft()).append(CR); +// addArrowAssignment(constant, CONSTANT_SCHEME); +// tlaBuffer.append(SEP).append(CR).append(CR); + } + } + + // symmetry + if (!symmetrySets.isEmpty()) { + final String label = SpecWriterUtilities.getValidIdentifier(TLAConstants.Schemes.SYMMETRY_SCHEME); + + tlaBuffer.append(TLAConstants.COMMENT).append(TLAConstants.KeyWords.SYMMETRY) + .append(" definition").append(TLAConstants.CR); + if (cfgBuffer != null) { + cfgBuffer.append(TLAConstants.COMMENT).append(TLAConstants.KeyWords.SYMMETRY) + .append(" definition").append(TLAConstants.CR); + } + + tlaBuffer.append(label).append(TLAConstants.DEFINES).append(TLAConstants.CR); + // symmetric model value sets added + for (int i = 0; i < symmetrySets.size(); i++) { + tlaBuffer.append(TLAConstants.BuiltInOperators.PERMUTATIONS).append("("); + tlaBuffer.append(symmetrySets.get(i)).append(")"); + if (i != symmetrySets.size() - 1) { + tlaBuffer.append(' ').append(TLAConstants.KeyWords.UNION).append(' '); + } + } + + tlaBuffer.append(CLOSING_SEP).append(TLAConstants.CR); + if (cfgBuffer != null) { + cfgBuffer.append(TLAConstants.KeyWords.SYMMETRY).append(TLAConstants.SPACE) + .append(label).append(TLAConstants.CR); + } + } + } + + public void addConstantsBis(final List<Assignment> constants, final String attributeConstants) { + for (int i = 0; i < constants.size(); i++) { + final Assignment constant = constants.get(i); + if (! constant.isModelValue()) { + if (cfgBuffer != null) { + // simple constant value assignment + cfgBuffer.append(TLAConstants.COMMENT).append(TLAConstants.KeyWords.CONSTANT).append(" definitions"); + cfgBuffer.append(TLAConstants.CR); + } + + tlaBuffer.append(TLAConstants.COMMENT).append(TLAConstants.KeyWords.CONSTANT).append(" definitions "); + tlaBuffer.append(TLAConstants.ATTRIBUTE).append(attributeConstants).append(TLAConstants.COLON); + tlaBuffer.append(i).append(constant.getLeft()).append(TLAConstants.CR); + + addArrowAssignment(constant, TLAConstants.Schemes.CONSTANT_SCHEME); + + tlaBuffer.append(TLAConstants.SEP).append(TLAConstants.CR).append(TLAConstants.CR); + } + } + } + + /** + * Adds the ASSUME PrintT statement and identifier for the constant expression + * evaluation. The MC.tla file will contain: + * + * const_expr_1232141234123 == + * expression + * ----- + * ASSUME PrintT(<<"$!@$!@$!@$!@$!", const_expr_1232141234123>>) + * + * See the comments in the method for an explanation of defining + * an identifier. + * + * @param expression + * @param attributeName + */ + public void addConstantExpressionEvaluation(final String expression, final String attributeName) { + if (expression.trim().length() != 0) { + /* + * Identifier definition We define an identifier for more sensible error + * messages For example, if the user enters "1+" into the constant expression + * field and "1+" is placed as the second element of the tuple that is the + * argument for PrintT(), then the parse error would be something like + * "Encountered >>" which would be mysterious to the user. With an identifier + * defined, the message says "Encountered ----" which is the separator after + * each section in TLA buffer. This error message is equally mysterious, but at + * least it is the same message that would appear were the same error present in + * another section in the model editor. We can potentially replace such messages + * with something more sensible in the future in the appendError() method in + * TLCErrorView. + */ + final String id = SpecWriterUtilities.getValidIdentifier(TLAConstants.Schemes.CONSTANTEXPR_SCHEME); + tlaBuffer.append(TLAConstants.COMMENT).append("Constant expression definition "); + tlaBuffer.append(TLAConstants.ATTRIBUTE).append(attributeName).append(TLAConstants.CR); + tlaBuffer.append(id).append(TLAConstants.DEFINES).append(TLAConstants.CR).append(expression); + tlaBuffer.append(CLOSING_SEP).append(TLAConstants.CR); + + // ASSUME PrintT(<<"$!@$!@$!@$!@$!", const_expr_23423232>>) statement + // The "$!@$!@$!@$!@$!" allows the toolbox to identify the + // value of the constant expression in the TLC output + tlaBuffer.append(TLAConstants.COMMENT).append("Constant expression ASSUME statement "); + tlaBuffer.append(TLAConstants.ATTRIBUTE).append(attributeName).append(TLAConstants.CR); + tlaBuffer.append("ASSUME PrintT(").append(TLAConstants.BEGIN_TUPLE); + tlaBuffer.append(TLAConstants.CONSTANT_EXPRESSION_EVAL_IDENTIFIER).append(TLAConstants.COMMA).append(id); + tlaBuffer.append(TLAConstants.END_TUPLE).append(")").append(CLOSING_SEP).append(TLAConstants.CR); + } + } + + /** + * New definitions are added to the TLA buffer only + * @param defnitions + * @param attributeName + */ + public void addNewDefinitions(final String definitions, final String attributeName) { + if (definitions.trim().length() == 0) { + return; + } + tlaBuffer.append(TLAConstants.COMMENT).append("New definitions "); + tlaBuffer.append(TLAConstants.ATTRIBUTE).append(attributeName).append(TLAConstants.CR); + tlaBuffer.append(definitions).append(CLOSING_SEP); + } + + /** + * Convenience method to call {@link #addFormulaList(List, String, String)} wrapping the {@code element} parameter. + * + * @param element + * @param keyword + * @param attributeName + */ + public void addFormulaList(final String element, final String keyword, final String attributeName) { + final List<String[]> elements = new ArrayList<>(1); + elements.add(new String[] {element, TLAConstants.EMPTY_STRING}); + addFormulaList(elements, keyword, attributeName); + } + + /** + * Puts (String[])element[0] to configuration buffer; if this instance was + * appropriately constructed, and element[1] to the TLA buffer; if element[2] + * present, uses it as index. + * + * @param elements a list of String[] elements + * @param keyword the keyword to be used in the CFG buffer + * @param attributeName the name of the attribute in the TLA buffer + */ + public void addFormulaList(final List<String[]> elements, final String keyword, final String attributeName) { + if (elements.isEmpty()) { + return; + } + + if (cfgBuffer != null) { + cfgBuffer.append(TLAConstants.COMMENT).append(keyword + " definition").append(TLAConstants.CR); + cfgBuffer.append(keyword).append(TLAConstants.CR); + } + + for (int i = 0; i < elements.size(); i++) { + final String[] element = elements.get(i); + + if (cfgBuffer != null) { + cfgBuffer.append(element[0]).append(TLAConstants.CR); + } + + // when a definition in the root module is overridden as a model value + // there is nothing to add to the MC.tla file so, we do not do the following + if (!element[1].equals(TLAConstants.EMPTY_STRING)) { + tlaBuffer.append(TLAConstants.COMMENT).append(keyword + " definition ").append(TLAConstants.ATTRIBUTE); + tlaBuffer.append(attributeName).append(TLAConstants.COLON).append(element.length > 2 ? element[2] : i); + tlaBuffer.append(TLAConstants.CR).append(element[1]).append(CLOSING_SEP); + } + } + } + + /** + * Add the view definition + * @param viewString the string that the user enters into the view field + * @param attributeName the attribute name of the view field + */ + public void addView(final String viewString, final String attributeName) { + if (viewString.trim().length() != 0) { + final String id = SpecWriterUtilities.getValidIdentifier(TLAConstants.Schemes.VIEW_SCHEME); + + if (cfgBuffer != null) { + cfgBuffer.append(TLAConstants.COMMENT).append("VIEW definition").append(TLAConstants.CR); + cfgBuffer.append("VIEW").append(TLAConstants.CR).append(id).append(TLAConstants.CR); + } + + tlaBuffer.append(TLAConstants.COMMENT).append("VIEW definition ").append(TLAConstants.ATTRIBUTE); + tlaBuffer.append(attributeName).append(TLAConstants.CR); + tlaBuffer.append(id).append(TLAConstants.DEFINES).append(TLAConstants.CR).append(viewString); + tlaBuffer.append(CLOSING_SEP).append(TLAConstants.CR); + } + } + + /** + * Assigns a right side to a label using an id generated from given schema + * @param constant, constant containing the values + * @param schema schema to generate the Id + * @return generated id + */ + public String addArrowAssignment(final Assignment constant, final String schema) { + return addArrowAssignmentToBuffers(tlaBuffer, cfgBuffer, constant, schema); + } + + /** + * Creates a serial version of an MV set in both files + * @param mvSet typed set containing the model values + * @param comment a comment to put before the declarations, null and empty strings are OK + */ + public void addMVTypedSet(final TypedSet mvSet, final String comment, final String attributeName) { + if (mvSet.getValueCount() != 0) { + // create a declaration line + // CONSTANTS + // a, b, c + if ((comment != null) && (comment.length() != 0)) { + tlaBuffer.append(TLAConstants.COMMENT).append(comment).append(TLAConstants.ATTRIBUTE); + tlaBuffer.append(attributeName).append(TLAConstants.CR); + } + tlaBuffer.append(TLAConstants.KeyWords.CONSTANTS).append(TLAConstants.CR).append(mvSet.toStringWithoutBraces()); + tlaBuffer.append(CLOSING_SEP).append(TLAConstants.CR); + + if (cfgBuffer != null) { + // create MV assignments + // a = a + // b = b + // c = c + if ((comment != null) && (comment.length() != 0)) { + cfgBuffer.append(TLAConstants.COMMENT).append(comment).append(TLAConstants.CR); + } + cfgBuffer.append(TLAConstants.KeyWords.CONSTANTS).append(TLAConstants.CR); + for (int i = 0; i < mvSet.getValueCount(); i++) { + final String mv = mvSet.getValue(i); + cfgBuffer.append(mv).append(TLAConstants.EQ).append(mv).append(TLAConstants.CR); + } + } + } + } +} diff --git a/tlatools/src/tlc2/output/AbstractTLACopier.java b/tlatools/src/tlc2/output/AbstractTLACopier.java new file mode 100644 index 0000000000000000000000000000000000000000..38c3a918fb7b896443584fa695032f209f089a59 --- /dev/null +++ b/tlatools/src/tlc2/output/AbstractTLACopier.java @@ -0,0 +1,35 @@ +package tlc2.output; + +import java.io.File; +import java.util.regex.Pattern; + +import util.TLAConstants; + +/** + * The abstract superclass for classes which copy a TLA file. + */ +abstract class AbstractTLACopier extends AbstractCopier { + protected static final Pattern CLOSING_BODY_PATTERN = Pattern.compile(TLAConstants.MODULE_CLOSING_REGEX); + + + protected final Pattern modulePattern; + + protected boolean inBody; + + AbstractTLACopier(final String originalName, final String newName, final File sourceLocation) { + super(originalName, newName, sourceLocation); + + final String regex = TLAConstants.MODULE_OPENING_PREFIX_REGEX + originalModuleName; + modulePattern = Pattern.compile(regex); + + inBody = false; + } + + /** + * {@inheritDoc} + */ + @Override + protected String getFileExtension() { + return TLAConstants.Files.TLA_EXTENSION; + } +} diff --git a/tlatools/src/tlc2/output/CFGCopier.java b/tlatools/src/tlc2/output/CFGCopier.java new file mode 100644 index 0000000000000000000000000000000000000000..89f80c77dedbff5b22d8cd65a3e16df5eda3eb35 --- /dev/null +++ b/tlatools/src/tlc2/output/CFGCopier.java @@ -0,0 +1,100 @@ +package tlc2.output; + +import java.io.BufferedWriter; +import java.io.File; +import java.io.IOException; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import util.TLAConstants; + +/** + * This class which copies a CFG file, mutating it appropriately to become a SpecTE CFG file. This is becoming + * pretty brittle; we would probably be better served at this point to work on the {@link ModelConfig} class + * and make it able to write itself to a stream (and from that point forward, just modify an instance of that + * directly.) (TODO) + */ +public class CFGCopier extends AbstractCopier { + private static final String SPECIFICATION_COMMENT_REGEX = "^\\\\\\* " + TLAConstants.KeyWords.SPECIFICATION + ".*$?"; + private static final Pattern SPECIFICATION_COMMENT_PATTERN = Pattern.compile(SPECIFICATION_COMMENT_REGEX); + private static final String INIT_COMMENT_REGEX = "^\\\\\\* " + TLAConstants.KeyWords.INIT + ".*$?"; + private static final Pattern INIT_COMMENT_PATTERN = Pattern.compile(INIT_COMMENT_REGEX); + private static final String NEXT_COMMENT_REGEX = "^\\\\\\* " + TLAConstants.KeyWords.NEXT + ".*$?"; + private static final Pattern NEXT_COMMENT_PATTERN = Pattern.compile(NEXT_COMMENT_REGEX); + + + private boolean skipNextLine; + + private final String initNextConfiguration; + + public CFGCopier(final String originalName, final String newName, final File sourceLocation, final String initNextCFG) { + super(originalName, newName, sourceLocation); + + skipNextLine = false; + + initNextConfiguration = initNextCFG; + } + + /** + * {@inheritDoc} + */ + @Override + protected String getFileExtension() { + return TLAConstants.Files.CONFIG_EXTENSION; + } + + /** + * {@inheritDoc} + */ + @Override + protected void copyLine(final BufferedWriter writer, final String originalLine, final int lineNumber) + throws IOException { + if (skipNextLine) { + skipNextLine = false; + return; + } + + final Matcher comment = SPECIFICATION_COMMENT_PATTERN.matcher(originalLine); + if (!comment.matches()) { + final Matcher init = INIT_COMMENT_PATTERN.matcher(originalLine); + + if (!init.matches()) { + final Matcher next = NEXT_COMMENT_PATTERN.matcher(originalLine); + + if (!next.matches()) { + final String trimmed = originalLine.trim(); + + if (TLAConstants.KeyWords.SPECIFICATION.equals(trimmed) + || TLAConstants.KeyWords.INIT.equals(trimmed) + || TLAConstants.KeyWords.NEXT.equals(trimmed)) { + skipNextLine = true; + } else if (trimmed.startsWith(TLAConstants.KeyWords.SPECIFICATION) + || trimmed.startsWith(TLAConstants.KeyWords.INIT) + || trimmed.startsWith(TLAConstants.KeyWords.NEXT)) { + // NO-OP - don't write since it starts with the keyword, but trimmed doesn't match + // the naming of the keyword-denoted-attribute is on this same line, e.g: + // SPECIFICATION MySpec + } else if (!trimmed.startsWith(TLAConstants.GENERATION_TIMESTAMP_PREFIX)) { + writer.write(originalLine + '\n'); + } + } + } + } + } + + @Override + protected void allInputHasBeenConsumed(final BufferedWriter writer) throws IOException { + writer.write(initNextConfiguration + '\n'); + writer.write(SpecWriterUtilities.getGeneratedTimeStampCommentLine().toString() + '\n'); + } + + + public static void main(final String[] args) throws Exception { + final String initNext = "INIT\ninit_abc_ldq\n\nNEXT\nnext_abc_ldq"; + final CFGCopier copier = new CFGCopier(TLAConstants.Files.MODEL_CHECK_FILE_BASENAME, + TLAConstants.TraceExplore.ERROR_STATES_MODULE_NAME, + new File(args[0]), + initNext); + copier.copy(); + } +} diff --git a/tlatools/src/tlc2/output/EC.java b/tlatools/src/tlc2/output/EC.java index 379b4e356fefa51467ac1b8f6422335c85fc367b..48203f947eb0b674ff3c768382525bffb4140380 100644 --- a/tlatools/src/tlc2/output/EC.java +++ b/tlatools/src/tlc2/output/EC.java @@ -1,251 +1,418 @@ -package tlc2.output; - - -/** - * Interface containing the error code constants - * @author Simon Zambrovski - * @version $Id$ - */ -public interface EC -{ - // Check and CheckImpl - // check if the TLC option is the same for params - public static final int CHECK_FAILED_TO_CHECK = 3000; - public static final int CHECK_COULD_NOT_READ_TRACE = 3001; - - public static final int CHECK_PARAM_EXPECT_CONFIG_FILENAME = 3100; - public static final int CHECK_PARAM_USAGE = 3101; - public static final int CHECK_PARAM_MISSING_TLA_MODULE = 3102; - public static final int CHECK_PARAM_NEED_TO_SPECIFY_CONFIG_DIR = 3103; - public static final int CHECK_PARAM_WORKER_NUMBER_REQUIRED = 3104; - public static final int CHECK_PARAM_WORKER_NUMBER_TOO_SMALL = 3105; - public static final int CHECK_PARAM_WORKER_NUMBER_REQUIRED2 = 3106; - public static final int CHECK_PARAM_DEPTH_REQUIRED = 3107; - public static final int CHECK_PARAM_DEPTH_REQUIRED2 = 3108; - public static final int CHECK_PARAM_TRACE_REQUIRED = 3109; - public static final int CHECK_PARAM_COVREAGE_REQUIRED = 3110; - public static final int CHECK_PARAM_COVREAGE_REQUIRED2 = 3111; - public static final int CHECK_PARAM_COVREAGE_TOO_SMALL = 3112; - public static final int CHECK_PARAM_UNRECOGNIZED = 3113; - public static final int CHECK_PARAM_TOO_MANY_INPUT_FILES = 3114; - - - public final static int SANY_PARSER_CHECK_1 = 4000; - public final static int SANY_PARSER_CHECK_2 = 4001; - public final static int SANY_PARSER_CHECK_3 = 4002; - - public static final int UNKNOWN = -1; // TODO remove all these - public final static int UNIT_TEST = -123456; - - public static final int GENERAL = 1000; - public static final int SYSTEM_OUT_OF_MEMORY = 1001; - public static final int SYSTEM_OUT_OF_MEMORY_TOO_MANY_INIT = 1002; - public static final int SYSTEM_STACK_OVERFLOW = 1005; - - public static final int WRONG_COMMANDLINE_PARAMS_SIMULATOR = 1101; - public static final int WRONG_COMMANDLINE_PARAMS_TLC = 1102; - - public static final int TLC_PP_PARSING_VALUE = 2000; - public static final int TLC_PP_FORMATING_VALUE = 2001; - - public static final int TLC_METADIR_EXISTS = 2100; - public static final int TLC_METADIR_CAN_NOT_BE_CREATED = 2101; - public static final int TLC_INITIAL_STATE = 2102; - public static final int TLC_NESTED_EXPRESSION = 2103; - public static final int TLC_ASSUMPTION_FALSE = 2104; - public static final int TLC_ASSUMPTION_EVALUATION_ERROR = 2105; - public static final int TLC_STATE_NOT_COMPLETELY_SPECIFIED_INITIAL = 2106; - - public static final int TLC_INVARIANT_VIOLATED_INITIAL = 2107; - public static final int TLC_PROPERTY_VIOLATED_INITIAL = 2108; - public static final int TLC_STATE_NOT_COMPLETELY_SPECIFIED_NEXT = 2109; - public static final int TLC_INVARIANT_VIOLATED_BEHAVIOR = 2110; - public static final int TLC_INVARIANT_EVALUATION_FAILED = 2111; - public static final int TLC_ACTION_PROPERTY_VIOLATED_BEHAVIOR = 2112; - public static final int TLC_ACTION_PROPERTY_EVALUATION_FAILED = 2113; - public static final int TLC_DEADLOCK_REACHED = 2114; - - public static final int TLC_TEMPORAL_PROPERTY_VIOLATED = 2116; - public static final int TLC_FAILED_TO_RECOVER_NEXT = 2117; - public static final int TLC_NO_STATES_SATISFYING_INIT = 2118; - public static final int TLC_STRING_MODULE_NOT_FOUND = 2119; - - public static final int TLC_ERROR_STATE = 2120; - public static final int TLC_BEHAVIOR_UP_TO_THIS_POINT = 2121; - public static final int TLC_BACK_TO_STATE = 2122; // The "back to state" message from a liveness-error trace. - public static final int TLC_FAILED_TO_RECOVER_INIT = 2123; - public static final int TLC_REPORTER_DIED = 2124; - - public static final int SYSTEM_ERROR_READING_POOL = 2125; - public static final int SYSTEM_CHECKPOINT_RECOVERY_CORRUPT = 2126; - public static final int SYSTEM_ERROR_WRITING_POOL = 2127; - public static final int SYSTEM_INDEX_ERROR = 2134; - public static final int SYSTEM_STREAM_EMPTY = 2135; - public static final int SYSTEM_FILE_NULL = 2137; - public static final int SYSTEM_INTERRUPTED = 2138; - public static final int SYSTEM_UNABLE_NOT_RENAME_FILE = 2160; - public static final int SYSTEM_DISK_IO_ERROR_FOR_FILE = 2161; - public static final int SYSTEM_METADIR_EXISTS = 2162; - public static final int SYSTEM_METADIR_CREATION_ERROR = 2163; - public static final int SYSTEM_UNABLE_TO_OPEN_FILE = 2167; - public static final int TLC_BUG = 2128; // TODO Bad description - - public static final int SYSTEM_DISKGRAPH_ACCESS = 2129; // TODO refactor - - public static final int TLC_AAAAAAA = 2130; - public static final int TLC_REGISTRY_INIT_ERROR = 2131; - public static final int TLC_CHOOSE_ARGUMENTS_WRONG = 2164; - public static final int TLC_CHOOSE_UPPER_BOUND = 2165; - - public static final int TLC_VALUE_ASSERT_FAILED = 2132; - public static final int TLC_MODULE_VALUE_JAVA_METHOD_OVERRIDE = 2154; - - public static final int TLC_FP_NOT_IN_SET = 2133; - public static final int TLC_FP_VALUE_ALREADY_ON_DISK = 2166; - - public static final int TLC_LIVE_BEGRAPH_FAILED_TO_CONSTRUCT = 2159; - public static final int TLC_PARAMETER_MUST_BE_POSTFIX = 2136; - public static final int TLC_COULD_NOT_DETERMINE_SUBSCRIPT = 2139; - public static final int TLC_SUBSCRIPT_CONTAIN_NO_STATE_VAR = 2140; - public static final int TLC_WRONG_TUPLE_FIELD_NAME = 2141; - public static final int TLC_WRONG_RECORD_FIELD_NAME = 2142; - public static final int TLC_UNCHANGED_VARIABLE_CHANGED = 2143; - public static final int TLC_EXCEPT_APPLIED_TO_UNKNOWN_FIELD = 2144; - - public static final int TLC_MODULE_TLCGET_UNDEFINED = 2145; - /** Attempted to compare %1% with the value\n%2% */ - public static final int TLC_MODULE_COMPARE_VALUE = 2155; - public static final int TLC_MODULE_CHECK_MEMBER_OF = 2158; - public static final int TLC_MODULE_TRANSITIVE_CLOSURE = 2157; - /** The %1% argument of %2% should be a %3%, but instead it is:<br>%4% */ - public static final int TLC_MODULE_ARGUMENT_ERROR = 2169; - /** Simon used an argument like "\bn apple" to TLC_MODULE_ARGUMENT_ERROR to turn - * an "a" into an "an". This doesn't work on the Toolbox's console. Hence, LL added - * the following message type on 21 May 2012. */ - public static final int TLC_MODULE_ARGUMENT_ERROR_AN = 2266; - public static final int TLC_ARGUMENT_MISMATCH = 2170; - public static final int TLC_PARSING_FAILED2 = 2171; - public static final int TLC_PARSING_FAILED = 3002; - public static final int TLC_TOO_MNY_POSSIBLE_STATES = 2172; - public static final int TLC_ERROR_REPLACING_MODULES = 2173; - public static final int SYSTEM_ERROR_READING_STATES = 2174; - public static final int SYSTEM_ERROR_WRITING_STATES = 2175; - public static final int TLC_MODULE_APPLYING_TO_WRONG_VALUE = 2176; - public static final int TLC_MODULE_BAG_UNION1 = 2177; - public static final int TLC_MODULE_OVERFLOW = 2178; - public static final int TLC_MODULE_DIVISION_BY_ZERO = 2179; - public static final int TLC_MODULE_NULL_POWER_NULL = 2180; - public static final int TLC_MODULE_COMPUTING_CARDINALITY = 2181; - public static final int TLC_MODULE_EVALUATING = 2182; - /** The %1% argument of %2% must be in the domain of its first argument:<br>%3%<br>, but instead it is<br>%4% */ - public static final int TLC_MODULE_ARGUMENT_NOT_IN_DOMAIN = 2183; - public static final int TLC_MODULE_APPLY_EMPTY_SEQ = 2184; - - public static final int TLC_STARTING = 2185; - public static final int TLC_FINISHED = 2186; - - // distributed TLC - - public static final int TLC_DISTRIBUTED_SERVER_RUNNING = 7000; - public static final int TLC_DISTRIBUTED_WORKER_REGISTERED = TLC_DISTRIBUTED_SERVER_RUNNING + 1; - public static final int TLC_DISTRIBUTED_WORKER_DEREGISTERED = TLC_DISTRIBUTED_WORKER_REGISTERED + 1; - public static final int TLC_DISTRIBUTED_WORKER_STATS = TLC_DISTRIBUTED_WORKER_DEREGISTERED + 1; - public static final int TLC_DISTRIBUTED_SERVER_NOT_RUNNING = TLC_DISTRIBUTED_WORKER_STATS + 1; - public static final int TLC_DISTRIBUTED_VM_VERSION = TLC_DISTRIBUTED_SERVER_NOT_RUNNING + 1; - public static final int TLC_DISTRIBUTED_WORKER_LOST = TLC_DISTRIBUTED_VM_VERSION + 1; - public static final int TLC_DISTRIBUTED_EXCEED_BLOCKSIZE = TLC_DISTRIBUTED_WORKER_LOST + 1; - public static final int TLC_DISTRIBUTED_SERVER_FPSET_WAITING = TLC_DISTRIBUTED_EXCEED_BLOCKSIZE + 1; - public static final int TLC_DISTRIBUTED_SERVER_FPSET_REGISTERED = TLC_DISTRIBUTED_SERVER_FPSET_WAITING + 1; - public static final int TLC_DISTRIBUTED_SERVER_FINISHED = TLC_DISTRIBUTED_SERVER_FPSET_REGISTERED + 1; - - // errors during parsing of the model configuration - - public static final int CFG_ERROR_READING_FILE = 5001; - public static final int CFG_GENERAL = 5002; - public static final int CFG_MISSING_ID = 5003; - public static final int CFG_TWICE_KEYWORD = 5004; - public static final int CFG_EXPECT_ID = 5005; - public static final int CFG_EXPECTED_SYMBOL = 5006; - public static final int TLC_MODE_MC = 2187; - public static final int TLC_MODE_SIMU = 2188; - public static final int TLC_COMPUTING_INIT = 2189; - public static final int TLC_INIT_GENERATED1 = 2190; - public static final int TLC_INIT_GENERATED2 = 2191; - public static final int TLC_INIT_GENERATED3 = 2207; - public static final int TLC_INIT_GENERATED4 = 2208; - public static final int TLC_CHECKING_TEMPORAL_PROPS = 2192; - public static final int TLC_SUCCESS = 2193; - public static final int TLC_SEARCH_DEPTH = 2194; - public static final int TLC_CHECKPOINT_START = 2195; - public static final int TLC_CHECKPOINT_END = 2196; - public static final int TLC_CHECKPOINT_RECOVER_START = 2197; - public static final int TLC_CHECKPOINT_RECOVER_END = 2198; - public static final int TLC_STATS = 2199; - public static final int TLC_STATS_DFID = 2204; - public static final int TLC_STATS_SIMU = 2210; - public static final int TLC_PROGRESS_STATS = 2200; - public static final int TLC_COVERAGE_START = 2201; - public static final int TLC_COVERAGE_END = 2202; - public static final int TLC_CHECKPOINT_RECOVER_END_DFID = 2203; - public static final int TLC_PROGRESS_START_STATS_DFID = 2205; - public static final int TLC_PROGRESS_STATS_DFID = 2206; - public static final int TLC_PROGRESS_SIMU = 2209; - public static final int TLC_FP_COMPLETED = 2211; - - public static final int TLC_LIVE_IMPLIED = 2212; - public static final int TLC_LIVE_CANNOT_HANDLE_FORMULA = 2213; - public static final int TLC_LIVE_WRONG_FORMULA_FORMAT = 2214; - public static final int TLC_LIVE_ENCOUNTERED_ACTIONS = 2249; - public static final int TLC_LIVE_STATE_PREDICATE_NON_BOOL = 2250; - public static final int TLC_LIVE_CANNOT_EVAL_FORMULA = 2251; - public static final int TLC_LIVE_ENCOUNTERED_NONBOOL_PREDICATE = 2252; - - - public static final int TLC_EXPECTED_VALUE = 2215; - public static final int TLC_EXPECTED_EXPRESSION = 2246; - public static final int TLC_EXPECTED_EXPRESSION_IN_COMPUTING = 2247; - public static final int TLC_EXPECTED_EXPRESSION_IN_COMPUTING2 = 2248; - - - // state printing - public static final int TLC_STATE_PRINT1 = 2216; - public static final int TLC_STATE_PRINT2 = 2217; - public static final int TLC_STATE_PRINT3 = 2218; // This seems to be a "Stuttering" message from a liveness-error trace - public static final int TLC_SANY_END = 2219; - public static final int TLC_SANY_START = 2220; - public static final int TLC_COVERAGE_VALUE = 2221; - - // config file errors - public static final int TLC_CONFIG_VALUE_NOT_ASSIGNED_TO_CONSTANT_PARAM = 2222; - public static final int TLC_CONFIG_RHS_ID_APPEARED_AFTER_LHS_ID = 2223; - public static final int TLC_CONFIG_WRONG_SUBSTITUTION = 2224; - public static final int TLC_CONFIG_WRONG_SUBSTITUTION_NUMBER_OF_ARGS = 2225; - public static final int TLC_CONFIG_ID_DOES_NOT_APPEAR_IN_SPEC = 2226; - public static final int TLC_CONFIG_NOT_BOTH_SPEC_AND_INIT = 2227; - public static final int TLC_CONFIG_ID_REQUIRES_NO_ARG = 2228; - public static final int TLC_CONFIG_SPECIFIED_NOT_DEFINED = 2229; - public static final int TLC_CONFIG_ID_HAS_VALUE = 2230; - public static final int TLC_CONFIG_MISSING_INIT = 2231; - public static final int TLC_CONFIG_MISSING_NEXT = 2232; - public static final int TLC_CONFIG_ID_MUST_NOT_BE_CONSTANT = 2233; - public static final int TLC_CONFIG_OP_NO_ARGS = 2234; - public static final int TLC_CONFIG_OP_NOT_IN_SPEC = 2235; - public static final int TLC_CONFIG_OP_IS_EQUAL = 2236; - public static final int TLC_CONFIG_SPEC_IS_TRIVIAL = 2237; - public static final int TLC_CANT_HANDLE_SUBSCRIPT = 2238; - public static final int TLC_CANT_HANDLE_CONJUNCT = 2239; - public static final int TLC_CANT_HANDLE_TOO_MANY_NEXT_STATE_RELS = 2240; - public static final int TLC_CONFIG_PROPERTY_NOT_CORRECTLY_DEFINED = 2241; - public static final int TLC_CONFIG_OP_ARITY_INCONSISTENT = 2242; - public static final int TLC_CONFIG_NO_STATE_TYPE = 2243; - public static final int TLC_CANT_HANDLE_REAL_NUMBERS = 2244; - public static final int TLC_NO_MODULES = 2245; - - public static final int TLC_ENABLED_WRONG_FORMULA = 2260; - public static final int TLC_ENCOUNTERED_FORMULA_IN_PREDICATE = 2261; - public static final int TLC_VERSION = 2262; - public static final int TLC_USAGE = 2263; - public static final int TLC_COUNTER_EXAMPLE = 2264; - - public static final int TLC_INTEGER_TOO_BIG = 2265; -} +package tlc2.output; + +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/** + * Interface containing the error code constants + * @author Simon Zambrovski + * @version $Id$ + */ +public interface EC +{ + // This is reserved so that an optional error code can be safely represented in a single int + public static final int NO_ERROR = 0; + + // Check and CheckImpl + // check if the TLC option is the same for params + public static final int CHECK_FAILED_TO_CHECK = 3000; + public static final int CHECK_COULD_NOT_READ_TRACE = 3001; + + public static final int CHECK_PARAM_EXPECT_CONFIG_FILENAME = 3100; + public static final int CHECK_PARAM_USAGE = 3101; + public static final int CHECK_PARAM_MISSING_TLA_MODULE = 3102; + public static final int CHECK_PARAM_NEED_TO_SPECIFY_CONFIG_DIR = 3103; + public static final int CHECK_PARAM_WORKER_NUMBER_REQUIRED = 3104; + public static final int CHECK_PARAM_WORKER_NUMBER_TOO_SMALL = 3105; + public static final int CHECK_PARAM_WORKER_NUMBER_REQUIRED2 = 3106; + public static final int CHECK_PARAM_DEPTH_REQUIRED = 3107; + public static final int CHECK_PARAM_DEPTH_REQUIRED2 = 3108; + public static final int CHECK_PARAM_TRACE_REQUIRED = 3109; + public static final int CHECK_PARAM_COVREAGE_REQUIRED = 3110; + public static final int CHECK_PARAM_COVREAGE_REQUIRED2 = 3111; + public static final int CHECK_PARAM_COVREAGE_TOO_SMALL = 3112; + public static final int CHECK_PARAM_UNRECOGNIZED = 3113; + public static final int CHECK_PARAM_TOO_MANY_INPUT_FILES = 3114; + + + public final static int SANY_PARSER_CHECK_1 = 4000; + public final static int SANY_PARSER_CHECK_2 = 4001; + public final static int SANY_PARSER_CHECK_3 = 4002; + + public static final int UNKNOWN = -1; // TODO remove all these + public final static int UNIT_TEST = -123456; + /** + * A feature of TLA+/TLC is not supported by TLC's current mode. E.g. + * TLCGet/TLCSet operator cannot be used in distributed TLC. + */ + public static final int TLC_FEATURE_UNSUPPORTED = 2156; + public static final int TLC_FEATURE_UNSUPPORTED_LIVENESS_SYMMETRY = 2279; + public static final int TLC_FEATURE_LIVENESS_CONSTRAINTS = 2284; + + public static final int GENERAL = 1000; + public static final int SYSTEM_OUT_OF_MEMORY = 1001; + public static final int SYSTEM_OUT_OF_MEMORY_TOO_MANY_INIT = 1002; + public static final int SYSTEM_OUT_OF_MEMORY_LIVENESS = 1003; + public static final int SYSTEM_STACK_OVERFLOW = 1005; + + public static final int WRONG_COMMANDLINE_PARAMS_SIMULATOR = 1101; + public static final int WRONG_COMMANDLINE_PARAMS_TLC = 1102; + + public static final int TLC_PP_PARSING_VALUE = 2000; + public static final int TLC_PP_FORMATING_VALUE = 2001; + + public static final int TLC_METADIR_EXISTS = 2100; + public static final int TLC_METADIR_CAN_NOT_BE_CREATED = 2101; + public static final int TLC_INITIAL_STATE = 2102; + public static final int TLC_NESTED_EXPRESSION = 2103; + public static final int TLC_ASSUMPTION_FALSE = 2104; + public static final int TLC_ASSUMPTION_EVALUATION_ERROR = 2105; + public static final int TLC_STATE_NOT_COMPLETELY_SPECIFIED_INITIAL = 2106; + + public static final int TLC_INVARIANT_VIOLATED_INITIAL = 2107; + public static final int TLC_PROPERTY_VIOLATED_INITIAL = 2108; + public static final int TLC_STATE_NOT_COMPLETELY_SPECIFIED_NEXT = 2109; + public static final int TLC_INVARIANT_VIOLATED_BEHAVIOR = 2110; + public static final int TLC_INVARIANT_EVALUATION_FAILED = 2111; + public static final int TLC_INVARIANT_VIOLATED_LEVEL = 2146; + public static final int TLC_ACTION_PROPERTY_VIOLATED_BEHAVIOR = 2112; + public static final int TLC_ACTION_PROPERTY_EVALUATION_FAILED = 2113; + public static final int TLC_DEADLOCK_REACHED = 2114; + + public static final int TLC_STATES_AND_NO_NEXT_ACTION = 2115; + public static final int TLC_TEMPORAL_PROPERTY_VIOLATED = 2116; + public static final int TLC_FAILED_TO_RECOVER_NEXT = 2117; + public static final int TLC_NO_STATES_SATISFYING_INIT = 2118; + public static final int TLC_STRING_MODULE_NOT_FOUND = 2119; + + public static final int TLC_ERROR_STATE = 2120; + public static final int TLC_BEHAVIOR_UP_TO_THIS_POINT = 2121; + public static final int TLC_BACK_TO_STATE = 2122; // The "back to state" message from a liveness-error trace. + public static final int TLC_FAILED_TO_RECOVER_INIT = 2123; + public static final int TLC_REPORTER_DIED = 2124; + + public static final int SYSTEM_ERROR_READING_POOL = 2125; + public static final int SYSTEM_CHECKPOINT_RECOVERY_CORRUPT = 2126; + public static final int SYSTEM_ERROR_WRITING_POOL = 2127; + public static final int SYSTEM_ERROR_CLEANING_POOL = 2270; + public static final int SYSTEM_INDEX_ERROR = 2134; + public static final int SYSTEM_STREAM_EMPTY = 2135; + public static final int SYSTEM_FILE_NULL = 2137; + public static final int SYSTEM_INTERRUPTED = 2138; + public static final int SYSTEM_UNABLE_NOT_RENAME_FILE = 2160; + public static final int SYSTEM_DISK_IO_ERROR_FOR_FILE = 2161; + public static final int SYSTEM_METADIR_EXISTS = 2162; + public static final int SYSTEM_METADIR_CREATION_ERROR = 2163; + public static final int SYSTEM_UNABLE_TO_OPEN_FILE = 2167; + public static final int TLC_BUG = 2128; // TODO Bad description + public static final int TLC_FINGERPRINT_EXCEPTION = 2147; + + public static final int SYSTEM_DISKGRAPH_ACCESS = 2129; // TODO refactor + + public static final int TLC_AAAAAAA = 2130; + public static final int TLC_REGISTRY_INIT_ERROR = 2131; + public static final int TLC_CHOOSE_ARGUMENTS_WRONG = 2164; + public static final int TLC_CHOOSE_UPPER_BOUND = 2165; + + public static final int TLC_VALUE_ASSERT_FAILED = 2132; + public static final int TLC_MODULE_VALUE_JAVA_METHOD_OVERRIDE = 2154; + public static final int TLC_MODULE_VALUE_JAVA_METHOD_OVERRIDE_LOADED = 2168; + public static final int TLC_MODULE_VALUE_JAVA_METHOD_OVERRIDE_MISMATCH = 2400; + public static final int TLC_MODULE_VALUE_JAVA_METHOD_OVERRIDE_MODULE_MISMATCH = 2402; + public static final int TLC_MODULE_VALUE_JAVA_METHOD_OVERRIDE_IDENTIFIER_MISMATCH = 2403; + // User-provided module overrides print to stdout. + public static final int TLC_MODULE_OVERRIDE_STDOUT = 20000; + + public static final int TLC_FP_NOT_IN_SET = 2133; + public static final int TLC_FP_VALUE_ALREADY_ON_DISK = 2166; + + public static final int TLC_LIVE_BEGRAPH_FAILED_TO_CONSTRUCT = 2159; + public static final int TLC_PARAMETER_MUST_BE_POSTFIX = 2136; + public static final int TLC_COULD_NOT_DETERMINE_SUBSCRIPT = 2139; + public static final int TLC_SUBSCRIPT_CONTAIN_NO_STATE_VAR = 2140; + public static final int TLC_WRONG_TUPLE_FIELD_NAME = 2141; + public static final int TLC_WRONG_RECORD_FIELD_NAME = 2142; + public static final int TLC_UNCHANGED_VARIABLE_CHANGED = 2143; + public static final int TLC_EXCEPT_APPLIED_TO_UNKNOWN_FIELD = 2144; + + public static final int TLC_MODULE_TLCGET_UNDEFINED = 2145; + /** Attempted to compare %1% with the value\n%2% */ + public static final int TLC_MODULE_COMPARE_VALUE = 2155; + public static final int TLC_MODULE_CHECK_MEMBER_OF = 2158; + public static final int TLC_MODULE_TRANSITIVE_CLOSURE = 2157; + /** The %1% argument of %2% should be a %3%, but instead it is:<br>%4% */ + public static final int TLC_MODULE_ARGUMENT_ERROR = 2169; + /** Simon used an argument like "\bn apple" to TLC_MODULE_ARGUMENT_ERROR to turn + * an "a" into an "an". This doesn't work on the Toolbox's console. Hence, LL added + * the following message type on 21 May 2012. */ + public static final int TLC_MODULE_ARGUMENT_ERROR_AN = 2266; + public static final int TLC_MODULE_ONE_ARGUMENT_ERROR = 2283; + public static final int TLC_ARGUMENT_MISMATCH = 2170; + public static final int TLC_PARSING_FAILED2 = 2171; + public static final int TLC_PARSING_FAILED = 3002; + public static final int TLC_TOO_MNY_POSSIBLE_STATES = 2172; + public static final int TLC_ERROR_REPLACING_MODULES = 2173; + public static final int SYSTEM_ERROR_READING_STATES = 2174; + public static final int SYSTEM_ERROR_WRITING_STATES = 2175; + public static final int TLC_MODULE_APPLYING_TO_WRONG_VALUE = 2176; + public static final int TLC_MODULE_BAG_UNION1 = 2177; + public static final int TLC_MODULE_OVERFLOW = 2178; + public static final int TLC_MODULE_DIVISION_BY_ZERO = 2179; + public static final int TLC_MODULE_NULL_POWER_NULL = 2180; + public static final int TLC_MODULE_COMPUTING_CARDINALITY = 2181; + public static final int TLC_MODULE_EVALUATING = 2182; + /** The %1% argument of %2% must be in the domain of its first argument:<br>%3%<br>, but instead it is<br>%4% */ + public static final int TLC_MODULE_ARGUMENT_NOT_IN_DOMAIN = 2183; + public static final int TLC_MODULE_APPLY_EMPTY_SEQ = 2184; + + public static final int TLC_SYMMETRY_SET_TOO_SMALL = 2300; + public static final int TLC_SPECIFICATION_FEATURES_TEMPORAL_QUANTIFIER = 2301; + + public static final int TLC_STARTING = 2185; + public static final int TLC_FINISHED = 2186; + + // distributed TLC + + public static final int TLC_DISTRIBUTED_SERVER_RUNNING = 7000; + public static final int TLC_DISTRIBUTED_WORKER_REGISTERED = TLC_DISTRIBUTED_SERVER_RUNNING + 1; + public static final int TLC_DISTRIBUTED_WORKER_DEREGISTERED = TLC_DISTRIBUTED_WORKER_REGISTERED + 1; + public static final int TLC_DISTRIBUTED_WORKER_STATS = TLC_DISTRIBUTED_WORKER_DEREGISTERED + 1; + public static final int TLC_DISTRIBUTED_SERVER_NOT_RUNNING = TLC_DISTRIBUTED_WORKER_STATS + 1; + public static final int TLC_DISTRIBUTED_VM_VERSION = TLC_DISTRIBUTED_SERVER_NOT_RUNNING + 1; + public static final int TLC_DISTRIBUTED_WORKER_LOST = TLC_DISTRIBUTED_VM_VERSION + 1; + public static final int TLC_DISTRIBUTED_EXCEED_BLOCKSIZE = TLC_DISTRIBUTED_WORKER_LOST + 1; + public static final int TLC_DISTRIBUTED_SERVER_FPSET_WAITING = TLC_DISTRIBUTED_EXCEED_BLOCKSIZE + 1; + public static final int TLC_DISTRIBUTED_SERVER_FPSET_REGISTERED = TLC_DISTRIBUTED_SERVER_FPSET_WAITING + 1; + public static final int TLC_DISTRIBUTED_SERVER_FINISHED = TLC_DISTRIBUTED_SERVER_FPSET_REGISTERED + 1; + + // errors during parsing of the model configuration + + public static final int CFG_ERROR_READING_FILE = 5001; + public static final int CFG_GENERAL = 5002; + public static final int CFG_MISSING_ID = 5003; + public static final int CFG_TWICE_KEYWORD = 5004; + public static final int CFG_EXPECT_ID = 5005; + public static final int CFG_EXPECTED_SYMBOL = 5006; + public static final int TLC_MODE_MC = 2187; + public static final int TLC_MODE_MC_DFS = 2271; + public static final int TLC_MODE_SIMU = 2188; + public static final int TLC_COMPUTING_INIT = 2189; + public static final int TLC_COMPUTING_INIT_PROGRESS = 2269; + public static final int TLC_INIT_GENERATED1 = 2190; + public static final int TLC_INIT_GENERATED2 = 2191; + public static final int TLC_INIT_GENERATED3 = 2207; + public static final int TLC_INIT_GENERATED4 = 2208; + public static final int TLC_CHECKING_TEMPORAL_PROPS = 2192; + public static final int TLC_CHECKING_TEMPORAL_PROPS_END = 2267; + public static final int TLC_SUCCESS = 2193; + public static final int TLC_SEARCH_DEPTH = 2194; + public static final int TLC_STATE_GRAPH_OUTDEGREE = 2268; + public static final int TLC_CHECKPOINT_START = 2195; + public static final int TLC_CHECKPOINT_END = 2196; + public static final int TLC_CHECKPOINT_RECOVER_START = 2197; + public static final int TLC_CHECKPOINT_RECOVER_END = 2198; + public static final int TLC_STATS = 2199; + public static final int TLC_STATS_DFID = 2204; + public static final int TLC_STATS_SIMU = 2210; + public static final int TLC_PROGRESS_STATS = 2200; + public static final int TLC_COVERAGE_START = 2201; + public static final int TLC_COVERAGE_END = 2202; + public static final int TLC_CHECKPOINT_RECOVER_END_DFID = 2203; + public static final int TLC_PROGRESS_START_STATS_DFID = 2205; + public static final int TLC_PROGRESS_STATS_DFID = 2206; + public static final int TLC_PROGRESS_SIMU = 2209; + public static final int TLC_FP_COMPLETED = 2211; + + public static final int TLC_LIVE_IMPLIED = 2212; + public static final int TLC_LIVE_CANNOT_HANDLE_FORMULA = 2213; + public static final int TLC_LIVE_WRONG_FORMULA_FORMAT = 2214; + public static final int TLC_LIVE_ENCOUNTERED_ACTIONS = 2249; + public static final int TLC_LIVE_STATE_PREDICATE_NON_BOOL = 2250; + public static final int TLC_LIVE_CANNOT_EVAL_FORMULA = 2251; + public static final int TLC_LIVE_ENCOUNTERED_NONBOOL_PREDICATE = 2252; + public static final int TLC_LIVE_FORMULA_TAUTOLOGY = 2253; + + + public static final int TLC_EXPECTED_VALUE = 2215; + public static final int TLC_EXPECTED_EXPRESSION = 2246; + public static final int TLC_EXPECTED_EXPRESSION_IN_COMPUTING = 2247; + public static final int TLC_EXPECTED_EXPRESSION_IN_COMPUTING2 = 2248; + + + // state printing + public static final int TLC_STATE_PRINT1 = 2216; + public static final int TLC_STATE_PRINT2 = 2217; + public static final int TLC_STATE_PRINT3 = 2218; // This seems to be a "Stuttering" message from a liveness-error trace + public static final int TLC_SANY_END = 2219; + public static final int TLC_SANY_START = 2220; + public static final int TLC_COVERAGE_MISMATCH = 2776; + public static final int TLC_COVERAGE_VALUE = 2221; + public static final int TLC_COVERAGE_VALUE_COST = 2775; + public static final int TLC_COVERAGE_NEXT = 2772; + public static final int TLC_COVERAGE_INIT = 2773; + public static final int TLC_COVERAGE_PROPERTY = 2774; + public static final int TLC_COVERAGE_END_OVERHEAD = 2777; + + // config file errors + public static final int TLC_CONFIG_VALUE_NOT_ASSIGNED_TO_CONSTANT_PARAM = 2222; + public static final int TLC_CONFIG_RHS_ID_APPEARED_AFTER_LHS_ID = 2223; + public static final int TLC_CONFIG_WRONG_SUBSTITUTION = 2224; + public static final int TLC_CONFIG_WRONG_SUBSTITUTION_NUMBER_OF_ARGS = 2225; + public static final int TLC_CONFIG_UNDEFINED_OR_NO_OPERATOR = 2280; + public static final int TLC_CONFIG_SUBSTITUTION_NON_CONSTANT = 2281; + public static final int TLC_CONFIG_ID_DOES_NOT_APPEAR_IN_SPEC = 2226; + public static final int TLC_CONFIG_NOT_BOTH_SPEC_AND_INIT = 2227; + public static final int TLC_CONFIG_ID_REQUIRES_NO_ARG = 2228; + public static final int TLC_CONFIG_SPECIFIED_NOT_DEFINED = 2229; + public static final int TLC_CONFIG_ID_HAS_VALUE = 2230; + public static final int TLC_CONFIG_MISSING_INIT = 2231; + public static final int TLC_CONFIG_MISSING_NEXT = 2232; + public static final int TLC_CONFIG_ID_MUST_NOT_BE_CONSTANT = 2233; + public static final int TLC_CONFIG_OP_NO_ARGS = 2234; + public static final int TLC_CONFIG_OP_NOT_IN_SPEC = 2235; + public static final int TLC_CONFIG_OP_IS_EQUAL = 2236; + public static final int TLC_CONFIG_SPEC_IS_TRIVIAL = 2237; + public static final int TLC_CANT_HANDLE_SUBSCRIPT = 2238; + public static final int TLC_CANT_HANDLE_CONJUNCT = 2239; + public static final int TLC_CANT_HANDLE_TOO_MANY_NEXT_STATE_RELS = 2240; + public static final int TLC_CONFIG_PROPERTY_NOT_CORRECTLY_DEFINED = 2241; + public static final int TLC_CONFIG_OP_ARITY_INCONSISTENT = 2242; + public static final int TLC_CONFIG_NO_STATE_TYPE = 2243; + public static final int TLC_CANT_HANDLE_REAL_NUMBERS = 2244; + public static final int TLC_NO_MODULES = 2245; + + public static final int TLC_ENABLED_WRONG_FORMULA = 2260; + public static final int TLC_ENCOUNTERED_FORMULA_IN_PREDICATE = 2261; + public static final int TLC_VERSION = 2262; + public static final int TLC_COUNTER_EXAMPLE = 2264; + + public static final int TLC_INTEGER_TOO_BIG = 2265; + public static final int TLC_TRACE_TOO_LONG = 2282; + + public static final int TLC_ENVIRONMENT_JVM_GC = 2401; + + + //**************************************************************// + // Mapping error constants above to process exit/return values. // + // Because Linux and macOS only support 8-bit exit values, this // + // mapping is necessary. // + //**************************************************************// + + public static class ExitStatus { + + public static final int ERROR = 255; + public static final int SUCCESS = 0; + + // (Safety/Liveness) Violations + public static final int VIOLATION_ASSUMPTION = 10; + public static final int VIOLATION_DEADLOCK = VIOLATION_ASSUMPTION + 1; + public static final int VIOLATION_SAFETY = VIOLATION_DEADLOCK + 1; + public static final int VIOLATION_LIVENESS = VIOLATION_SAFETY + 1; + public static final int VIOLATION_ASSERT = VIOLATION_LIVENESS + 1; + + // Evaluation failures + public static final int FAILURE_SPEC_EVAL = 75; + public static final int FAILURE_SAFETY_EVAL = FAILURE_SPEC_EVAL + 1; + public static final int FAILURE_LIVENESS_EVAL = FAILURE_SAFETY_EVAL + 1; + + // Errors + public static final int ERROR_SPEC_PARSE = 150; + public static final int ERROR_CONFIG_PARSE = ERROR_SPEC_PARSE + 1; + public static final int ERROR_STATESPACE_TOO_LARGE = ERROR_CONFIG_PARSE + 1; + public static final int ERROR_SYSTEM = ERROR_STATESPACE_TOO_LARGE + 1; + + /** + * Returns an exit status for an error code. + */ + public static int errorConstantToExitStatus(final int ec) { + // TODO Allocate a range of exit status to indicate classes of errors. + // For a great example of potential classes see: https://github.com/tlaplus/tlaplus/pull/308#discussion_r285840112 + switch (ec) { + case NO_ERROR: + return SUCCESS; + + // failures + + case TLC_LIVE_FORMULA_TAUTOLOGY: + return FAILURE_LIVENESS_EVAL; + + case TLC_STATE_NOT_COMPLETELY_SPECIFIED_NEXT: + case TLC_STATES_AND_NO_NEXT_ACTION: + case TLC_NESTED_EXPRESSION: + case TLC_FINGERPRINT_EXCEPTION: + return FAILURE_SPEC_EVAL; + + case TLC_INVARIANT_EVALUATION_FAILED: + case TLC_INVARIANT_VIOLATED_LEVEL: + return FAILURE_SAFETY_EVAL; + + // violations + + case TLC_INVARIANT_VIOLATED_INITIAL: + case TLC_INVARIANT_VIOLATED_BEHAVIOR: + return VIOLATION_SAFETY; + + case TLC_ACTION_PROPERTY_VIOLATED_BEHAVIOR: + case TLC_ACTION_PROPERTY_EVALUATION_FAILED: + case TLC_TEMPORAL_PROPERTY_VIOLATED: + case TLC_PROPERTY_VIOLATED_INITIAL: + return VIOLATION_LIVENESS; + + case TLC_DEADLOCK_REACHED: + return VIOLATION_DEADLOCK; + + case TLC_ASSUMPTION_FALSE: + case TLC_ASSUMPTION_EVALUATION_ERROR: + return VIOLATION_ASSUMPTION; + + case TLC_VALUE_ASSERT_FAILED: + return VIOLATION_ASSERT; + + // errors + case TLC_CONFIG_VALUE_NOT_ASSIGNED_TO_CONSTANT_PARAM: + case TLC_CONFIG_RHS_ID_APPEARED_AFTER_LHS_ID: + case TLC_CONFIG_WRONG_SUBSTITUTION: + case TLC_CONFIG_WRONG_SUBSTITUTION_NUMBER_OF_ARGS: + case TLC_CONFIG_UNDEFINED_OR_NO_OPERATOR: + case TLC_CONFIG_SUBSTITUTION_NON_CONSTANT: + case TLC_CONFIG_ID_DOES_NOT_APPEAR_IN_SPEC: + case TLC_CONFIG_NOT_BOTH_SPEC_AND_INIT: + case TLC_CONFIG_ID_REQUIRES_NO_ARG: + case TLC_CONFIG_SPECIFIED_NOT_DEFINED: + case TLC_CONFIG_ID_HAS_VALUE: + case TLC_CONFIG_MISSING_INIT: + case TLC_CONFIG_MISSING_NEXT: + case TLC_CONFIG_ID_MUST_NOT_BE_CONSTANT: + case TLC_CONFIG_OP_NO_ARGS: + case TLC_CONFIG_OP_NOT_IN_SPEC: + case TLC_CONFIG_OP_IS_EQUAL: + case TLC_CONFIG_SPEC_IS_TRIVIAL: + case TLC_CANT_HANDLE_SUBSCRIPT: + case TLC_CANT_HANDLE_CONJUNCT: + case TLC_CANT_HANDLE_TOO_MANY_NEXT_STATE_RELS: + case TLC_CONFIG_PROPERTY_NOT_CORRECTLY_DEFINED: + case TLC_CONFIG_OP_ARITY_INCONSISTENT: + case TLC_CONFIG_NO_STATE_TYPE: + case TLC_CANT_HANDLE_REAL_NUMBERS: // might also be in the spec + case TLC_NO_MODULES: + return ERROR_CONFIG_PARSE; + + case TLC_PARSING_FAILED2: + case TLC_PARSING_FAILED: + return ERROR_SPEC_PARSE; + + default: + return 255; + } + } + + private static final Set<Integer> knownExitValues = Stream.of(SUCCESS, FAILURE_LIVENESS_EVAL, FAILURE_SPEC_EVAL, + FAILURE_SAFETY_EVAL, VIOLATION_SAFETY, VIOLATION_LIVENESS, VIOLATION_DEADLOCK, VIOLATION_ASSUMPTION, + VIOLATION_ASSERT, ERROR_CONFIG_PARSE, ERROR_SPEC_PARSE).collect(Collectors.toSet()); + + public static boolean exitStatusToCrash(final int exitStatus) { + return !knownExitValues.contains(exitStatus); + } + } +} diff --git a/tlatools/src/tlc2/output/MP.java b/tlatools/src/tlc2/output/MP.java index 593529e5ecb2981b7f27b3574abc67be9449dd1d..0d1299c019fa4c9b037f3192f7b52bf5aff227f2 100644 --- a/tlatools/src/tlc2/output/MP.java +++ b/tlatools/src/tlc2/output/MP.java @@ -12,8 +12,12 @@ import tlc2.tool.TLCState; import tlc2.tool.TLCStateInfo; import tlc2.tool.liveness.LiveWorker; import tlc2.util.statistics.IBucketStatistics; +import util.Assert; +import util.Assert.TLCRuntimeException; import util.DebugPrinter; import util.Set; +import util.TLAConstants; +import util.TLAFlightRecorder; import util.ToolIO; /** @@ -76,6 +80,82 @@ import util.ToolIO; */ public class MP { + // https://stackoverflow.com/a/45444716/6291195 + public static class Colors { + // Reset + public static final String RESET = "\033[0m"; // Text Reset + + // Regular Colors + public static final String BLACK = "\033[0;30m"; // BLACK + public static final String RED = "\033[0;31m"; // RED + public static final String GREEN = "\033[0;32m"; // GREEN + public static final String YELLOW = "\033[0;33m"; // YELLOW + public static final String BLUE = "\033[0;34m"; // BLUE + public static final String PURPLE = "\033[0;35m"; // PURPLE + public static final String CYAN = "\033[0;36m"; // CYAN + public static final String WHITE = "\033[0;37m"; // WHITE + + // Bold + public static final String BLACK_BOLD = "\033[1;30m"; // BLACK + public static final String RED_BOLD = "\033[1;31m"; // RED + public static final String GREEN_BOLD = "\033[1;32m"; // GREEN + public static final String YELLOW_BOLD = "\033[1;33m"; // YELLOW + public static final String BLUE_BOLD = "\033[1;34m"; // BLUE + public static final String PURPLE_BOLD = "\033[1;35m"; // PURPLE + public static final String CYAN_BOLD = "\033[1;36m"; // CYAN + public static final String WHITE_BOLD = "\033[1;37m"; // WHITE + + // Underline + public static final String BLACK_UNDERLINED = "\033[4;30m"; // BLACK + public static final String RED_UNDERLINED = "\033[4;31m"; // RED + public static final String GREEN_UNDERLINED = "\033[4;32m"; // GREEN + public static final String YELLOW_UNDERLINED = "\033[4;33m"; // YELLOW + public static final String BLUE_UNDERLINED = "\033[4;34m"; // BLUE + public static final String PURPLE_UNDERLINED = "\033[4;35m"; // PURPLE + public static final String CYAN_UNDERLINED = "\033[4;36m"; // CYAN + public static final String WHITE_UNDERLINED = "\033[4;37m"; // WHITE + + // Background + public static final String BLACK_BACKGROUND = "\033[40m"; // BLACK + public static final String RED_BACKGROUND = "\033[41m"; // RED + public static final String GREEN_BACKGROUND = "\033[42m"; // GREEN + public static final String YELLOW_BACKGROUND = "\033[43m"; // YELLOW + public static final String BLUE_BACKGROUND = "\033[44m"; // BLUE + public static final String PURPLE_BACKGROUND = "\033[45m"; // PURPLE + public static final String CYAN_BACKGROUND = "\033[46m"; // CYAN + public static final String WHITE_BACKGROUND = "\033[47m"; // WHITE + + // High Intensity + public static final String BLACK_BRIGHT = "\033[0;90m"; // BLACK + public static final String RED_BRIGHT = "\033[0;91m"; // RED + public static final String GREEN_BRIGHT = "\033[0;92m"; // GREEN + public static final String YELLOW_BRIGHT = "\033[0;93m"; // YELLOW + public static final String BLUE_BRIGHT = "\033[0;94m"; // BLUE + public static final String PURPLE_BRIGHT = "\033[0;95m"; // PURPLE + public static final String CYAN_BRIGHT = "\033[0;96m"; // CYAN + public static final String WHITE_BRIGHT = "\033[0;97m"; // WHITE + + // Bold High Intensity + public static final String BLACK_BOLD_BRIGHT = "\033[1;90m"; // BLACK + public static final String RED_BOLD_BRIGHT = "\033[1;91m"; // RED + public static final String GREEN_BOLD_BRIGHT = "\033[1;92m"; // GREEN + public static final String YELLOW_BOLD_BRIGHT = "\033[1;93m";// YELLOW + public static final String BLUE_BOLD_BRIGHT = "\033[1;94m"; // BLUE + public static final String PURPLE_BOLD_BRIGHT = "\033[1;95m";// PURPLE + public static final String CYAN_BOLD_BRIGHT = "\033[1;96m"; // CYAN + public static final String WHITE_BOLD_BRIGHT = "\033[1;97m"; // WHITE + + // High Intensity backgrounds + public static final String BLACK_BACKGROUND_BRIGHT = "\033[0;100m";// BLACK + public static final String RED_BACKGROUND_BRIGHT = "\033[0;101m";// RED + public static final String GREEN_BACKGROUND_BRIGHT = "\033[0;102m";// GREEN + public static final String YELLOW_BACKGROUND_BRIGHT = "\033[0;103m";// YELLOW + public static final String BLUE_BACKGROUND_BRIGHT = "\033[0;104m";// BLUE + public static final String PURPLE_BACKGROUND_BRIGHT = "\033[0;105m"; // PURPLE + public static final String CYAN_BACKGROUND_BRIGHT = "\033[0;106m"; // CYAN + public static final String WHITE_BACKGROUND_BRIGHT = "\033[0;107m"; // WHITE + } + /** * */ @@ -83,15 +163,15 @@ public class MP /** * */ - private static final String CR = "\n"; + public static final String CR = TLAConstants.CR; /** * */ - private static final String SPACE = " "; + public static final String SPACE = TLAConstants.SPACE; /** * */ - public static final String COLON = ":"; + public static final String COLON = TLAConstants.COLON; public static final String DELIM = "@!@!@"; //$NON-NLS-1$ public static final String STARTMSG = "STARTMSG "; //$NON-NLS-1$ @@ -128,7 +208,7 @@ public class MP private static MP instance = null; private static MPRecorder recorder = new MPRecorder(); - private Set warningHistory; + private final Set warningHistory; private static final String CONFIG_FILE_ERROR = "TLC found an error in the configuration file at line %1%\n"; private static final SimpleDateFormat SDF = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); //$NON-NLS-1$ private static final DecimalFormat df = new DecimalFormat("###,###.###"); @@ -164,15 +244,13 @@ public class MP * @param parameters string of parameters to be replaced in the message * @return the formatted message */ - private synchronized static String getMessage(int messageClass, int messageCode, String[] parameters) + public synchronized static String getMessage(int messageClass, int messageCode, String[] parameters) { if (parameters == null) { parameters = EMPTY_PARAMS; } - OutputCollector.saveMessage(messageClass, messageCode, parameters); - DebugPrinter.print("entering MP.getMessage() with error code " + messageCode + " and " + parameters.length //$NON-NLS-1$ + " parameters"); //$NON-NLS-1$ @@ -210,7 +288,42 @@ public class MP break; } } - // fill with different message depending on the error code + + b.append(getMessage0(messageClass, messageCode, parameters)); + + if (TLCGlobals.tool) + { + // for the tool we always print the message code + b.append(CR).append(DELIM).append(ENDMSG).append(messageCode).append(SPACE).append(DELIM); + } else + { + + // post processing + switch (messageClass) { + case WARNING: + if (instance.warningHistory.isEmpty()) { + b.append("\n(Use the -nowarning option to disable this warning.)"); + } + break; + case ERROR: + if (TLCGlobals.tool) + { + b.append("\n--End Error."); + } + break; + case TLCBUG: + case NONE: + default: + break; + } + } + DebugPrinter.print("Leaving getMessage()"); //$NON-NLS-1$ + return b.toString(); + } + + private static String getMessage0(int messageClass, int messageCode, String[] parameters) { + final StringBuffer b = new StringBuffer(); + // fill with different message depending on the error code switch (messageCode) { case EC.UNIT_TEST: b.append("[%1%][%2%]"); @@ -226,6 +339,11 @@ public class MP + "pool (heap) may fix this. But it won't help if some state has an enormous\n" + "number of successor states, or if TLC must compute the value of a huge set."); break; + case EC.SYSTEM_OUT_OF_MEMORY_LIVENESS: + b.append("Java ran out of memory during liveness checking. Running Java with a larger memory\n" + + "allocation pool (heap) may fix this. But it won't help if paths in the liveness graph\n" + + "have an enormous number of states."); + break; case EC.SYSTEM_OUT_OF_MEMORY_TOO_MANY_INIT: b.append("Out Of Memory. There are probably too many initial states."); break; @@ -247,6 +365,13 @@ public class MP case EC.SYSTEM_ERROR_WRITING_POOL: b.append("when writing the disk (StatePoolWriter.run):\n%1%"); break; + case EC.SYSTEM_ERROR_CLEANING_POOL: + if (messageClass == ERROR) { + b.append("Exception cleaning up an obsolete disk file.\n%1%"); + } else if (messageClass == WARNING) { + b.append("Failed to clean up an obsolete disk file. Please manually delete %1% if free disk space is low."); + } + break; case EC.SYSTEM_DISKGRAPH_ACCESS: b.append("DiskGraph.toString()"); @@ -294,9 +419,6 @@ public class MP b.append("%1%\nUsage: java tlc2.Simulator [-option] inputfile"); break; /* ----------------------------------------------------------------- */ - case EC.TLC_USAGE: - b.append(Messages.getString("HelpMessage"));// $NON-NLS-1$ - break; case EC.TLC_VERSION: b.append("TLC2 %1%"); break; @@ -341,7 +463,7 @@ public class MP b.append("Evaluating assumption %1% failed.\n%2%"); break; case EC.TLC_STATE_NOT_COMPLETELY_SPECIFIED_INITIAL: - b.append("State is not completely specified by the " + "initial predicate:\n%2%"); + b.append("State is not completely specified by the initial predicate:\n%1%"); break; case EC.TLC_INVARIANT_VIOLATED_INITIAL: b.append("Invariant %1% is violated by the initial state:\n%2%"); @@ -351,14 +473,35 @@ public class MP break; case EC.TLC_STATE_NOT_COMPLETELY_SPECIFIED_NEXT: - b.append("Successor state is not completely specified by the" + " next-state action.\n"); + if (parameters.length == 3) { + b.append( + "Successor state is not completely specified by action %1% of the next-state relation. The following variable%2% not assigned: %3%.\n"); + } else if (parameters.length == 2) { + b.append( + "Successor state is not completely specified by the next-state action. The following variable%1% not assigned: %2%.\n"); + } else { + b.append("Successor state is not completely specified by the next-state action.\n"); + } break; case EC.TLC_INVARIANT_VIOLATED_BEHAVIOR: b.append("Invariant %1% is violated."); break; + case EC.TLC_INVARIANT_VIOLATED_LEVEL: + b.append("The invariant %1% is not a state predicate (one with no primes or temporal operators)."); + if (parameters.length > 1) { + b.append("\nNote that a bug can cause TLC to incorrectly report this error.\n" + + "If you believe your TLA+ or PlusCal specification to be correct,\n" + + "please check if this bug described in LevelNode.java starting at line 590ff affects you."); + } + break; case EC.TLC_INVARIANT_EVALUATION_FAILED: - b.append("Evaluating invariant %1% failed:\n%2%"); + if (parameters.length == 1) { + b.append("Evaluating invariant %1% failed."); + } + else if (parameters.length == 2) { + b.append("Evaluating invariant %1% failed.\n%2%"); + } break; case EC.TLC_ACTION_PROPERTY_VIOLATED_BEHAVIOR: @@ -366,7 +509,12 @@ public class MP break; case EC.TLC_ACTION_PROPERTY_EVALUATION_FAILED: - b.append("Evaluating action property %1% failed."); + if (parameters.length == 1) { + b.append("Evaluating action property %1% failed."); + } + else if (parameters.length == 2) { + b.append("Evaluating action property %1% failed.\n%2%"); + } break; case EC.TLC_DEADLOCK_REACHED: @@ -380,6 +528,9 @@ public class MP break; // this is a TLC bug + case EC.TLC_STATES_AND_NO_NEXT_ACTION: + b.append("No next state actions defined to generate successor states from."); + break; case EC.TLC_FAILED_TO_RECOVER_NEXT: b.append("Failed to recover the next state from its fingerprint."); break; @@ -392,6 +543,16 @@ public class MP b.append("This is probably a TLC bug(%1%)."); break; + case EC.TLC_FINGERPRINT_EXCEPTION: + b.append("TLC was unable to fingerprint." + + "\n" + + "\nFingerprint Stack Trace:" + + "\n%1%" + + "\nReason:" + + "\n%2%"); // reason must come last, with nothing following, because Tool cannot parse the embedded error otherwise + + break; + case EC.TLC_NO_STATES_SATISFYING_INIT: b.append("There is no state satisfying the initial state predicate."); break; @@ -404,9 +565,19 @@ public class MP if (TLCGlobals.tool) { // format same as state printing for easier // parsing by toolbox - b.append("%1%: Back to state.\n"); + if (parameters.length == 1) { + b.append("%1%: ").append(TLAConstants.BACK_TO_STATE).append("\n"); + } + else if (parameters.length == 2) { + b.append("%1%: ").append(TLAConstants.BACK_TO_STATE).append(": %2%\n"); + } } else { - b.append("Back to state %1%.\n"); + if (parameters.length == 1) { + b.append(TLAConstants.BACK_TO_STATE).append(" %1%\n"); + } + else if (parameters.length == 2) { + b.append(TLAConstants.BACK_TO_STATE).append(" %1%: %2%\n"); + } } break; @@ -487,7 +658,12 @@ public class MP break; case EC.CHECK_PARAM_UNRECOGNIZED: - b.append("Unrecognized option: %1%"); + if (parameters.length == 1) { + b.append("Unrecognized option: %1%"); + } + else if (parameters.length == 2) { + b.append("Unrecognized option in file %2%: %1%"); + } break; case EC.CHECK_PARAM_TOO_MANY_INPUT_FILES: b.append("More than one input files: %1% and %2%"); @@ -545,6 +721,14 @@ public class MP case EC.TLC_EXCEPT_APPLIED_TO_UNKNOWN_FIELD: b.append("The EXCEPT was applied to non-existing fields of the value at\n%1%"); break; + + case EC.TLC_SYMMETRY_SET_TOO_SMALL: + b.append("The set%1% %2% %3% been defined to be a symmetry set but contain%4% less than two elements."); + break; + + case EC.TLC_SPECIFICATION_FEATURES_TEMPORAL_QUANTIFIER: + b.append("TLC does not support temporal existential, nor universal, quantification over state variables."); + break; /* ************************************************************************ */ case EC.TLC_MODULE_TLCGET_UNDEFINED: b.append("TLCGet(%1%) was undefined."); @@ -554,6 +738,10 @@ public class MP b.append("Overflow when computing %1%"); break; + case EC.TLC_MODULE_ONE_ARGUMENT_ERROR: + b.append("The argument of %1% should be a %2%, but instead it is:\n%3%"); + break; + case EC.TLC_MODULE_ARGUMENT_ERROR: b.append("The %1% argument of %2% should be a %3%, but instead it is:\n%4%"); break; @@ -592,15 +780,15 @@ public class MP break; case EC.TLC_MODULE_BAG_UNION1: - b.append("Attemtped to apply BagUnion to the following set, whose\nelement is not a bag:\n%1%"); + b.append("Attempted to apply BagUnion to the following set, whose\nelement is not a bag:\n%1%"); break; case EC.TLC_MODULE_TRANSITIVE_CLOSURE: - b.append("Attemtped to apply TransitiveClosure to a set containing\nthe following value:\n%1%"); + b.append("Attempted to apply TransitiveClosure to a set containing\nthe following value:\n%1%"); break; case EC.TLC_MODULE_COMPUTING_CARDINALITY: - b.append("Attemtped to compute cardinality of the value\n%1%"); + b.append("Attempted to compute cardinality of the value\n%1%"); break; case EC.TLC_MODULE_EVALUATING: b.append("Evaluating an expression of the form %1% when s is not a %2%:\n%3%"); @@ -610,6 +798,37 @@ public class MP b.append("Attempted to apply the operator overridden by the Java method" + "\n%1%,\nbut it produced the following error:\n%2%"); break; + case EC.TLC_MODULE_VALUE_JAVA_METHOD_OVERRIDE_LOADED: + b.append("Loading %1% operator override from %2% with signature: %3%."); + break; + case EC.TLC_MODULE_VALUE_JAVA_METHOD_OVERRIDE_MISMATCH: + b.append("Failed to match %1% operator override from %2% with signature: %3%."); + break; + case EC.TLC_MODULE_VALUE_JAVA_METHOD_OVERRIDE_IDENTIFIER_MISMATCH: + b.append("Failed to match %1% operator override from %2% with signature: %3% (no such operator)."); + break; + case EC.TLC_MODULE_VALUE_JAVA_METHOD_OVERRIDE_MODULE_MISMATCH: + b.append("Failed to match %1% operator override from %2% with signature: %3% (no such module)."); + break; + case EC.TLC_MODULE_OVERRIDE_STDOUT: + b.append("%1%"); + break; + case EC.TLC_FEATURE_UNSUPPORTED: + b.append("%1%"); + break; + case EC.TLC_FEATURE_UNSUPPORTED_LIVENESS_SYMMETRY: + b.append("Declaring symmetry during liveness checking is dangerous. " + + "It might cause TLC to miss violations of the stated liveness properties. " + + "Please check liveness without symmetry defined."); + break; + case EC.TLC_FEATURE_LIVENESS_CONSTRAINTS: + // Specifying Systems Section 14.3.5 page 247. + // https://lamport.azurewebsites.net/tla/book.html + b.append("Declaring state or action constraints during liveness checking is dangerous: " + + "Please read section 14.3.5 on page 247 of Specifying Systems " + + "(https://lamport.azurewebsites.net/tla/book.html) and optionally the " + + "discussion at https://discuss.tlapl.us/msg00994.html for more details."); + break; /* Liveness errors */ case EC.TLC_LIVE_BEGRAPH_FAILED_TO_CONSTRUCT: @@ -619,7 +838,11 @@ public class MP b.append("Implied-temporal checking--satisfiability problem has %1% branches."); break; case EC.TLC_LIVE_CANNOT_HANDLE_FORMULA: - b.append("TLC cannot handle the temporal formula %1%"); + if (parameters.length > 1) { + b.append("TLC cannot handle the temporal formula %1%:\n%2%"); + } else { + b.append("TLC cannot handle the temporal formula %1%"); + } break; case EC.TLC_LIVE_WRONG_FORMULA_FORMAT: b.append("Temporal formulas containing actions must be of forms <>[]A or []<>A."); @@ -636,6 +859,9 @@ public class MP case EC.TLC_LIVE_ENCOUNTERED_NONBOOL_PREDICATE: b.append("Encountered an action predicate that's not a boolean."); break; + case EC.TLC_LIVE_FORMULA_TAUTOLOGY: + b.append("Temporal formula is a tautology (its negation is unsatisfiable)."); + break; case EC.TLC_EXPECTED_VALUE: b.append("TLC expected a %1% value, but did not find one. %2%"); @@ -688,90 +914,139 @@ public class MP /*------------------------------------------- */ // TLC distributed case EC.TLC_DISTRIBUTED_SERVER_RUNNING: - b.append("TLC server at %1% is ready (").append(SDF.format(new Date())).append(")"); + b.append("TLC server at %1% is ready (").append(now()).append(")"); break; case EC.TLC_DISTRIBUTED_WORKER_REGISTERED: - b.append("Registration for worker at %1% completed (").append(SDF.format(new Date())).append(")"); + b.append("Registration for worker at %1% completed (").append(now()).append(")"); break; case EC.TLC_DISTRIBUTED_WORKER_DEREGISTERED: - b.append("TLC worker %1% disconnected (").append(SDF.format(new Date())).append(")"); + b.append("TLC worker %1% disconnected (").append(now()).append(")"); break; case EC.TLC_DISTRIBUTED_WORKER_STATS: //new Date() + " Worker: " + name + " Sent: " + sentStates + " Rcvd: " + receivedStates - b.append("Worker: %1% Sent: %2% Rcvd: %3% CacheRatio: %4% (").append(SDF.format(new Date())).append(")"); + b.append("Worker: %1% Sent: %2% Rcvd: %3% CacheRatio: %4% (").append(now()).append(")"); break; case EC.TLC_DISTRIBUTED_SERVER_NOT_RUNNING: - b.append("TLCServer is gone due to %1%, exiting worker... (").append(SDF.format(new Date())).append(")"); + b.append("TLCServer is gone due to %1%, exiting worker... (").append(now()).append(")"); break; case EC.TLC_DISTRIBUTED_SERVER_FINISHED: - b.append("TLCServer has finished, exiting worker... (").append(SDF.format(new Date())).append(")"); + b.append("TLCServer has finished, exiting worker... (").append(now()).append(")"); break; case EC.TLC_DISTRIBUTED_VM_VERSION: b.append( "VM does not allow to get the UnicastRef port.\nWorker will be identified with port 0 in output (") - .append(SDF.format(new Date())).append(")"); + .append(now()).append(")"); break; case EC.TLC_DISTRIBUTED_WORKER_LOST: - b.append("TLC worker connection lost %1% (").append(SDF.format(new Date())).append(")"); + b.append("TLC worker connection lost %1% (").append(now()).append(")"); break; case EC.TLC_DISTRIBUTED_EXCEED_BLOCKSIZE: - b.append("Trying to limit max block size (to recover from transport failure): %1% (").append(SDF.format(new Date())).append(")"); + b.append("Trying to limit max block size (to recover from transport failure): %1% (").append(now()).append(")"); break; case EC.TLC_DISTRIBUTED_SERVER_FPSET_REGISTERED: - b.append("%1% out of %2% FPSet server(s) registered (").append(SDF.format(new Date())).append(")"); + b.append("%1% out of %2% FPSet server(s) registered (").append(now()).append(")"); break; case EC.TLC_DISTRIBUTED_SERVER_FPSET_WAITING: - b.append("Waiting for %1% FPSet server(s) to register (").append(SDF.format(new Date())).append(")"); + b.append("Waiting for %1% FPSet server(s) to register (").append(now()).append(")"); break; /*------------------------------------------- */ case EC.TLC_STARTING: - b.append("Starting... (").append(SDF.format(new Date())).append(")"); + b.append("Starting... (").append(now()).append(")"); break; case EC.TLC_FINISHED: - b.append("Finished. (").append(SDF.format(new Date())).append(")"); - break; + b.append("Finished in %1% at (").append(now()).append(")"); + break; + /* + * The two startup banners below are parsed by the Toolbox in + * org.lamport.tla.toolbox.tool.tlc.output.data.TLCModelLaunchDataProvider. + * startupMessagePattern. Remember to update when the banners below + * get changed + * (see org.lamport.tla.toolbox.tool.tlc.output.data.TLCModelLaunchDataProviderTest)!!! + */ case EC.TLC_MODE_MC: - b.append("Running in Model-Checking mode."); + b.append("Running breadth-first search Model-Checking with fp %13% and seed %12% with %1% worker%2% on %3% cores with %10%MB heap and %11%MB offheap memory"); + if (!"".equals(parameters[13])) { + b.append(" [pid: %14%]"); + } else { + // Make sure subsequent parameters, %15%... are processed below in the + // replaceString method. replaceString terminates if a string is not present, + // thus we replace %14% with the zero-length string to not change the output but + // to make replaceString happy. + b.append("%14%"); + } + b.append(" (%4% %5% %6%, %7% %8% %9%, %15%, %16%)."); + break; + case EC.TLC_MODE_MC_DFS: + b.append("Running depth-first search Model-Checking with fp %13% and seed %12% with %1% worker%2% on %3% cores with %10%MB heap and %11%MB offheap memory"); + if (!"".equals(parameters[13])) { + b.append(" [pid: %14%]"); + } else { + // see TLC_MODE_MC above. + b.append("%14%"); + } + b.append(" (%4% %5% %6%, %7% %8% %9%)."); break; case EC.TLC_MODE_SIMU: - b.append("Running Random Simulation with seed %1%."); + b.append("Running Random Simulation with seed %1% with %2% worker%3% on %4% cores with %11%MB heap and %12%MB offheap memory"); + if (!"".equals(parameters[12])) { + b.append(" [pid: %13%]"); + } else { + // see TLC_MODE_MC above. + b.append("%13%"); + } + b.append(" (%5% %6% %7%, %8% %9% %10%)."); break; case EC.TLC_COMPUTING_INIT: b.append("Computing initial states..."); break; + case EC.TLC_COMPUTING_INIT_PROGRESS: + b.append("Computed %1% initial states..."); + break; case EC.TLC_INIT_GENERATED1: - b.append("Finished computing initial states: %1% distinct state%2% generated."); + b.append("Finished computing initial states: %1% distinct state%2% generated at ") + .append(now()).append("."); break; case EC.TLC_INIT_GENERATED2: - b.append("Finished computing initial states: %1% states generated, with %2% of them distinct."); + b.append("Finished computing initial states: %1% state%2% generated, with %3% of them distinct at ") + .append(now()).append("."); break; case EC.TLC_INIT_GENERATED3: - b.append("Finished computing initial states: %1% states generated.\n" - + "Because TLC recovers from a previous checkpoint, only %2% of them require further exploration."); + b.append("Finished computing initial states: %1% states generated.\n" + + "Because TLC recovers from a previous checkpoint, only %2% of them require further exploration at ") + .append(now()).append("."); break; case EC.TLC_INIT_GENERATED4: b.append("Finished computing initial states: %1% states generated, with %2% of them distinct."); break; case EC.TLC_CHECKING_TEMPORAL_PROPS: - b.append("Checking temporal properties for the %1% state space with %2% distinct states at (") - .append(SDF.format(new Date())).append(")"); + b.append("Checking %3%temporal properties for the %1% state space with %2% total distinct states at (") + .append(now()).append(")"); break; - + case EC.TLC_CHECKING_TEMPORAL_PROPS_END: + b.append("Finished checking temporal properties in %1% at " + now()); + break; case EC.TLC_SUCCESS: b.append("Model checking completed. No error has been found.\n" - + " Estimates of the probability that TLC did not check all reachable states\n" - + " because two distinct states had the same fingerprint:\n" + " calculated (optimistic): %1%\n" - + " based on the actual fingerprints: %2%"); + + " Estimates of the probability that TLC did not check all reachable states\n"); + if (parameters.length == 1) { + b.append(" because two distinct states had the same fingerprint:\n" + " calculated (optimistic): %1%"); + } else { + b.append(" because two distinct states had the same fingerprint:\n" + + " calculated (optimistic): %1%\n" + " based on the actual fingerprints: %2%"); + } break; case EC.TLC_SEARCH_DEPTH: - b.append("The depth of the complete state graph search is %1%."); + b.append("The depth of the complete state graph search is %1%."); + break; + case EC.TLC_STATE_GRAPH_OUTDEGREE: + b.append("The average outdegree of the complete state graph is %2% (minimum is %1%, the maximum %4% and the 95th percentile is %3%)."); break; - case EC.TLC_CHECKPOINT_START: + case EC.TLC_CHECKPOINT_START: b.append("Checkpointing of run %1%"); break; case EC.TLC_CHECKPOINT_END: - b.append("Checkpointing completed at (").append(SDF.format(new Date())).append(")"); + b.append("Checkpointing completed at (").append(now()).append(")"); break; case EC.TLC_CHECKPOINT_RECOVER_START: b.append("Starting recovery from checkpoint %1%"); @@ -793,9 +1068,13 @@ public class MP b.append("The number of states generated: %1%\nSimulation using seed %2% and aril %3%"); break; case EC.TLC_PROGRESS_STATS: - b.append("Progress(%1%) at " + SDF.format(new Date()) + ": %2% states generated (" - + df.format(Long.valueOf(parameters[4])) + " s/min), %3% distinct states found (" - + df.format(Long.valueOf(parameters[5])) + " ds/min), %4% states left on queue."); + if (parameters.length == 4) { + b.append("Progress(%1%) at " + now() + ": %2% states generated, " + + "%3% distinct states found, " + "%4% states left on queue."); + } else if (parameters.length == 6) { + b.append("Progress(%1%) at " + now() + ": %2% states generated (" + + "%5% s/min), %3% distinct states found (%6% ds/min), %4% states left on queue."); + } break; case EC.TLC_PROGRESS_START_STATS_DFID: b.append("Starting level %1%: %2% states generated, %3% distinct states found."); @@ -804,7 +1083,7 @@ public class MP if (TLCGlobals.tool) { // same format as model checking progress reporting for easier parsing by the toolbox - b.append("Progress(" + NOT_APPLICABLE_VAL + ") at " + SDF.format(new Date()) + b.append("Progress(" + NOT_APPLICABLE_VAL + ") at " + now() + ": %1% states generated, %2% distinct states found, " + NOT_APPLICABLE_VAL + " states left on queue."); } else @@ -816,7 +1095,7 @@ public class MP if (TLCGlobals.tool) { // same format as model checking progress reporting for easier parsing by the toolbox - b.append("Progress(" + NOT_APPLICABLE_VAL + ") at " + SDF.format(new Date()) + b.append("Progress(" + NOT_APPLICABLE_VAL + ") at " + now() + ": %1% states generated, " + NOT_APPLICABLE_VAL + " distinct states found, " + NOT_APPLICABLE_VAL + " states left on queue."); } else @@ -826,15 +1105,34 @@ public class MP break; case EC.TLC_COVERAGE_START: - b.append("The coverage statistics at " + SDF.format(new Date())); + b.append("The coverage statistics at " + now()); break; case EC.TLC_COVERAGE_VALUE: b.append(" %1%: %2%"); break; - + case EC.TLC_COVERAGE_VALUE_COST: + b.append(" %1%: %2%:%3%"); + break; + case EC.TLC_COVERAGE_INIT: + b.append("%1%: %2%:%3%"); + break; + case EC.TLC_COVERAGE_NEXT: + b.append("%1%: %2%:%3%"); + break; + case EC.TLC_COVERAGE_PROPERTY: + b.append("%1%"); + break; + case EC.TLC_COVERAGE_MISMATCH: + b.append( + "CostModel lookup failed for expression <%1%>. Reporting costs into <%2%> instead (Safety and Liveness checking is unaffected. Please report a bug.)"); + break; case EC.TLC_COVERAGE_END: b.append("End of statistics."); break; + case EC.TLC_COVERAGE_END_OVERHEAD: + b.append("End of statistics (please note that for performance reasons large models\n" + + "are best checked with coverage and cost statistics disabled)."); + break; /* ************************************************************************ */ // errors evaluating the config file and the MC file @@ -848,6 +1146,12 @@ public class MP case EC.TLC_CONFIG_WRONG_SUBSTITUTION: b.append("The configuration file substitutes for %1% with the undefined identifier %2%."); break; + case EC.TLC_CONFIG_UNDEFINED_OR_NO_OPERATOR: + b.append("In evaluation, the identifier %1% is either undefined or not an operator.\n%2%"); + break; + case EC.TLC_CONFIG_SUBSTITUTION_NON_CONSTANT: + b.append("The configuration file substitutes constant %1% with non-constant %2%%3%"); + break; case EC.TLC_CONFIG_WRONG_SUBSTITUTION_NUMBER_OF_ARGS: b.append("The configuration file substitutes for %1% with %2% of different number of arguments."); break; @@ -858,7 +1162,12 @@ public class MP b.append("The configuration file cannot specify both INIT/NEXT and SPECIFICATION fields."); break; case EC.TLC_CONFIG_ID_REQUIRES_NO_ARG: - b.append("TLC requires %1% not to take any argument."); + if (parameters.length == 1) { + b.append("TLC requires %1% not to take any argument."); + } + else if (parameters.length == 2) { + b.append("TLC requires %1% not to take any argument, but one was given: %2%"); + } break; case EC.TLC_CONFIG_SPECIFIED_NOT_DEFINED: b.append("The %1% %2% specified in the configuration file" + "\nis not defined in the specification."); @@ -868,7 +1177,13 @@ public class MP break; case EC.TLC_CONFIG_MISSING_INIT: b.append("The configuration file did not specify the initial state predicate." + - // Following part of error message added by LL on 15 Nov 2012 + // The below part of the error message was added by LL on 15 Nov 2012 + // + // ldq, 13 Feb 2020: I don't think this is semantically correct; I receive + // no errors when defining a specification that references + // a formula which is a parameterized INSTANCE. I *do* receive + // such an error when that formula is being constrained via + // the temporal existential qualifier. "\nCan also be caused by trying to run TLC on a specification from" + "\na module imported with a parameterized INSTANCE statement."); break; @@ -900,6 +1215,11 @@ public class MP b.append("The specification contains more than one conjunct of the form [][Next]_v," + "\nbut TLC can handle only specifications with one next-state relation."); break; + case EC.TLC_TRACE_TOO_LONG: + b.append("The specification contains one or more behaviors with 65536 or more states," + + "\nbut TLC can only handle behaviors of length up to 65535 states. The last\n" + + "state in the behavior is:\n%1%"); + break; case EC.TLC_CONFIG_PROPERTY_NOT_CORRECTLY_DEFINED: b.append("The property %1% is not correctly defined."); break; @@ -925,6 +1245,10 @@ public class MP case EC.TLC_ENCOUNTERED_FORMULA_IN_PREDICATE: b.append("TLC encountered a temporal formula (%1%) when evaluating" + " a predicate or action.\n%2%"); break; + case EC.TLC_ENVIRONMENT_JVM_GC: + b.append( + "Please run the Java VM which executes TLC with a throughput optimized garbage collector by passing the \"-XX:+UseParallelGC\" property."); + break; /* ************************************************************************ */ // state printing @@ -932,10 +1256,14 @@ public class MP b.append("%1%:\n%2%"); break; case EC.TLC_STATE_PRINT2: - b.append("%1%: %2%\n%3%"); + if (DO_DEBUG) { + b.append("%1%: %2%\n%3%fp: %4%\n"); + } else { + b.append("%1%: %2%\n%3%"); + } break; case EC.TLC_STATE_PRINT3: - b.append("%1%: Stuttering"); + b.append("%1%:").append(TLAConstants.STUTTERING); break; /* ************************************************************************ */ @@ -985,34 +1313,8 @@ public class MP } replaceString(b, parameters); - - if (TLCGlobals.tool) - { - // for the tool we always print the message code - b.append(CR).append(DELIM).append(ENDMSG).append(messageCode).append(SPACE).append(DELIM); - } else - { - - // post processing - switch (messageClass) { - case WARNING: - b.append("\n(Use the -nowarning option to disable this warning.)"); - break; - case ERROR: - if (TLCGlobals.tool) - { - b.append("\n--End Error."); - } - break; - case TLCBUG: - case NONE: - default: - break; - } - } - DebugPrinter.print("Leaving getMessage()"); //$NON-NLS-1$ return b.toString(); - } + } /** * Returns the error @@ -1041,6 +1343,7 @@ public class MP */ public static String getError(int errorCode, String[] parameters) { + recorder.record(errorCode, (Object[]) parameters); return getMessage(ERROR, errorCode, parameters); } @@ -1071,6 +1374,7 @@ public class MP */ public static String getMessage(int errorCode, String[] parameters) { + recorder.record(errorCode, (Object[]) parameters); return getMessage(NONE, errorCode, parameters); } @@ -1088,9 +1392,9 @@ public class MP * Prints the error for a given error code * @param errorCode */ - public static void printError(int errorCode) + public static int printError(int errorCode) { - printError(errorCode, EMPTY_PARAMS); + return printError(errorCode, EMPTY_PARAMS); } /** @@ -1098,9 +1402,9 @@ public class MP * @param errorCode * @param parameter */ - public static void printError(int errorCode, String parameter) + public static int printError(int errorCode, String parameter) { - printError(errorCode, new String[] { parameter }); + return printError(errorCode, new String[] { parameter }); } /** @@ -1109,13 +1413,14 @@ public class MP * @param parameters a list of string parameters to be inserted into the message, by replacing * %i% with the i-th parameter in the array */ - public static void printError(int errorCode, String[] parameters) + public static int printError(int errorCode, String[] parameters) { recorder.record(errorCode, (Object[]) parameters); // write the output DebugPrinter.print("entering printError(int, String[]) with errorCode " + errorCode); //$NON-NLS-1$ ToolIO.out.println(getMessage(ERROR, errorCode, parameters)); DebugPrinter.print("leaving printError(int, String[])"); //$NON-NLS-1$ + return errorCode; } /** @@ -1197,7 +1502,14 @@ public class MP } else { msg = msg + "\nThe error occurred when TLC was " + cause + "."; } - msg = msg + "\nThe exception was a " + throwable.getClass().getName(); + if (throwable instanceof Assert.TLCRuntimeException) { + // MK 07/25/2017: Disguise TLCRuntimeException with its parent class + // RuntimeException to not change the externally visible TLC output + // when an exception gets reports. + msg = msg + "\nThe exception was a " + RuntimeException.class.getName() + "\n"; + } else { + msg = msg + "\nThe exception was a " + throwable.getClass().getName() + "\n"; + } if (throwable.getMessage() != null) { msg = msg + ": " + throwable.getMessage(); @@ -1233,13 +1545,14 @@ public class MP * @param errorCode * @param cause */ - public static void printError(int errorCode, Throwable cause) + public static int printError(int errorCode, Throwable cause) { if (errorCode == EC.GENERAL) { printError(errorCode, "", cause); } else { printError(errorCode, cause.getMessage(), cause, true); } + return errorCode; } /** @@ -1267,15 +1580,31 @@ public class MP * @param parameters a list of string parameters to be inserted into the message, by replacing * %i% with the i-th parameter in the array */ - public static void printMessage(int errorCode, String[] parameters) + public static void printMessage(int errorCode, String... parameters) { recorder.record(errorCode, (Object[]) parameters); DebugPrinter.print("entering printMessage(int, String[]) with errorCode " + errorCode); //$NON-NLS-1$ // write the output - ToolIO.out.println(getMessage(NONE, errorCode, parameters)); + ToolIO.out.println(getMessage(NONE, errorCode, parameters)); + // Don't log the start and end markers when in -tool mode. + TLAFlightRecorder.message(getMessage0(NONE, errorCode, parameters)); DebugPrinter.print("leaving printError(int, String[]) with errorCode "); //$NON-NLS-1$ } + public static int printTLCRuntimeException(final TLCRuntimeException tre) { + if (tre.parameters != null) { + recorder.record(tre.errorCode, (Object[]) new Object[] {tre}); + DebugPrinter.print("entering printTLCRuntimeException(TLCRuntimeException) with errorCode " + tre.errorCode); //$NON-NLS-1$ + // write the output + ToolIO.out.println(getMessage(ERROR, tre.errorCode, tre.parameters)); + DebugPrinter.print("leaving printTLCRuntimeException(TLCRuntimeException) with errorCode "); //$NON-NLS-1$ + } else { + // Legacy code path except actual errorCode instead of EC.General. + printError(tre.errorCode, tre); + } + return tre.errorCode; + } + /** * Prints the state * @param parameters @@ -1311,6 +1640,13 @@ public class MP DebugPrinter.print("leaving printTLCBug(int, String[])"); //$NON-NLS-1$ } + /** + * @see MP#printWarning(int) + */ + public static void printWarning(final int errorCode) { + printWarning(errorCode, new String[0]); + } + /** * @see MP#printWarning(int, String[]) */ @@ -1323,7 +1659,7 @@ public class MP * @param errorCode * @param parameters */ - public static void printWarning(int errorCode, String[] parameters) + public static void printWarning(int errorCode, String... parameters) { recorder.record(errorCode, (Object[]) parameters); DebugPrinter.print("entering printWarning(int, String[]) with errorCode " + errorCode); //$NON-NLS-1$ @@ -1424,4 +1760,18 @@ public class MP public static void setRecorder(MPRecorder aRecorder) { recorder = aRecorder; } + + private static String now() { + if (Boolean.getBoolean(MP.class.getName() + ".noTimestamps")) { + // Return NOW if requested by setting -Dtlc2.output.MP.noTimestamps=true on the + // command-line. Can be useful if one wants to compare TLC's output from + // multiple executions. + return "NOW"; + } + return SDF.format(new Date()); + } + + public static String format(final long l) { + return df.format(l); + } } diff --git a/tlatools/src/tlc2/output/Message.java b/tlatools/src/tlc2/output/Message.java deleted file mode 100644 index 83bbb86372defcbe5d1d1d4d755fe785090d94ca..0000000000000000000000000000000000000000 --- a/tlatools/src/tlc2/output/Message.java +++ /dev/null @@ -1,42 +0,0 @@ -package tlc2.output; - -import java.util.Date; - -public class Message { - - private int messageClass; - private int errorCode; - private String[] parameters; - private Date date; - - public Message(int errorCode, String[] parameters, Date date){ - this.errorCode = errorCode; - this.parameters = parameters; - this.date = date; - } - - public Message(int messageClass, int errorCode, String[] parameters, - Date date) { - this.messageClass = messageClass; - this.errorCode = errorCode; - this.parameters = parameters; - this.date = date; - } - - public int getMessageClass(){ - return messageClass; - } - - public int getMessageCode() { - return errorCode; - } - - public String[] getParameters() { - return parameters; - } - - public Date getDate(){ - return date; - } - -} diff --git a/tlatools/src/tlc2/output/OutputCollector.java b/tlatools/src/tlc2/output/OutputCollector.java deleted file mode 100644 index 6a7db4b5d56329dcd53f597be182766e6bd9983c..0000000000000000000000000000000000000000 --- a/tlatools/src/tlc2/output/OutputCollector.java +++ /dev/null @@ -1,80 +0,0 @@ -package tlc2.output; - -import java.util.ArrayList; -import java.util.Date; -import java.util.Hashtable; - -import tla2sany.semantic.ExprNode; -import tla2sany.semantic.ModuleNode; -import tla2sany.st.Location; -import tlc2.tool.TLCState; -import tlc2.tool.TLCStateInfo; - -public class OutputCollector { - - private static TLCState initialState = null; - private static ArrayList<TLCStateInfo> trace = null; - private static ArrayList<Message> allMessages = new ArrayList<Message>(); - private static Hashtable<Location, Long> lineCount = new Hashtable<Location, Long>(); - private static ModuleNode moduleNode = null; - private static ArrayList<ExprNode> violatedAssumptions = new ArrayList<>(); - - public static ArrayList<TLCStateInfo> getTrace() { - return trace; - } - - public static void setTrace(ArrayList<TLCStateInfo> trace) { - OutputCollector.trace = trace; - } - - public static void addStateToTrace(TLCStateInfo tlcStateInfo) { - if (trace == null) { - trace = new ArrayList<TLCStateInfo>(); - } - trace.add(tlcStateInfo); - } - - public static void setInitialState(TLCState initialState) { - OutputCollector.initialState = initialState; - } - - public static TLCState getInitialState() { - return OutputCollector.initialState; - } - - public static ArrayList<Message> getAllMessages() { - return allMessages; - } - - public static void addViolatedAssumption(ExprNode assumption) { - violatedAssumptions.add(assumption); - } - - public static ArrayList<ExprNode> getViolatedAssumptions() { - return violatedAssumptions; - } - - public synchronized static void saveMessage(int messageClass, - int messageCode, String[] parameters) { - - Message m = new Message(messageClass, messageCode, parameters, - new Date()); - allMessages.add(m); - } - - public static ModuleNode getModuleNode() { - return moduleNode; - } - - public static void setModuleNode(ModuleNode moduleNode) { - OutputCollector.moduleNode = moduleNode; - } - - public static Hashtable<Location, Long> getLineCountTable() { - return new Hashtable<Location, Long>(lineCount); - } - - public static void putLineCount(Location location, long val) { - lineCount.put(location, val); - } -} diff --git a/tlatools/src/tlc2/output/SpecTraceExpressionWriter.java b/tlatools/src/tlc2/output/SpecTraceExpressionWriter.java new file mode 100644 index 0000000000000000000000000000000000000000..38d4cc4e6968030a9c7fa253ca8c8f2a2b1c14f9 --- /dev/null +++ b/tlatools/src/tlc2/output/SpecTraceExpressionWriter.java @@ -0,0 +1,847 @@ +package tlc2.output; + +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +import tlc2.model.Assignment; +import tlc2.model.Formula; +import tlc2.model.MCState; +import tlc2.model.MCVariable; +import tlc2.model.TraceExpressionInformationHolder; +import util.TLAConstants; + +/** + * This is a reified class of spec writer which can produce specs capable of containing trace expressions; it is also + * the parent class for a more specialized version used by the toolbox, {@code TraceExpressionModelWriter}. + */ +public class SpecTraceExpressionWriter extends AbstractSpecWriter { + private static final String TRACE_EXPRESSION_VARIABLE = "TraceExp"; + private static final String TRI_INDENT = TLAConstants.INDENT + TLAConstants.INDENT + TLAConstants.INDENT; + + /** + * This will generate three identifiers equal to the initial and next state + * predicate for the trace, and the action constraint. + * + * @param tlaBuffer the buffer into which the TLA code will be placed + * @param cfgBuffer if non-null, the buffer into which the CFG code will be placed + * @param trace + * @param expressionData data on trace explorer expressions, can be null + * @return String[], first element is the identifier for the initial state predicate, + * second element is the identifier for the next-state action, the third element is the identifier for + * the action contraint + * @see #addInitNextToBuffers(StringBuilder, StringBuilder, List, TraceExpressionInformationHolder[], String, String, String) + */ + public static String[] addInitNextToBuffers(final StringBuilder tlaBuffer, final StringBuilder cfgBuffer, + final List<MCState> trace, final TraceExpressionInformationHolder[] expressionData) { + final String initId = SpecWriterUtilities.getValidIdentifier(TLAConstants.Schemes.INIT_SCHEME); + final String nextId = SpecWriterUtilities.getValidIdentifier(TLAConstants.Schemes.NEXT_SCHEME); + final String actionConstraintId = SpecWriterUtilities.getValidIdentifier(TLAConstants.Schemes.ACTIONCONSTRAINT_SCHEME); + + addInitNextToBuffers(tlaBuffer, cfgBuffer, trace, expressionData, initId, nextId, actionConstraintId); + + return new String[] { initId, nextId, actionConstraintId }; + } + + /** + * This calls: + * {@code addInitNextToBuffers(tlaBuffer, cfgBuffer, trace, expressionData, initId, nextId, actionConstraintId, TLAConstants.Schemes.NEXT_SCHEME, false);} + * + * @param tlaBuffer the buffer into which the TLA code will be placed + * @param cfgBuffer if non-null, the buffer into which the CFG code will be placed + * @param trace + * @param expressionData data on trace explorer expressions, can be null + * @param initId the identifier to be used for the initial state predicate, cannot be null + * @param nextId the identifier to be used for the next-state action, cannot be null + * @param actionConstraintId the indentified used for the action constraint + * @see #addInitNextToBuffers(StringBuilder, StringBuilder, List, TraceExpressionInformationHolder[], String, String, String, String) + */ + public static void addInitNextToBuffers(final StringBuilder tlaBuffer, final StringBuilder cfgBuffer, + final List<MCState> trace, final TraceExpressionInformationHolder[] expressionData, final String initId, + final String nextId, final String actionConstraintId) { + addInitNextToBuffers(tlaBuffer, cfgBuffer, trace, expressionData, initId, nextId, actionConstraintId, + TLAConstants.Schemes.NEXT_SCHEME, false); + } + + /** + * This calls: + * {@code addInitNextToBuffers(cfgBuffer, trace, expressionData, initId, nextId, actionConstraintId, nextSubActionBasename, leaveStubsForTraceExpression);} + * and then concatenates the returned {@code StringBuilder} instances in order to tlaBuffer. + * + * @param tlaBuffer the buffer into which the TLA code will be placed + * @param cfgBuffer if non-null, the buffer into which the CFG code will be placed + * @param trace + * @param expressionData data on trace explorer expressions, can be null + * @param initId the identifier to be used for the initial state predicate, cannot be null + * @param nextId the identifier to be used for the next-state action, cannot be null + * @param actionConstraintId the indentified used for the action constraint + * @param nextSubActionBasename the base string to be used as the prefix to unique names for next sub-actions + * @param leaveStubsForTraceExpression if true, then a variable will be defined {@link TRACE_EXPRESSION_VARIABLE}, + * yet commented out, and similarly conjoined, but commented out in the SpecTE Init and Next + * declarations + */ + public static void addInitNextToBuffers(final StringBuilder tlaBuffer, final StringBuilder cfgBuffer, + final List<MCState> trace, final TraceExpressionInformationHolder[] expressionData, final String initId, + final String nextId, final String actionConstraintId, final String nextSubActionBasename, + final boolean leaveStubsForTraceExpression) { + final StringBuilder[] tlaBuffers = addInitNextToBuffers(cfgBuffer, trace, expressionData, initId, nextId, + actionConstraintId, nextSubActionBasename, leaveStubsForTraceExpression); + + tlaBuffer.append(tlaBuffers[0].toString()); + tlaBuffer.append(tlaBuffers[1].toString()); + } + + /** + * This will set initId equal to the initial state predicate, nextId equal to the next state + * action for the trace, and actionConstraintId equal to the action constraint for the trace. + * If expressionData is not null, it should contain information about trace explorer expressions. This + * information is used to appropriately put the variables representing trace explorer expressions + * in the trace. In the following example, trace explorer expressions are used, but if expressionData + * is null, those variables will not appear in the init and next definitions, but everything else will be the same. + * + * Note: In the following example, the expressions expr1,...,expr6, texpr1, texpr2 can take up multiple + * lines. + * + * Consider the following trace: + * + * <Initial predicate> <State num 1> + * var1=expr1 + * var2=expr2 + * + * <Action...> <State num 2> + * var1=expr3 + * var2=expr4 + * + * <Action...> <State num 3> + * var1=expr5 + * var2=expr6 + * + * The user has defined two expressions in the trace explorer: + * + * texpr1 (level 2 represented by var3) + * texpr2 (level 1 represented by var4) + * + * This method defines the following identifiers: + * + * init_4123123123 == + * var1=( + * expr1 + * )/\ + * var2=( + * expr2 + * )/\ + * var3=( + * "--" + * )/\ + * var4=( + * texpr2 + * ) + * + * next_12312312312 == + * (var1=( + * expr1 + * )/\ + * var2=( + * expr2 + * )/\ + * var1'=( + * expr3 + * )/\ + * var2'=( + * expr4 + * )/\ + * var3'=( + * texpr1 + * )/\ + * var4'=( + * texpr2 + * )') + * \/ + * (var1=( + * expr3 + * )/\ + * var2=( + * expr4 + * )/\ + * var1'=( + * expr5 + * )/\ + * var2'=( + * expr6 + * )/\ + * var3'=( + * texpr1 + * )/\ + * var4'=( + * texpr2 + * )') + * + * If the last state is back to state i, then this method treats + * the trace as if it has the state labeled "Back to state i" removed and + * replaced with a copy of state i. + * + * If the last state is stuttering, then this method treats the trace as if it + * has the state labeled "Stuttering" removed and replaced with a copy + * of the state before the state labeled "Stuttering". + * + * @param cfgBuffer if non-null, the buffer into which the CFG code will be placed + * @param trace + * @param expressionData data on trace explorer expressions, can be null + * @param initId the identifier to be used for the initial state predicate, cannot be null + * @param nextId the identifier to be used for the next-state action, cannot be null + * @param actionConstraintId the indentified used for the action constraint + * @param nextSubActionBasename the base string to be used as the prefix to unique names for next sub-actions + * @param leaveStubsForTraceExpression if true, then a variable will be defined {@link TRACE_EXPRESSION_VARIABLE}, + * yet commented out, and similarly conjoined, but commented out in the SpecTE Init and Next + * declarations + * @return an array of length 2, the first element is a buffer containing all trace expression subaction + * declarations followed by the action constraint definition; the second element is a buffer + * containing a potential VARIABLE stub for the trace expression variable, followed by the + * definitions for Init and finally Next. This will return null if {@code trace.size() == 0} + */ + public static StringBuilder[] addInitNextToBuffers(final StringBuilder cfgBuffer, + final List<MCState> trace, + final TraceExpressionInformationHolder[] expressionData, + final String initId, final String nextId, + final String actionConstraintId, + final String nextSubActionBasename, + final boolean leaveStubsForTraceExpression) { + if (trace.size() > 0) { + final Iterator<MCState> it = trace.iterator(); + MCState currentState = it.next(); + final StringBuilder subActionsAndConstraint = new StringBuilder(); + final StringBuilder initAndNext = new StringBuilder(); + + /******************************************************* + * Add the init definition. * + *******************************************************/ + if (cfgBuffer != null) { + cfgBuffer.append(TLAConstants.COMMENT).append(TLAConstants.KeyWords.INIT).append(" definition"); + cfgBuffer.append(TLAConstants.CR).append(TLAConstants.KeyWords.INIT).append(TLAConstants.CR); + cfgBuffer.append(initId).append(TLAConstants.CR); + } + + if (leaveStubsForTraceExpression) { + initAndNext.append(TLAConstants.COMMENT).append(TLAConstants.KeyWords.VARIABLE).append(' '); + initAndNext.append(TRACE_EXPRESSION_VARIABLE).append(TLAConstants.CR).append(TLAConstants.CR); + } + + initAndNext.append(TLAConstants.COMMENT).append("TRACE INIT definition "); + initAndNext.append(TLAConstants.TraceExplore.TRACE_EXPLORE_INIT).append(TLAConstants.CR); + initAndNext.append(initId).append(TLAConstants.DEFINES_CR); + final MCVariable[] vars = currentState.getVariables(); + + // variables from spec + for (int i = 0; i < vars.length; i++) { + final MCVariable var = vars[i]; + /* + * /\ var = ( + * expr + * ) + */ + initAndNext.append(TLAConstants.INDENTED_CONJUNCTIVE); + initAndNext.append(var.getName()).append(TLAConstants.EQ).append(TLAConstants.L_PAREN); + initAndNext.append(TLAConstants.CR); + + initAndNext.append(var.getValueAsStringReIndentedAs(TRI_INDENT)).append(TLAConstants.CR); + + initAndNext.append(TLAConstants.INDENT).append(TLAConstants.INDENT); + initAndNext.append(TLAConstants.R_PAREN).append(TLAConstants.CR); + } + + // variables representing trace explorer expressions + if (expressionData != null) { + for (int i = 0; i < expressionData.length; i++) { + final TraceExpressionInformationHolder expressionInfo = expressionData[i]; + initAndNext.append(TLAConstants.INDENTED_CONJUNCTIVE); + initAndNext.append(expressionInfo.getVariableName()).append(TLAConstants.EQ); + initAndNext.append(TLAConstants.L_PAREN).append(TLAConstants.CR); + + initAndNext.append(TRI_INDENT); + if (expressionInfo.getLevel() == 2) { + // add "--" if the expression is temporal level + initAndNext.append(TLAConstants.TRACE_NA); + } else { + // add the actual expression if it is not temporal level + initAndNext.append(expressionInfo.getIdentifier()); + } + + initAndNext.append(TLAConstants.CR).append(TLAConstants.INDENT).append(TLAConstants.INDENT); + initAndNext.append(TLAConstants.R_PAREN).append(TLAConstants.CR); + } + } + + if (leaveStubsForTraceExpression) { + initAndNext.append(TLAConstants.COMMENT).append(TLAConstants.INDENTED_CONJUNCTIVE); + initAndNext.append(TRACE_EXPRESSION_VARIABLE).append(TLAConstants.EQ); + initAndNext.append(TLAConstants.KeyWords.TRUE).append(TLAConstants.CR); + } + + initAndNext.append(CLOSING_SEP).append(TLAConstants.CR); + + /********************************************************** + * Now add the next state actions definition * + **********************************************************/ + if (cfgBuffer != null) { + cfgBuffer.append(TLAConstants.COMMENT).append(TLAConstants.KeyWords.NEXT).append(" definition"); + cfgBuffer.append(TLAConstants.CR).append(TLAConstants.KeyWords.NEXT).append(TLAConstants.CR); + cfgBuffer.append(nextId).append(TLAConstants.CR); + } + + MCState nextState; + final boolean isSingleState; + if (it.hasNext()) { + nextState = it.next(); + isSingleState = false; + } else { + nextState = currentState; + isSingleState = true; + } + + /* + * MAK 09/25/2019: Previously, TE.tla was a next-state relation consisting of + * disjuncts of (unnamed) sub-actions: + * + * Next_123 == (x=1 /\ x'=2) \/ (x=2 /\ x'=3) \/ ... \/ (x=42 /\ x'=42) + * + * At runtime, TLC created an Action for each sub-action of the next-state + * relation (42 for the example above). For each state generated during + * breadth-first search, all Actions were evaluated, but the assumption was + * that only the one corresponding to the level of the current state would + * generate a valid successor state. However, this is not true if a trace expression This poses two problems: + * 1) Actions may + * + * However, for some next-state relations + * + * Non-determinism in trace expression + */ + final StringBuilder nextDisjunctBuffer = new StringBuilder(); + nextDisjunctBuffer.append(nextId).append(TLAConstants.DEFINES_CR); + final String firstIndent; + if (leaveStubsForTraceExpression) { + nextDisjunctBuffer.append(TLAConstants.TLA_AND).append(' '); + firstIndent = " "; + } else { + firstIndent = TLAConstants.INDENT; + } + + final StringBuilder actionConstraintBuffer = new StringBuilder(); + actionConstraintBuffer.append(actionConstraintId).append(TLAConstants.DEFINES_CR); + actionConstraintBuffer.append(TLAConstants.BEGIN_TUPLE).append(TLAConstants.CR); + + if (cfgBuffer != null) { + cfgBuffer.append(TLAConstants.COMMENT).append("Action Constraint definition").append(TLAConstants.CR); + cfgBuffer.append(TLAConstants.COMMENT).append(TLAConstants.KeyWords.ACTION_CONSTRAINT).append(TLAConstants.CR); + cfgBuffer.append(TLAConstants.COMMENT).append(actionConstraintId).append(TLAConstants.CR); + } + + int subActionIndex = 0; + while (nextState != null) { + final String nextDisjunct = String.format("%s_sa_%d", nextSubActionBasename, subActionIndex); + nextDisjunctBuffer.append((subActionIndex == 0) ? firstIndent : TLAConstants.INDENT); + nextDisjunctBuffer.append(TLAConstants.TLA_OR).append(' ').append(nextDisjunct).append(TLAConstants.CR); + actionConstraintBuffer.append(nextDisjunct); + + subActionsAndConstraint.append(TLAConstants.COMMENT).append("TRACE Sub-Action definition "); + subActionsAndConstraint.append(subActionIndex++).append(TLAConstants.CR); + subActionsAndConstraint.append(nextDisjunct).append(TLAConstants.DEFINES_CR); + /* + * Handle Back to state and stuttering. + * + * nextState is assigned to the state which the "Back to state" + * or "Stuttering" state represents. If nextState is "Back to state i", + * then it is assigned to state i. If nextState is "Stuttering", then + * it is assigned to the current state. + */ + if (nextState.isBackToState()) { + nextState = trace.get(nextState.getStateNumber() - 1); + } else if (nextState.isStuttering()) { + nextState = currentState; + } + + /* + * Write the action: + * + * (/\ var1=( + * expr1 + * ) + * /\ var2=( + * expr2 + * ) + * /\ var1'=( + * expr3 + * ) + * /\ var2'=( + * expr4 + * )) + */ + subActionsAndConstraint.append(TLAConstants.INDENT).append(TLAConstants.L_PAREN).append(TLAConstants.CR); + + final MCVariable[] currentStateVars = currentState.getVariables(); + final MCVariable[] nextStateVars = nextState.getVariables(); + + /* + * Iterate through current state variables. This adds: + * + * /\ var1=( + * expr1 + * ) + * /\ var2=( + * expr2 + * ) + * + */ + for (int i = 0; i < currentStateVars.length; i++) { + final MCVariable currentStateVar = currentStateVars[i]; + subActionsAndConstraint.append(TLAConstants.INDENT).append(TLAConstants.INDENTED_CONJUNCTIVE); + subActionsAndConstraint.append(currentStateVar.getName()).append(TLAConstants.EQ); + subActionsAndConstraint.append(TLAConstants.L_PAREN).append(TLAConstants.CR); + subActionsAndConstraint.append(currentStateVar.getValueAsStringReIndentedAs(TRI_INDENT + TLAConstants.INDENT)); + subActionsAndConstraint.append(TLAConstants.CR); + subActionsAndConstraint.append(TRI_INDENT).append(TLAConstants.R_PAREN).append(TLAConstants.CR); + } + + /* + * If the trace is a single state, make the next state + * action never enabled. The model will deadlock in the initial state. + * This adds: + * + * /\ FALSE + */ + if (isSingleState) { + subActionsAndConstraint.append(TLAConstants.INDENT).append(TLAConstants.INDENTED_CONJUNCTIVE); + subActionsAndConstraint.append("FALSE").append(TLAConstants.CR); + } + + /* + * Iterate through next state variables. This adds: + * + * /\ var1'=( + * expr3 + * ) + * /\ var2'=( + * expr4 + * ) + */ + for (int i = 0; i < currentStateVars.length; i++) { + final MCVariable nextStateVar = nextStateVars[i]; + subActionsAndConstraint.append(TLAConstants.INDENT).append(TLAConstants.INDENTED_CONJUNCTIVE); + subActionsAndConstraint.append(nextStateVar.getName()).append(TLAConstants.PRIME); + subActionsAndConstraint.append(TLAConstants.EQ).append(TLAConstants.L_PAREN).append(TLAConstants.CR); + subActionsAndConstraint.append(nextStateVar.getValueAsStringReIndentedAs(TRI_INDENT + TLAConstants.INDENT)); + subActionsAndConstraint.append(TLAConstants.CR); + subActionsAndConstraint.append(TRI_INDENT).append(TLAConstants.R_PAREN).append(TLAConstants.CR); + } + + /* + * Iterate through the trace explorer expressions if there are any. This adds: + * + * /\ var3'=( + * texpr1 + * ) + * /\ var4'=( + * texpr2 + * )' + * + */ + if (expressionData != null) { + for (int i = 0; i < expressionData.length; i++) { + final TraceExpressionInformationHolder expressionInfo = expressionData[i]; + subActionsAndConstraint.append(TLAConstants.INDENT).append(TLAConstants.INDENTED_CONJUNCTIVE); + subActionsAndConstraint.append(expressionInfo.getVariableName()).append(TLAConstants.PRIME); + subActionsAndConstraint.append(TLAConstants.EQ).append(TLAConstants.L_PAREN).append(TLAConstants.CR); + subActionsAndConstraint.append(TRI_INDENT); + subActionsAndConstraint.append(expressionInfo.getIdentifier()).append(TLAConstants.CR); + subActionsAndConstraint.append(TRI_INDENT).append(TLAConstants.R_PAREN); + + if (expressionInfo.getLevel() < 2) { + subActionsAndConstraint.append(TLAConstants.PRIME); + } + subActionsAndConstraint.append(TLAConstants.CR); + } + } + + subActionsAndConstraint.append(TLAConstants.INDENT).append(TLAConstants.R_PAREN); + subActionsAndConstraint.append(TLAConstants.CR).append(TLAConstants.CR); + + if (it.hasNext()) { + actionConstraintBuffer.append(TLAConstants.COMMA); + } + actionConstraintBuffer.append(TLAConstants.CR); + + currentState = nextState; + + if (it.hasNext()) { + nextState = it.next(); + } else { + nextState = null; + } + } + + initAndNext.append(TLAConstants.COMMENT).append("TRACE NEXT definition "); + initAndNext.append(TLAConstants.TraceExplore.TRACE_EXPLORE_NEXT).append(TLAConstants.CR); + initAndNext.append(nextDisjunctBuffer.toString()); + + if (leaveStubsForTraceExpression) { + initAndNext.append(TLAConstants.COMMENT).append(TLAConstants.TLA_AND).append(' '); + initAndNext.append(TRACE_EXPRESSION_VARIABLE).append(TLAConstants.PRIME).append(TLAConstants.EQ); + initAndNext.append(TRACE_EXPRESSION_VARIABLE).append(TLAConstants.CR); + } + + initAndNext.append(TLAConstants.CR).append(TLAConstants.CR); + + + subActionsAndConstraint.append(TLAConstants.COMMENT).append("TRACE Action Constraint definition "); + subActionsAndConstraint.append(TLAConstants.TraceExplore.TRACE_EXPLORE_ACTION_CONSTRAINT); + subActionsAndConstraint.append(TLAConstants.CR).append(actionConstraintBuffer.toString()); + subActionsAndConstraint.append(TLAConstants.END_TUPLE).append("[TLCGet(\"level\")]"); + + subActionsAndConstraint.append(CLOSING_SEP).append(TLAConstants.CR); + + + return new StringBuilder[] { subActionsAndConstraint, initAndNext }; + } + + return null; + } + + public static void addTraceFunctionToBuffers(final StringBuilder tlaBuffer, final StringBuilder cfgBuffer, + final List<MCState> input) { + // Filter stuttering or back2state instances from trace. + final List<MCState> trace = input.stream() + .filter(state -> !state.isBackToState() && !state.isStuttering()) + .collect(Collectors.toList()); + + if (trace.isEmpty()) { + return; + } + + // Trace + final StringBuilder traceFunctionDef = new StringBuilder(); + traceFunctionDef.append(TLAConstants.BEGIN_TUPLE).append(TLAConstants.CR); + for (int j = 0; j < trace.size(); j++) { + final MCState state = trace.get(j); + + traceFunctionDef.append(TLAConstants.L_PAREN).append(state.asSimpleRecord()).append(TLAConstants.R_PAREN); + + if (j < trace.size() - 1) { + traceFunctionDef.append(TLAConstants.COMMA).append(TLAConstants.CR); + } + } + traceFunctionDef.append(TLAConstants.CR).append(TLAConstants.END_TUPLE); + traceFunctionDef.append(CLOSING_SEP).append(TLAConstants.CR); + + addArrowAssignmentToBuffers(tlaBuffer, cfgBuffer, + new Assignment(TLAConstants.TraceExplore.TRACE, new String[0], traceFunctionDef.toString()), + TLAConstants.Schemes.DEFOV_SCHEME); + } + + + public SpecTraceExpressionWriter() { + super(true); + } + + /** + * This only changes the tla file. This method generates and adds a variable declaration + * for each expression in the list. It also creates an identifier for each + * expression and defines the identifier to be that expression. + * It returns an array of {@link TraceExpressionInformationHolder} where each element + * contains the expression, the identifier, and the variable name. + * + * If the expressions are x' + y and x > 3, The tla file will contain something like + * + *\* comment line + * VARIABLES __trace_var_21034978347834, __trace_var_90234782309 + * + * \* comment line + * trace_def_3214234234234 == + * x' + y + * ---- + * + * \* comment line + * trace_def_2342342342342 == + * x > 3 + * ---- + * + * @param expressions a list of formulas, each one an expression the user wants to have evaluated + * at each state of the trace + * @return array of {@link TraceExpressionInformationHolder} where each element + * contains the expression, the identifier, and the variable name + */ + public TraceExpressionInformationHolder[] createAndAddVariablesAndDefinitions(final List<Formula> expressions, + final String attributeName) { + final TraceExpressionInformationHolder[] expressionData + = TraceExpressionInformationHolder.createHolders(expressions, attributeName); + + addVariablesAndDefinitions(expressionData, attributeName, true); + + return expressionData; + } + + @Override + public void addPrimer(final String moduleFilename, final String extendedModuleName) { + addPrimer(moduleFilename, extendedModuleName, new HashSet<>()); + } + + public void addPrimer(final String moduleFilename, final String extendedModuleName, final Set<String> extraExtendedModules) { + if (extendedModuleName != null) { + extraExtendedModules.add(extendedModuleName); + } + + // Not sure why this is required by TE.tla. + extraExtendedModules.add("TLC"); + + // A TE spec has to extend Toolbox to have access to _TETrace and _TEPosition + // operators. + extraExtendedModules.add("Toolbox"); + + tlaBuffer.append(SpecWriterUtilities.getExtendingModuleContent(moduleFilename, + extraExtendedModules.toArray(new String[extraExtendedModules.size()]))); + } + + /** + * This only changes the tla file. This method adds a variable declaration + * for each element of traceExpressionData and, if the flag addDefinitions is true, + * defines the identifier of each element to be the expression for that element. + * + * If the expressions are x' + y and x > 3, The tla file will contain something like + * + *\* comment line + * VARIABLES __trace_var_21034978347834, __trace_var_90234782309 + * + * \* comment line + * trace_def_3214234234234 == + * x' + y + * ---- + * + * \* comment line + * trace_def_2342342342342 == + * x > 3 + * ---- + * + * @param traceExpressionData information about the trace expressions + * @param attributeName + * @param addDefinitions whether or not to define each identifier as the expression + */ + public void addVariablesAndDefinitions(final TraceExpressionInformationHolder[] traceExpressionData, final String attributeName, + final boolean addDefinitions) { + if (traceExpressionData.length == 0) { + return; + } + + final StringBuilder variableDecls = new StringBuilder(); + final StringBuilder definitions = new StringBuilder(); + for (int i = 0; i < traceExpressionData.length; i++) { + final TraceExpressionInformationHolder expressionInfo = traceExpressionData[i]; + + variableDecls.append(expressionInfo.getVariableName()); + // we add a comma after every variable except for the last + if (i != traceExpressionData.length - 1) + { + variableDecls.append(TLAConstants.COMMA); + } + + if (addDefinitions) + { + // define the identifier corresponding to this expression - looks like: + // \* comment line + // trace_def_213123123123 == + // expression + // ---- + definitions.append(TLAConstants.COMMENT).append("TRACE EXPLORER identifier definition "); + definitions.append(TLAConstants.ATTRIBUTE).append(attributeName).append(TLAConstants.COLON); + definitions.append(i).append(TLAConstants.CR); + definitions.append(expressionInfo.getIdentifier()).append(TLAConstants.DEFINES_CR); + definitions.append(expressionInfo.getExpression()).append(CLOSING_SEP).append(TLAConstants.CR); + } + } + + // variable declaration + tlaBuffer.append(TLAConstants.COMMENT).append("TRACE EXPLORER variable declaration "); + tlaBuffer.append(TLAConstants.ATTRIBUTE).append(attributeName).append(TLAConstants.CR); + tlaBuffer.append("VARIABLES ").append(variableDecls.toString()).append(CLOSING_SEP).append(TLAConstants.CR); + + if (addDefinitions) { + // append the expression definitions + tlaBuffer.append(definitions.toString()); + } + } + + /** + * Adds the invariant ~(P) where P is the formula describing finalState. The format + * in the tla file is as follows: + * + * inv_12312321321 == + * ~( + * P + * ) + * ---- + * + * @param finalState + */ + public void addInvariant(final MCState finalState) { + final String id = SpecWriterUtilities.getValidIdentifier(TLAConstants.Schemes.INVARIANT_SCHEME); + cfgBuffer.append(TLAConstants.COMMENT).append(TLAConstants.KeyWords.INVARIANT).append(" definition"); + cfgBuffer.append(TLAConstants.CR).append(TLAConstants.KeyWords.INVARIANT).append(TLAConstants.CR); + cfgBuffer.append(id).append(TLAConstants.CR); + + tlaBuffer.append(TLAConstants.COMMENT).append(TLAConstants.KeyWords.INVARIANT).append(" definition"); + tlaBuffer.append(TLAConstants.CR).append(id).append(TLAConstants.DEFINES_CR); + tlaBuffer.append(TLAConstants.TLA_NOT).append(TLAConstants.L_PAREN).append(TLAConstants.CR); + tlaBuffer.append(getStateConjunction(finalState)).append(TLAConstants.CR).append(TLAConstants.R_PAREN); + + tlaBuffer.append(CLOSING_SEP).append(TLAConstants.CR); + } + + /** + * Adds the temporal property ~<>[](P) where P is the formula describing finalState. + * The format in the tla file is as follows: + * + * prop_23112321 == + * ~<>[]( + * P + * ) + * ---- + * + * @param finalState + */ + public void addStutteringProperty(final MCState finalState) { + String id = SpecWriterUtilities.getValidIdentifier(TLAConstants.Schemes.PROP_SCHEME); + cfgBuffer.append(TLAConstants.COMMENT).append(TLAConstants.KeyWords.PROPERTY).append(" definition"); + cfgBuffer.append(TLAConstants.CR).append(TLAConstants.KeyWords.PROPERTY).append(TLAConstants.CR); + cfgBuffer.append(id).append(TLAConstants.CR); + + tlaBuffer.append(TLAConstants.COMMENT).append(TLAConstants.KeyWords.PROPERTY).append(" definition"); + tlaBuffer.append(TLAConstants.CR).append(id).append(TLAConstants.DEFINES_CR); + tlaBuffer.append(TLAConstants.TLA_NOT).append(TLAConstants.TLA_EVENTUALLY_ALWAYS); + tlaBuffer.append(TLAConstants.L_PAREN).append(TLAConstants.CR).append(getStateConjunction(finalState)); + tlaBuffer.append(TLAConstants.CR).append(TLAConstants.R_PAREN).append(CLOSING_SEP).append(TLAConstants.CR); + } + + /** + * Adds the temporal property ~([]<>P /\ []<>Q), where P is the formula describing finalState and + * Q the formula describing backToState. The formating in the tla file is as follows: + * + * prop_21321312 == + * ~(([]<>( + * P + * ))/\([]<>( + * Q + * ))) + * ---- + * + * @param finalState + * @param backToState + */ + public void addBackToStateProperty(final MCState finalState, final MCState backToState) { + final String id = SpecWriterUtilities.getValidIdentifier(TLAConstants.Schemes.PROP_SCHEME); + cfgBuffer.append(TLAConstants.COMMENT).append(TLAConstants.KeyWords.PROPERTY).append(" definition"); + cfgBuffer.append(TLAConstants.CR).append(TLAConstants.KeyWords.PROPERTY).append(TLAConstants.CR); + cfgBuffer.append(id).append(TLAConstants.CR); + + tlaBuffer.append(TLAConstants.COMMENT).append(TLAConstants.KeyWords.PROPERTY).append(" definition"); + tlaBuffer.append(TLAConstants.CR).append(id).append(TLAConstants.DEFINES_CR); + tlaBuffer.append(TLAConstants.TLA_NOT).append(TLAConstants.L_PAREN).append(TLAConstants.L_PAREN); + tlaBuffer.append(TLAConstants.TLA_INF_OFTEN).append(TLAConstants.L_PAREN).append(TLAConstants.CR); + tlaBuffer.append(getStateConjunction(finalState)).append(TLAConstants.CR).append(TLAConstants.R_PAREN); + tlaBuffer.append(TLAConstants.R_PAREN).append(TLAConstants.TLA_AND).append(TLAConstants.L_PAREN); + tlaBuffer.append(TLAConstants.TLA_INF_OFTEN).append(TLAConstants.L_PAREN).append(TLAConstants.CR); + tlaBuffer.append(getStateConjunction(backToState)).append(TLAConstants.CR).append(TLAConstants.R_PAREN); + tlaBuffer.append(TLAConstants.R_PAREN).append(TLAConstants.R_PAREN).append(CLOSING_SEP).append(TLAConstants.CR); + } + + /** + * Writes comments that will be used for associating variable names with expressions + * and will give the level of each expression. In particular, for each expression "expr" + * with level x and variable name ___trace_var_3242348934343 this + * will append the following comment to the tla file: + * + * \* :x:___trace_var_3242348934343:expr"$!@$!@$!@$!@$!" + * + * @param traceExpressionData + */ + public void addInfoComments(final TraceExpressionInformationHolder[] traceExpressionData) { + for (final TraceExpressionInformationHolder expressionData : traceExpressionData) { + tlaBuffer.append(TLAConstants.COMMENT).append(TLAConstants.COLON).append(expressionData.getLevel()); + tlaBuffer.append(TLAConstants.COLON).append(expressionData.getVariableName()).append(TLAConstants.COLON); + tlaBuffer.append(expressionData.getExpression()).append(TLAConstants.CONSTANT_EXPRESSION_EVAL_IDENTIFIER); + tlaBuffer.append(TLAConstants.CR); + } + } + + /** + * @see #addInitNextToBuffers(StringBuilder, StringBuilder, List, TraceExpressionInformationHolder[]) + */ + public String[] addInitNext(final List<MCState> trace, final TraceExpressionInformationHolder[] expressionData) { + return addInitNextToBuffers(tlaBuffer, cfgBuffer, trace, expressionData); + } + + /** + * @see #addInitNextToBuffers(StringBuilder, StringBuilder, List, TraceExpressionInformationHolder[], String, String, String) + */ + public void addInitNext(final List<MCState> trace, final TraceExpressionInformationHolder[] expressionData, + final String initId, String nextId, final String actionConstraintId) { + addInitNextToBuffers(tlaBuffer, cfgBuffer, trace, expressionData, initId, nextId, actionConstraintId); + } + + /** + * @see #addInitNextToBuffers(StringBuilder, StringBuilder, List, TraceExpressionInformationHolder[], String, String, String, String, boolean) + */ + public void addInitNext(final List<MCState> trace, final TraceExpressionInformationHolder[] expressionData, + final String initId, String nextId, final String actionConstraintId, + final String nextSubActionBasename) { + addInitNextToBuffers(tlaBuffer, cfgBuffer, trace, expressionData, initId, nextId, actionConstraintId, + nextSubActionBasename, true); + } + + public void addTraceFunction(final List<MCState> input) { + addTraceFunctionToBuffers(tlaBuffer, cfgBuffer, input); + } + + /** + * Returns a string representing the formula describing the state. + * If the state has var1=expr1, var2 = expr2, and var3=expr3, then this returns: + * + * var1=( + * expr1 + * )/\ + * var2=( + * expr2 + * )/\ + * var3=( + * expr3 + * ) + * + * + * The expressions expr1, expr2, and expr3 can take up multiple lines. + * + * This will return null if the state is stuttering or back to state. + * + * @param state + * @return + */ + private static String getStateConjunction(final MCState state) { + if (state.isBackToState()) { + return null; + } else if (state.isStuttering()) { + return null; + } else { + final StringBuilder formula = new StringBuilder(); + final MCVariable[] vars = state.getVariables(); + for (int i = 0; i < vars.length; i++) { + final MCVariable var = vars[i]; + formula.append(var.getName()).append(TLAConstants.EQ).append(TLAConstants.L_PAREN).append(TLAConstants.CR); + formula.append(var.getValueAsString()).append(TLAConstants.CR).append(TLAConstants.R_PAREN); + + // append /\ except for the last variable + if (i != (vars.length - 1)) { + formula.append(TLAConstants.TLA_AND).append(TLAConstants.CR); + } + } + + return formula.toString(); + } + } +} diff --git a/tlatools/src/tlc2/output/SpecWriterUtilities.java b/tlatools/src/tlc2/output/SpecWriterUtilities.java new file mode 100644 index 0000000000000000000000000000000000000000..32229aa9f49405a21bdf10f094480ab9631f0328 --- /dev/null +++ b/tlatools/src/tlc2/output/SpecWriterUtilities.java @@ -0,0 +1,349 @@ +package tlc2.output; + +import java.io.File; +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.concurrent.atomic.AtomicLong; +import java.util.regex.Pattern; + +import tla2sany.modanalyzer.SpecObj; +import tla2sany.semantic.OpDefNode; +import tlc2.model.Assignment; +import tlc2.model.Formula; +import util.StringHelper; +import util.TLAConstants; + +/** + * There were a lot of utility methods in the toolbox's ModelWriter class that made what statefulness of that class which + * existed, obscured. I broke out the utility methods into their own class on migration and clean-up. + */ +public final class SpecWriterUtilities { + /* + * Constants used for displaying modification history. + * It has the syntax: + * modificationHistory + + * (lastModified + date + modifiedBy + + * username + newline)* + * + * Note: The StringHelper.newline string wasn't being found on my 64-bit + * Windows 7 machine. So on 8 Dec 2010 I removed it from here, and added + * a "\n" before it when writing a new file. + */ + public static String MODIFICATION_HISTORY = /* StringHelper.PLATFORM_NEWLINE + */ "\\* Modification History"; + public static String LAST_MODIFIED = StringHelper.PLATFORM_NEWLINE + "\\* Last modified "; + public static String MODIFIED_BY = " by "; + + /** + * @param rightMarginWidth the margin width of the output + * @param addModificationHistory whether to add the modification history + * @return the content for the end of the module + */ + public static StringBuilder getModuleClosingTag(final int rightMarginWidth, final boolean addModificationHistory) { + final StringBuilder buffer = new StringBuilder(); + buffer.append(StringHelper.copyString("=", rightMarginWidth)).append(StringHelper.PLATFORM_NEWLINE); + + if (addModificationHistory) { + buffer.append(MODIFICATION_HISTORY).append(StringHelper.PLATFORM_NEWLINE) + .append("\\* Created ").append(new Date()).append(MODIFIED_BY) + .append(System.getProperty("user.name")).append(StringHelper.PLATFORM_NEWLINE); + } + return buffer; + } + + /** + * @return the content for the end of the module + */ + public static StringBuilder getGeneratedTimeStampCommentLine() { + final StringBuilder buffer = new StringBuilder(); + buffer.append(TLAConstants.GENERATION_TIMESTAMP_PREFIX).append(new Date()); + return buffer; + } + + /** + * A pattern to match IDs generated by the {@link AbstractSpecWriter#getValidIdentifier(String)} method + */ + public static final Pattern ID_MATCHER + = Pattern.compile("(" + TLAConstants.Schemes.SPEC_SCHEME + "|" + + TLAConstants.Schemes.INIT_SCHEME + "|" + + TLAConstants.Schemes.NEXT_SCHEME + "|" + + TLAConstants.Schemes.CONSTANT_SCHEME + "|" + + TLAConstants.Schemes.SYMMETRY_SCHEME + "|" + + TLAConstants.Schemes.DEFOV_SCHEME + "|" + + TLAConstants.Schemes.CONSTRAINT_SCHEME + "|" + + TLAConstants.Schemes.ACTIONCONSTRAINT_SCHEME + "|" + + TLAConstants.Schemes.INVARIANT_SCHEME + "|" + + TLAConstants.Schemes.PROP_SCHEME + ")_[0-9]{17,}"); + + /** + * Creates a new valid unqiue identifier from given scheme + * @param scheme a naming scheme, one of the {@link TLAConstants.Schemes} constants + * @return a valid identifier + */ + public static String getValidIdentifier(final String scheme) { + return String.format("%s_%s%s", scheme, System.currentTimeMillis(), 1000 * COUNTER.incrementAndGet()); + } + + /** + * Converts formula list to a string representation + * @param formulaList list of assignments + * @param labelingScheme one of the {@link TLAConstants.Schemes} constants + * @return + */ + public static List<String[]> createListContent(final List<Formula> formulaList, final String labelingScheme) { + ArrayList<String[]> resultContent = new ArrayList<>(formulaList.size()); + String[] content; + String label; + for (int i = 0; i < formulaList.size(); i++) { + label = getValidIdentifier(labelingScheme); + // formulas + // to .cfg : <id> + // to _MC.tla : <id> == <expression> + content = new String[] { label, label + TLAConstants.DEFINES_CR + formulaList.get(i).getFormula(), String.valueOf(i) }; + resultContent.add(content); + } + return resultContent; + } + + /** + * Retrieves the name of the module (filename without extension) + * + * @param moduleFilename + * filename of a module + * @param checkExistence + * if true, the method returns module name, iff the specified file exists or null, if the specified file + * does not exist, if false - only string manipulations are executed + * @return module name + */ + public static String getModuleNameChecked(final String moduleFilename, final boolean checkExistence) { + final File f = new File(moduleFilename); + final int index = f.getName().lastIndexOf('.'); + if (checkExistence) { + return (f.exists()) + ? (index != -1) ? f.getName().substring(0, index) : f.getName() + : null; + } + return (index != -1) ? f.getName().substring(0, index) : f.getName(); + } + + /** + * Creates a simple content for a new TLA+ module + * + * @param moduleFileName, name of the file + * @return the stream with content + */ + public static StringBuilder getExtendingModuleContent(final String moduleFilename, final String... extendedModuleName) { + final StringBuilder buffer = new StringBuilder(); + buffer.append(TLAConstants.SEP).append(' ').append(TLAConstants.KeyWords.MODULE).append(' '); + buffer.append(getModuleNameChecked(moduleFilename, false)).append(' ').append(TLAConstants.SEP).append('\n'); + buffer.append(TLAConstants.KeyWords.EXTENDS).append(' '); + buffer.append(String.join(", ", extendedModuleName)); + buffer.append("\n\n"); + return buffer; + } + + public static List<String[]> createFalseInit(final String var) { + final List<String[]> list = new ArrayList<>(); + final String identifier = getValidIdentifier(TLAConstants.Schemes.INIT_SCHEME); + list.add(new String[] { identifier, + (identifier + TLAConstants.DEFINES_CR + "FALSE/\\" + var + TLAConstants.EQ + "0") }); + return list; + } + + public static List<String[]> createFalseNext(final String var) { + final List<String[]> list = new ArrayList<>(); + final String identifier = getValidIdentifier(TLAConstants.Schemes.NEXT_SCHEME); + list.add(new String[] { identifier, + (identifier + TLAConstants.DEFINES_CR + "FALSE/\\" + var + TLAConstants.PRIME + + TLAConstants.EQ + var) }); + return list; + } + + /** + * Converts formula list to a string representation + * @param serializedFormulaList, list of strings representing formulas (with enablement flag) + * @param labelingScheme one of the {@link TLAConstants.Schemes} constants + * @return + */ + public static List<String[]> createFormulaListContent(final List<String> serializedFormulaList, + final String labelingScheme) { + List<Formula> formulaList = Formula.deserializeFormulaList(serializedFormulaList); + return createFormulaListContentFormula(formulaList, labelingScheme); + } + + public static List<String[]> createFormulaListContentFormula(final List<Formula> serializedFormulaList, + final String labelingScheme) { + return SpecWriterUtilities.createListContent(serializedFormulaList, labelingScheme); + } + + /** + * @param value if null or empty, an empty list will be returned + * @param labelingScheme one of the {@link TLAConstants.Schemes} constants + * @return the content for a single source element + */ + public static List<String[]> createSourceContent(final String value, final String labelingScheme) { + return createSourceContent(value, labelingScheme, true); + } + + /** + * @param value if null or empty, an empty list will be returned + * @param identifierOrLabelingScheme an identifier or one of the + * {@link TLAConstants.Schemes} constants + * @param isScheme true if {@code identifierOrLabelingScheme} + * is one of the scheme constants (in which + * case an identifier will be generated based + * off of it.) + * @return the content for a single source element + */ + public static List<String[]> createSourceContent(final String value, final String identifierOrLabelingScheme, + final boolean isScheme) { + final ArrayList<String[]> result = new ArrayList<>(); + if ((value == null) || (value.trim().length() == 0)) { + return result; + } + + final String identifier = isScheme ? SpecWriterUtilities.getValidIdentifier(identifierOrLabelingScheme) + : identifierOrLabelingScheme; + final StringBuilder buffer = new StringBuilder(); + + buffer.append(identifier).append(TLAConstants.DEFINES_CR); + buffer.append(value); + + result.add(new String[] { identifier, buffer.toString() }); + return result; + } + + /** + * Create a list of overrides. If the override is not in the spec's root module, then + * the config file will have A <- [M] id . This means that A is defined in module M, + * and its definition is being overriden in the spec root module which is dependent upon M. + * The following is an example from Leslie Lamport that explains what occurred before changing + * the code and what occurs now. + * Consider the root module + + ----------------- MODULE TestA -------------------- + M(a,b) == INSTANCE TestB WITH CB <- a, CD <- b + ================================================== + + which imports the module + + ----------------- MODULE TestB -------------------- + CONSTANTS CB, CD + + Foo(x) == <<x, CB, CD>> + ================================================== + + If you go to definition overrides, you'll find the option of + overriding M!Foo. Selecting it, the toolbox asks you to define an operator + M!Foo of 3 arguments. If you do it and run TLC, you get the error + + The configuration file substitutes for Foo with + def_ov_12533499062845000 of different number of arguments. + + Here's what's going on. The INSTANCE statement imports the operator + M!Foo into module TestA. As you may recall, you use that operator + in an expression by writing something like + + M(42, "a")!F(-13) + + but in the semantic tree, it looks just as if M!F were any other + operator with three arguments. When TLC sees the override instruction + + Foo <- [TestB]def_ov_12533495599614000 + + in the .cfg file, it tries to substitute an operator def_ov_... with + 3 arguments for the operator Foo of module TestB that has only a + single argument. Hence, the error. + + ------ + + Here's the fix. Instead of giving the user the option of overriding + M!Foo, in the menu, he should simply see Foo and, if he clicks once + it, he should see that it's in module TestB. If he chooses to override + Foo, he should be asked to define an operator of one argument. + + * @param overrides + * @param labelingScheme one of the {@link TLAConstants.Schemes} constants + * @param specObj + * @return + * + * Was throwing null-pointer exception when called with spec unparsed. + * Hacked a fix to handle this case. LL 20 Sep 2009 + */ + public static List<String[]> createOverridesContent(final List<Assignment> overrides, final String labelingScheme, + final SpecObj specObj) { + final ArrayList<String[]> resultContent = new ArrayList<>(overrides.size()); + String[] content; + String id; + Assignment formula; + + // getting the opdefnodes is necessary for retrieving the correct label + // to appear in the cfg file as explained in the documentation for this method + if (specObj == null) { + return resultContent; + } + final OpDefNode[] opDefNodes = specObj.getExternalModuleTable().getRootModule().getOpDefs(); + final HashMap<String, OpDefNode> nodeTable = new HashMap<>(opDefNodes.length); + + for (int j = 0; j < opDefNodes.length; j++) { + final String key = opDefNodes[j].getName().toString(); + nodeTable.put(key, opDefNodes[j]); + } + + for (int i = 0; i < overrides.size(); i++) { + id = getValidIdentifier(labelingScheme); + // formulas + // to .cfg : <id> + // to _MC.tla : <id> == <expression> + formula = overrides.get(i); + + final OpDefNode defNode = nodeTable.get(formula.getLabel()); + if (defNode == null) { + // should raise an error + content = null; + } else { + final OpDefNode source = defNode.getSource(); + if (source == defNode) { + // user is overriding a definition in the root module + if (formula.isModelValue() && !formula.isSetOfModelValues()) { + // model value + content = new String[] { formula.getLabel() + TLAConstants.EQ + formula.getLabel(), TLAConstants.EMPTY_STRING }; + } else { + // not a model value + content = new String[] { formula.getLabel() + TLAConstants.ARROW + id, + formula.getParametrizedLabel(id) + TLAConstants.DEFINES_CR + formula.getRight() }; + } + } else if (source.getSource() == source) { + // user is overriding a definition that is not in the root module + if (formula.isModelValue() && !formula.isSetOfModelValues()) { + // model value + content = new String[] { + source.getName().toString() + TLAConstants.ARROW + "[" + + source.getOriginallyDefinedInModuleNode().getName().toString() + "]" + id + + " " + id + TLAConstants.EQ + source.getName().toString(), "CONSTANT " + id }; + } else { + // not a model value + content = new String[] { + source.getName().toString() + TLAConstants.ARROW + "[" + + source.getOriginallyDefinedInModuleNode().getName().toString() + "]" + id, + formula.getParametrizedLabel(id) + TLAConstants.DEFINES_CR + formula.getRight() }; + } + } else { + // should raise an error window + content = null; + } + } + + resultContent.add(content); + } + + return resultContent; + } + + /** + * Counter to be able to generate unique identifiers + */ + private static final AtomicLong COUNTER = new AtomicLong(1L); + +} diff --git a/tlatools/src/tlc2/output/StatePrinter.java b/tlatools/src/tlc2/output/StatePrinter.java index 295712ed3aadcc0e6547c898dc2e6e2ca41bcd51..aa1078a1603cdacb8fdf5e7b4ac96a6313c9b10d 100644 --- a/tlatools/src/tlc2/output/StatePrinter.java +++ b/tlatools/src/tlc2/output/StatePrinter.java @@ -45,6 +45,15 @@ public class StatePrinter MP.printState(EC.TLC_STATE_PRINT1, new String[] { "", currentState.toString() }, currentState, -1); } + public static void printState(TLCStateInfo currentStateInfo) { + if (currentStateInfo.predecessorState == null) { + // It's an initial state + printState(currentStateInfo, null, (int) currentStateInfo.stateNumber); + } else { + printState(currentStateInfo, currentStateInfo.predecessorState.state, (int) currentStateInfo.stateNumber); + } + } + /** * Prints the state information * if the TLC runs in print-diff-only mode and the last state is set, it will print the diff only @@ -61,9 +70,14 @@ public class StatePrinter { stateString = currentStateInfo.state.toString(); } - MP.printState(EC.TLC_STATE_PRINT2, new String[] { String.valueOf(num), currentStateInfo.info.toString(), - stateString }, currentStateInfo, num); - OutputCollector.addStateToTrace(currentStateInfo); + if (currentStateInfo.state.allAssigned()) { + MP.printState(EC.TLC_STATE_PRINT2, new String[] { String.valueOf(num), currentStateInfo.info.toString(), + stateString, String.valueOf(currentStateInfo.fingerPrint()) }, currentStateInfo, num); + } else { + // fingerprint can't be calculated when state is incomplete, just return a random value. + MP.printState(EC.TLC_STATE_PRINT2, new String[] { String.valueOf(num), currentStateInfo.info.toString(), + stateString, "-1" }, currentStateInfo, num); + } } /** @@ -71,7 +85,21 @@ public class StatePrinter */ public static void printStutteringState(int num) { - MP.printState(EC.TLC_STATE_PRINT3, new String[] { String.valueOf(num) }, (TLCState) null, num); + MP.printState(EC.TLC_STATE_PRINT3, new String[] { String.valueOf(num + 1) }, (TLCState) null, num + 1); } + /** + * Prints a marker (EC.TLC_BACK_TO_STATE) looping back to the state with the + * given stateNum. + * @param currentStateInfo + * + * @param stateNum + */ + public static void printBackToState(final TLCStateInfo currentStateInfo, final long stateNum) { + if (TLCGlobals.tool) { + MP.printState(EC.TLC_BACK_TO_STATE, new String[] { "" + stateNum, currentStateInfo.info.toString() }, (TLCState) null, -1); + } else { + MP.printMessage(EC.TLC_BACK_TO_STATE, new String[] {"" + stateNum, currentStateInfo.info.toString()}); + } + } } diff --git a/tlatools/src/tlc2/output/TLACopier.java b/tlatools/src/tlc2/output/TLACopier.java new file mode 100644 index 0000000000000000000000000000000000000000..13682602103235961ac6f21b52a93ded74ad5318 --- /dev/null +++ b/tlatools/src/tlc2/output/TLACopier.java @@ -0,0 +1,69 @@ +package tlc2.output; + +import java.io.BufferedWriter; +import java.io.File; +import java.io.IOException; +import java.util.regex.Matcher; + +import util.TLAConstants; + +/** + * This class which copies a TLA file, mutating it appropriately to become a SpecTE TLA file. + */ +public class TLACopier extends AbstractTLACopier { + private final String initNextDefinitions; + + private final boolean needExtendTLC; + private final boolean needExtendToolbox; + + public TLACopier(final String originalName, final String newName, final File sourceLocation, + final String initNextDefinitionTLA, final boolean originalExtendsTLC, + final boolean originalExtendsToolbox) { + super(originalName, newName, sourceLocation); + + initNextDefinitions = initNextDefinitionTLA; + + needExtendTLC = !originalExtendsTLC; + needExtendToolbox = !originalExtendsToolbox; + } + + /** + * {@inheritDoc} + */ + @Override + protected void copyLine(final BufferedWriter writer, final String originalLine, final int lineNumber) + throws IOException { + if (!inBody) { + final Matcher m = modulePattern.matcher(originalLine); + final String lineToWrite; + + if (m.find()) { + lineToWrite = m.group(1) + ' ' + newModuleName + ' ' + TLAConstants.SEP; + inBody = true; + } else { + lineToWrite = originalLine; + } + + writer.write(lineToWrite + '\n'); + } else { + if (originalLine.trim().startsWith(TLAConstants.KeyWords.EXTENDS)) { + String line = originalLine; + if (needExtendTLC) { + line += ", " + TLAConstants.BuiltInModules.TLC; + } + if (needExtendToolbox) { + line += ", " + TLAConstants.BuiltInModules.TRACE_EXPRESSIONS; + } + writer.write(line + '\n'); + } else { + final Matcher m = CLOSING_BODY_PATTERN.matcher(originalLine); + + if (m.matches()) { + writer.write(initNextDefinitions + '\n'); + } + + writer.write(originalLine + '\n'); + } + } + } +} diff --git a/tlatools/src/tlc2/output/TLAMonolithCreator.java b/tlatools/src/tlc2/output/TLAMonolithCreator.java new file mode 100644 index 0000000000000000000000000000000000000000..3ce15e66acc9a54bc29b3a438d224643f1f11981 --- /dev/null +++ b/tlatools/src/tlc2/output/TLAMonolithCreator.java @@ -0,0 +1,288 @@ +package tlc2.output; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.nio.file.CopyOption; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardCopyOption; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.Stack; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import tla2sany.semantic.StandardModules; +import tlc2.input.MCParser; +import tlc2.input.MCParserResults; +import util.TLAConstants; + +public class TLAMonolithCreator extends AbstractTLACopier { + private static final String NESTED_MODULE_INDENT = " "; + private static final String LOCAL_INSTANCE_REGEX = "^LOCAL INSTANCE ([^\\s]+)\\s*$"; + private static final Pattern LOCAL_INSTANCE_PATTERN = Pattern.compile(LOCAL_INSTANCE_REGEX); + + + // these are modules which SANY logs that it has parsed, scrubbed of Standard Modules and in reverse order + private final List<File> modulesToEmbed; + private final Set<String> moduleNamesBeingEmbedded; + // these are the modules which the root ModuleNode or one of its sub-ModuleNodes (or one or their sub-ModuleNodes + // and so on, turtles all the way down) has defined as EXTENDS-ing in their spec + private final Set<String> modulesToSpecifyInExtends; + // TODO this is insufficient for nestings beyond one level + private final List<File> modulesToNest; + + /** + * This is the constructor for the version which embeds no dependent modules. + * + * @param entireExtendsList the modules which the root ModuleNode or one of its sub-ModuleNodes (or one or their + * sub-ModuleNodes and so on, turtles all the way down) has defined as EXTENDS-ing + * in their spec. + */ + public TLAMonolithCreator(final String rootSpecName, final File sourceLocation, final Set<String> entireExtendsList) { + this(rootSpecName, sourceLocation, null, entireExtendsList, null); + } + + /** + * @param rootSpecName + * @param sourceLocation + * @param extendeds these are modules which SANY logs that it has parsed; we expect to receive this in the order + * which SANY emits it in logging; if that order changes in the future, the monolith + * spec will potentially break due to dependent functions being declared in the + * wrong order + * @param entireExtendsList the modules which the root ModuleNode or one of its sub-ModuleNodes (or one or their + * sub-ModuleNodes and so on, turtles all the way down) has defined as EXTENDS-ing + * in their spec; this will get the non Standard Modules filtered out of it prior to + * usage in this class since those will get embedded as a dependent module. + * @param allInstantiatedModules + */ + public TLAMonolithCreator(final String rootSpecName, final File sourceLocation, final List<File> extendeds, + final Set<String> entireExtendsList, final Set<String> allInstantiatedModules) { + super(rootSpecName, ("tmp_" + System.currentTimeMillis() + "_monolith"), sourceLocation); + + final boolean willEmbedDependents = (extendeds != null); + + moduleNamesBeingEmbedded = new HashSet<>(); + modulesToNest = new ArrayList<>(); + modulesToEmbed = new ArrayList<>(); + if (willEmbedDependents) { + final HashSet<String> instantiatedModules = new HashSet<>(allInstantiatedModules); + final Stack<File> embedStack = new Stack<>(); + for (final File f : extendeds) { + final String name = f.getName(); + final int index = name.toLowerCase().indexOf(TLAConstants.Files.TLA_EXTENSION); + final boolean keep; + final String moduleName; + if (index == -1) { + // this should never be the case + keep = true; + moduleName = name; + } else { + moduleName = name.substring(0, index); + + keep = !StandardModules.isDefinedInStandardModule(moduleName); + } + + if (keep) { + embedStack.push(f); + instantiatedModules.remove(moduleName); + moduleNamesBeingEmbedded.add(moduleName); + } + } + + while (!embedStack.isEmpty()) { + modulesToEmbed.add(embedStack.pop()); + } + + for (final String module : instantiatedModules) { + if (!StandardModules.isDefinedInStandardModule(module)) { + modulesToNest.add(new File(sourceLocation, (module + TLAConstants.Files.TLA_EXTENSION))); + } + } + } + + modulesToSpecifyInExtends = new HashSet<>(entireExtendsList); + if (willEmbedDependents) { + StandardModules.filterNonStandardModulesFromSet(modulesToSpecifyInExtends); + } + // for TLC things + modulesToSpecifyInExtends.add(TLAConstants.BuiltInModules.TLC); + // for _TE things + modulesToSpecifyInExtends.add(TLAConstants.BuiltInModules.TRACE_EXPRESSIONS); + } + + @Override + protected void copyLine(final BufferedWriter writer, final String originalLine, final int lineNumber) + throws IOException { + if (!inBody) { + final Matcher m = modulePattern.matcher(originalLine); + + inBody = m.find(); + + if (!vetoLocalInstanceLine(originalLine)) { + writer.write(originalLine + '\n'); + } + } else { + if (originalLine.trim().startsWith(TLAConstants.KeyWords.EXTENDS)) { + writer.write(TLAConstants.KeyWords.EXTENDS + " " + String.join(", ", modulesToSpecifyInExtends) + "\n"); + + for (final File f : modulesToNest) { + insertModuleIntoMonolith(f, writer, true); + } + + for (final File f : modulesToEmbed) { + insertModuleIntoMonolith(f, writer, false); + } + + final StringBuilder commentLine = new StringBuilder(TLAConstants.CR); + commentLine.append(TLAConstants.COMMENT).append(TLAConstants.CR); + commentLine.append(TLAConstants.COMMENT).append(' '); + commentLine.append(originalModuleName); + commentLine.append(" follows\n"); + commentLine.append(TLAConstants.COMMENT).append(TLAConstants.CR).append(TLAConstants.CR); + writer.write(commentLine.toString()); + } else { + writer.write(originalLine + '\n'); + } + } + } + + @Override + protected void copyHasFinished() throws IOException { + final Path originalPath = sourceFile.toPath(); + Files.delete(originalPath); + + final Path monolithPath = destinationFile.toPath(); + Files.move(monolithPath, originalPath, new CopyOption[0]); + } + + private void insertModuleIntoMonolith(final File module, final BufferedWriter monolithWriter, + final boolean nestedModule) + throws IOException { + final StringBuilder commentLine = new StringBuilder(TLAConstants.CR); + final String moduleFilename = module.getName(); + final int index = moduleFilename.indexOf(TLAConstants.Files.TLA_EXTENSION); + final String moduleName; + if (index != -1) { + moduleName = moduleFilename.substring(0, index); + } else { + moduleName = moduleFilename; + } + commentLine.append(TLAConstants.COMMENT).append(TLAConstants.CR); + commentLine.append(TLAConstants.COMMENT).append(' ').append(moduleName).append(" follows\n"); + commentLine.append(TLAConstants.COMMENT).append(TLAConstants.CR).append(TLAConstants.CR); + + monolithWriter.write(commentLine.toString()); + + final String regex = TLAConstants.MODULE_OPENING_PREFIX_REGEX + moduleName; + final Pattern insertingModuleMatcher = Pattern.compile(regex); + + try (final BufferedReader br = new BufferedReader(new FileReader(module))) { + String line; + boolean inModuleBody = false; + + while ((line = br.readLine()) != null) { + if (!inModuleBody) { + final Matcher m = insertingModuleMatcher.matcher(line); + + inModuleBody = m.find(); + if (inModuleBody && nestedModule) { + monolithWriter.write(NESTED_MODULE_INDENT + line + '\n'); + } + } else { + if (!line.trim().startsWith(TLAConstants.KeyWords.EXTENDS)) { + final Matcher m = CLOSING_BODY_PATTERN.matcher(line); + + if (m.matches()) { + if (nestedModule) { + monolithWriter.write(NESTED_MODULE_INDENT + line + '\n'); + } + break; + } + + if (!vetoLocalInstanceLine(line)) { + if (nestedModule) { + monolithWriter.write(NESTED_MODULE_INDENT); + } + monolithWriter.write(line + '\n'); + } + } + } + } + } + } + + private boolean vetoLocalInstanceLine(final String line) { + final Matcher m = LOCAL_INSTANCE_PATTERN.matcher(line); + if (m.matches()) { + return moduleNamesBeingEmbedded.contains(m.group(1)); + } + return false; + } + + + public static void main(final String[] args) { + if (args.length == 0) { + System.out.println("java tlc2.output.TLAMonolithCreator \\\n" + + "\t[-sourceDir=_directory_containing_spec_tla_] \\\n" + + "\t_specName_"); + + System.exit(-2); + } + + final File sourceDirectory; + final String specName; + if (args.length == 1) { + sourceDirectory = new File(System.getProperty("user.dir")); + specName = args[0]; + } else { + sourceDirectory = new File(args[0]); + specName = args[1]; + } + final File originalTLA = new File(sourceDirectory, (specName + TLAConstants.Files.TLA_EXTENSION)); + if (!originalTLA.exists()) { + System.out.println("Excepted to find the TLA file but could not: " + originalTLA.getAbsolutePath()); + + System.exit(-3); + } + + final MCParser parser = new MCParser(sourceDirectory, specName, true); + final MCParserResults results = parser.parse(); + + final File backupTLA = new File(sourceDirectory, (specName + "_orig" + TLAConstants.Files.TLA_EXTENSION)); + + try { + Files.copy(originalTLA.toPath(), backupTLA.toPath(), StandardCopyOption.COPY_ATTRIBUTES); + } catch (final IOException ioe) { + System.out.println("Exception encountered while making a backup of the original TLA file: " + + ioe.getMessage()); + + System.exit(-1); + } + + try { + final ArrayList<File> extendeds = new ArrayList<>(); + for (final String extended : results.getOriginalExtendedModules()) { + extendeds.add(new File(sourceDirectory, (extended + TLAConstants.Files.TLA_EXTENSION))); + } + + final TLAMonolithCreator creator = new TLAMonolithCreator(specName, sourceDirectory, extendeds, + results.getAllExtendedModules(), + results.getAllInstantiatedModules()); + creator.copy(); + + System.out.println("The monolith file is now present at: " + originalTLA.getAbsolutePath()); + } catch (final IOException ioe) { + System.out.println("Exception encountered while making creating the monolith: " + ioe.getMessage()); + + System.exit(-4); + } finally { + System.out.println("The original TLA file was backed up to: " + backupTLA.getAbsolutePath()); + } + } +} diff --git a/tlatools/src/tlc2/output/TeeOutputStream.java b/tlatools/src/tlc2/output/TeeOutputStream.java new file mode 100644 index 0000000000000000000000000000000000000000..84247e67c25971b58f84c42532a6a4acad379887 --- /dev/null +++ b/tlatools/src/tlc2/output/TeeOutputStream.java @@ -0,0 +1,107 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package tlc2.output; + +import java.io.FilterOutputStream; +import java.io.IOException; +import java.io.OutputStream; + +/** + * Classic splitter of OutputStream. Named after the unix 'tee' command. It + * allows a stream to be branched off so there are now two streams. + * + * This code is copy-and-pasted from commons-io 2.6 + */ +public class TeeOutputStream extends FilterOutputStream { + /** the second OutputStream to write to */ + protected OutputStream branch; //TODO consider making this private + + /** + * Constructs a TeeOutputStream. + * @param out the main OutputStream + * @param branch the second OutputStream + */ + public TeeOutputStream(final OutputStream out, final OutputStream branch) { + super(out); + this.branch = branch; + } + + /** + * Write the bytes to both streams. + * @param b the bytes to write + * @throws IOException if an I/O error occurs + */ + @Override + public synchronized void write(final byte[] b) throws IOException { + out.write(b); + this.branch.write(b); + } + + /** + * Write the specified bytes to both streams. + * @param b the bytes to write + * @param off The start offset + * @param len The number of bytes to write + * @throws IOException if an I/O error occurs + */ + @Override + public synchronized void write(final byte[] b, final int off, final int len) throws IOException { + out.write(b, off, len); + this.branch.write(b, off, len); + } + + /** + * Write a byte to both streams. + * @param b the byte to write + * @throws IOException if an I/O error occurs + */ + @Override + public synchronized void write(final int b) throws IOException { + out.write(b); + this.branch.write(b); + } + + /** + * Flushes both streams. + * @throws IOException if an I/O error occurs + */ + @Override + public void flush() throws IOException { + out.flush(); + this.branch.flush(); + } + + /** + * Closes both output streams. + * + * If closing the main output stream throws an exception, attempt to close the branch output stream. + * + * If closing the main and branch output streams both throw exceptions, which exceptions is thrown by this method is + * currently unspecified and subject to change. + * + * @throws IOException + * if an I/O error occurs + */ + @Override + public void close() throws IOException { + try { + out.close(); + } finally { + this.branch.close(); + } + } +} diff --git a/tlatools/src/tlc2/output/messages.properties b/tlatools/src/tlc2/output/messages.properties index 6e118859bce31c2ce34f9bc422bb4802b3d7d981..e26b31fc964efb984e748d1ef92c66384398d4cc 100644 --- a/tlatools/src/tlc2/output/messages.properties +++ b/tlatools/src/tlc2/output/messages.properties @@ -1,53 +1,22 @@ -VersionTag=Version 2.02 of 3 August 2009 -HelpMessage=The model checker (TLC) provides the functionalities of model\n\ -checking or simulation of TLA+ specifications. The syntax for this command is:\n\n\ -java tlc2.TLC [GENERAL-SWITCHES] [MODE-SWITCHES] SPEC\n\n\ -where SPEC is the name of the specification's root module\n\ -and the optional GENERAL-SWITCHES are:\n\ - -checkpoint num: interval between check point (in minutes)\n\ - \tDefaults 30 if not provided.\n\ - -cleanup: clean up the states directory\n\ - -config file: provide the config file.\n\ - \tDefaults to SPEC.cfg if not provided\n\ - -continue: continue running even when invariant is violated\n\ - \tDefaults to stop at the first violation if not specified\n\ - -coverage minutes: interval between collection of coverage information \n\ - \ton the spec in minutes. Defaults to no coverage if not specified\n\ - -deadlock: do not check for deadlock.\n\ - \tDefaults to check deadlock if not specified\n\ - -difftrace: show only the differences between successive states,\n\ - \twhen printing trace. Defaults to print full state descriptions\n\ - -debug: debugging information (not for production use)\n\ - -dump file: dump all the states into file\n\ - -fp num: use the num'th irreducible polynomial from the list \n\ - \tstored in the class FP64.\n\ - -gzip: control if gzip is applied to value input/output stream.\n\ - \tDefaults to use gzip.\n\ - -metadir path: store metadata in the directory at path\n\ - \tDefaults to SPEC-directory/states if not specified\n\ - -recover id: recover from the checkpoint with id\n\ - \tDefaults to scratch run if not specified\n\ - -terse: do not expand values in Print statement\n\ - \tDefaults to expand value if not specified\n\ - -tool: tool mode (put message codes on console)\n\ - -nowarning: disable all the warnings.\n\ - \tDefaults to report warnings if not specified\n\ - -workers num: the number of TLC worker threads. Defaults to 1.\n\n\ +TLCDescription=\ +The model checker (TLC) provides the functionalities of model checking\n\ +or simulation of TLA+ specifications. It may be invoked from the command\n\ +line, or via the model checking functionality of the Toolbox.\n\n\ By default, TLC starts in the model checking mode using breadth-first\n\ -approach for the state space exploration. This can be changed using\n\ -the MODE-SWITCHES. In contrast to the GENERAL-SWITCHES these can be used \n\ -if applied in certain combinations only:\n\n\ -{[-dfid num][ -view]|-simulate[ -depth num][ -aril num][ -seed num]}\n\n\ - -modelcheck: run in model checking mode using breadth-first approach for the\n\ - \tstate space exploration (default)\n\ - -dfid num: run in model checking mode and use depth-first iterative deepening\n\ - \twith initial depth num\n\ - -view: apply VIEW (if provided) when printing out states.\n\ - -simulate: run in simulation mode\n\ - -depth num: specify the depth of random simulation\n\ - \tDefaults to 100 if not specified\n\ - -seed num: provide the seed for random simulation\n\ - \tDefaults to a random seed if not specified)\n\ - -aril num: Adjust the seed for random simulation\n\ - \tDefaults to 0 if not specified\n\ - \ No newline at end of file +approach for the state space exploration. +TraceExplorerDescription=\ +TraceExplorer performs the parsing / consuming of the TLC model checking\n\ +'tool mode' output. With this parsed output, it allows the user to do\n\ +three actions, at this time:\n\ +\to pretty print the error states in a fashion similar to what would\n\ +\t be seen in the Toolbox\n\ +\to generate a SpecTE tla/cfg file pair which allows a user to then\n\ +\t work with their model so that it is at the error state\n\ +\to evalute trace expressions at each error state; this action also\n\ +\t also generates the SpecTE tla/cfg pair if it hasn't already\n\ +\t been done\n\n\ +TraceExplorer also allows the user to evaluate any single expression\n\ +via the model checker.\n\n\ +If a SpecName is not specified, it is expected that tool output will\n\ +be arriving on stdin; in this case, any specification of -source will\n\ +be ignored. diff --git a/tlatools/src/tlc2/overrides/Evaluation.java b/tlatools/src/tlc2/overrides/Evaluation.java new file mode 100644 index 0000000000000000000000000000000000000000..2422f496837c47d71a92c0ff080cc7cb24e193ea --- /dev/null +++ b/tlatools/src/tlc2/overrides/Evaluation.java @@ -0,0 +1,72 @@ +/******************************************************************************* + * Copyright (c) 2019 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.overrides; + +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +/** + * @see EvaluatingValue. + */ +@Retention(RUNTIME) +@Target(METHOD) +public @interface Evaluation { + + /** + * @return The identifier of a TLA+ state or action predicate. + */ + String definition(); + + /** + * @return The name of the TLA+ module declaring the definition above. + */ + String module(); + + /** + * @return The minimum level that will be assigned to the OpDefNode that + * represents the EvaluatingValue in the semantic graph. Unless + * the actual level checking in Spec.getLevelBound assigns a + * greater value, the OpDefNode is a constant-level expression if + * 0 causing it to be eagerly evaluated in + * SpecProcessor.processConstantDefns. + * @see tla2sany.semantic.LevelNode.getLevel() + * @see tlc2.tool.impl.Spec.getLevelBound(SemanticNode, Context) + * @see tlc2.value.impl.EvaluatingValue + * @see tlc2.tool.impl.SpecProcessor.processConstantDefns() + */ + int minLevel() default 0; + + /** + * @return true if a warning should be printed when a EV cannot be mapped to the + * given TLA+ definition in module. + * @see Evaluation#definition() + * @see Evaluation#module() + */ + boolean warn() default true; +} diff --git a/tlatools/src/tlc2/overrides/ITLCOverrides.java b/tlatools/src/tlc2/overrides/ITLCOverrides.java new file mode 100644 index 0000000000000000000000000000000000000000..3ac6fb6f0870c8a98ffa204edf71918ea30efa6e --- /dev/null +++ b/tlatools/src/tlc2/overrides/ITLCOverrides.java @@ -0,0 +1,33 @@ +/******************************************************************************* + * Copyright (c) 2019 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.overrides; + +// This is provisional and might change or disappear in future version. +public interface ITLCOverrides { + + Class[] get(); + +} diff --git a/tlatools/src/tlc2/overrides/TLAPlusCallable.java b/tlatools/src/tlc2/overrides/TLAPlusCallable.java new file mode 100644 index 0000000000000000000000000000000000000000..529176f51606a060304de1be249b674af9d6994c --- /dev/null +++ b/tlatools/src/tlc2/overrides/TLAPlusCallable.java @@ -0,0 +1,72 @@ +/******************************************************************************* + * Copyright (c) 2020 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.overrides; + +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +/** + * @see MethodValue. + */ +@Retention(RUNTIME) +@Target(METHOD) +public @interface TLAPlusCallable { + + /** + * @return The identifier of a TLA+ state or action predicate. + */ + String definition(); + + /** + * @return The name of the TLA+ module declaring the definition above. + */ + String module(); + + /** + * @return The minimum level that will be assigned to the OpDefNode that + * represents the EvaluatingValue in the semantic graph. Unless + * the actual level checking in Spec.getLevelBound assigns a + * greater value, the OpDefNode is a constant-level expression if + * 0 causing it to be eagerly evaluated in + * SpecProcessor.processConstantDefns. + * @see tla2sany.semantic.LevelNode.getLevel() + * @see tlc2.tool.impl.Spec.getLevelBound(SemanticNode, Context) + * @see tlc2.value.impl.EvaluatingValue + * @see tlc2.tool.impl.SpecProcessor.processConstantDefns() + */ + int minLevel() default 0; + + /** + * @return true if a warning should be printed when a EV cannot be mapped to the + * given TLA+ definition in module. + * @see Evaluation#definition() + * @see Evaluation#module() + */ + boolean warn() default true; +} diff --git a/tlatools/src/tlc2/overrides/TLAPlusOperator.java b/tlatools/src/tlc2/overrides/TLAPlusOperator.java new file mode 100644 index 0000000000000000000000000000000000000000..780c0f8dfa50f1d9b6a4094415a2db02ff8a6565 --- /dev/null +++ b/tlatools/src/tlc2/overrides/TLAPlusOperator.java @@ -0,0 +1,60 @@ +/******************************************************************************* + * Copyright (c) 2019 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.overrides; + +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +/** + * @see MethodValue. + */ +@Retention(RUNTIME) +@Target(METHOD) +public @interface TLAPlusOperator { + + /** + * @return The identifier of a TLA+ operator. + */ + String identifier(); + + /** + * @return The name of the TLA+ module declaring the definition above. + */ + String module(); + + /** + * @see tlc2.overrides.Evaluation.minLevel() + */ + int minLevel() default 0; + + /** + * @return tlc2.overrides.Evaluation.warn() + */ + boolean warn() default true; +} diff --git a/tlatools/src/tlc2/tool/AbstractChecker.java b/tlatools/src/tlc2/tool/AbstractChecker.java index 7ba4caea0207d961d797c682c1ec44380db506f1..7c2c6722d224b2004f4f5abd4b8ea7f9f92f25dc 100644 --- a/tlatools/src/tlc2/tool/AbstractChecker.java +++ b/tlatools/src/tlc2/tool/AbstractChecker.java @@ -1,58 +1,64 @@ package tlc2.tool; import java.io.IOException; -import java.util.Hashtable; -import java.util.concurrent.atomic.AtomicLong; +import java.math.BigDecimal; +import java.math.MathContext; +import java.util.Timer; +import java.util.TimerTask; -import tla2sany.modanalyzer.SpecObj; -import tla2sany.semantic.SemanticNode; -import tla2sany.st.Location; +import tlc2.TLC; import tlc2.TLCGlobals; import tlc2.output.EC; import tlc2.output.MP; -import tlc2.output.OutputCollector; +import tlc2.tool.coverage.CostModelCreator; +import tlc2.tool.impl.FastTool; +import tlc2.tool.liveness.AddAndCheckLiveCheck; import tlc2.tool.liveness.ILiveCheck; import tlc2.tool.liveness.LiveCheck; import tlc2.tool.liveness.Liveness; import tlc2.tool.liveness.NoOpLiveCheck; +import tlc2.util.IStateWriter; import tlc2.util.IdThread; -import tlc2.util.ObjLongTable; -import tlc2.util.StateWriter; -import tlc2.util.statistics.BucketStatistics; +import tlc2.util.statistics.ConcurrentBucketStatistics; import tlc2.util.statistics.DummyBucketStatistics; import tlc2.util.statistics.IBucketStatistics; +import tlc2.value.IValue; import util.DebugPrinter; -import util.FileUtil; -import util.FilenameToStream; /** * The abstract checker * @author Simon Zambrovski - * @version $Id$ */ -public abstract class AbstractChecker implements Cancelable +public abstract class AbstractChecker { + /** + * True when unit tests explicitly request to use + * {@link AddAndCheckLiveCheck} to run liveness checking after each + * insertion into the behavior graph. This should only be true if you + * exactly know what you are doing. If you don't and this is true, make sure + * it's false. + */ + public static boolean LIVENESS_TESTING_IMPLEMENTATION = Boolean.getBoolean(ILiveCheck.class.getName() + ".testing"); + protected static final boolean LIVENESS_STATS = Boolean.getBoolean(Liveness.class.getPackage().getName() + ".statistics"); - // SZ Mar 9, 2009: static modifier removed - protected AtomicLong numOfGenStates; protected TLCState predErrState; protected TLCState errState; + protected int errorCode; protected boolean done; protected boolean keepCallStack; - protected boolean checkDeadlock; - protected boolean checkLiveness; - protected String fromChkpt; - public String metadir; - public Tool tool; - public final SpecObj specObj; - public Action[] invariants; - public Action[] impliedActions; - public Action[] impliedInits; - public Action[] actions; - protected StateWriter allStateWriter; - protected boolean cancellationFlag; + protected final boolean checkDeadlock; + protected final boolean checkLiveness; + protected final String fromChkpt; + public final String metadir; + public final ITool tool; + protected final IStateWriter allStateWriter; + protected IWorker[] workers; protected final ILiveCheck liveCheck; + /** + * Timestamp of when model checking started. + */ + protected final long startTime; /** * Constructor of the abstract model checker @@ -65,59 +71,63 @@ public abstract class AbstractChecker implements Cancelable * @param resolver * @param spec - pre-built specification object (e.G. from calling SANY from the tool previously) */ - public AbstractChecker(String specFile, String configFile, String dumpFile, boolean deadlock, String fromChkpt, - boolean preprocess, FilenameToStream resolver, SpecObj spec) throws EvalException, IOException - { - this.cancellationFlag = false; - - this.checkDeadlock = deadlock; - - int lastSep = specFile.lastIndexOf(FileUtil.separatorChar); - String specDir = (lastSep == -1) ? "" : specFile.substring(0, lastSep + 1); - specFile = specFile.substring(lastSep + 1); - - this.tool = new Tool(specDir, specFile, configFile, resolver); - - this.specObj = this.tool.init(preprocess, spec); + public AbstractChecker(ITool tool, String metadir, final IStateWriter stateWriter, + boolean deadlock, String fromChkpt, final long startTime) throws EvalException, IOException { + this.tool = tool; + + this.checkDeadlock = deadlock; this.checkLiveness = !this.tool.livenessIsTrue(); - OutputCollector.setModuleNode(this.tool.rootModule); - // moved to file utilities - this.metadir = FileUtil.makeMetaDir(specDir, fromChkpt); + this.metadir = metadir; - this.numOfGenStates = new AtomicLong(0); this.errState = null; this.predErrState = null; this.done = false; + this.errorCode = EC.NO_ERROR; this.keepCallStack = false; this.fromChkpt = fromChkpt; + + this.allStateWriter = stateWriter; + + this.startTime = startTime; - // Initialize dumpFile: - if (dumpFile != null) - { - this.allStateWriter = new StateWriter(dumpFile); + if (TLCGlobals.isCoverageEnabled()) { + CostModelCreator.create(this.tool); } - - this.impliedInits = this.tool.getImpliedInits(); // implied-inits to be checked - this.invariants = this.tool.getInvariants(); // invariants to be checked - this.impliedActions = this.tool.getImpliedActions(); // implied-actions to be checked - this.actions = this.tool.getActions(); // the sub-actions - + if (this.checkLiveness) { + if (tool.hasSymmetry()) { + // raise warning... + MP.printWarning(EC.TLC_FEATURE_UNSUPPORTED_LIVENESS_SYMMETRY); + } + if (tool.hasStateOrActionConstraints()) { + MP.printWarning(EC.TLC_FEATURE_LIVENESS_CONSTRAINTS); + } // Initialization for liveness checking: report("initializing liveness checking"); IBucketStatistics stats = new DummyBucketStatistics(); if (LIVENESS_STATS) { - stats = new BucketStatistics("Histogram vertex out-degree", LiveCheck.class.getPackage().getName(), + stats = new ConcurrentBucketStatistics("Histogram vertex out-degree", LiveCheck.class.getPackage().getName(), "DiskGraphsOutDegree"); } - this.liveCheck = new LiveCheck(this.tool, this.actions, this.metadir, stats); + if (LIVENESS_TESTING_IMPLEMENTATION) { + this.liveCheck = new AddAndCheckLiveCheck(this.tool, this.metadir, stats); + } else { + this.liveCheck = new LiveCheck(this.tool, this.metadir, stats, stateWriter); + } report("liveness checking initialized"); } else { this.liveCheck = new NoOpLiveCheck(this.tool, this.metadir); } + + scheduleTermination(new TimerTask() { + @Override + public void run() { + AbstractChecker.this.stop(); + } + }); } public final void setDone() @@ -125,21 +135,19 @@ public abstract class AbstractChecker implements Cancelable this.done = true; } - protected final void incNumOfGenStates(int n) - { - this.numOfGenStates.getAndAdd(n); - } - /** * Set the error state. * <strong>Note:</note> this method must be protected by lock */ - public boolean setErrState(TLCState curState, TLCState succState, boolean keep) + public boolean setErrState(TLCState curState, TLCState succState, boolean keep, int errorCode) { - if (!TLCGlobals.continuation && this.done) + assert Thread.holdsLock(this) : "Caller thread has to hold monitor!"; + if (!TLCGlobals.continuation && this.done) return false; + IdThread.resetCurrentState(); this.predErrState = curState; this.errState = (succState == null) ? curState : succState; + this.errorCode = errorCode; this.done = true; this.keepCallStack = keep; return true; @@ -151,46 +159,221 @@ public abstract class AbstractChecker implements Cancelable */ protected void reportCoverage(IWorker[] workers) { - if (TLCGlobals.coverageInterval >= 0) - { - MP.printMessage(EC.TLC_COVERAGE_START); - // First collecting all counts from all workers: - ObjLongTable counts = this.tool.getPrimedLocs(); - OutputCollector.setModuleNode(this.tool.rootModule); - Hashtable<String, Location> locationTable = new Hashtable<String, Location>(); - for (int i = 0; i < workers.length; i++) - { - ObjLongTable counts1 = workers[i].getCounts(); - ObjLongTable.Enumerator keys = counts1.keys(); - Object key; - while ((key = keys.nextElement()) != null) - { - String loc = ((SemanticNode) key).getLocation().toString(); - counts.add(loc, counts1.get(key)); - locationTable.put(loc, ((SemanticNode) key).getLocation()); + // Without actions (empty spec) there won't be any statistics anyway. + if (TLCGlobals.isCoverageEnabled() && this.tool.getActions().length > 0) + { + CostModelCreator.report(this.tool, this.startTime); + } + } + + public static final double calculateOptimisticProbability(final long numOfDistinctStates, final long numOfGenStates) { + return numOfDistinctStates * ((numOfGenStates - numOfDistinctStates) / Math.pow(2, 64)); + } + + public static final void reportSuccess(final long numOfDistinctStates, final long numOfGenStates) + throws IOException { + final double optimisticProb = calculateOptimisticProbability(numOfDistinctStates, numOfGenStates); + MP.printMessage(EC.TLC_SUCCESS, new String[] { "val = " + ProbabilityToString(optimisticProb, 2) }); + } + + public static final void reportSuccess(final long numOfDistinctStates, final long actualDistance, + final long numOfGenStates) throws IOException { + // Prevent div-by-zero when calculating collision probabilities when no states + // are generated. + if (numOfDistinctStates == numOfGenStates && numOfGenStates == 0) { + // When the number of states is zero, printing a collision probability is + // useless anyway. But the Toolbox will probably crash if omitted. + MP.printMessage(EC.TLC_SUCCESS, new String[] { "val = 0.0", "val = 0.0" }); + return; + } + // shown as 'calculated' in Toolbox + final String optimisticProbStr = "val = " + + ProbabilityToString(calculateOptimisticProbability(numOfDistinctStates, numOfGenStates), 2); + + // shown as 'observed' in Toolbox + final BigDecimal actualProb = BigDecimal.valueOf(1d).divide(BigDecimal.valueOf(actualDistance), + new MathContext(2)); + final String actualProbStr = "val = " + ProbabilityToString(actualProb.doubleValue(), 2); + MP.printMessage(EC.TLC_SUCCESS, new String[] { optimisticProbStr, actualProbStr }); + } + + /** + * This method added by LL on 17 April 2012 to replace the use of the PrintfFormat + * method in reportSuccess. + * + * Returns a string representing the decimal representation of a probability to + * a given number of significant digits. If the input is not a probability, or if + * some error is found, then it returns the result of applying Double.toString(long) + * to the value. + * + * Warning: the code makes the following assumption: + * - Double.toString(v) returns a decimal representation of v of the + * form [d]* ["." [d]+ ["E" [+ | -] [d]+] where d is a decimal digit and + * [x] = 0 or 1 instance of x + * [x]* = any number of instances of x + * [x]+ = any non-zero number of instances of x + * x | y = an x or a y + * + * @param val - the probability represented as a long; must satisfy 0 <= val <= 1. + * @param significantDigits - the number of significant digits to include; must be > 0. + * @return + */ + private static final String ProbabilityToString(double val, int significantDigits) { + /* + * If val = 0 (which shouldn't happen), return "0.0" + */ + if (val == 0) { + return "0.0"; + } + + String valString = Double.toString(val) ; + int valStringLen = valString.length(); + + String result = ""; + int next = 0; // pointer to the next character in valString to examine. + int significantDigitsFound = 0; + + /* + * Skip past leading zeros. + */ + while ((next < valStringLen) && (valString.charAt(next) == '0')) { + next++ ; + } + + /* + * Append all the following digits to result, incrementing + * significantDigits for each one. + */ + while ( (next < valStringLen) && + Character.isDigit(valString.charAt(next))) { + result = result + valString.charAt(next); + significantDigitsFound++; + next++ ; + } + + /* + * IF next character is not "." + * THEN IF at end THEN return result + * ELSE return valString. + */ + if (next == valStringLen) { + return result; + } else if (valString.charAt(next) != '.') { + return valString; + } + + + /* + * IF significantDigitsFound >= significantDigits, + * THEN skip over "." and the following digits. + * (this should not happen) + * ELSE append "." to result ; + * IF significantDigitsFound = 0 + * THEN copy each of the following "0"s of valString to result; + * copy up to significantDigits - significantDigitsFound + * following digits of valString to result; + * IF next char of valString a digit >= "5" + * THEN propagate a carry backwards over the digits of result + * -- e.g., changing ".019" to ".020"; + * Skip over remaining digits of valString; + */ + if (significantDigitsFound >= significantDigits) { + next++ ; + while ( (next < valStringLen) && + Character.isDigit(valString.charAt(next))) { + next++ ; + } + } else { + next++; + result = result + "."; + if (significantDigitsFound == 0) { + while ((next < valStringLen) && (valString.charAt(next) == '0')) { + next++ ; + result = result + "0"; } } - // Reporting: - Object[] skeys = counts.sortStringKeys(); - for (int i = 0; i < skeys.length; i++) - { - long val = counts.get(skeys[i]); - //MP.printMessage(EC.TLC_COVERAGE_VALUE, new String[] { skeys[i].toString(), String.valueOf(val) }); - Location location = locationTable.get(skeys[i]); - if(location != null){ - OutputCollector.putLineCount(location, val); + while ((next < valStringLen) && + Character.isDigit(valString.charAt(next)) && + significantDigitsFound < significantDigits ) { + result = result + valString.charAt(next); + next++; + significantDigitsFound++; + } + if ((next < valStringLen) && + Character.isDigit(valString.charAt(next)) && + Character.digit(valString.charAt(next), 10) >= 5) { + int prev = result.length()-1; // the next digit of result to increment + boolean done = false; + while (!done) { + if (prev < 0) { + result = "1" + result; + done = true; + } else { + char prevChar = result.charAt(prev); + String front = result.substring(0, prev); + String back = result.substring(prev+1); + if (Character.isDigit(prevChar)) { + if (prevChar == '9') { + result = front + '0' + back; + } else { + result = front + Character.forDigit(Character.digit(prevChar, 10)+1, 10) + back; + done = true; + } + + } else { + // prevChar must be '.', so just continue + } + } + prev--; } } - MP.printMessage(EC.TLC_COVERAGE_END); + while ((next < valStringLen) && + Character.isDigit(valString.charAt(next))) { + next++; + } + } + + /* + * IF next at end of valString or at "E" + * THEN copy remaining chars of valString to result; + * return result + * ELSE return valString + */ + if (next >= valStringLen) { + return result; + } + if (valString.charAt(next)=='E') { + next++; + result = result + "E"; + while (next < valStringLen) { + result = result + valString.charAt(next); + next++; + } + return result; } + return valString; } +// The following method used for testing ProbabilityToString +// +// public static void main(String[] args) { +// double[] test = new double[] {.5, .0995, .00000001, 001.000, .0022341, +// .0022351, 3.14159E-12, +// 00.999, .002351111, 22.8E-14, 0.000E-12, +// 37, 0033D, 04.85, -35.3}; +// int i = 0; +// while (i < test.length) { +// System.out.println("" + i + ": " + Double.toString(test[i]) + " -> " + ProbabilityToString(test[i],2)); +// i++; +// } +// } + /** * Initialize the model checker - * @return + * @return an error code, or <code>EC.NO_ERROR</code> on success * @throws Throwable */ - public abstract boolean doInit(boolean ignoreCancel) throws Throwable; + public abstract int doInit(boolean ignoreCancel) throws Throwable; /** * I believe this method is called after the initial states are computed @@ -199,30 +382,20 @@ public abstract class AbstractChecker implements Cancelable * Create the partial state space for given starting state up * to the given depth or the number of states. */ - public final boolean runTLC(int depth) throws Exception + public final int runTLC(int depth) throws Exception { - // SZ Feb 23, 2009: exit if canceled - if (this.cancellationFlag) - { - return false; - } - if (depth < 2) { - return true; + return EC.NO_ERROR; } - // Start all the workers: - IdThread[] workers = startWorkers(this, depth); + workers = startWorkers(this, depth); // Check progress periodically: // Comment added by LL on 9 April 2012. The coverage is printed // every `count' times that the progress is printed. int count = TLCGlobals.coverageInterval / TLCGlobals.progressInterval; - // work to be done prior loop entry - runTLCPreLoop(); - // I added the `if (!this.done)' to the following statement. // I have no idea what this wait is for, but apparently // because of changes made by Simon, it caused TLC to wait for @@ -253,11 +426,13 @@ public abstract class AbstractChecker implements Cancelable // SZ Feb 23, 2009: exit if canceled // added condition to run in the cycle // while (true) { - while (!this.cancellationFlag) + int result = EC.NO_ERROR; + while (true) { - if (!this.doPeriodicWork()) + result = this.doPeriodicWork(); + if (result != EC.NO_ERROR) { - return false; + return result; } synchronized (this) { @@ -284,14 +459,19 @@ public abstract class AbstractChecker implements Cancelable { workers[i].join(); } - return true; - } - - public void setCancelFlag(boolean flag) - { - this.cancellationFlag = flag; + return EC.NO_ERROR; } + public final void setAllValues(int idx, IValue val) { + for (int i = 0; i < this.workers.length; i++) { + workers[i].setLocalValue(idx, val); + } + } + + public final IValue getValue(int i, int idx) { + return workers[i].getLocalValue(idx); + } + /** * Debugging support * @param message @@ -307,24 +487,17 @@ public abstract class AbstractChecker implements Cancelable * @param checkIndex the check level (depth or level) * @return the array of initialized worker threads */ - protected abstract IdThread[] startWorkers(AbstractChecker checker, int checkIndex); - - /** - * Hook to run some work before entering the worker loop - */ - protected void runTLCPreLoop() - { - } + protected abstract IWorker[] startWorkers(AbstractChecker checker, int checkIndex); /** * Usually * Check liveness: check liveness properties on the partial state graph. * Checkpoint: checkpoint three data structures: the state set, the * state queue, and the state trace. - * @return + * @return an error code, or <code>EC.NO_ERROR</code> on success * @throws Exception */ - public abstract boolean doPeriodicWork() throws Exception; + public abstract int doPeriodicWork() throws Exception; /** * Method called from the main worker loop @@ -336,7 +509,55 @@ public abstract class AbstractChecker implements Cancelable /** * Main method of the model checker + * @return an error code, or <code>EC.NO_ERROR</code> on success * @throws Exception */ - public abstract void modelCheck() throws Exception; + final public int modelCheck() throws Exception { + final int result = modelCheckImpl(); + return (result != EC.NO_ERROR) ? result : errorCode; + } + + protected abstract int modelCheckImpl() throws Exception; + + public int getProgress() { + return -1; + } + + public void stop() { + throw new UnsupportedOperationException("stop not implemented"); + } + + public void suspend() { + throw new UnsupportedOperationException("suspend not implemented"); + } + + public void resume() { + throw new UnsupportedOperationException("resume not implemented"); + } + + static void scheduleTermination(final TimerTask tt) { + // Stops model checker after the given time in seconds. If model checking + // terminates before stopAfter seconds, the timer task will never run. + // Contrary to TLCSet("exit",...) this does not require a spec modification. Is + // is likely of little use for regular TLC users. In other words, this is meant + // to be a developer only feature and thus configured via a system property and + // not a regular TLC parameter. + final long stopAfter = Long.getLong(TLC.class.getName() + ".stopAfter", -1L); + if (stopAfter > 0) { + final Timer stopTimer = new Timer("TLCStopAfterTimer"); + stopTimer.schedule(tt, stopAfter * 1000L); // seconds to milliseconds. + } + } + + protected boolean isTimeBound() { + return Long.getLong(TLC.class.getName() + ".stopAfter", -1L) != -1; + } + + public long getStateQueueSize() { + return -1; + } + + public long getDistinctStatesGenerated() { + return -1; + } } diff --git a/tlatools/src/tlc2/tool/Action.java b/tlatools/src/tlc2/tool/Action.java index ba497238ed2933ab19af1d30d32c3b17f4b0984d..0a756b6a49810ccc66f5d130a2d25d21bff1f1db 100644 --- a/tlatools/src/tlc2/tool/Action.java +++ b/tlatools/src/tlc2/tool/Action.java @@ -5,28 +5,94 @@ package tlc2.tool; import java.io.Serializable; +import tla2sany.semantic.OpDefNode; import tla2sany.semantic.SemanticNode; +import tla2sany.st.Location; +import tla2sany.st.SyntaxTreeConstants; +import tla2sany.st.TreeNode; +import tlc2.tool.coverage.CostModel; import tlc2.util.Context; +import util.UniqueString; public final class Action implements ToolGlobals, Serializable { + private static final UniqueString UNNAMED_ACTION = UniqueString.uniqueStringOf("UnnamedAction"); + /* A TLA+ action. */ /* Fields */ public final SemanticNode pred; // Expression of the action public final Context con; // Context of the action + private final UniqueString actionName; + private OpDefNode opDef = null; + public CostModel cm = CostModel.DO_NOT_RECORD; /* Constructors */ public Action(SemanticNode pred, Context con) { - this.pred = pred; - this.con = con; + this(pred, con, UNNAMED_ACTION); + } + + private Action(SemanticNode pred, Context con, UniqueString actionName) { + this.pred = pred; + this.con = con; + this.actionName = actionName; } - /* Returns a string representation of this action. */ + public Action(SemanticNode pred, Context con, OpDefNode opDef) { + // opDef null when action not declared, i.e. Spec == x = 0 /\ ... + // See test64 and test64a and others. + this(pred, con, opDef != null ? opDef.getName() : UNNAMED_ACTION); + this.opDef = opDef; + } + +/* Returns a string representation of this action. */ public final String toString() { return "<Action " + pred.toString() + ">"; } public final String getLocation() { - return "<Action " + pred.getLocation() + ">"; + // It is possible that actionName is "Action" but lets ignore it for now. + if (actionName != UNNAMED_ACTION && actionName != null && !"".equals(actionName.toString())) { + // If known, print the action name instead of the generic string "Action". + return "<" + actionName + " " + pred.getLocation() + ">"; + } + return "<Action " + pred.getLocation() + ">"; + } + + /** + * @return The name of this action. Can be {@link Action#UNNAMED_ACTION}. + */ + public final UniqueString getName() { + return actionName; } + + /** + * @return The OpDefNode corresponding to this Action or <code>null</code> if + * the Action is not explicitly declared. I.e. "Spec == x = 42 /\ [][x' + * = x + 1]_x". + */ + public final OpDefNode getOpDef() { + return this.opDef; + } + + public final boolean isDeclared() { + // Spec == x = 0 /\ [][x' = x + 1]_x has no declared actions. + return getDeclaration() != Location.nullLoc; + } + + /** + * @return The {@link Location} of the <code>Action</code>'s declaration or + * <code>Location.nullLoc</code> if {@link #isDeclared()} is + * false. + */ + public Location getDeclaration() { + if (this.opDef != null) { + final TreeNode tn = opDef.getTreeNode(); + if (tn != null && tn.one() != null && tn.one().length >= 1) { + final TreeNode treeNode = tn.one()[0]; + assert treeNode.isKind(SyntaxTreeConstants.N_IdentLHS); + return treeNode.getLocation(); + } + } + return Location.nullLoc; + } } diff --git a/tlatools/src/tlc2/tool/CallStack.java b/tlatools/src/tlc2/tool/CallStack.java index efda0f2d88b00852a0f402db065c07c1ec07bb2e..2d1e4360e17cb69151d5052135db3e793aef3215 100644 --- a/tlatools/src/tlc2/tool/CallStack.java +++ b/tlatools/src/tlc2/tool/CallStack.java @@ -16,6 +16,7 @@ public class CallStack { private SemanticNode[] stack; // the call stack private int index; // pointer to the empty slot + private boolean frozen; public final void push(SemanticNode expr) { if (this.index == this.stack.length) { @@ -24,47 +25,60 @@ public class CallStack { this.stack[this.index++] = expr; } - public final void pop() { this.index--; } + public final void pop() { if(!frozen) this.index--; } - public final int size() { return this.index; } + /** + * Calling freeze turns all subsequent pop operations into no-ops. + */ + public void freeze() { + this.frozen = true; + } + public final int size() { return this.index; } + private final void resize() { int len = 2 * this.stack.length; SemanticNode[] stack1 = new SemanticNode[len]; System.arraycopy(this.stack, 0, stack1, 0, this.stack.length); this.stack = stack1; } - + // Returns a string representation of this. - public final String toString() + public final String toString() { - /* - * Moved in the distinction if the call stack is empty or not (from Tool) - */ - if (this.index > 0) - { - StringBuffer sb = new StringBuffer(); - for (int i = 0; i < this.index; i++) { - sb.append(i + ". "); - SemanticNode expr = this.stack[i]; - Location loc = expr.getTreeNode().getLocation(); - sb.append("Line "); - sb.append(loc.beginLine()); - sb.append(", column "); - sb.append(loc.beginColumn()); - sb.append(" to line "); - sb.append(loc.endLine()); - sb.append(", column "); - sb.append(loc.endColumn()); - sb.append(" in "); - sb.append(loc.source() + "\n"); - } - sb.append("\n"); - return sb.toString(); - - } else { - return " The error call stack is empty.\n"; + /* + * Moved in the distinction if the call stack is empty or not (from Tool) + */ + if (this.index > 0) + { + final StringBuffer sb = new StringBuffer(); + SemanticNode expr = null; + int stackDepth = 0; + for (int i = 0; i < this.index; i++) { + if(expr == this.stack[i]) { + // Skip consecutive identical SemanticNodes. + continue; + } + expr = this.stack[i]; + Location loc = expr.getTreeNode().getLocation(); + sb.append(stackDepth + ". "); + sb.append("Line "); + sb.append(loc.beginLine()); + sb.append(", column "); + sb.append(loc.beginColumn()); + sb.append(" to line "); + sb.append(loc.endLine()); + sb.append(", column "); + sb.append(loc.endColumn()); + sb.append(" in "); + sb.append(loc.source() + "\n"); + stackDepth++; } + sb.append("\n"); + return sb.toString(); + } else { + return " The error call stack is empty.\n"; + } } } diff --git a/tlatools/src/tlc2/tool/Cancelable.java b/tlatools/src/tlc2/tool/Cancelable.java deleted file mode 100644 index 0cdc618662ca8fcdc782bbd38cffa09cbe433350..0000000000000000000000000000000000000000 --- a/tlatools/src/tlc2/tool/Cancelable.java +++ /dev/null @@ -1,15 +0,0 @@ -package tlc2.tool; - -/** - * The interface for a cancelable. - * @author Simon Zambrovski - * @version $Id$ - */ -public interface Cancelable -{ - /** - * Sets the flag to cancel the instance - * @param flag cancellation flag - */ - public void setCancelFlag(boolean flag); -} diff --git a/tlatools/src/tlc2/tool/CheckImpl.java b/tlatools/src/tlc2/tool/CheckImpl.java index 49337bf10cbd27d2a52b855318e547e420b9b961..9b5b91f5a3e74b9b9343fdcf8aa35b915f78731c 100644 --- a/tlatools/src/tlc2/tool/CheckImpl.java +++ b/tlatools/src/tlc2/tool/CheckImpl.java @@ -6,11 +6,13 @@ package tlc2.tool; import java.io.IOException; import tlc2.TLCGlobals; +import tlc2.output.EC; import tlc2.output.StatePrinter; import tlc2.tool.fp.FPSet; import tlc2.tool.fp.FPSetConfiguration; import tlc2.tool.fp.FPSetFactory; import tlc2.tool.queue.DiskStateQueue; +import tlc2.util.NoopStateWriter; import util.ToolIO; /** @@ -30,15 +32,15 @@ public abstract class CheckImpl extends ModelChecker { * @param fpMemSize : This parameter added by Yuan Yu on 6 Apr 2010 * because same parameter was added to the ModelChecker constructor. */ - public CheckImpl(String specFile, String configFile, boolean deadlock, + public CheckImpl(ITool tool, String metadir, boolean deadlock, int depth, String fromChkpt, final FPSetConfiguration fpSetConfig) throws IOException { // SZ Feb 20, 2009: patched due to changes to ModelCheker - super(specFile, configFile, null, deadlock, fromChkpt, null, null, fpSetConfig); // no name resolver and no specobj + super(tool, metadir, new NoopStateWriter(), deadlock, fromChkpt, fpSetConfig, System.currentTimeMillis()); // no name resolver and no specobj this.depth = depth; this.curState = null; this.coverSet = FPSetFactory.getFPSet(); - this.coverSet.init(TLCGlobals.getNumWorkers(), this.metadir, specFile+"_cs"); + this.coverSet.init(TLCGlobals.getNumWorkers(), this.metadir, tool.getRootName()+"_cs"); this.stateEnum = null; } @@ -68,10 +70,11 @@ public abstract class CheckImpl extends ModelChecker { this.doInit(false); } ToolIO.out.println("Creating a partial state space of depth " + - this.depth + " ... "); - if (!this.runTLC(this.depth)) { + this.depth + " ... "); + final int result = this.runTLC(this.depth); + if (result != EC.NO_ERROR) { ToolIO.out.println("\nExit: failed to create the partial state space."); - System.exit(1); + System.exit(EC.ExitStatus.errorConstantToExitStatus(result)); } ToolIO.out.println("completed."); this.lastTraceTime = System.currentTimeMillis(); @@ -92,8 +95,9 @@ public abstract class CheckImpl extends ModelChecker { int depth1= this.trace.getLevel(st.uid) + depth; this.theStateQueue = new DiskStateQueue(this.metadir); this.theStateQueue.enqueue(st); - if (!this.runTLC(depth1)) { - System.exit(1); + final int result = this.runTLC(depth1); + if (result != EC.NO_ERROR) { + System.exit(EC.ExitStatus.errorConstantToExitStatus(result)); } } @@ -115,9 +119,9 @@ public abstract class CheckImpl extends ModelChecker { StatePrinter.printState(s1); return false; } - int cnt = this.impliedActions.length; + int cnt = this.tool.getImpliedActions().length; for (int i = 0; i < cnt; i++) { - if (!this.tool.isValid(this.impliedActions[i], s0, s1)) { + if (!this.tool.isValid(this.tool.getImpliedActions()[i], s0, s1)) { ToolIO.out.println("Error: Action property " + this.tool.getImpliedActNames()[i] + " is violated."); StatePrinter.printState(s0); @@ -138,12 +142,11 @@ public abstract class CheckImpl extends ModelChecker { boolean seen = this.coverSet.put(fp); if (!seen) { if (!this.theFPSet.contains(fp)) { - long loc = this.trace.writeState(this.curState, fp); - state.uid = loc; + state.uid = this.trace.writeState(this.curState, fp); // Check invariant properties of the state: - int cnt = this.invariants.length; + int cnt = this.tool.getInvariants().length; for (int j = 0; j < cnt; j++) { - if (!this.tool.isValid(this.invariants[j], state)) { + if (!this.tool.isValid(this.tool.getInvariants()[j], state)) { // We get here because of invariant violation: ToolIO.out.println("Error: Invariant " + this.tool.getInvNames()[j] + " is violated. The behavior up to this point is:"); diff --git a/tlatools/src/tlc2/tool/CheckImplFile.java b/tlatools/src/tlc2/tool/CheckImplFile.java index a4f6eaaecb9c82b55b19f62a667bb0e4ce91ef84..b813a91d12f3d2e2031b6c2a2b2939555b32c12f 100644 --- a/tlatools/src/tlc2/tool/CheckImplFile.java +++ b/tlatools/src/tlc2/tool/CheckImplFile.java @@ -19,9 +19,11 @@ import tlc2.TLCGlobals; import tlc2.output.EC; import tlc2.output.MP; import tlc2.tool.fp.FPSetConfiguration; +import tlc2.tool.impl.FastTool; import tlc2.util.FP64; import util.Assert; import util.FileUtil; +import util.TLAConstants; import util.ToolIO; import util.UniqueString; @@ -39,10 +41,10 @@ public class CheckImplFile extends CheckImpl * to ModelChecker constructor. * */ - public CheckImplFile(String specFile, String configFile, boolean deadlock, int depth, String fromChkpt, + public CheckImplFile(ITool tool, String metadir, boolean deadlock, int depth, String fromChkpt, String traceFile, final FPSetConfiguration fpSetConfig) throws IOException { - super(specFile, configFile, deadlock, depth, fromChkpt, fpSetConfig); + super(tool, metadir, deadlock, depth, fromChkpt, fpSetConfig); this.traceFile = traceFile; this.states = null; this.sidx = 0; @@ -178,9 +180,11 @@ public class CheckImplFile extends CheckImpl index++; if (index < args.length) { configFile = args[index++]; - int len = configFile.length(); - if (configFile.startsWith(".cfg", len-4)) { - configFile = configFile.substring(0, len-4); + if (configFile.endsWith(TLAConstants.Files.CONFIG_EXTENSION)) + { + configFile + = configFile.substring(0, + (configFile.length() - TLAConstants.Files.CONFIG_EXTENSION.length())); } } else { @@ -280,13 +284,12 @@ public class CheckImplFile extends CheckImpl return; } if (mainFile != null) { - printErrorMsg(MP.getError(EC.CHECK_PARAM_UNRECOGNIZED, new String[]{mainFile, args[index] })); + printErrorMsg(MP.getError(EC.CHECK_PARAM_UNRECOGNIZED, new String[]{args[index], mainFile })); return; } mainFile = args[index++]; - int len = mainFile.length(); - if (mainFile.startsWith(".tla", len-4)) { - mainFile = mainFile.substring(0, len-4); + if (mainFile.endsWith(TLAConstants.Files.TLA_EXTENSION)) { + mainFile = mainFile.substring(0, (mainFile.length() - TLAConstants.Files.TLA_EXTENSION.length())); } } } @@ -299,6 +302,9 @@ public class CheckImplFile extends CheckImpl if (configFile == null) configFile = mainFile; if (traceFile == null) traceFile = mainFile + "_trace"; + final File f = new File(mainFile); + String metadir = FileUtil.makeMetaDir(f.isAbsolute() ? f.getParent() : "", fromChkpt); + try { // Initialize: if (fromChkpt != null) { @@ -308,7 +314,8 @@ public class CheckImplFile extends CheckImpl FP64.Init(0); // Start the checker: - CheckImplFile checker = new CheckImplFile(mainFile, configFile, deadlock, + final ITool tool = new FastTool(mainFile, configFile); + CheckImplFile checker = new CheckImplFile(tool, metadir, deadlock, depth, fromChkpt, traceFile, new FPSetConfiguration()); checker.init(); while (true) { diff --git a/tlatools/src/tlc2/tool/ConcurrentTLCTrace.java b/tlatools/src/tlc2/tool/ConcurrentTLCTrace.java new file mode 100644 index 0000000000000000000000000000000000000000..0cf1ce2b3a15d4f16e9ff95eb7b6db2911b2fc6f --- /dev/null +++ b/tlatools/src/tlc2/tool/ConcurrentTLCTrace.java @@ -0,0 +1,276 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool; + +import java.io.File; +import java.io.IOException; + +import tlc2.TLCGlobals; +import tlc2.util.LongVec; + +/** + * This implementation of a Trace is concurrent in that multiple workers can add + * states to it whereas with {@link TLCTrace} concurrent adds/appends block and + * are serialized. The latter has been identified to be a scalability bottleneck + * of bread-first search (safety checking). + * <p> + * {@link ConcurrentTLCTrace} - in comparison to {@link TLCTrace} - maintains + * one trace file per worker and thus supports concurrent appends: Each worker + * adds/appends an entry to its (dedicate) file. When a counter-example has to be + * created, the actual error-trace gets created from the union of all (partial) + * trace files. + */ +public class ConcurrentTLCTrace extends TLCTrace { + + private final Worker workers[]; + + public ConcurrentTLCTrace(String metadir, String specFile, TraceApp tool) throws IOException { + super(metadir, specFile, tool); + this.workers = new Worker[TLCGlobals.getNumWorkers()]; + } + + public Worker addWorker(Worker worker) { + this.workers[worker.myGetId()] = worker; + return worker; + } + + /** + * @see ConcurrentTLCTrace#getLevel() + */ + @Override + public final int getLevelForReporting() throws IOException { + return getLevel(); + } + + @Override + public synchronized final int getLevel() throws IOException { + int maxLevel = 1; // With a single, init state the level/progress/diameter is 1, not 0! + for (Worker worker : workers) { + maxLevel = Math.max(maxLevel, worker.getMaxLevel()); + } + return maxLevel; + } + + /** + * @see TLCTrace#getTrace(LongVec) + */ + public TLCStateInfo[] getTrace(final TLCState state) throws IOException { + if (state.isInitial()) { + return new TLCStateInfo[] {new TLCStateInfo(state)}; + } + + final LongVec fps = new LongVec(); + + // Starting at the given start fingerprint (which is the end of the + // trace from the point of the initial states), the sequence of + // predecessors fingerprints are reconstructed from the trace files up to + // an initial state. + synchronized (this) { + Record record = Record.getPredecessor(state, this.workers); + while (!record.isInitial()) { + fps.addElement(record.fp); + record = record.getPredecessor(); + } + // The fp of the final initial state. + fps.addElement(record.fp); + assert 0 <= fps.size() && fps.size() <= getLevel(); + } + + return getTrace(fps); + } + + public TLCStateInfo[] getTrace(final TLCState from, final TLCState to) throws IOException { + if (to.isInitial() || from.equals(to)) { + return new TLCStateInfo[] {new TLCStateInfo(to)}; + } + + final LongVec fps = new LongVec(); + + // Starting at the given start fingerprint (which is the end of the + // trace from the point of the initial states), the sequence of + // predecessors fingerprints are reconstructed from the trace files up to + // an initial state. + synchronized (this) { + Record record = Record.getPredecessor(to, this.workers); + while (record.fp != from.fingerPrint()) { + fps.addElement(record.fp); + record = record.getPredecessor(); + } + // The fp of the final initial state. + fps.addElement(record.fp); + assert 0 <= fps.size() && fps.size() <= getLevel(); + } + + return getTrace(new TLCStateInfo(from), fps); + } + + /** + * Write out a sequence of states that reaches s2 from an initial state, + * according to the spec. s2 is a next state of s1. + * + * @param s1 + * may not be null. + * @param s2 + * may be null. + * @throws IOException + * @throws WorkerException + */ + public void printTrace(final TLCState s1, final TLCState s2) throws IOException, WorkerException { + if (s1.isInitial()) { + printTrace(s1, s2, new TLCStateInfo[0]); + } else { + printTrace(s1, s2, getTrace(s1)); + } + } + + /* Checkpoint. */ + + public synchronized void beginChkpt() throws IOException { + for (Worker worker : workers) { + worker.beginChkpt(); + } + } + + public void commitChkpt() throws IOException { + for (Worker worker : workers) { + worker.commitChkpt(); + } + // MAK 06/08/2020: + // Touch MC.st.chkpt (in addition to the worker-created MC-${N}.chkpt files) + // that (single-worker) TLCTrace creates. The Toolbox checks for the existence + // of checkpoints by looking for this file (see + // org.lamport.tla.toolbox.tool.tlc.model.Model.getCheckpoints(boolean)). If + // the Toolbox check fails, a user cannot resume/restart model-checking from + // the checkpoint. Addresses Github issue #469 at + // https://github.com/tlaplus/tlaplus/issues/469 + // + // We create an empty file here instead of changing the Toolbox in case 3rd party + // integrations and scripts rely on the file's existence. Reading the empty + // MC.st.chkpt with an older TLC release will crash and thus make a user aware + // that recovery fails. + // + // createNewFile is safe to call periodically when a checkpoint get taken + // because it does *not* throw an exception if the file already exists. + new File(filename + ".chkpt").createNewFile(); + } + + public void recover() throws IOException { + // TODO Check that the number of disk .st files is >= workers.length. If it is + // lower, TLC runs with fewer workers than when the checkpoint was taken. Take + // this case into account. + + for (Worker worker : workers) { + worker.recover(); + } + } + + /* Enumerator */ + + public synchronized Enumerator elements() throws IOException { + final Worker.Enumerator[] enums = new Worker.Enumerator[workers.length]; + for (int j = 0; j < workers.length; j++) { + enums[j] = workers[j].elements(); + } + return new Enumerator() { + private int idx = 0; + + @Override + public long nextPos() { + if (idx >= workers.length) { + return -1L; + } + if (enums[idx].hasMoreFP()) { + return 42L; + } else if (idx + 1 >= workers.length) { + return -1L; + } else { + idx = idx + 1; + return enums[idx].hasMoreFP() ? 42L : -1L; + } + } + + @Override + public long nextFP() throws IOException { + if (!enums[idx].hasMoreFP()) { + idx = idx + 1; + } + return enums[idx].nextFP(); + } + + @Override + public void close() throws IOException { + for (Worker.Enumerator enumerator : enums) { + enumerator.close(); + } + } + + @Override + public void reset(long i) throws IOException { + idx = 0; + } + }; + } + + public static class Record { + + + static Record getPredecessor(final TLCState state, final Worker[] workers) throws IOException { + Record record = workers[state.workerId].readStateRecord(state.uid); + record.workers = workers; + return record.getPredecessor(); + } + + private final long ptr; + private final int worker; + private final long fp; + private Worker[] workers; + + public Record(final long ptr, final int worker, final long fp) { + this.ptr = ptr; + this.worker = worker; + this.fp = fp; + } + + public Worker getWorker() { + return this.workers[this.worker]; + } + + public boolean isInitial() { + return ptr == 1L; + } + + @Override + public String toString() { + return "Record [ptr=" + ptr + ", worker=" + worker + ", fp=" + fp + ", initial=" + isInitial() + "]"; + } + + Record getPredecessor() throws IOException { + final Record record = getWorker().readStateRecord(this.ptr); + record.workers = this.workers; + return record; + } + } +} diff --git a/tlatools/src/tlc2/tool/DFIDModelChecker.java b/tlatools/src/tlc2/tool/DFIDModelChecker.java index 6cf83959cfe510fa255c5168eafb90180dc4e0f2..c333ed588ace262232bccedf31ab9ff82803cf8b 100644 --- a/tlatools/src/tlc2/tool/DFIDModelChecker.java +++ b/tlatools/src/tlc2/tool/DFIDModelChecker.java @@ -3,21 +3,25 @@ package tlc2.tool; import java.io.IOException; +import java.util.Set; import java.util.concurrent.atomic.AtomicLong; +import java.util.stream.Collectors; -import tla2sany.modanalyzer.SpecObj; import tla2sany.semantic.ExprNode; +import tla2sany.semantic.OpDeclNode; import tlc2.TLCGlobals; import tlc2.output.EC; import tlc2.output.MP; import tlc2.tool.fp.dfid.FPIntSet; import tlc2.tool.fp.dfid.MemFPIntSet; +import tlc2.tool.impl.CallStackTool; import tlc2.tool.liveness.LiveException; +import tlc2.util.IStateWriter; import tlc2.util.IdThread; import tlc2.util.LongVec; -import tlc2.util.ObjLongTable; +import tlc2.util.SetOfStates; +import util.Assert; import util.FileUtil; -import util.FilenameToStream; import util.UniqueString; /** @@ -35,40 +39,63 @@ public class DFIDModelChecker extends AbstractChecker public TLCState[] theInitStates; // the set of initial states public long[] theInitFPs; // ... and their fps public FPIntSet theFPSet; // the set of reachable states (SZ: note the type) - protected DFIDWorker[] workers; // the workers + private final AtomicLong numOfGenStates; + + protected final ThreadLocal<Integer> threadLocal = new ThreadLocal<Integer>() { + protected Integer initialValue() { + return 1; + } + }; + + protected static final int INITIAL_CAPACITY = 16; /** * Constructor for running DFID + * @param startTime * @param resolver */ - public DFIDModelChecker(String specFile, String configFile, String dumpFile, boolean deadlock, String fromChkpt, - boolean preprocess, FilenameToStream resolver, SpecObj specObj) throws EvalException, IOException - { + public DFIDModelChecker(ITool tool, String metadir, final IStateWriter stateWriter, + boolean deadlock, String fromChkpt, long startTime) throws EvalException, IOException { // call the abstract constructor - super(specFile, configFile, dumpFile, deadlock, fromChkpt, preprocess, resolver, specObj); + super(tool, metadir, stateWriter, deadlock, fromChkpt, startTime); - this.theInitStates = null; + // https://github.com/tlaplus/tlaplus/issues/548 + Assert.check(TLCGlobals.getNumWorkers() == 1, EC.GENERAL, + "Depth-First Iterative Deepening mode does not support multiple workers (https://github.com/tlaplus/tlaplus/issues/548). Please run TLC with a single worker."); + // https://github.com/tlaplus/tlaplus/issues/548 + Assert.check(this.checkLiveness == false, EC.GENERAL, + "Depth-First Iterative Deepening mode does not support checking liveness properties (https://github.com/tlaplus/tlaplus/issues/548). Please check liveness properties in Breadth-First-Search mode."); + + this.theInitStates = null; this.theInitFPs = null; this.theFPSet = new MemFPIntSet(); // init the state set - this.theFPSet.init(TLCGlobals.getNumWorkers(), this.metadir, specFile); + this.theFPSet.init(TLCGlobals.getNumWorkers(), this.metadir, this.tool.getRootFile()); // Initialize all the workers: this.workers = new DFIDWorker[TLCGlobals.getNumWorkers()]; + this.numOfGenStates = new AtomicLong(0); } /** * This method does model checking on a TLA+ spec. All the visited * states are stored in the variable theFPSet. */ - public void modelCheck() throws Exception + @Override + protected int modelCheckImpl() throws Exception { + int result = EC.NO_ERROR; boolean recovered = this.recover(); try { - if (!this.checkAssumptions()) - return; - if (!this.doInit(false)) - return; + if (this.checkLiveness && liveCheck.getNumChecker() == 0) { + return MP.printError(EC.TLC_LIVE_FORMULA_TAUTOLOGY); + } + result = this.checkAssumptions(); + if (result != EC.NO_ERROR) + return result; + result = this.doInit(false); + if (result != EC.NO_ERROR) + return result; } catch (Throwable e) { // Initial state computation fails with an exception: @@ -81,18 +108,17 @@ public class DFIDModelChecker extends AbstractChecker } // Replay the error with the error stack recorded: - this.tool.setCallStack(); try { - this.numOfGenStates = new AtomicLong(0); - this.doInit(true); + this.numOfGenStates.set(0); + this.doInit(new CallStackTool(this.tool), true); } catch (Throwable e1) { - MP.printError(EC.TLC_NESTED_EXPRESSION, this.tool.getCallStack().toString()); + result = MP.printError(EC.TLC_NESTED_EXPRESSION, this.tool.toString()); } this.printSummary(false); this.cleanup(false); - return; + return result; } if (recovered) @@ -106,15 +132,15 @@ public class DFIDModelChecker extends AbstractChecker } // Return if there is no next state predicate: - if (this.actions.length == 0) + if (this.tool.getActions().length == 0) { this.reportSuccess(); this.printSummary(true); this.cleanup(true); - return; + return result; } - boolean success = false; + result = EC.GENERAL; try { boolean terminated = false; @@ -128,47 +154,56 @@ public class DFIDModelChecker extends AbstractChecker // Always check liveness properties at the end: if (this.checkLiveness) { - MP.printMessage(EC.TLC_CHECKING_TEMPORAL_PROPS, - new String[] { "complete", Long.toString(this.theFPSet.size()) }); + // Print progress statistics prior to liveness checking. + // Liveness checking can take a substantial amount of time + // and thus give the user some clues at what stage safety + // checking is. + MP.printMessage(EC.TLC_PROGRESS_STATS_DFID, new String[] { + String.valueOf(this.numOfGenStates), String.valueOf(theFPSet.size()) }); // SZ Jul 10, 2009: what for? // ToolIO.out.flush(); - success = liveCheck.finalCheck(); - if (!success) - return; + result = liveCheck.finalCheck(tool); + if (result != EC.NO_ERROR) + return result; } // We get here because the checking has been completed. - success = true; + result = EC.NO_ERROR; this.reportSuccess(); } else if (this.keepCallStack) { // Replay the error with the error stack recorded: - this.tool.setCallStack(); + final CallStackTool cTool = new CallStackTool(this.tool); try { - this.doNext(this.predErrState, this.predErrState.fingerPrint(), true, new ObjLongTable(10), + this.doNext(cTool, this.predErrState, this.predErrState.fingerPrint(), true, new StateVec(1), new LongVec()); } catch (Throwable e) { - MP.printError(EC.TLC_NESTED_EXPRESSION, this.tool.getCallStack().toString()); + MP.printError(EC.TLC_NESTED_EXPRESSION, cTool.toString()); } } break; } // Start working on this level: - MP.printMessage(EC.TLC_PROGRESS_STATS_DFID, new String[] { String.valueOf(level), + MP.printMessage(EC.TLC_PROGRESS_START_STATS_DFID, new String[] { String.valueOf(level), String.valueOf(this.numOfGenStates), String.valueOf(this.theFPSet.size()) }); FPIntSet.incLevel(); - success = this.runTLC(level); - if (!success) - return; + result = this.runTLC(level); + // Recent done flag before after the workers have checked the + // current level in preparation for the next level. + synchronized (this) { + this.done = false; + } + if (result != EC.NO_ERROR) + return result; // Check if we should stop at this level: for (int i = 0; i < this.workers.length; i++) { - if (this.workers[i].isTerminated()) + if (((DFIDWorker) this.workers[i]).isTerminated()) { terminated = true; break; @@ -177,7 +212,7 @@ public class DFIDModelChecker extends AbstractChecker boolean moreLevel = false; for (int i = 0; i < this.workers.length; i++) { - if (this.workers[i].hasMoreLevel()) + if (((DFIDWorker) this.workers[i]).hasMoreLevel()) { moreLevel = true; break; @@ -185,16 +220,21 @@ public class DFIDModelChecker extends AbstractChecker } terminated = terminated || !moreLevel; } + return result; } catch (Exception e) { // Assert.printStack(e); - success = false; - if (!(e instanceof LiveException)) + if (e instanceof LiveException) + { + result = ((LiveException)e).errorCode; + } else { - MP.printError(EC.GENERAL, e); // LL changed call 7 April 2012 + result = MP.printError(EC.GENERAL, e); // LL changed call 7 April 2012 } + return result; } finally { + final boolean success = result == EC.NO_ERROR; this.printSummary(success); this.cleanup(success); } @@ -202,7 +242,7 @@ public class DFIDModelChecker extends AbstractChecker /* Check the assumptions. * This code is a clone of the same method in ModelChecker */ - public final boolean checkAssumptions() + public final int checkAssumptions() { ExprNode[] assumps = this.tool.getAssumptions(); boolean[] isAxiom = this.tool.getAssumptionIsAxiom(); @@ -212,29 +252,30 @@ public class DFIDModelChecker extends AbstractChecker { if ((!isAxiom[i]) && !this.tool.isValid(assumps[i])) { - MP.printError(EC.TLC_ASSUMPTION_FALSE, assumps[i].toString()); - return false; + return MP.printError(EC.TLC_ASSUMPTION_FALSE, assumps[i].toString()); } } catch (Exception e) { // Assert.printStack(e); - MP.printError(EC.TLC_ASSUMPTION_EVALUATION_ERROR, + return MP.printError(EC.TLC_ASSUMPTION_EVALUATION_ERROR, new String[] { assumps[i].toString(), e.getMessage() }); - return false; } } - return true; + return EC.NO_ERROR; } /* Compute the set of initial states. */ // SZ Feb 23, 2009: added ignore cancel flag - public final boolean doInit(boolean ignoreCancel) throws Throwable + public final int doInit(boolean ignoreCancel) throws Throwable { + return doInit(this.tool, ignoreCancel); + } + private final int doInit(final ITool tool, boolean ignoreCancel) throws Throwable { TLCState curState = null; try { // Generate the initial states: - StateVec states = this.tool.getInitStates(); + StateVec states = tool.getInitStates(); this.numOfGenStates.set(states.size()); final long l = this.numOfGenStates.get(); //TODO casting to int is potentially dangerous @@ -245,12 +286,11 @@ public class DFIDModelChecker extends AbstractChecker { curState = states.elementAt(i); // Check if the state is a legal state - if (!this.tool.isGoodState(curState)) + if (!tool.isGoodState(curState)) { - MP.printError(EC.TLC_STATE_NOT_COMPLETELY_SPECIFIED_INITIAL, curState.toString()); - return false; + return MP.printError(EC.TLC_STATE_NOT_COMPLETELY_SPECIFIED_INITIAL, curState.toString()); } - boolean inModel = this.tool.isInModel(curState); + boolean inModel = tool.isInModel(curState); int status = FPIntSet.NEW; if (inModel) { @@ -262,40 +302,36 @@ public class DFIDModelChecker extends AbstractChecker this.theInitFPs[idx++] = fp; // Write out the state if asked - if (this.allStateWriter != null) - { - this.allStateWriter.writeState(curState); - } + this.allStateWriter.writeState(curState); // build behavior graph for liveness checking if (this.checkLiveness) { - liveCheck.addInitState(curState, fp); + liveCheck.addInitState(tool, curState, fp); } } } // Check properties of the state: if (status == FPIntSet.NEW) { - for (int j = 0; j < this.invariants.length; j++) + for (int j = 0; j < tool.getInvariants().length; j++) { - if (!this.tool.isValid(this.invariants[j], curState)) + if (!tool.isValid(tool.getInvariants()[j], curState)) { // We get here because of invariant violation: - MP.printError(EC.TLC_INVARIANT_VIOLATED_INITIAL, new String[] { this.tool.getInvNames()[j], + MP.printError(EC.TLC_INVARIANT_VIOLATED_INITIAL, new String[] { tool.getInvNames()[j], curState.toString() }); if (!TLCGlobals.continuation) - return false; + return EC.TLC_INVARIANT_VIOLATED_INITIAL; } } - for (int j = 0; j < this.impliedInits.length; j++) + for (int j = 0; j < tool.getImpliedInits().length; j++) { - if (!this.tool.isValid(this.impliedInits[j], curState)) + if (!tool.isValid(tool.getImpliedInits()[j], curState)) { // We get here because of implied-inits violation: - MP.printError(EC.TLC_PROPERTY_VIOLATED_INITIAL, new String[] { - this.tool.getImpliedInitNames()[j], curState.toString() }); - return false; + return MP.printError(EC.TLC_PROPERTY_VIOLATED_INITIAL, new String[] { + tool.getImpliedInitNames()[j], curState.toString() }); } } } @@ -316,13 +352,12 @@ public class DFIDModelChecker extends AbstractChecker // Assert.printStack(e); if (e instanceof OutOfMemoryError) { - MP.printError(EC.SYSTEM_OUT_OF_MEMORY_TOO_MANY_INIT); - return false; + return MP.printError(EC.SYSTEM_OUT_OF_MEMORY_TOO_MANY_INIT); } this.errState = curState; throw e; } - return true; + return EC.NO_ERROR; } /** @@ -332,18 +367,20 @@ public class DFIDModelChecker extends AbstractChecker * not been done in nextStates. Return true if it finds a leaf * successor of curState. */ - public final boolean doNext(TLCState curState, long cfp, boolean isLeaf, ObjLongTable counts, StateVec states, + public final boolean doNext(TLCState curState, long cfp, boolean isLeaf, StateVec states, + LongVec fps) throws Throwable { + return doNext(this.tool, curState, cfp, isLeaf, states, fps); + } + public final boolean doNext(final ITool tool, TLCState curState, long cfp, boolean isLeaf, StateVec states, LongVec fps) throws Throwable { boolean deadLocked = true; TLCState succState = null; - StateVec liveNextStates = null; - LongVec liveNextFPs = null; + SetOfStates liveNextStates = null; if (this.checkLiveness && isLeaf) { - liveNextStates = new StateVec(2); - liveNextFPs = new LongVec(2); + liveNextStates = new SetOfStates(INITIAL_CAPACITY * threadLocal.get()); } try @@ -351,37 +388,41 @@ public class DFIDModelChecker extends AbstractChecker int k = 0; boolean allSuccDone = true; boolean allSuccNonLeaf = true; - for (int i = 0; i < this.actions.length; i++) + for (int i = 0; i < tool.getActions().length; i++) { - StateVec nextStates = this.tool.getNextStates(this.actions[i], curState); + StateVec nextStates = tool.getNextStates(tool.getActions()[i], curState); int sz = nextStates.size(); - this.incNumOfGenStates(sz); + this.numOfGenStates.getAndAdd(sz); deadLocked = deadLocked && (sz == 0); for (int j = 0; j < sz; j++) { succState = nextStates.elementAt(j); // Check if the state is a legal state. - if (!this.tool.isGoodState(succState)) + if (!tool.isGoodState(succState)) { - if (this.setErrState(curState, succState, false)) - { - this.printTrace(EC.TLC_STATE_NOT_COMPLETELY_SPECIFIED_NEXT, null, curState, succState); - - synchronized (this) - { - this.notify(); - } - } + synchronized (this) { + final int errorCode = EC.TLC_STATE_NOT_COMPLETELY_SPECIFIED_NEXT; + if (this.setErrState(curState, succState, false, errorCode)) { + final Set<OpDeclNode> unassigned = succState.getUnassigned(); + String[] parameters; + if (tool.getActions().length == 1) { + parameters = new String[] { unassigned.size() > 1 ? "s are" : " is", + unassigned.stream().map(n -> n.getName().toString()) + .collect(Collectors.joining(", ")) }; + } else { + parameters = new String[] { tool.getActions()[i].getName().toString(), + unassigned.size() > 1 ? "s are" : " is", + unassigned.stream().map(n -> n.getName().toString()) + .collect(Collectors.joining(", ")) }; + } + this.printTrace(errorCode, parameters, curState, succState); + } + } return allSuccNonLeaf; } - if (TLCGlobals.coverageInterval >= 0) - { - ((TLCStateMutSource) succState).addCounts(counts); - } - - boolean inModel = (this.tool.isInModel(succState) && this.tool.isInActions(curState, succState)); + boolean inModel = (tool.isInModel(succState) && tool.isInActions(curState, succState)); int status = FPIntSet.NEW; if (inModel) { @@ -391,10 +432,7 @@ public class DFIDModelChecker extends AbstractChecker allSuccNonLeaf = allSuccNonLeaf && !FPIntSet.isLeaf(status); // Write out the state when new and asked: - if (status == FPIntSet.NEW && this.allStateWriter != null) - { - this.allStateWriter.writeState(succState); - } + this.allStateWriter.writeState(curState, succState, status == FPIntSet.NEW); // Remember succState if it has not been completed at this level: if (!FPIntSet.isCompleted(status)) @@ -406,8 +444,7 @@ public class DFIDModelChecker extends AbstractChecker // For liveness checking: if (this.checkLiveness && isLeaf) { - liveNextStates.addElement(succState); - liveNextFPs.addElement(fp); + liveNextStates.put(fp, succState); } } @@ -416,10 +453,10 @@ public class DFIDModelChecker extends AbstractChecker { try { - int len = this.invariants.length; + int len = tool.getInvariants().length; for (k = 0; k < len; k++) { - if (!tool.isValid(this.invariants[k], succState)) + if (!tool.isValid(tool.getInvariants()[k], succState)) { // We get here because of invariant violation: synchronized (this) @@ -427,14 +464,14 @@ public class DFIDModelChecker extends AbstractChecker if (TLCGlobals.continuation) { this.printTrace(EC.TLC_INVARIANT_VIOLATED_BEHAVIOR, - new String[] { this.tool.getInvNames()[k] }, curState, succState); + new String[] { tool.getInvNames()[k] }, curState, succState); break; } else { - if (this.setErrState(curState, succState, false)) + if (this.setErrState(curState, succState, false, EC.TLC_INVARIANT_VIOLATED_BEHAVIOR)) { this.printTrace(EC.TLC_INVARIANT_VIOLATED_BEHAVIOR, - new String[] { this.tool.getInvNames()[k] }, curState, + new String[] { tool.getInvNames()[k] }, curState, succState); this.notify(); } @@ -447,23 +484,25 @@ public class DFIDModelChecker extends AbstractChecker continue; } catch (Exception e) { - if (this.setErrState(curState, succState, true)) - { - this.printTrace(EC.TLC_INVARIANT_EVALUATION_FAILED, new String[] { this.tool - .getInvNames()[k] }, curState, succState); - this.notify(); - } - return allSuccNonLeaf; + synchronized (this) { + if (this.setErrState(curState, succState, true, EC.TLC_INVARIANT_EVALUATION_FAILED)) + { + this.printTrace(EC.TLC_INVARIANT_EVALUATION_FAILED, new String[] { tool + .getInvNames()[k] }, curState, succState); + this.notify(); + } + return allSuccNonLeaf; + } } } // Check if the state violates any implied action. We need to do it // even if succState is not new. try { - int len = this.impliedActions.length; + int len = tool.getImpliedActions().length; for (k = 0; k < len; k++) { - if (!tool.isValid(this.impliedActions[k], curState, succState)) + if (!tool.isValid(tool.getImpliedActions()[k], curState, succState)) { // We get here because of implied-action violation: synchronized (this) @@ -472,16 +511,15 @@ public class DFIDModelChecker extends AbstractChecker { this .printTrace(EC.TLC_ACTION_PROPERTY_VIOLATED_BEHAVIOR, - new String[] { this.tool.getImpliedActNames()[k] }, curState, + new String[] { tool.getImpliedActNames()[k] }, curState, succState); break; } else { - if (this.setErrState(curState, succState, false)) + if (this.setErrState(curState, succState, false, EC.TLC_ACTION_PROPERTY_VIOLATED_BEHAVIOR)) { - this.printTrace(EC.TLC_ACTION_PROPERTY_VIOLATED_BEHAVIOR, - new String[] { this.tool.getImpliedActNames()[k] }, curState, + new String[] { tool.getImpliedActNames()[k] }, curState, succState); this.notify(); } @@ -494,13 +532,15 @@ public class DFIDModelChecker extends AbstractChecker continue; } catch (Exception e) { - if (this.setErrState(curState, succState, true)) - { - this.printTrace(EC.TLC_ACTION_PROPERTY_EVALUATION_FAILED, new String[] { this.tool - .getImpliedActNames()[k] }, curState, succState); - this.notify(); - } - return allSuccNonLeaf; + synchronized (this) { + if (this.setErrState(curState, succState, true, EC.TLC_ACTION_PROPERTY_EVALUATION_FAILED)) + { + this.printTrace(EC.TLC_ACTION_PROPERTY_EVALUATION_FAILED, new String[] { tool + .getImpliedActNames()[k] }, curState, succState); + this.notify(); + } + } + return allSuccNonLeaf; } } @@ -513,7 +553,7 @@ public class DFIDModelChecker extends AbstractChecker { synchronized (this) { - if (this.setErrState(curState, null, false)) + if (this.setErrState(curState, null, false, EC.TLC_DEADLOCK_REACHED)) { this.printTrace(EC.TLC_DEADLOCK_REACHED, null, curState, null); this.notify(); @@ -525,12 +565,20 @@ public class DFIDModelChecker extends AbstractChecker // Finally, add curState into the behavior graph for liveness checking: if (this.checkLiveness && isLeaf) { + final long curStateFP = curState.fingerPrint(); // Add a stuttering step for curState: - long curStateFP = curState.fingerPrint(); - liveNextStates.addElement(curState); - liveNextFPs.addElement(curStateFP); + liveNextStates.put(curStateFP, curState); + this.allStateWriter.writeState(curState, curState, true, IStateWriter.Visualization.STUTTERING); // Add curState to the behavior graph: - liveCheck.addNextState(curState, curStateFP, liveNextStates, liveNextFPs); + liveCheck.addNextState(tool, curState, curStateFP, liveNextStates); + + // Poor man's version of a controller. If necessary, try e.g. + // PID controller instead. + final int multiplier = threadLocal.get(); + if (liveNextStates.capacity() > (multiplier * INITIAL_CAPACITY)) { + // Increase initial size for as long as the set has to grow + threadLocal.set(multiplier + 1); + } } // We set curState DONE if @@ -548,19 +596,23 @@ public class DFIDModelChecker extends AbstractChecker boolean keep = ((e instanceof StackOverflowError) || (e instanceof OutOfMemoryError)); synchronized (this) { - if (this.setErrState(curState, succState, !keep)) + final int errorCode; + if (e instanceof StackOverflowError) + { + errorCode = EC.SYSTEM_STACK_OVERFLOW; + } else if (e instanceof OutOfMemoryError) + { + errorCode = EC.SYSTEM_OUT_OF_MEMORY; + } else + { + errorCode = EC.GENERAL; + } + + if (this.setErrState(curState, succState, !keep, errorCode)) { String[] parameters = null; - int errorCode; - if (e instanceof StackOverflowError) - { - errorCode = EC.SYSTEM_STACK_OVERFLOW; - } else if (e instanceof OutOfMemoryError) + if (errorCode == EC.GENERAL) { - errorCode = EC.SYSTEM_OUT_OF_MEMORY; - } else - { - errorCode = EC.GENERAL; // LL changed error message on 7 April 2012 parameters = new String[] { MP.ECGeneralMsg("computing the set of next states", e) }; @@ -575,16 +627,16 @@ public class DFIDModelChecker extends AbstractChecker private final void printTrace(int errorCode, String[] parameters, TLCState s1, TLCState s2) { - this.workers[IdThread.GetId()].printTrace(errorCode, parameters, s1, s2); + ((DFIDWorker) this.workers[IdThread.GetId()]).printTrace(errorCode, parameters, s1, s2); } /** * Set the error state. * <strong>Note:</note> this method must be protected by lock */ - public boolean setErrState(TLCState curState, TLCState succState, boolean keep) + public boolean setErrState(TLCState curState, TLCState succState, boolean keep, int errorCode) { - boolean result = super.setErrState(curState, succState, keep); + boolean result = super.setErrState(curState, succState, keep, errorCode); if (!result) { return false; @@ -603,7 +655,7 @@ public class DFIDModelChecker extends AbstractChecker { for (int i = 0; i < this.workers.length; i++) { - this.workers[i].setStop(code); + ((DFIDWorker) this.workers[i]).setStop(code); } } @@ -613,16 +665,18 @@ public class DFIDModelChecker extends AbstractChecker * Checkpoint: checkpoint three data structures: the state set, the * state queue, and the state trace. */ - public final boolean doPeriodicWork() throws Exception + @Override + public final int doPeriodicWork() throws Exception { synchronized (this.theFPSet) { // Run liveness checking, if needed: if (this.checkLiveness) { - if (!liveCheck.check(false)) + final int result = liveCheck.check(tool, false); + if (result != EC.NO_ERROR) { - return false; + return result; } } @@ -647,7 +701,7 @@ public class DFIDModelChecker extends AbstractChecker MP.printMessage(EC.TLC_CHECKPOINT_END); } } - return true; + return EC.NO_ERROR; } public final boolean recover() throws IOException @@ -674,8 +728,7 @@ public class DFIDModelChecker extends AbstractChecker this.theFPSet.close(); if (this.checkLiveness) liveCheck.close(); - if (this.allStateWriter != null) - this.allStateWriter.close(); + this.allStateWriter.close(); // SZ Feb 23, 2009: // FileUtil.deleteDir(new File(this.metadir), success); FileUtil.deleteDir(this.metadir, success); @@ -700,36 +753,30 @@ public class DFIDModelChecker extends AbstractChecker String.valueOf(this.theFPSet.size()) }); } - public final void reportSuccess() throws IOException + private final void reportSuccess() throws IOException { - long d = this.theFPSet.size(); - double prob1 = (d * (this.numOfGenStates.get() - d)) / Math.pow(2, 64); - double prob2 = this.theFPSet.checkFPs(); - - MP.printMessage(EC.TLC_SUCCESS, new String[] { String.valueOf(prob1), String.valueOf(prob2) }); + reportSuccess(this.theFPSet.size(), this.theFPSet.checkFPs(), numOfGenStates.get()); } /** * Create workers */ - protected IdThread[] startWorkers(AbstractChecker checker, int checkIndex) + protected IWorker[] startWorkers(AbstractChecker checker, int checkIndex) { for (int i = 0; i < this.workers.length; i++) { this.workers[i] = new DFIDWorker(i, checkIndex, checker); + } + // Start all workers once instantiated to avoid a race with setStop, + // when setStop is being called concurrently with startWorkers. This + // happens, if a DFIDWorker terminates immediately. + for (int i = 0; i < this.workers.length; i++) + { this.workers[i].start(); } return this.workers; } - /** - * Run prior the worker loop - */ - protected void runTLCPreLoop() - { - this.done = false; - } - /** * Process calculation * @param count diff --git a/tlatools/src/tlc2/tool/DFIDWorker.java b/tlatools/src/tlc2/tool/DFIDWorker.java index 0af5b2babad23565111a8a2d56434c209fb5b565..5b7c1be71c2456ca20759703284d5713b6e8bb7c 100644 --- a/tlatools/src/tlc2/tool/DFIDWorker.java +++ b/tlatools/src/tlc2/tool/DFIDWorker.java @@ -9,7 +9,6 @@ import tlc2.output.StatePrinter; import tlc2.tool.fp.dfid.FPIntSet; import tlc2.util.IdThread; import tlc2.util.LongVec; -import tlc2.util.ObjLongTable; import tlc2.util.RandomGenerator; public class DFIDWorker extends IdThread implements IWorker { @@ -29,7 +28,6 @@ public class DFIDWorker extends IdThread implements IWorker { private TLCState[] theInitStates; private long[] theInitFPs; private int initLen; - private ObjLongTable astCounts; private int toLevel; private int curLevel; private int stopCode; @@ -55,15 +53,12 @@ public class DFIDWorker extends IdThread implements IWorker { this.theInitFPs = new long[this.initLen]; System.arraycopy(this.tlc.theInitStates, 0, this.theInitStates, 0, this.initLen); System.arraycopy(this.tlc.theInitFPs, 0, this.theInitFPs, 0, this.initLen); - this.astCounts = new ObjLongTable(10); this.toLevel = toLevel; this.curLevel = 0; this.stopCode = 0; this.moreLevel = false; } - public final ObjLongTable getCounts() { return this.astCounts; } - public final void setStop(int code) { this.stopCode = code; } public final boolean isTerminated() { return this.stopCode == 2; } @@ -136,6 +131,9 @@ public class DFIDWorker extends IdThread implements IWorker { { StatePrinter.printState(this.stateStack[idx], ++idx); } + // the prefix printed by the while loop should end at s1. + assert s1.equals(this.stateStack[idx]); + StatePrinter.printState(s1, ++idx); if (s2 != null) { StatePrinter.printState(s2, idx+1); @@ -167,7 +165,6 @@ public class DFIDWorker extends IdThread implements IWorker { this.succFPStack[0].reset(); boolean isLeaf = this.toLevel < 2; boolean noLeaf = this.tlc.doNext(curState, cfp, isLeaf, - this.astCounts, this.succStateStack[0], this.succFPStack[0]); this.moreLevel = this.moreLevel || !noLeaf; @@ -193,7 +190,6 @@ public class DFIDWorker extends IdThread implements IWorker { this.succFPStack[this.curLevel].reset(); isLeaf = (this.curLevel >= this.toLevel-1); noLeaf = this.tlc.doNext(curState, cfp, isLeaf, - this.astCounts, this.succStateStack[this.curLevel], this.succFPStack[this.curLevel]); this.moreLevel = this.moreLevel || !noLeaf; @@ -207,7 +203,7 @@ public class DFIDWorker extends IdThread implements IWorker { // Assert.printStack(e); this.tlc.setStop(2); synchronized(this.tlc) { - if (this.tlc.setErrState(curState, null, true)) { + if (this.tlc.setErrState(curState, null, true, EC.GENERAL)) { MP.printError(EC.GENERAL, e); // LL changed call 7 April 2012 } this.tlc.setDone(); diff --git a/tlatools/src/tlc2/tool/Defns.java b/tlatools/src/tlc2/tool/Defns.java index 848f688ac0dfe520500b1d7f0d3e7964c2d79e2b..39e463ed8d02839241014151004f2beff1a360d5 100644 --- a/tlatools/src/tlc2/tool/Defns.java +++ b/tlatools/src/tlc2/tool/Defns.java @@ -47,6 +47,12 @@ public class Defns implements ToolGlobals, Serializable this.table = new Object[defnIdx + 32]; } + Defns(Defns other) { + this.defnIdx = other.defnIdx; + this.table = new Object[other.table.length]; + System.arraycopy(other.table, 0, this.table, 0, other.table.length); + } + /** * Returns the definition of key if its definition exists. * Otherwise, returns null. @@ -111,4 +117,8 @@ public class Defns implements ToolGlobals, Serializable { this.defnIdx = index; } + + public Defns snapshot() { + return new Defns(this); + } } diff --git a/tlatools/src/tlc2/tool/EvalControl.java b/tlatools/src/tlc2/tool/EvalControl.java index 4c110b0ddd3425831ba15f164ad00174260cb0c2..c7062adcb7ed03fb078ad16fe649db416aef12ef 100644 --- a/tlatools/src/tlc2/tool/EvalControl.java +++ b/tlatools/src/tlc2/tool/EvalControl.java @@ -8,13 +8,34 @@ package tlc2.tool; public class EvalControl { public static final int KeepLazy = 1; - public static final int Primed = 2; - public static final int Enabled = 4; - + /** + * Current evaluation within a primed variable. If isPrimed is true, lookup in + * the Context chain terminates on a branch. + * + * @see tlc2.util.Context.lookup(SymbolNode, boolean) + */ + public static final int Primed = 1 << 1; + /** + * Current evaluation scope is within ENABLED. In the ENABLED scope, caching of + * LazyValue is disabled. + */ + public static final int Enabled = 1 << 2; + /** + * Evaluation in the scope of {@link ITool#getInitStates()} or + * {@link ITool#getInitStates(IStateFunctor)}. In other words set during the + * generation of initial states. + */ + public static final int Init = 1 << 3; + public static final int Clear = 0; + private static boolean isSet(final int control, final int constant) { + // Zero all bits except constant bit(s). + return (control & constant) > 0; + } + public static boolean isKeepLazy(int control) { - return (control & KeepLazy) > 0; + return isSet(control, KeepLazy); } public static int setKeepLazy(int control) { @@ -22,15 +43,32 @@ public class EvalControl { } public static boolean isPrimed(int control) { - return (control & Primed) > 0; + return isSet(control, Primed); } public static int setPrimed(int control) { return control | Primed; } + /** + * Sets {@link EvalControl#Primed} iff {@link EvalControl#Enabled} is already set. + */ + public static int setPrimedIfEnabled(int control) { + if (isEnabled(control)) { + return setPrimed(control); + } + return control; + } + public static boolean isEnabled(int control) { - return (control & Enabled) > 0; + return isSet(control, Enabled); + } + + public static int setEnabled(int control) { + return control | Enabled; } + public static boolean isInit(int control) { + return isSet(control, Init); + } } diff --git a/tlatools/src/tlc2/tool/EvalException.java b/tlatools/src/tlc2/tool/EvalException.java index 1a872c961848806e8a824a38b0b43ed6a6fcc944..11c64865172b6e245edd54997794f451e91698d5 100644 --- a/tlatools/src/tlc2/tool/EvalException.java +++ b/tlatools/src/tlc2/tool/EvalException.java @@ -20,21 +20,39 @@ public class EvalException extends RuntimeException // private int type; - public EvalException(int errorCode, String[] parameters) + private final int errorCode; + private final String[] parameters; + + public EvalException(int errorCode, String[] parameters) { super(MP.getMessage(errorCode, parameters)); + this.errorCode = errorCode; + this.parameters = parameters; } public EvalException(int errorCode, String parameter) { - super(MP.getMessage(errorCode, parameter)); + this(errorCode, new String[] {parameter}); } public EvalException(int errorCode) { - super(MP.getMessage(errorCode)); + super(MP.getMessage(errorCode)); + this.errorCode = errorCode; + this.parameters = null; + } + + public int getErrorCode() { + return errorCode; } + public String[] getParameters() { + return parameters; + } + + public boolean hasParameters() { + return parameters != null; + } // SZ Jul 14, 2009: refactored and deprecated, all usage changed to standard constructor // public EvalException(int type, String message) diff --git a/tlatools/src/tlc2/tool/FingerprintException.java b/tlatools/src/tlc2/tool/FingerprintException.java new file mode 100644 index 0000000000000000000000000000000000000000..8d468ebbcff6e3d85e20e149d62759485efcddda --- /dev/null +++ b/tlatools/src/tlc2/tool/FingerprintException.java @@ -0,0 +1,103 @@ +/******************************************************************************* + * Copyright (c) 2017 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Ian Morris Nieves - initial design and implementation + ******************************************************************************/ + +package tlc2.tool; + +import tla2sany.semantic.SemanticNode; +import tlc2.value.IValue; + +public class FingerprintException extends RuntimeException { + + final public IValue value; + final public FingerprintException next; + + private FingerprintException(Throwable initCauseThrowable, IValue value, FingerprintException next) { + initCause(initCauseThrowable); + this.value = value; + this.next = next; + } + + public static FingerprintException getNewHead(IValue v, Throwable t){ + FingerprintException fpe = null; + if(t instanceof FingerprintException) + fpe = ((FingerprintException) t).prependNewHead(v); + else + fpe = FingerprintException.createNewHead(v, t); + return fpe; + } + + private static FingerprintException createNewHead(IValue value, Throwable initCauseThrowable){ + if(value == null || initCauseThrowable == null) + return null; + else + return new FingerprintException(initCauseThrowable, value, null); + } + + private FingerprintException prependNewHead(IValue value){ + if(value == null) + return null; + else + return new FingerprintException(null, value, this); + } + + public Throwable getRootCause(){ + FingerprintException nextFPE = this; + while(nextFPE.next != null) + nextFPE = nextFPE.next; + return nextFPE.getCause(); + } + + public String getTrace(){ + return getTraceImpl(0, null); + } + + private String getTraceImpl(final int traceIndexLabel, final Integer lastSemanticNodeUid){ + SemanticNode semanticNode = value.getSource(); + if(semanticNode == null){ + if(next == null) + return ""; + else + return next.getTraceImpl(traceIndexLabel, lastSemanticNodeUid); + } + else{ + Integer semanticNodeUid = semanticNode.getUid(); + if(semanticNodeUid.equals(lastSemanticNodeUid)){ // same SemanticNode compared to current top of stack + if(next == null) + return ""; + else + return next.getTraceImpl(traceIndexLabel, lastSemanticNodeUid); + } + else{ // different SemanticNode compared to current top of stack + String description = traceIndexLabel + ") " + semanticNode.toString() + "\n"; + if(next == null) + return description; + else + return next.getTraceImpl(traceIndexLabel+1, semanticNodeUid) + description; + } + } + } + +} diff --git a/tlatools/src/tlc2/tool/IActionItemList.java b/tlatools/src/tlc2/tool/IActionItemList.java new file mode 100644 index 0000000000000000000000000000000000000000..e02279024302ef6fce51329234b3db350e5421ee --- /dev/null +++ b/tlatools/src/tlc2/tool/IActionItemList.java @@ -0,0 +1,52 @@ +/******************************************************************************* + * Copyright (c) 2019 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool; + +import tla2sany.semantic.SemanticNode; +import tlc2.tool.coverage.CostModel; +import tlc2.util.Context; + +public interface IActionItemList { + + /** + * predicate of a conjunction + */ + int CONJUNCT = 0; + /** + * predicate + */ + int PRED = -1; + /** + * UNCHANGED predicate + */ + int UNCHANGED = -2; + /** + * pred' # pred + */ + int CHANGED = -3; + + IActionItemList cons(SemanticNode exprOrOpArgNode, Context c, CostModel cm, int ailconst); +} \ No newline at end of file diff --git a/tlatools/src/tlc2/tool/IContextEnumerator.java b/tlatools/src/tlc2/tool/IContextEnumerator.java new file mode 100644 index 0000000000000000000000000000000000000000..258abbea9d3afff40a929a35a0e0467f8e5e2223 --- /dev/null +++ b/tlatools/src/tlc2/tool/IContextEnumerator.java @@ -0,0 +1,9 @@ +package tlc2.tool; + +import tlc2.util.Context; + +public interface IContextEnumerator { + + Context nextElement(); + +} \ No newline at end of file diff --git a/tlatools/src/tlc2/tool/INextStateFunctor.java b/tlatools/src/tlc2/tool/INextStateFunctor.java new file mode 100644 index 0000000000000000000000000000000000000000..4412d89f06590b411d1fda2be78a2ea8abda8e99 --- /dev/null +++ b/tlatools/src/tlc2/tool/INextStateFunctor.java @@ -0,0 +1,35 @@ +/******************************************************************************* + * Copyright (c) 2019 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool; + +public interface INextStateFunctor extends IStateFunctor { + + Object addElement(final TLCState s, final Action a, final TLCState t); + + public static class InvariantViolatedException extends RuntimeException { + + } +} diff --git a/tlatools/src/tlc2/tool/IStateFunctor.java b/tlatools/src/tlc2/tool/IStateFunctor.java new file mode 100644 index 0000000000000000000000000000000000000000..1bbb29c21233bdaa11696a5079248c8711dfa9c4 --- /dev/null +++ b/tlatools/src/tlc2/tool/IStateFunctor.java @@ -0,0 +1,40 @@ +/******************************************************************************* + * Copyright (c) 2015 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ + +package tlc2.tool; + +/** + * An {@link IStateFunctor}'s responsibility is to accept generated (init) + * states via addElement and to process them. As the time of writing, known + * implementors are StateVec which simply stores all states passed to it for + * later use and ModelChecker.DoInitFunctor which checks each state right + * away. + */ +public interface IStateFunctor { + + Object addElement(TLCState state); + +} \ No newline at end of file diff --git a/tlatools/src/tlc2/tool/ITool.java b/tlatools/src/tlc2/tool/ITool.java new file mode 100644 index 0000000000000000000000000000000000000000..2917e4085a1f160e89896618fd03e680891cb17e --- /dev/null +++ b/tlatools/src/tlc2/tool/ITool.java @@ -0,0 +1,245 @@ +/******************************************************************************* + * Copyright (c) 2019 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool; + +import java.io.File; +import java.util.List; + +import tla2sany.semantic.ExprNode; +import tla2sany.semantic.ExprOrOpArgNode; +import tla2sany.semantic.OpApplNode; +import tla2sany.semantic.OpDefNode; +import tla2sany.semantic.SemanticNode; +import tla2sany.semantic.SymbolNode; +import tlc2.tool.coverage.CostModel; +import tlc2.tool.impl.ModelConfig; +import tlc2.util.Context; +import tlc2.util.ObjLongTable; +import tlc2.util.Vect; +import tlc2.value.IFcnLambdaValue; +import tlc2.value.IMVPerm; +import tlc2.value.IValue; +import util.FilenameToStream; + +public interface ITool extends TraceApp { + + /** + * This method returns the set of all possible actions of the + * spec, and sets the actions field of this object. In fact, we + * could simply treat the next predicate as one "giant" action. + * But for efficiency, we preprocess the next state predicate by + * splitting it into a set of actions for the maximum prefix + * of disjunction and existential quantification. + */ + Action[] getActions(); + + /* + * This method returns the set of possible initial states that + * satisfies the initial state predicate. Initial state predicate + * can be under-specified. Too many possible initial states will + * probably make tools like TLC useless. + */ + StateVec getInitStates(); + + void getInitStates(IStateFunctor functor); + + /* Create the state specified by pred. */ + TLCState makeState(SemanticNode pred); + + /** + * This method returns the set of next states when taking the action + * in the given state. + */ + StateVec getNextStates(Action action, TLCState state); + + boolean getNextStates(final INextStateFunctor functor, final TLCState state); + + IValue eval(SemanticNode expr, Context c, TLCState s0); + + IValue eval(SemanticNode expr, Context c, TLCState s0, TLCState s1, int control); + + IValue eval(SemanticNode expr, Context c, TLCState s0, TLCState s1, int control, CostModel cm); + + /** + * This method determines if the argument is a valid state. A state + * is good iff it assigns legal explicit values to all the global + * state variables. + */ + boolean isGoodState(TLCState state); + + /* This method determines if a state satisfies the model constraints. */ + boolean isInModel(TLCState state) throws EvalException; + + /* This method determines if a pair of states satisfy the action constraints. */ + boolean isInActions(TLCState s1, TLCState s2) throws EvalException; + + boolean hasStateOrActionConstraints(); + + /** + * This method determines if an action is enabled in the given state. + * More precisely, it determines if (act.pred /\ (sub' # sub)) is + * enabled in the state s and context act.con. + */ + TLCState enabled(SemanticNode pred, Context c, TLCState s0, TLCState s1, ExprNode subscript, final int ail); + TLCState enabled(SemanticNode pred, Context c, TLCState s0, TLCState s1); + TLCState enabled(SemanticNode pred, IActionItemList acts, Context c, TLCState s0, TLCState s1); + TLCState enabled(SemanticNode pred, IActionItemList acts, Context c, TLCState s0, TLCState s1, CostModel cm); + + /* This method determines if the action predicate is valid in (s0, s1). */ + boolean isValid(Action act, TLCState s0, TLCState s1); + + /* Returns true iff the predicate is valid in the state. */ + boolean isValid(Action act, TLCState state); + + /* Returns true iff the predicate is valid in the state. */ + boolean isValid(Action act); + + boolean isValid(ExprNode expr); + + /* Reconstruct the initial state whose fingerprint is fp. */ + TLCStateInfo getState(long fp); + + /** + * Reconstruct the next state of state s whose fingerprint is fp. + * + * @return Returns the TLCState wrapped in TLCStateInfo. TLCStateInfo stores + * the stateNumber (relative to the given sinfo) and a pointer to + * the predecessor. + */ + TLCStateInfo getState(long fp, TLCStateInfo sinfo); + + /* Reconstruct the next state of state s whose fingerprint is fp. */ + TLCStateInfo getState(long fp, TLCState s); + + /* Reconstruct the info for s1. */ + TLCStateInfo getState(TLCState s1, TLCState s); + + /* Return the set of all permutations under the symmetry assumption. */ + IMVPerm[] getSymmetryPerms(); + + boolean hasSymmetry(); + + Context getFcnContext(IFcnLambdaValue fcn, ExprOrOpArgNode[] args, Context c, TLCState s0, TLCState s1, int control); + + Context getFcnContext(IFcnLambdaValue fcn, ExprOrOpArgNode[] args, Context c, TLCState s0, TLCState s1, int control, + CostModel cm); + + IContextEnumerator contexts(OpApplNode appl, Context c, TLCState s0, TLCState s1, int control); + + Vect<Action> getInitStateSpec(); + + Action[] getInvariants(); + + ObjLongTable<SemanticNode> getPrimedLocs(); + + Context getOpContext(OpDefNode odn, ExprOrOpArgNode[] args, Context ctx, boolean b); + + ExprNode[] getAssumptions(); + + boolean[] getAssumptionIsAxiom(); + + int checkAssumptions(); + + String[] getInvNames(); + + String[] getImpliedActNames(); + + /** + * @return The name of the root module. + */ + String getRootName(); + + /** + * @return The file name of the root module which might contain the + * full or relative path information. + */ + String getRootFile(); + + String getConfigFile(); + + String getSpecDir(); + + String[] getImpliedInitNames(); + + /** + * Initial predicate of the liveness property Prop (see impliedActions above). + * Most common used when checking if a Spec implements another one, i.e. ASpec + * => BSpec. + * <p> + * See the following tests:<br> + * tlc2.tool.suite.Test55 + * <ul> + * <li>Action line 7, col 1 to line 7, col 41 of module test55</li> + * <li>Action line 7, col 1 to line 7, col 41 of module test55</li> + * </ul> + * tlc2.tool.suite.Test63 + * <ul> + * <li>Action line 52, col 1 to line 52, col 21 of module test63</li> + * </ul> + */ + Action[] getImpliedInits(); + + /** + * Checking a liveness property Prop (declared by the PROPERTY keyword in the + * config file) means to verify Spec => Prop. An implied action is the [][A]_x + * (A \/ x' = x) part of Prop where A is an action and x is a variable. + * + * See the following tests:<br> + * tlc2.tool.suite.Test52 + * <ul> + * <li></li> + * <li></li> + * </ul> + * tlc2.tool.suite.Test56 + * <ul> + * <li></li> + * </ul> + */ + Action[] getImpliedActions(); + + boolean livenessIsTrue(); + + Action[] getImpliedTemporals(); + + Action[] getTemporals(); + + Object lookup(SymbolNode opNode, Context con, boolean b); + + Object lookup(SymbolNode operator); + + Object getVal(ExprOrOpArgNode expr, Context con, boolean b); + + Action getNextStateSpec(); + + SemanticNode getViewSpec(); + + int getId(); + + List<File> getModuleFiles(FilenameToStream resolver); + + ModelConfig getModelConfig(); + +} \ No newline at end of file diff --git a/tlatools/src/tlc2/tool/IWorker.java b/tlatools/src/tlc2/tool/IWorker.java index f6dd6acd96aa52fddee93fff2ab9257885060697..58bfc7cdbb6f3ac0f84e39dde91c82109183cafd 100644 --- a/tlatools/src/tlc2/tool/IWorker.java +++ b/tlatools/src/tlc2/tool/IWorker.java @@ -1,17 +1,28 @@ package tlc2.tool; -import tlc2.util.ObjLongTable; +import tlc2.TLCGlobals; +import tlc2.value.IValue; /** * A common interface for workers * @author Simon Zambrovski - * @version $Id$ */ public interface IWorker { - /** - * extracted from Worker and DFID worker - * used in the {@link AbstractChecker#reportCoverage(IWorker[])} - */ - public ObjLongTable getCounts(); + /** + * @return A worker's id in the range 0 to {@link TLCGlobals#getNumWorkers()} - 1 + */ + public int myGetId(); + + // see Thread + + public void start(); + + public void join() throws InterruptedException; + + // see IdThread + + public IValue getLocalValue(int idx); + + public void setLocalValue(int idx, IValue val); } diff --git a/tlatools/src/tlc2/tool/ModelChecker.java b/tlatools/src/tlc2/tool/ModelChecker.java index f5fe5a09d3c13b1e142579adc5549f9a0c02ee67..53bbe671db92b82740d2f063346dbad88d4e012e 100644 --- a/tlatools/src/tlc2/tool/ModelChecker.java +++ b/tlatools/src/tlc2/tool/ModelChecker.java @@ -1,33 +1,39 @@ // Copyright (c) 2003 Compaq Corporation. All rights reserved. // Portions Copyright (c) 2003 Microsoft Corporation. All rights reserved. -// Last modified on Wed 4 Jul 2007 at 17:46:34 PST by lamport -// modified on Fri Jan 18 11:33:51 PST 2002 by yuanyu +// Last modified on Wed 12 Jul 2017 at 16:10:00 PST by ian morris nieves +// modified on Wed 4 Jul 2007 at 17:46:34 PST by lamport +// modified on Fri Jan 18 11:33:51 PST 2002 by yuanyu package tlc2.tool; +import java.io.File; import java.io.IOException; -import java.util.concurrent.atomic.AtomicLong; +import java.util.List; +import java.util.Set; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; +import java.util.stream.Collectors; -import tla2sany.modanalyzer.SpecObj; -import tla2sany.semantic.ExprNode; +import tla2sany.semantic.OpDeclNode; import tlc2.TLCGlobals; import tlc2.output.EC; import tlc2.output.MP; -import tlc2.output.OutputCollector; import tlc2.tool.fp.FPSet; import tlc2.tool.fp.FPSetConfiguration; import tlc2.tool.fp.FPSetFactory; +import tlc2.tool.impl.CallStackTool; import tlc2.tool.liveness.LiveCheck; +import tlc2.tool.queue.DiskByteArrayQueue; import tlc2.tool.queue.DiskStateQueue; import tlc2.tool.queue.IStateQueue; -import tlc2.util.IdThread; -import tlc2.util.LongVec; -import tlc2.util.ObjLongTable; +import tlc2.util.IStateWriter; +import tlc2.util.SetOfStates; import tlc2.util.statistics.BucketStatistics; -import tlc2.value.Value; +import util.Assert; import util.DebugPrinter; import util.FileUtil; import util.FilenameToStream; +import util.TLAFlightRecorder; import util.UniqueString; /** @@ -41,52 +47,73 @@ import util.UniqueString; public class ModelChecker extends AbstractChecker { + protected static final boolean coverage = TLCGlobals.isCoverageEnabled(); /** * If the state/ dir should be cleaned up after a successful model run */ - private static final boolean VETO_CLEANUP = Boolean.getBoolean(ModelChecker.class.getName() + ".vetoCleanup"); + public static final boolean VETO_CLEANUP = Boolean.getBoolean(ModelChecker.class.getName() + ".vetoCleanup"); + private long numberOfInitialStates; public FPSet theFPSet; // the set of reachable states (SZ: note the type) public IStateQueue theStateQueue; // the state queue - public TLCTrace trace; // the trace file - protected Worker[] workers; // the workers + public final ConcurrentTLCTrace trace; // the trace file // used to calculate the spm metric public long distinctStatesPerMinute, statesPerMinute = 0L; protected long oldNumOfGenStates, oldFPSetSize = 0L; + /** + * The ratio between time spend on safety checking and liveness checking. + */ + private double runtimeRatio = 0d; + /** + * Flag set via JMX if liveness checking should be triggered. + */ + private boolean forceLiveCheck = false; /* Constructors */ + public ModelChecker(ITool tool, String metadir, final IStateWriter stateWriter, boolean deadlock, String fromChkpt, + final Future<FPSet> future, long startTime) throws EvalException, IOException, InterruptedException, ExecutionException { + this(tool, metadir, stateWriter, deadlock, fromChkpt, startTime); + this.theFPSet = future.get(); + + // Initialize all the workers: + this.workers = new Worker[TLCGlobals.getNumWorkers()]; + for (int i = 0; i < this.workers.length; i++) + { + this.workers[i] = this.trace.addWorker(new Worker(i, this, this.metadir, this.tool.getRootName())); + } + } + + public ModelChecker(ITool tool, String metadir, final IStateWriter stateWriter, boolean deadlock, String fromChkpt, + final FPSetConfiguration fpSetConfig, long startTime) throws EvalException, IOException { + this(tool, metadir, stateWriter, deadlock, fromChkpt, startTime); + this.theFPSet = FPSetFactory.getFPSet(fpSetConfig).init(TLCGlobals.getNumWorkers(), metadir, tool.getRootName()); + + // Initialize all the workers: + this.workers = new Worker[TLCGlobals.getNumWorkers()]; + for (int i = 0; i < this.workers.length; i++) + { + this.workers[i] = this.trace.addWorker(new Worker(i, this, this.metadir, this.tool.getRootName())); + } + } + /** * The only used constructor of the TLA+ model checker * SZ Feb 20, 2009 * @param resolver name resolver to be able to load files (specs and configs) from managed environments * @param specObj external SpecObj added to enable to work on existing specification - * Modified on 6 Apr 2010 by Yuan Yu to add fpMemSize parameter. */ - public ModelChecker(String specFile, String configFile, String dumpFile, boolean deadlock, String fromChkpt, - FilenameToStream resolver, SpecObj specObj, final FPSetConfiguration fpSetConfig) throws EvalException, IOException - { + private ModelChecker(ITool tool, String metadir, final IStateWriter stateWriter, boolean deadlock, String fromChkpt, + long startTime) throws EvalException, IOException { // call the abstract constructor - super(specFile, configFile, dumpFile, deadlock, fromChkpt, true, resolver, specObj); + super(tool, metadir, stateWriter, deadlock, fromChkpt, startTime); - // SZ Feb 20, 2009: this is a selected alternative - this.theStateQueue = new DiskStateQueue(this.metadir); + this.theStateQueue = useByteArrayQueue() + ? new DiskByteArrayQueue(this.metadir) + : new DiskStateQueue(this.metadir); // this.theStateQueue = new MemStateQueue(this.metadir); - //TODO why used to div by 20? - this.theFPSet = FPSetFactory.getFPSet(fpSetConfig); - - // initialize the set - this.theFPSet.init(TLCGlobals.getNumWorkers(), this.metadir, specFile); - // Finally, initialize the trace file: - this.trace = new TLCTrace(this.metadir, specFile, this.tool); - - // Initialize all the workers: - this.workers = new Worker[TLCGlobals.getNumWorkers()]; - for (int i = 0; i < this.workers.length; i++) - { - this.workers[i] = new Worker(i, this); - } + this.trace = new ConcurrentTLCTrace(this.metadir, this.tool.getRootName(), this.tool); } /** @@ -95,30 +122,37 @@ public class ModelChecker extends AbstractChecker * next states have not been explored are stored in the variable * theStateQueue. */ - public void modelCheck() throws Exception + @Override + protected int modelCheckImpl() throws Exception { + int result = EC.NO_ERROR; report("entering modelCheck()"); // needed to calculate state/minute in final progress report - final long startTime = System.currentTimeMillis(); - boolean recovered = this.recover(); + boolean recovered = this.recover(); if (!recovered) { + if (this.checkLiveness && liveCheck.getNumChecker() == 0) { + return MP.printError(EC.TLC_LIVE_FORMULA_TAUTOLOGY); + } + // We start from scratch. Initialize the state queue and the - // state set to contain all the initial states. - if (!this.checkAssumptions()) - return; + // state set to contain all the initial states. + result = this.checkAssumptions(); + if (result != EC.NO_ERROR) + return result; try { report("doInit(false)"); MP.printMessage(EC.TLC_COMPUTING_INIT); - // SZ Feb 23, 2009: do not ignore cancel on creation of the init states - if (!this.doInit(false)) + // SZ Feb 23, 2009: do not ignore cancel on creation of the init states + result = this.doInit(false); + if (result != EC.NO_ERROR) { report("exiting, because init failed"); - return; + return result; } } catch (Throwable e) { @@ -149,97 +183,122 @@ public class ModelChecker extends AbstractChecker } // Replay the error with the error stack recorded: - this.tool.setCallStack(); + final CallStackTool cTool = new CallStackTool(this.tool); try { - this.numOfGenStates = new AtomicLong(0); + numberOfInitialStates = 0; // SZ Feb 23, 2009: ignore cancel on error reporting - this.doInit(true); - } catch (Throwable e1) - { + this.doInit(cTool, true); + } catch (FingerprintException fe){ + result = MP.printError(EC.TLC_FINGERPRINT_EXCEPTION, new String[]{fe.getTrace(), fe.getRootCause().getMessage()}); + } catch (Throwable e1) { // Assert.printStack(e); - MP.printError(EC.TLC_NESTED_EXPRESSION, this.tool.getCallStack().toString()); + result = MP.printError(EC.TLC_NESTED_EXPRESSION, cTool.toString()); } this.printSummary(false, startTime); this.cleanup(false); report("exiting, because init failed with exception"); - return; + return result; } - if (this.numOfGenStates.get() == this.theFPSet.size()) + long statesGenerated = getStatesGenerated(); + final String plural = (statesGenerated == 1) ? "" : "s"; + if (statesGenerated == this.theFPSet.size()) { - String plural = (this.numOfGenStates.get() == 1) ? "" : "s"; - MP.printMessage(EC.TLC_INIT_GENERATED1, new String[] { String.valueOf(this.numOfGenStates), plural }); + MP.printMessage(EC.TLC_INIT_GENERATED1, new String[] { String.valueOf(statesGenerated), plural }); } else { - MP.printMessage(EC.TLC_INIT_GENERATED1, new String[] { String.valueOf(this.numOfGenStates), + MP.printMessage(EC.TLC_INIT_GENERATED2, new String[] { String.valueOf(statesGenerated), plural, String.valueOf(this.theFPSet.size()) }); } } report("init processed"); + // Finished if there is no next state predicate: - if (this.actions.length == 0) + if (this.tool.getActions().length == 0) { - reportSuccess(this.theFPSet, this.numOfGenStates.get()); - this.printSummary(true, startTime); + if (this.theStateQueue.isEmpty()) { + reportSuccess(this.theFPSet, getStatesGenerated()); + this.printSummary(true, startTime); + } else { + result = MP.printError(EC.TLC_STATES_AND_NO_NEXT_ACTION); + } this.cleanup(true); report("exiting with actions.length == 0"); - return; + return result; } - boolean success = false; + result = EC.GENERAL; try { report("running TLC"); - success = this.runTLC(Integer.MAX_VALUE); - if (!success) + result = this.runTLC(Integer.MAX_VALUE); + if (result != EC.NO_ERROR) { report("TLC terminated with error"); - return; + return result; } if (this.errState == null) { // Always check liveness properties at the end: if (this.checkLiveness) { - MP.printMessage(EC.TLC_CHECKING_TEMPORAL_PROPS, - new String[] { "complete", Long.toString(this.theFPSet.size()) }); + // Print progress statistics prior to liveness checking. + // Liveness checking can take a substantial amount of time + // and thus give the user some clues at what stage safety + // checking is. + MP.printMessage(EC.TLC_PROGRESS_STATS, new String[] { + String.valueOf(this.trace.getLevelForReporting()), + MP.format(getStatesGenerated()), + MP.format(theFPSet.size()), + MP.format(this.theStateQueue.size()) }); + report("checking liveness"); - success = liveCheck.finalCheck(); + result = liveCheck.finalCheck(tool); report("liveness check complete"); - if (!success) + if (result != EC.NO_ERROR) { report("exiting error status on liveness check"); - return; + return result; } } // We get here because the checking has been completed. - success = true; - reportSuccess(this.theFPSet, this.numOfGenStates.get()); + result = EC.NO_ERROR; + reportSuccess(this.theFPSet, getStatesGenerated()); } else if (this.keepCallStack) { // Replay the error with the error stack recorded: - this.tool.setCallStack(); + final CallStackTool cTool = new CallStackTool(this.tool); try { - this.doNext(this.predErrState, new ObjLongTable(10)); + // Not adding newly created Worker to trace#addWorker because it is not supposed + // to rewrite the trace file but to reconstruct actual states referenced by + // their fingerprints in the trace. + this.doNext(cTool, this.predErrState, this.checkLiveness ? new SetOfStates() : null, + new Worker(4223, this, this.metadir, tool.getRootName())); + } catch (FingerprintException e) + { + result = MP.printError(EC.TLC_FINGERPRINT_EXCEPTION, new String[]{e.getTrace(), e.getRootCause().getMessage()}); + } catch (EvalException e) { + // Do not replace the actual error code, such as assert violation, with TLC_NESTED_EXPRESSION. + MP.printError(EC.TLC_NESTED_EXPRESSION, cTool.toString()); + result = e.getErrorCode(); } catch (Throwable e) { // Assert.printStack(e); - MP.printError(EC.TLC_NESTED_EXPRESSION, this.tool.getCallStack().toString()); + result = MP.printError(EC.TLC_NESTED_EXPRESSION, cTool.toString()); } } } catch (Exception e) { report("TLC terminated with error"); // Assert.printStack(e); - success = false; - MP.printError(EC.GENERAL, e); // LL changed call 7 April 2012 + result = MP.printError(EC.GENERAL, e); // LL changed call 7 April 2012 } finally { - + final boolean success = result == EC.NO_ERROR; this.printSummary(success, startTime); if (this.checkLiveness) { @@ -255,42 +314,19 @@ public class ModelChecker extends AbstractChecker } this.cleanup(success); - } - report("exiting modelCheck()"); + report("exiting modelCheck()"); + } + + return result; } /** * Check the assumptions. */ - public boolean checkAssumptions() + public int checkAssumptions() { - ExprNode[] assumps = this.tool.getAssumptions(); - boolean[] isAxiom = this.tool.getAssumptionIsAxiom(); - boolean assumptionsAreTRUE = true; - for (int i = 0; i < assumps.length; i++) - { - try - { - if ((!isAxiom[i]) && !this.tool.isValid(assumps[i])) - { - OutputCollector.addViolatedAssumption(assumps[i]); - MP.printError(EC.TLC_ASSUMPTION_FALSE, assumps[i].toString()); - //return false; - assumptionsAreTRUE = false; - } - } catch (Exception e) - { - // Assert.printStack(e); - OutputCollector.addViolatedAssumption(assumps[i]); - MP.printError(EC.TLC_ASSUMPTION_EVALUATION_ERROR, - new String[] { assumps[i].toString(), e.getMessage() }); - //return false; - assumptionsAreTRUE = false; - } - } - //return true; - return assumptionsAreTRUE; + return this.tool.checkAssumptions(); } /** @@ -298,92 +334,52 @@ public class ModelChecker extends AbstractChecker * @return status, if false, the processing should be stopped * @throws Throwable */ - public final boolean doInit(boolean ignoreCancel) throws Throwable + @Override + public final int doInit(boolean ignoreCancel) throws Throwable { + return doInit(this.tool, ignoreCancel); + } + + private final int doInit(final ITool tool, boolean ignoreCancel) throws Throwable { - // SZ Feb 23, 2009: cancel flag set, quit - if (!ignoreCancel && this.cancellationFlag) - { - return false; - } - - TLCState curState = null; - - try - { - // Generate the initial states: - StateVec theInitStates = this.tool.getInitStates(); - this.numOfGenStates.set(theInitStates.size()); - for (int i = 0; i < theInitStates.size(); i++) - { - curState = theInitStates.elementAt(i); - // Check if the state is a legal state - if (!this.tool.isGoodState(curState)) - { - MP.printError(EC.TLC_INITIAL_STATE, curState.toString()); - return false; - } - boolean inModel = this.tool.isInModel(curState); - boolean seen = false; - if (inModel) - { - long fp = curState.fingerPrint(); - seen = this.theFPSet.put(fp); - if (!seen) - { - if (this.allStateWriter != null) - { - this.allStateWriter.writeState(curState); - } - curState.uid = this.trace.writeState(fp); - this.theStateQueue.enqueue(curState); - - // build behavior graph for liveness checking - if (this.checkLiveness) - { - liveCheck.addInitState(curState, fp); - } - } - } - // Check properties of the state: - OutputCollector.setInitialState(curState); - if (!seen) - { - for (int j = 0; j < this.invariants.length; j++) - { - if (!this.tool.isValid(this.invariants[j], curState)) - { - // We get here because of invariant violation: - MP.printError(EC.TLC_INVARIANT_VIOLATED_INITIAL, new String[] { - this.tool.getInvNames()[j].toString(), curState.toString() }); - if (!TLCGlobals.continuation) - return false; - } - } - for (int j = 0; j < this.impliedInits.length; j++) - { - if (!this.tool.isValid(this.impliedInits[j], curState)) - { - // We get here because of implied-inits violation: - MP.printError(EC.TLC_PROPERTY_VIOLATED_INITIAL, new String[] { - this.tool.getImpliedInitNames()[j], curState.toString() }); - return false; - } - } - } - } - } catch (Throwable e) - { - // Assert.printStack(e); - if (e instanceof OutOfMemoryError) - { - MP.printError(EC.SYSTEM_OUT_OF_MEMORY_TOO_MANY_INIT); - return false; - } - this.errState = curState; - throw e; + // Generate the initial states. + // + // The functor is passed to getInitStates() to - instead of adding all + // init states into an intermediate StateVec to check and add each state + // in a subsequent loop - directly check each state one-by-one and add + // it to the queue, fingerprint set and trace file. This avoids + // allocating memory for StateVec (which depending on the number of init + // states can grow to be GBs) and the subsequent loop over StateVec. + final DoInitFunctor functor; + if (ignoreCancel) { + // Rerunning state space exploration to reconstruct the error stack to determine + // what caused Tool to fail to evaluate the init predicate expressions. Thus, + // re-check all invariants even if state is already known (= part of theFPSet). + functor = new DoInitFunctor(tool, ignoreCancel); + } else { + functor = new DoInitFunctor(tool); } - return true; - } + try { + tool.getInitStates(functor); + } catch (DoInitFunctor.InvariantViolatedException ive) { + this.errState = functor.errState; + return functor.returnValue; + } catch (Assert.TLCRuntimeException e) { + this.errState = functor.errState; + throw e; + } + + // Iff one of the init states' checks violates any properties, the + // functor will record it. + if (functor.errState != null) { + this.errState = functor.errState; + if (functor.e != null) { + throw functor.e; + } + } + + // Return whatever the functor has recorded. + return functor.returnValue; + } /** * Compute the set of the next states. For each next state, check that @@ -393,257 +389,266 @@ public class ModelChecker extends AbstractChecker * * This method is called from the workers on every step */ - public final boolean doNext(TLCState curState, ObjLongTable counts) throws Throwable + private final boolean doNext(final ITool tool, TLCState curState, final SetOfStates liveNextStates, final Worker worker) throws Throwable { - // SZ Feb 23, 2009: cancel the calculation - if (this.cancellationFlag) - { - return false; - } - boolean deadLocked = true; TLCState succState = null; - StateVec liveNextStates = null; - LongVec liveNextFPs = null; - - if (this.checkLiveness) - { - liveNextStates = new StateVec(2); - liveNextFPs = new LongVec(2); - } - try { - int k = 0; - // <-- - // <-- - for (int i = 0; i < this.actions.length; i++) + for (int i = 0; i < tool.getActions().length; i++) { - // SZ Feb 23, 2009: cancel the calculation - if (this.cancellationFlag) - { - return false; - } - - StateVec nextStates = this.tool.getNextStates(this.actions[i], curState); - int sz = nextStates.size(); - this.incNumOfGenStates(sz); - deadLocked = deadLocked && (sz == 0); + final Action action = tool.getActions()[i]; + final StateVec nextStates = tool.getNextStates(action, curState); + final int sz = nextStates.size(); + worker.incrementStatesGenerated(sz); + deadLocked = deadLocked && (sz == 0); for (int j = 0; j < sz; j++) { - succState = nextStates.elementAt(j); - // Check if succState is a legal state. - if (!this.tool.isGoodState(succState)) - { - if (this.setErrState(curState, succState, false)) - { - MP.printError(EC.TLC_STATE_NOT_COMPLETELY_SPECIFIED_NEXT); - this.trace.printTrace(curState, succState); - this.theStateQueue.finishAll(); - - synchronized (this) - { - this.notify(); - } - } - return true; - } - if (TLCGlobals.coverageInterval >= 0) + succState = nextStates.elementAt(j); + // Check if succState is a legal state. + if (!tool.isGoodState(succState)) { - ((TLCStateMutSource) succState).addCounts(counts); - } + return doNextSetErr(curState, succState, action); + } - boolean inModel = (this.tool.isInModel(succState) && this.tool.isInActions(curState, succState)); - boolean seen = false; + final boolean inModel = (tool.isInModel(succState) && tool.isInActions(curState, succState)); + boolean unseen = true; if (inModel) { - long fp = succState.fingerPrint(); - seen = this.theFPSet.put(fp); - if (!seen) - { - // Write out succState when needed: - if (this.allStateWriter != null) - { - this.allStateWriter.writeState(succState); - } - // Enqueue succState only if it satisfies the model constraints: - long loc = this.trace.writeState(curState, fp); - succState.uid = loc; - this.theStateQueue.sEnqueue(succState); - } - // For liveness checking: - if (this.checkLiveness) - { - liveNextStates.addElement(succState); - liveNextFPs.addElement(fp); - } - } - // Check if succState violates any invariant: - if (!seen) + unseen = !isSeenState(curState, succState, action, worker, liveNextStates); + } + // Check if an unseen succState violates any invariant: + if (unseen) { - try - { - int len = this.invariants.length; - for (k = 0; k < len; k++) - { - // SZ Feb 23, 2009: cancel the calculation - if (this.cancellationFlag) - { - return false; - } - - if (!tool.isValid(this.invariants[k], succState)) - { - // We get here because of invariant violation: - synchronized (this) - { - if (TLCGlobals.continuation) - { - MP.printError(EC.TLC_INVARIANT_VIOLATED_BEHAVIOR, - this.tool.getInvNames()[k]); - this.trace.printTrace(curState, succState); - break; - } else - { - if (this.setErrState(curState, succState, false)) - { - MP.printError(EC.TLC_INVARIANT_VIOLATED_BEHAVIOR, this.tool - .getInvNames()[k]); - this.trace.printTrace(curState, succState); - this.theStateQueue.finishAll(); - this.notify(); - } - return true; - } - } - } - } - if (k < len) - continue; - } catch (Exception e) - { - if (this.setErrState(curState, succState, true)) - { - MP.printError(EC.TLC_INVARIANT_EVALUATION_FAILED, new String[] { - this.tool.getInvNames()[k], - (e.getMessage()==null)?e.toString():e.getMessage() }); - this.trace.printTrace(curState, succState); - this.theStateQueue.finishAll(); - this.notify(); - } - throw e; - } - } + if (doNextCheckInvariants(tool, curState, succState)) { + return true; + } + } // Check if the state violates any implied action. We need to do it // even if succState is not new. - try - { - int len = this.impliedActions.length; - for (k = 0; k < len; k++) - { - // SZ Feb 23, 2009: cancel the calculation - if (this.cancellationFlag) - { - return false; - } - - if (!tool.isValid(this.impliedActions[k], curState, succState)) - { - // We get here because of implied-action violation: - synchronized (this) - { - if (TLCGlobals.continuation) - { - MP.printError(EC.TLC_ACTION_PROPERTY_VIOLATED_BEHAVIOR, this.tool - .getImpliedActNames()[k]); - this.trace.printTrace(curState, succState); - break; - } else - { - if (this.setErrState(curState, succState, false)) - { - MP.printError(EC.TLC_ACTION_PROPERTY_VIOLATED_BEHAVIOR, this.tool - .getImpliedActNames()[k]); - this.trace.printTrace(curState, succState); - this.theStateQueue.finishAll(); - this.notify(); - } - return true; - } - } - } - } - if (k < len) - continue; - } catch (Exception e) - { - if (this.setErrState(curState, succState, true)) - { - MP.printError(EC.TLC_ACTION_PROPERTY_EVALUATION_FAILED, new String[] { - this.tool.getImpliedActNames()[k], - (e.getMessage()==null)?e.toString():e.getMessage() }); - this.trace.printTrace(curState, succState); - this.theStateQueue.finishAll(); - this.notify(); - } - throw e; + if (doNextCheckImplied(tool, curState, succState)) { + return true; } - } - // Must set state to null!!! - succState = null; - } - // Check for deadlock: + if (inModel && unseen) { + // The state is inModel, unseen and neither invariants + // nor implied actions are violated. It is thus eligible + // for further processing by other workers. + this.theStateQueue.sEnqueue(succState); + } + } + // Must set state to null!!! + succState = null; + } + // Check for deadlock: if (deadLocked && this.checkDeadlock) { - synchronized (this) - { - if (this.setErrState(curState, null, false)) - { - MP.printError(EC.TLC_DEADLOCK_REACHED); - this.trace.printTrace(curState, null); - this.theStateQueue.finishAll(); - this.notify(); - } - } - return true; - } - // Finally, add curState into the behavior graph for liveness checking: - if (this.checkLiveness) + return doNextSetErr(curState, null, false, EC.TLC_DEADLOCK_REACHED, null); + } + return false; + } catch (final Throwable e) + { + doNextFailed(curState, succState, e); + throw e; + } + } + + private final boolean isSeenState(final TLCState curState, final TLCState succState, + final Action action, final Worker worker, final SetOfStates liveNextStates) throws IOException { + final long fp = succState.fingerPrint(); + final boolean seen = this.theFPSet.put(fp); + // Write out succState when needed: + this.allStateWriter.writeState(curState, succState, !seen, action); + if (!seen) + { + // Write succState to trace only if it satisfies the + // model constraints. Do not enqueue it yet, but wait + // for implied actions and invariants to be checked. + // Those checks - if violated - will cause model checking + // to terminate. Thus we cannot let concurrent workers start + // exploring this new state. Conversely, the state has to + // be in the trace in case either invariant or implied action + // checks want to print the trace. + worker.writeState(curState, fp, succState); + if (coverage) { + action.cm.incSecondary(); + } + } + // For liveness checking: + if (this.checkLiveness) + { + liveNextStates.put(fp, succState); + } + return seen; + } + + private final boolean doNextCheckInvariants(final ITool tool, final TLCState curState, final TLCState succState) throws IOException, WorkerException, Exception { + int k = 0; + try + { + for (k = 0; k < tool.getInvariants().length; k++) { - // Add the stuttering step: - long curStateFP = curState.fingerPrint(); - liveNextStates.addElement(curState); - liveNextFPs.addElement(curStateFP); - liveCheck.addNextState(curState, curStateFP, liveNextStates, liveNextFPs); - } - return false; - } catch (Throwable e) + if (!tool.isValid(tool.getInvariants()[k], succState)) + { + // We get here because of invariant violation: + if (TLCGlobals.continuation) { + synchronized (this) + { + MP.printError(EC.TLC_INVARIANT_VIOLATED_BEHAVIOR, + tool.getInvNames()[k]); + this.trace.printTrace(curState, succState); + return false; + } + } else { + return doNextSetErr(curState, succState, false, + EC.TLC_INVARIANT_VIOLATED_BEHAVIOR, tool.getInvNames()[k]); + } + } + } + } catch (Exception e) { - // Assert.printStack(e); - boolean keep = ((e instanceof StackOverflowError) || (e instanceof OutOfMemoryError)); - synchronized (this) + doNextEvalFailed(curState, succState, EC.TLC_INVARIANT_EVALUATION_FAILED, + tool.getInvNames()[k], e); + } + return false; + } + + private final boolean doNextCheckImplied(final ITool tool, final TLCState curState, final TLCState succState) throws IOException, WorkerException, Exception { + int k = 0; + try + { + for (k = 0; k < tool.getImpliedActions().length; k++) { - if (this.setErrState(curState, succState, !keep)) + if (!tool.isValid(tool.getImpliedActions()[k], curState, succState)) { - if (e instanceof StackOverflowError) - { - MP.printError(EC.SYSTEM_STACK_OVERFLOW, e); - } else if (e instanceof OutOfMemoryError) + // We get here because of implied-action violation: + if (TLCGlobals.continuation) { - MP.printError(EC.SYSTEM_OUT_OF_MEMORY, e); - } else if (e.getMessage() != null) - { - MP.printError(EC.GENERAL, e); // LL changed call 7 April 2012 - } - this.trace.printTrace(curState, succState); - this.theStateQueue.finishAll(); - this.notify(); - } - } - throw e; - } - } + synchronized (this) + { + MP.printError(EC.TLC_ACTION_PROPERTY_VIOLATED_BEHAVIOR, tool + .getImpliedActNames()[k]); + this.trace.printTrace(curState, succState); + return false; + } + } else { + return doNextSetErr(curState, succState, false, + EC.TLC_ACTION_PROPERTY_VIOLATED_BEHAVIOR, + tool.getImpliedActNames()[k]); + } + } + } + } catch (Exception e) + { + doNextEvalFailed(curState, succState, EC.TLC_ACTION_PROPERTY_EVALUATION_FAILED, + tool.getImpliedActNames()[k], e); + } + return false; + } + + final boolean doNextSetErr(TLCState curState, TLCState succState, boolean keep, int ec, String param) throws IOException, WorkerException { + synchronized (this) + { + if (this.setErrState(curState, succState, keep, ec)) + { + if (param == null) { + MP.printError(ec); + } else { + MP.printError(ec, param); + } + this.trace.printTrace(curState, succState); + this.theStateQueue.finishAll(); + this.notify(); + } + } + return true; + } + + final boolean doNextSetErr(TLCState curState, TLCState succState, Action action) throws IOException, WorkerException { + synchronized (this) { + final int errorCode = EC.TLC_STATE_NOT_COMPLETELY_SPECIFIED_NEXT; + if (this.setErrState(curState, succState, false, errorCode)) + { + final Set<OpDeclNode> unassigned = succState.getUnassigned(); + if (this.tool.getActions().length == 1) { + MP.printError(errorCode, + new String[] { unassigned.size() > 1 ? "s are" : " is", + unassigned.stream().map(n -> n.getName().toString()) + .collect(Collectors.joining(", ")) }); + } else { + MP.printError(errorCode, + new String[] { action.getName().toString(), + unassigned.size() > 1 ? "s are" : " is", + unassigned.stream().map(n -> n.getName().toString()) + .collect(Collectors.joining(", ")) }); + } + this.trace.printTrace(curState, succState); + this.theStateQueue.finishAll(); + this.notify(); + } + return true; + } + } + + final void doNextEvalFailed(TLCState curState, TLCState succState, int ec, String param, Exception e) + throws IOException, WorkerException, Exception { + synchronized (this) { + if (this.setErrState(curState, succState, true, ec)) + { + MP.printError(ec, new String[] { param, (e.getMessage() == null) ? e.toString() : e.getMessage() }); + this.trace.printTrace(curState, succState); + this.theStateQueue.finishAll(); + this.notify(); + } + throw e; + } + } + + final void doNextFailed(TLCState curState, TLCState succState, Throwable e) + throws IOException, WorkerException, Throwable { + // Assert.printStack(e); + final boolean keep = ((e instanceof StackOverflowError) || (e instanceof OutOfMemoryError) + || (e instanceof AssertionError)); + synchronized (this) + { + final int ec; + if (e instanceof StackOverflowError) + { + ec = EC.SYSTEM_STACK_OVERFLOW; + } else if (e instanceof OutOfMemoryError) + { + ec = EC.SYSTEM_OUT_OF_MEMORY; + } else if (e instanceof AssertionError) + { + ec = EC.TLC_BUG; + } else if (e instanceof EvalException) + { + ec = ((EvalException) e).getErrorCode(); + } else + { + ec = EC.GENERAL; + } + + if (this.setErrState(curState, succState, !keep, ec)) + { + if (!(ec == EC.GENERAL && e.getMessage() == null)) + { + if (e instanceof EvalException && ((EvalException) e).hasParameters()) { + // An EvalException pretty-prints itself in its constructor, i.e. converts the + // parameters into the human readable string. However, MP.print* will + // pretty-print it a second time, which is why we pass the original parameters + // instead of the EvalException itself. Exception handling in TLC is a mess! + MP.printError(ec, ((EvalException) e).getParameters(), e); + } else { + MP.printError(ec, e); + } + } + this.trace.printTrace(curState, succState); + this.theStateQueue.finishAll(); + this.notify(); + } + } + } /** * Things need to be done here: @@ -651,47 +656,114 @@ public class ModelChecker extends AbstractChecker * Create checkpoint: checkpoint three data structures: the state set, * the state queue, and the state trace. */ - public final boolean doPeriodicWork() throws Exception + @Override + public final int doPeriodicWork() throws Exception { + // Remember if checkpointing should be run. doCheckPoint() when called + // internally diffs the time expired since its last invocation which is + // only milliseconds here when called twice. + final boolean createCheckPoint = TLCGlobals.doCheckPoint(); + if ((!this.checkLiveness || runtimeRatio > TLCGlobals.livenessRatio || !liveCheck.doLiveCheck()) && !forceLiveCheck && !createCheckPoint) { + updateRuntimeRatio(0L); + + // Do not suspend the state queue if neither check-pointing nor + // liveness-checking is going to happen. Suspending is expensive. + // It stops all workers. + return EC.NO_ERROR; + } + if (this.theStateQueue.suspendAll()) { // Run liveness checking, if needed: - if (this.checkLiveness) + // The ratio set in TLCGlobals defines an upper bound for the + // runtime dedicated to liveness checking. + if (this.checkLiveness && (runtimeRatio < TLCGlobals.livenessRatio || forceLiveCheck)) { - if (!liveCheck.check(false)) - return false; + final long preLivenessChecking = System.currentTimeMillis(); + final int result = liveCheck.check(tool, forceLiveCheck); + if (result != EC.NO_ERROR) + { + return result; + } + forceLiveCheck = false; + updateRuntimeRatio(System.currentTimeMillis() - preLivenessChecking); + } else if (runtimeRatio > TLCGlobals.livenessRatio) { + updateRuntimeRatio(0L); } - if (TLCGlobals.doCheckPoint()) { + if (createCheckPoint) { // Checkpoint: - MP.printMessage(EC.TLC_CHECKPOINT_START, this.metadir); - - // start checkpointing: - this.theStateQueue.beginChkpt(); - this.trace.beginChkpt(); - this.theFPSet.beginChkpt(); - this.theStateQueue.resumeAll(); - UniqueString.internTbl.beginChkpt(this.metadir); - if (this.checkLiveness) - { - liveCheck.beginChkpt(); - } - // commit checkpoint: - this.theStateQueue.commitChkpt(); - this.trace.commitChkpt(); - this.theFPSet.commitChkpt(); - UniqueString.internTbl.commitChkpt(this.metadir); - if (this.checkLiveness) - { - liveCheck.commitChkpt(); - } - MP.printMessage(EC.TLC_CHECKPOINT_END); + checkpoint(); } else { // Just resume worker threads when checkpointing is skipped this.theStateQueue.resumeAll(); } } - return true; + return EC.NO_ERROR; + } + + protected void checkpoint() throws IOException { + // start checkpointing: + MP.printMessage(EC.TLC_CHECKPOINT_START, this.metadir); + this.theStateQueue.beginChkpt(); + this.trace.beginChkpt(); + this.theFPSet.beginChkpt(); + this.theStateQueue.resumeAll(); + UniqueString.internTbl.beginChkpt(this.metadir); + if (this.checkLiveness) + { + liveCheck.beginChkpt(); + } + // commit checkpoint: + this.theStateQueue.commitChkpt(); + this.trace.commitChkpt(); + this.theFPSet.commitChkpt(); + UniqueString.internTbl.commitChkpt(this.metadir); + if (this.checkLiveness) + { + liveCheck.commitChkpt(); + } + MP.printMessage(EC.TLC_CHECKPOINT_END); + } + + public void forceLiveCheck() { + forceLiveCheck = true; + } + + protected void updateRuntimeRatio(final long delta) { + assert delta >= 0L; + + // Absolute runtime from TLC startup to now (includes liveness + // checking, even the current delta). + long totalRuntime = System.currentTimeMillis() - startTime; + + // Subtract a progressInterval to account for the fact that the + // previously recorded runtimeRatio was calculated with totalRuntime + // from the previous progressReporting interval. updateRuntimeRatio is + // called from doPeriodicWork which executes every progressIntervall. + // This is an approximation because the last invocation could have + // happened longer ago than progressInterval if e.g. checkpointing + // blocked the doPeriodicWork thread. + totalRuntime = totalRuntime - TLCGlobals.progressInterval; + + // Subtract delta from the totalRuntime + totalRuntime = totalRuntime - delta; + + // Absolute time spent on all liveness checks from TLC + // startup up to now (without delta). Iff no liveness checking has been + // executed so far, the absolute time is obviously 0. totalRuntime + // can also be negative. + final double absLivenessRuntime = Math.max(totalRuntime * runtimeRatio, 0); + + // Sum up the absLivenessRuntime with the new delta. It is the current + // absolute time for liveness checking. Divide it by overall + // totalRuntime (including progressInterval and delta) to calculate the + // new ratio. + runtimeRatio = (delta + absLivenessRuntime) / (totalRuntime + TLCGlobals.progressInterval + delta); + } + + public double getRuntimeRatio() { + return runtimeRatio; } public final boolean recover() throws IOException @@ -703,31 +775,55 @@ public class ModelChecker extends AbstractChecker MP.printMessage(EC.TLC_CHECKPOINT_RECOVER_START, this.fromChkpt); this.trace.recover(); this.theStateQueue.recover(); - this.theFPSet.recover(); + this.theFPSet.recover(this.trace); if (this.checkLiveness) { + // Liveness checking requires the initial states to be + // available as part of behaviors. Initial states are not part + // of the checkpoint, but we can easily recreate them. + // See bug #22 "Recovering from a checkpoint silently breaks + // liveness checking" at + // https://github.com/tlaplus/tlaplus/issues/22 + this.tool.getInitStates(new IStateFunctor() { + public Object addElement(TLCState state) { + liveCheck.addInitState(tool, state, state.fingerPrint()); + return true; + } + }); liveCheck.recover(); } MP.printMessage(EC.TLC_CHECKPOINT_RECOVER_END, new String[] { String.valueOf(this.theFPSet.size()), String.valueOf(this.theStateQueue.size()) }); recovered = true; - this.numOfGenStates.set(this.theFPSet.size()); + // Not all states are true initial states, but who cares at this point? + numberOfInitialStates = this.theFPSet.size(); } return recovered; } private final void cleanup(boolean success) throws IOException { + boolean vetoCleanup = VETO_CLEANUP; + + // If model checking is not done, checkpoints are (explicitly) enabled, and + // either and error has been found or time-bound model checking is enabled, take + // a snapshot to allow users to continue model checking if needed. + if (TLCGlobals.chkptExplicitlyEnabled() + && !theStateQueue.isEmpty() && (this.errState != null || isTimeBound())) { + checkpoint(); + vetoCleanup = true; + } + this.theFPSet.close(); this.trace.close(); - if (this.checkLiveness) - liveCheck.close(); - if (this.allStateWriter != null) - this.allStateWriter.close(); - if (!VETO_CLEANUP) { - FileUtil.deleteDir(this.metadir, success); - } - } + if (this.checkLiveness) { + liveCheck.close(); + } + this.allStateWriter.close(); + if (!vetoCleanup) { + FileUtil.deleteDir(this.metadir, success); + } + } public final void printSummary(boolean success, final long startTime) throws IOException { @@ -740,18 +836,32 @@ public class ModelChecker extends AbstractChecker */ if (TLCGlobals.tool) { - printProgresStats(startTime); + printProgresStats(startTime, true); } - MP.printMessage(EC.TLC_STATS, new String[] { String.valueOf(this.numOfGenStates), + MP.printMessage(EC.TLC_STATS, new String[] { String.valueOf(getStatesGenerated()), String.valueOf(this.theFPSet.size()), String.valueOf(this.theStateQueue.size()) }); if (success) { MP.printMessage(EC.TLC_SEARCH_DEPTH, String.valueOf(this.trace.getLevelForReporting())); + + // Aggregate outdegree from statistics maintained by individual workers. + final BucketStatistics aggOutDegree = new BucketStatistics("State Graph OutDegree"); + for (IWorker worker : workers) { + aggOutDegree.add(((Worker) worker).getOutDegree()); + } + // Print graph statistics iff data points were actually collected. + if (aggOutDegree.getObservations() > 0) { + MP.printMessage(EC.TLC_STATE_GRAPH_OUTDEGREE, + new String[] { Integer.toString(aggOutDegree.getMin()), + Long.toString(Math.round(aggOutDegree.getMean())), + Long.toString(Math.round(aggOutDegree.getPercentile(.95))), + Integer.toString(aggOutDegree.getMax()) }); + } } } - private final void printProgresStats(final long startTime) throws IOException { + private final void printProgresStats(final long startTime, final boolean isFinal) throws IOException { final long fpSetSize = this.theFPSet.size(); // print progress showing states per minute metric (spm) @@ -764,232 +874,47 @@ public class ModelChecker extends AbstractChecker oldFPSetSize = 0; factor = (System.currentTimeMillis() - startTime) / 60000d; } - long l = numOfGenStates.get(); + final long l = getStatesGenerated(); statesPerMinute = (long) ((l - oldNumOfGenStates) / factor); oldNumOfGenStates = l; distinctStatesPerMinute = (long) ((fpSetSize - oldFPSetSize) / factor); oldFPSetSize = fpSetSize; - MP.printMessage(EC.TLC_PROGRESS_STATS, new String[] { String.valueOf(this.trace.getLevelForReporting()), - String.valueOf(this.numOfGenStates), String.valueOf(fpSetSize), - String.valueOf(this.theStateQueue.size()), String.valueOf(statesPerMinute), String.valueOf(distinctStatesPerMinute) }); + MP.printMessage(EC.TLC_PROGRESS_STATS, new String[] { + String.valueOf(this.trace.getLevelForReporting()), + MP.format(l), + MP.format(fpSetSize), + MP.format(this.theStateQueue.size()), + MP.format(statesPerMinute), + MP.format(distinctStatesPerMinute) }); + + TLAFlightRecorder.progress(isFinal, this.trace.getLevelForReporting(), l, fpSetSize, this.theStateQueue.size(), + statesPerMinute, distinctStatesPerMinute); } public static final void reportSuccess(final FPSet anFpSet, final long numOfGenStates) throws IOException { - final long fpSetSize = anFpSet.size(); - final double actualProb = anFpSet.checkFPs(); - reportSuccess(fpSetSize, actualProb, numOfGenStates); - } - - public static final void reportSuccess(final long numOfDistinctStates, final double actualProb, final long numOfGenStates) throws IOException - { - // shown as 'calculated' in Toolbox - final double optimisticProb = numOfDistinctStates * ((numOfGenStates - numOfDistinctStates) / Math.pow(2, 64)); - /* The following code added by LL on 3 Aug 2009 to print probabilities - * to only one decimal point. Removed by LL on 17 April 2012 because it - * seemed to report probabilities > 10-4 as probability 0. - */ - // final PrintfFormat fmt = new PrintfFormat("val = %.1G"); - // final String optimisticProbStr = fmt.sprintf(optimisticProb); - // final String actualProbStr = fmt.sprintf(actualProb); - - // Following two lines added by LL on 17 April 2012 - final String optimisticProbStr = "val = " + ProbabilityToString(optimisticProb, 2); - // shown as 'observed' in Toolbox - final String actualProbStr = "val = " + ProbabilityToString(actualProb, 2); - MP.printMessage(EC.TLC_SUCCESS, new String[] { optimisticProbStr, actualProbStr }); - } - - /** - * This method added by LL on 17 April 2012 to replace the use of the PrintfFormat - * method in reportSuccess. - * - * Returns a string representing the decimal representation of a probability to - * a given number of significant digits. If the input is not a probability, or if - * some error is found, then it returns the result of applying Double.toString(long) - * to the value. - * - * Warning: the code makes the following assumption: - * - Double.toString(v) returns a decimal representation of v of the - * form [d]* ["." [d]+ ["E" [+ | -] [d]+] where d is a decimal digit and - * [x] = 0 or 1 instance of x - * [x]* = any number of instances of x - * [x]+ = any non-zero number of instances of x - * x | y = an x or a y - * - * @param val - the probability represented as a long; must satisfy 0 <= val <= 1. - * @param significantDigits - the number of significant digits to include; must be > 0. - * @return - */ - private static final String ProbabilityToString(double val, int significantDigits) { - /* - * If val = 0 (which shouldn't happen), return "0.0" - */ - if (val == 0) { - return "0.0"; - } - - String valString = Double.toString(val) ; - int valStringLen = valString.length(); - - String result = ""; - int next = 0; // pointer to the next character in valString to examine. - int significantDigitsFound = 0; - - /* - * Skip past leading zeros. - */ - while ((next < valStringLen) && (valString.charAt(next) == '0')) { - next++ ; - } - - /* - * Append all the following digits to result, incrementing - * significantDigits for each one. - */ - while ( (next < valStringLen) && - Character.isDigit(valString.charAt(next))) { - result = result + valString.charAt(next); - significantDigitsFound++; - next++ ; - } - - /* - * IF next character is not "." - * THEN IF at end THEN return result - * ELSE return valString. - */ - if (next == valStringLen) { - return result; - } else if (valString.charAt(next) != '.') { - return valString; - } - - - /* - * IF significantDigitsFound >= significantDigits, - * THEN skip over "." and the following digits. - * (this should not happen) - * ELSE append "." to result ; - * IF significantDigitsFound = 0 - * THEN copy each of the following "0"s of valString to result; - * copy up to significantDigits - significantDigitsFound - * following digits of valString to result; - * IF next char of valString a digit >= "5" - * THEN propagate a carry backwards over the digits of result - * -- e.g., changing ".019" to ".020"; - * Skip over remaining digits of valString; - */ - if (significantDigitsFound >= significantDigits) { - next++ ; - while ( (next < valStringLen) && - Character.isDigit(valString.charAt(next))) { - next++ ; - } + final long numOfDistinctStates = anFpSet.size(); + final double optimisticProb = calculateOptimisticProbability(numOfDistinctStates, numOfGenStates); + if (optimisticProb < 1E-10) { + // If the optimistic probability is sufficiently low, don't waste time + // calculating the actual probability. + reportSuccess(numOfDistinctStates, numOfGenStates); } else { - next++; - result = result + "."; - if (significantDigitsFound == 0) { - while ((next < valStringLen) && (valString.charAt(next) == '0')) { - next++ ; - result = result + "0"; - } - } - while ((next < valStringLen) && - Character.isDigit(valString.charAt(next)) && - significantDigitsFound < significantDigits ) { - result = result + valString.charAt(next); - next++; - significantDigitsFound++; - } - if ((next < valStringLen) && - Character.isDigit(valString.charAt(next)) && - Character.digit(valString.charAt(next), 10) >= 5) { - int prev = result.length()-1; // the next digit of result to increment - boolean done = false; - while (!done) { - if (prev < 0) { - result = "1" + result; - done = true; - } else { - char prevChar = result.charAt(prev); - String front = result.substring(0, prev); - String back = result.substring(prev+1); - if (Character.isDigit(prevChar)) { - if (prevChar == '9') { - result = front + '0' + back; - } else { - result = front + Character.forDigit(Character.digit(prevChar, 10)+1, 10) + back; - done = true; - } - - } else { - // prevChar must be '.', so just continue - } - } - prev--; - } - } - while ((next < valStringLen) && - Character.isDigit(valString.charAt(next))) { - next++; - } + reportSuccess(numOfDistinctStates, anFpSet.checkFPs(), numOfGenStates); } - - /* - * IF next at end of valString or at "E" - * THEN copy remaining chars of valString to result; - * return result - * ELSE return valString - */ - if (next >= valStringLen) { - return result; - } - if (valString.charAt(next)=='E') { - next++; - result = result + "E"; - while (next < valStringLen) { - result = result + valString.charAt(next); - next++; - } - return result; - } - return valString; - } - -// The following method used for testing ProbabilityToString -// -// public static void main(String[] args) { -// double[] test = new double[] {.5, .0995, .00000001, 001.000, .0022341, -// .0022351, 3.14159E-12, -// 00.999, .002351111, 22.8E-14, 0.000E-12, -// 37, 0033D, 04.85, -35.3}; -// int i = 0; -// while (i < test.length) { -// System.out.println("" + i + ": " + Double.toString(test[i]) + " -> " + ProbabilityToString(test[i],2)); -// i++; -// } -// } - - public final void setAllValues(int idx, Value val) - { - for (int i = 0; i < this.workers.length; i++) - { - workers[i].setLocalValue(idx, val); - } - } - - public final Value getValue(int i, int idx) - { - return workers[i].getLocalValue(idx); } /** * Spawn the worker threads */ - protected IdThread[] startWorkers(AbstractChecker checker, int checkIndex) + protected IWorker[] startWorkers(AbstractChecker checker, int checkIndex) { + // Generation of initial states is done at this point. Thus set the + // number of workers on the fpset, for it to adapt any synchronization + // if necessary (e.g. OffHeapDiskFPSet). + this.theFPSet.incWorkers(this.workers.length); + for (int i = 0; i < this.workers.length; i++) { this.workers[i].start(); @@ -997,14 +922,6 @@ public class ModelChecker extends AbstractChecker return this.workers; } - /** - * Work to be done prior entering to the worker loop - */ - protected void runTLCPreLoop() - { - // nothing to do in this implementation - } - /** * Process calculation. * @@ -1026,7 +943,7 @@ public class ModelChecker extends AbstractChecker { final int level = this.trace.getLevel(); - printProgresStats(-1); + printProgresStats(-1, false); if (level > depth) { @@ -1060,8 +977,207 @@ public class ModelChecker extends AbstractChecker { DebugPrinter.print(e); } + + private static boolean useByteArrayQueue() { + return Boolean.getBoolean(ModelChecker.class.getName() + ".BAQueue"); + } + + public static String getStateQueueName() { + // Ideally, this wouldn't hard-code the simple name of the classes but we don't + // have access to the class file yet. + return useByteArrayQueue() ? "DiskByteArrayQueue" : "DiskStateQueue"; + } public long getStatesGenerated() { - return numOfGenStates.get(); + long sum = numberOfInitialStates; + for (final IWorker worker : workers) { + sum += ((Worker) worker).getStatesGenerated(); + } + return sum; } + + /* (non-Javadoc) + * @see tlc2.tool.AbstractChecker#getProgress() + */ + @Override + public int getProgress() { + try { + return trace.getLevelForReporting(); + } catch (IOException e) { + // The modelchecker trace file might be closed already (e.g. it + // gets closed at the end of the modelchecker run) + return -1; + } + } + + /* (non-Javadoc) + * @see tlc2.tool.AbstractChecker#stop() + */ + @Override + public void stop() { + synchronized (this) { + this.setDone(); + this.theStateQueue.finishAll(); + this.notifyAll(); + } + } + + public void suspend() { + synchronized (this) { + this.theStateQueue.suspendAll(); + this.notifyAll(); + } + } + + public void resume() { + synchronized (this) { + this.theStateQueue.resumeAll(); + this.notifyAll(); + } + } + + /* (non-Javadoc) + * @see tlc2.tool.AbstractChecker#getStateQueueSize() + */ + @Override + public long getStateQueueSize() { + return theStateQueue.size(); + } + + /* (non-Javadoc) + * @see tlc2.tool.AbstractChecker#getDistinctStatesGenerated() + */ + @Override + public long getDistinctStatesGenerated() { + return theFPSet.size(); + } + + /** + * An implementation of {@link IStateFunctor} for + * {@link ModelChecker#doInit(boolean)}. + */ + private class DoInitFunctor implements IStateFunctor { + + @SuppressWarnings("serial") + public class InvariantViolatedException extends RuntimeException { + } + + /** + * Non-Null iff a violation occurred. + */ + private TLCState errState; + private Throwable e; + + /** + * The return values of addElement are meaningless, but doInit wants to + * know the actual outcome when all init states have been processed. + * This outcome is stored as returnValue. + */ + private int returnValue = EC.NO_ERROR; + + private final boolean forceChecks; + private final ITool tool; + + public DoInitFunctor(ITool tool) { + this(tool, false); + } + + public DoInitFunctor(ITool tool, boolean forceChecks) { + this.forceChecks = forceChecks; + this.tool = tool; + } + + /* (non-Javadoc) + * @see tlc2.tool.IStateFunctor#addElement(tlc2.tool.TLCState) + */ + public Object addElement(final TLCState curState) { + if (Long.bitCount(numberOfInitialStates) == 1 && numberOfInitialStates > 1) { + MP.printMessage(EC.TLC_COMPUTING_INIT_PROGRESS, Long.toString(numberOfInitialStates)); + } + numberOfInitialStates++; + + // getInitStates() does not support aborting init state generation + // once a violation has been found (that is why the return values of + // addElement are meaningless). It continues until all init + // states have been generated. Thus, the functor simply ignores + // subsequent states once a violation has been recorded. + if (errState != null) { + if (returnValue == EC.NO_ERROR) + returnValue = EC.TLC_INITIAL_STATE; + return returnValue; + } + + try { + // Check if the state is a legal state + if (!tool.isGoodState(curState)) { + MP.printError(EC.TLC_INITIAL_STATE, new String[]{ "current state is not a legal state", curState.toString() }); + this.errState = curState; + returnValue = EC.TLC_INITIAL_STATE; + throw new InvariantViolatedException(); + } + boolean inModel = tool.isInModel(curState); + boolean seen = false; + if (inModel) { + long fp = curState.fingerPrint(); + seen = theFPSet.put(fp); + if (!seen) { + allStateWriter.writeState(curState); + ((Worker) workers[0]).writeState(curState, fp); + theStateQueue.enqueue(curState); + + // build behavior graph for liveness checking + if (checkLiveness) { + liveCheck.addInitState(tool, curState, fp); + } + } + } + // Check properties of the state: + if (!seen || forceChecks) { + for (int j = 0; j < tool.getInvariants().length; j++) { + if (!tool.isValid(tool.getInvariants()[j], curState)) { + // We get here because of invariant violation: + MP.printError(EC.TLC_INVARIANT_VIOLATED_INITIAL, + new String[] { tool.getInvNames()[j].toString(), curState.toString() }); + if (!TLCGlobals.continuation) { + this.errState = curState; + returnValue = EC.TLC_INVARIANT_VIOLATED_INITIAL; + throw new InvariantViolatedException(); + } + } + } + for (int j = 0; j < tool.getImpliedInits().length; j++) { + if (!tool.isValid(tool.getImpliedInits()[j], curState)) { + // We get here because of implied-inits violation: + MP.printError(EC.TLC_PROPERTY_VIOLATED_INITIAL, + new String[] { tool.getImpliedInitNames()[j], curState.toString() }); + this.errState = curState; + returnValue = EC.TLC_PROPERTY_VIOLATED_INITIAL; + throw new InvariantViolatedException(); + } + } + } + } catch (InvariantViolatedException | Assert.TLCRuntimeException | EvalException e) { + // IVE gets thrown above when an Invariant is violated. TLCRuntimeException gets + // thrown when Tool fails to evaluate a statement because of e.g. too large sets + // or type errors such as in DoInitFunctorInvariantMinimalErrorStackTest test. + this.errState = curState; + this.e = e; + throw e; + } catch (OutOfMemoryError e) { + MP.printError(EC.SYSTEM_OUT_OF_MEMORY_TOO_MANY_INIT); + returnValue = EC.SYSTEM_OUT_OF_MEMORY_TOO_MANY_INIT; + return returnValue; + } catch (Throwable e) { + // Assert.printStack(e); + this.errState = curState; + this.e = e; + } + return returnValue; + } + } + + public List<File> getModuleFiles(FilenameToStream resolver) { + return this.tool.getModuleFiles(resolver); + } } + \ No newline at end of file diff --git a/tlatools/src/tlc2/tool/SimulationWorker.java b/tlatools/src/tlc2/tool/SimulationWorker.java new file mode 100644 index 0000000000000000000000000000000000000000..0e40e2890342e1b8dc16e84a740c51133e2e2354 --- /dev/null +++ b/tlatools/src/tlc2/tool/SimulationWorker.java @@ -0,0 +1,421 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * William Schultz - initial API and implementation + ******************************************************************************/ +package tlc2.tool; + +import java.io.PrintWriter; +import java.util.Optional; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.atomic.LongAdder; + +import tlc2.output.EC; +import tlc2.tool.liveness.ILiveCheck; +import tlc2.util.IdThread; +import tlc2.util.RandomGenerator; +import util.FileUtil; + +/** + * A SimulationWorker repeatedly checks random traces of a spec. + * + * It is constructed with an initial seed which it uses to initialize its + * internal random number generator. A simulation worker continually generates + * random traces, even if it encounters errors of any kind i.e. + * invariant/liveness violations, evaluation errors, etc. A worker communicates + * these errors by pushing its results onto a result queue, that is provided to + * it at construction time. + * + * Workers may terminate in two possible ways. They will terminate if they + * receive an "interruption" signal, and they will terminate if they reach their + * maximum trace count limit. Otherwise, they will continue to run forever, + * generating new traces. It is the job of an external thread/client to + * determine if the errors produced are cause for termination or can be ignored. + * + * Upon termination due to any cause, a worker thread will always push a final + * OK result onto its result queue. This acts as a way to signal external + * clients that this thread has terminated. + */ +public class SimulationWorker extends IdThread { + + // This worker's local source of randomness. + final RandomGenerator localRng; + + // The state currently being processed. + TLCState curState; + + // Stores a trace generated by this worker. + StateVec stateTrace; + + // The set of initial states for the spec. + private StateVec initStates; + + // The queue that the worker places its results onto. + private final BlockingQueue<SimulationWorkerResult> resultQueue; + + // Tracks the number of traces that have been generated so far. + private int traceCnt = 0; + + // The maximum number of traces this worker should generate before terminating. + private final long maxTraceNum; + + // The maximum length of any generated trace. + private final long maxTraceDepth; + + // Should this worker check traces for deadlock. + private final boolean checkDeadlock; + + // The base name of the file that this worker writes out generated traces to. If it is null, + // no trace files are generated. + private final String traceFile; + + // A counter that tracks the number of generated states/traces. This counter may be + // shared among workers, so its count may be greater than the number of states/traces + // generated by this worker alone. It is the duty of this worker, though, to + // update it whenever it generates a new state or trace. + private final LongAdder numOfGenStates; + private final LongAdder numOfGenTraces; + + private final ITool tool; + private final ILiveCheck liveCheck; + + /** + * Encapsulates information about an error produced by a simulation worker. + */ + public static class SimulationWorkerError { + + public SimulationWorkerError(int errorCode, String[] parameters, TLCState state, StateVec stateTrace, + Exception e) { + this.errorCode = errorCode; + this.parameters = parameters; + this.state = state; + this.stateTrace = stateTrace; + this.exception = e; + } + + // The error code to report. + public int errorCode; + + // Any additional information to be included in a reported error string. + public String[] parameters; + + // The TLC state associated with the error. + public TLCState state; + + // The TLC trace associated with the error. + public StateVec stateTrace; + + // An exception associated with the error. + public Exception exception; + } + + + /** + * Encapsulates information about a result produced by a simulation worker. + * + * A result can either be an error or OK (indicating no error). Each result is + * associated with a specific worker. + */ + public static class SimulationWorkerResult { + + /** + * Constructs an OK result. + */ + static SimulationWorkerResult OK(int workerId) { + SimulationWorkerResult res = new SimulationWorkerResult(); + res.workerId = workerId; + return res; + } + + /** + * Constructs an error result. + */ + static SimulationWorkerResult Error(int workerId, SimulationWorkerError err) { + SimulationWorkerResult res = new SimulationWorkerResult(); + res.error = Optional.of(err); + res.workerId = workerId; + return res; + } + + /** + * Returns whether this result is an error. + */ + public boolean isError() { + return error.isPresent(); + } + + /** + * Returns the error for this result. Only valid to call if an error is present. + */ + public SimulationWorkerError error() { + return error.get(); + } + + public int workerId() { + return workerId; + } + + private int workerId; // The worker that generated this result. + private Optional<SimulationWorkerError> error = Optional.empty(); + + } + + + public SimulationWorker(int id, ITool tool, BlockingQueue<SimulationWorkerResult> resultQueue, + long seed, long maxTraceDepth, long maxTraceNum, boolean checkDeadlock, String traceFile, + ILiveCheck liveCheck, LongAdder numOfGenStates, LongAdder numOfGenTraces) { + super(id); + this.localRng = new RandomGenerator(seed); + this.tool = tool; + this.maxTraceDepth = maxTraceDepth; + this.maxTraceNum = maxTraceNum; + this.resultQueue = resultQueue; + this.checkDeadlock = checkDeadlock; + this.traceFile = traceFile; + this.liveCheck = liveCheck; + this.numOfGenStates = numOfGenStates; + this.numOfGenTraces = numOfGenTraces; + } + + /** + * The main worker loop. Continually generates random traces until the trace count limit + * is reached or until the worker is interrupted. + * + * We use the standard Java notion of thread "interruption" as a mechanism for + * halting/cancelling the execution of a worker thread. There's nothing particularly + * special about this. The Thread.interrupt() just sets a boolean flag that the + * running thread then periodically checks via calls to Thread.interrupted(). We could + * implement this manually but it's simpler to use the built-in mechanism. + */ + public final void run() { + while(true) { + try { + // The trace simulation method should do appropriately frequent interruption + // checks. + final Optional<SimulationWorkerError> res = simulateRandomTrace(); + traceCnt++; + this.numOfGenTraces.increment(); + + // If we have an error result, place it on the output queue. + if (res.isPresent()) { + final SimulationWorkerError err = res.get(); + resultQueue.put(SimulationWorkerResult.Error(this.myGetId(), err)); + } + + // Abide by the maximum trace generation count. + if (traceCnt >= maxTraceNum) { + resultQueue.put(SimulationWorkerResult.OK(this.myGetId())); + return; + } + } catch (final InterruptedException e) { + // Gracefully terminate if we were interrupted. + resultQueue.offer(SimulationWorkerResult.OK(this.myGetId())); + return; + } catch (final Exception e) { + final SimulationWorkerError err = new SimulationWorkerError(0, null, this.curState, this.stateTrace, e); + resultQueue.offer(SimulationWorkerResult.Error(this.myGetId(), err)); + return; + } + } + } + + /** + * Check to see if the worker thread has been interrupted. + */ + private void checkForInterrupt() throws InterruptedException { + if (Thread.interrupted()) { + throw new InterruptedException(); + } + } + + /** + * This method returns the set of next states generated by a randomly chosen + * action. It returns null if there is no possible next state. + */ + public final StateVec randomNextStates(RandomGenerator rng, TLCState state) { + final Action[] actions = this.tool.getActions(); + final int len = actions.length; + int index = (int) Math.floor(rng.nextDouble() * len); + final int p = rng.nextPrime(); + for (int i = 0; i < len; i++) { + final StateVec pstates = this.tool.getNextStates(actions[index], state); + if (!pstates.empty()) { + return pstates; + } + index = (index + p) % len; + } + return null; + } + + /** + * This method returns a state that is randomly chosen from the set of states. + * It returns null if the set of states is empty. + */ + private final TLCState randomState(RandomGenerator rng, StateVec states) throws EvalException { + final int len = states.size(); + if (len > 0) { + final int index = (int) Math.floor(rng.nextDouble() * len); + return states.elementAt(index); + } + return null; + } + + /** + * Generates a single random trace. + * + * The core steps of this process are as follows: + * + * a) Pick one of the initial states. + * b) Randomly pick an action to generate the successor states (more than 1) to the current initial state. + * c) Check all of the generated successors for their validity. + * d) Randomly pick a generated successor and make it the new current state. + * + * Returns an Optional error representing the outcome of the trace generation. If the trace generation produced no error, + * returns Optional.empty(). + * + */ + private Optional<SimulationWorkerError> simulateRandomTrace() throws Exception { + this.curState = null; + this.stateTrace = new StateVec((int) maxTraceDepth); + stateTrace.clear(); + + // a) Randomly select a state from the set of init states. + curState = randomState(this.localRng, initStates); + setCurrentState(curState); + + boolean inConstraints = tool.isInModel(curState); + + // Simulate a trace up to the maximum specified length. + for (int traceIdx = 0; traceIdx < maxTraceDepth; traceIdx++) { + // We don't want this thread to run for too long without checking for + // interruption, so we do so on every iteration of the main trace generation + // loop. + checkForInterrupt(); + + // Add the curState to the trace regardless of its inModel property. + stateTrace.addElement(curState); + + // Make sure this state satisfies the model constraints. + if (!inConstraints) { + break; + } + + // b) Get the current state's successor states. + final StateVec nextStates = randomNextStates(this.localRng, curState); + if (nextStates == null) { + if (checkDeadlock) { + // We get here because of deadlock. + return Optional.of(new SimulationWorkerError(EC.TLC_DEADLOCK_REACHED, null, curState, stateTrace, null)); + } + break; + } + + // c) Check all generated next states before all but one are discarded. + for (int i = 0; i < nextStates.size(); i++) { + numOfGenStates.increment(); + final TLCState state = nextStates.elementAt(i); + + if (!tool.isGoodState(state)) { + return Optional.of(new SimulationWorkerError(EC.TLC_STATE_NOT_COMPLETELY_SPECIFIED_NEXT, null, state, + stateTrace, null)); + } + + // Check invariants. + int idx = 0; + try { + for (idx = 0; idx < this.tool.getInvariants().length; idx++) { + if (!tool.isValid(this.tool.getInvariants()[idx], state)) { + // We get here because of an invariant violation. + return Optional.of(new SimulationWorkerError(EC.TLC_INVARIANT_VIOLATED_BEHAVIOR, + new String[] { tool.getInvNames()[idx] }, state, stateTrace, null)); + } + } + } catch (final Exception e) { + return Optional.of(new SimulationWorkerError(EC.TLC_INVARIANT_EVALUATION_FAILED, + new String[] { tool.getInvNames()[idx], e.getMessage() }, state, stateTrace, null)); + } + + // Check action properties. + try { + for (idx = 0; idx < this.tool.getImpliedActions().length; idx++) { + if (!tool.isValid(this.tool.getImpliedActions()[idx], curState, state)) { + // We get here because of implied-action violation. + return Optional.of(new SimulationWorkerError(EC.TLC_ACTION_PROPERTY_VIOLATED_BEHAVIOR, + new String[] { tool.getImpliedActNames()[idx] }, state, stateTrace, null)); + } + } + } catch (final Exception e) { + return Optional.of(new SimulationWorkerError(EC.TLC_ACTION_PROPERTY_EVALUATION_FAILED, + new String[] { tool.getImpliedActNames()[idx], e.getMessage() }, state, stateTrace, null)); + } + + } + + // At this point all generated successor states have been checked for + // their respective validity (isGood/isValid/impliedActions/...). + + // d) Randomly select one of them and make it the current state for the next + // iteration of the loop. + final TLCState s1 = randomState(localRng, nextStates); + inConstraints = (tool.isInModel(s1) && tool.isInActions(curState, s1)); + s1.setPredecessor(curState); + curState = s1; + setCurrentState(curState); + } + + // Check for interruption once more before entering liveness checking. + checkForInterrupt(); + + // Check if the current trace satisfies liveness properties. + liveCheck.checkTrace(tool, stateTrace); + + + // Write the trace out if desired. The trace is printed in the + // format of TLA module, so that it can be read by TLC again. + if (traceFile != null) { + // Make sure each worker outputs to its own set of trace files. + final String fileName = traceFile + "_" + String.valueOf(this.myGetId()) + "_" + this.traceCnt; + // TODO is it ok here? + final PrintWriter pw = new PrintWriter(FileUtil.newBFOS(fileName)); + pw.println("---------------- MODULE " + fileName + " -----------------"); + for (int idx = 0; idx < stateTrace.size(); idx++) { + pw.println("STATE_" + (idx + 1) + " == "); + pw.println(stateTrace.elementAt(idx) + "\n"); + } + pw.println("================================================="); + pw.close(); + } + + // Finished trace generation without any errors. + return Optional.empty(); + } + + public final StateVec getTrace() { + return stateTrace; + } + + public void start(StateVec initStates) { + this.initStates = initStates; + this.start(); + } +} \ No newline at end of file diff --git a/tlatools/src/tlc2/tool/Simulator.java b/tlatools/src/tlc2/tool/Simulator.java index 6edceb687878dad53fd38dcdd03733b9c46f6454..d972edaea356225a8a8e86dc895c802423e4d393 100644 --- a/tlatools/src/tlc2/tool/Simulator.java +++ b/tlatools/src/tlc2/tool/Simulator.java @@ -6,49 +6,46 @@ package tlc2.tool; import java.io.IOException; -import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.TimerTask; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.atomic.LongAdder; -import tla2sany.modanalyzer.SpecObj; -import tla2sany.semantic.SemanticNode; import tlc2.TLCGlobals; import tlc2.output.EC; import tlc2.output.MP; import tlc2.output.StatePrinter; +import tlc2.tool.SimulationWorker.SimulationWorkerError; +import tlc2.tool.SimulationWorker.SimulationWorkerResult; +import tlc2.tool.coverage.CostModelCreator; +import tlc2.tool.impl.FastTool; import tlc2.tool.liveness.ILiveCheck; import tlc2.tool.liveness.LiveCheck; import tlc2.tool.liveness.LiveCheck1; import tlc2.tool.liveness.LiveException; import tlc2.tool.liveness.NoOpLiveCheck; -import tlc2.util.ObjLongTable; import tlc2.util.RandomGenerator; import tlc2.util.statistics.DummyBucketStatistics; +import tlc2.value.IValue; +import util.Assert.TLCRuntimeException; import util.FileUtil; import util.FilenameToStream; -public class Simulator implements Cancelable { +public class Simulator { - public static boolean EXPERIMENTAL_LIVENESS_SIMULATION = Boolean.getBoolean(Simulator.class.getName() + ".experimentalLiveness"); - + public static boolean EXPERIMENTAL_LIVENESS_SIMULATION = Boolean + .getBoolean(Simulator.class.getName() + ".experimentalLiveness"); /* Constructors */ - /** - * SZ Feb 20, 2009: added the possibility to pass the SpecObject, this is - * compatibility constructor - * @throws IOException - * - * @deprecated use - * {@link Simulator#Simulator(String, String, String, boolean, int, long, RandomGenerator, long, boolean, FilenameToStream, SpecObj)} - * instead and pass the <code>null</code> as SpecObj - */ - public Simulator(String specFile, String configFile, String traceFile, boolean deadlock, int traceDepth, - long traceNum, RandomGenerator rng, long seed, boolean preprocess, FilenameToStream resolver) throws IOException { - this(specFile, configFile, traceFile, deadlock, traceDepth, traceNum, rng, seed, preprocess, resolver, null); - } // SZ Feb 20, 2009: added the possibility to pass the SpecObject public Simulator(String specFile, String configFile, String traceFile, boolean deadlock, int traceDepth, - long traceNum, RandomGenerator rng, long seed, boolean preprocess, FilenameToStream resolver, - SpecObj specObj) throws IOException { + long traceNum, RandomGenerator rng, long seed, FilenameToStream resolver, + int numWorkers) throws IOException { int lastSep = specFile.lastIndexOf(FileUtil.separatorChar); String specDir = (lastSep == -1) ? "" : specFile.substring(0, lastSep + 1); specFile = specFile.substring(lastSep + 1); @@ -57,16 +54,11 @@ public class Simulator implements Cancelable { // SZ Mar 5, 2009: removed it again because of the bug in simulator // ToolIO.setUserDir(specDir); - this.tool = new Tool(specDir, specFile, configFile, resolver); - - this.tool.init(preprocess, specObj); // parse and process the spec + this.tool = new FastTool(specDir, specFile, configFile, resolver); - this.checkDeadlock = deadlock; + this.checkDeadlock = deadlock && tool.getModelConfig().getCheckDeadlock(); this.checkLiveness = !this.tool.livenessIsTrue(); - this.actions = this.tool.getActions(); this.invariants = this.tool.getInvariants(); - this.impliedActions = this.tool.getImpliedActions(); - this.numOfGenStates = 0; if (traceDepth != -1) { // this.actionTrace = new Action[traceDepth]; // SZ: never read // locally @@ -80,287 +72,347 @@ public class Simulator implements Cancelable { this.rng = rng; this.seed = seed; this.aril = 0; - this.astCounts = new ObjLongTable(10); // Initialization for liveness checking if (this.checkLiveness) { if (EXPERIMENTAL_LIVENESS_SIMULATION) { final String tmpDir = System.getProperty("java.io.tmpdir"); - liveCheck = new LiveCheck(this.tool, new Action[0], tmpDir, new DummyBucketStatistics()); + liveCheck = new LiveCheck(this.tool, tmpDir, new DummyBucketStatistics()); } else { liveCheck = new LiveCheck1(this.tool); } } else { liveCheck = new NoOpLiveCheck(tool, specDir); } + + this.numWorkers = numWorkers; + this.workers = new ArrayList<>(numWorkers); + for (int i = 0; i < this.numWorkers; i++) { + this.workers.add(new SimulationWorker(i, this.tool, this.workerResultQueue, this.rng.nextLong(), + this.traceDepth, this.traceNum, this.checkDeadlock, this.traceFile, this.liveCheck, + this.numOfGenStates, this.numOfGenTraces)); + } + + if (TLCGlobals.isCoverageEnabled()) { + CostModelCreator.create(this.tool); + } + + //TODO Eventually derive Simulator from AbstractChecker. + AbstractChecker.scheduleTermination(new TimerTask() { + @Override + public void run() { + Simulator.this.stop(); + } + }); } /* Fields */ private final ILiveCheck liveCheck; - private final Tool tool; - private final Action[] actions; // the sub actions + private final ITool tool; private final Action[] invariants; // the invariants to be checked - private final Action[] impliedActions; // the implied-actions to be checked private final boolean checkDeadlock; // check deadlock? private final boolean checkLiveness; // check liveness? - private long numOfGenStates; + + // The total number of states/traces generated by all workers. May be written to + // concurrently, so we use a LongAdder to reduce potential contention. + private final LongAdder numOfGenStates = new LongAdder(); + private final LongAdder numOfGenTraces = new LongAdder(); + // private Action[] actionTrace; // SZ: never read locally private final String traceFile; + + // The maximum length of a simulated trace. private final long traceDepth; + + // The maximum number of total traces to generate. private final long traceNum; + + // The number of worker threads to use for simulation. + private int numWorkers = 1; + private final RandomGenerator rng; private final long seed; private long aril; - private final ObjLongTable astCounts; - private boolean isCancelled; // SZ Feb 24, 2009: cancellation added + + // Each simulation worker pushes their results onto this shared queue. + private BlockingQueue<SimulationWorkerResult> workerResultQueue = new LinkedBlockingQueue<>(); + + /** + * Timestamp of when simulation started. + */ + private final long startTime = System.currentTimeMillis(); + + private final List<SimulationWorker> workers; + + /** + * Returns whether a given error code is considered "continuable". That is, if + * any worker returns this error, should we consider continuing to run the + * simulator. These errors are considered "fatal" since they most likely + * indicate an error in the way the spec is written. + */ + private boolean isNonContinuableError(int ec) { + return ec == EC.TLC_INVARIANT_EVALUATION_FAILED || + ec == EC.TLC_ACTION_PROPERTY_EVALUATION_FAILED || + ec == EC.TLC_STATE_NOT_COMPLETELY_SPECIFIED_NEXT; + } + + /** + * Shut down all of the given workers and make sure they have stopped. + */ + private void shutdownAndJoinWorkers(final List<SimulationWorker> workers) throws InterruptedException { + for (SimulationWorker worker : workers) { + worker.interrupt(); + worker.join(); + } + } /* - * This method does simulation on a TLA+ spec. Its argument specifies the - * main module of the TLA+ spec. + * This method does random simulation on a TLA+ spec. + * + * It runs until en error is encountered or we have generated the maximum number of traces. + * + * @return an error code, or <code>EC.NO_ERROR</code> on success */ - public void simulate() throws Exception { - StateVec theInitStates = null; + public int simulate() throws Exception { + final int res = this.tool.checkAssumptions(); + if (res != EC.NO_ERROR) { + return res; + } + TLCState curState = null; - if (isCancelled) { - return; - } - // Compute the initial states: + // The init states are calculated only ever once and never change + // in the loops below. Ideally the variable would be final. + StateVec initStates = this.tool.getInitStates(); + + // + // Compute the initial states. + // try { - // The init states are calculated only ever once and never change - // in the loops below. Ideally the variable would be final. - theInitStates = this.tool.getInitStates(); - this.numOfGenStates = theInitStates.size(); - for (int i = 0; i < theInitStates.size(); i++) { - curState = theInitStates.elementAt(i); + + + // This counter should always be initialized at zero. + assert (this.numOfGenStates.longValue() == 0); + this.numOfGenStates.add(initStates.size()); + + MP.printMessage(EC.TLC_COMPUTING_INIT_PROGRESS, this.numOfGenStates.toString()); + + // Check all initial states for validity. + for (int i = 0; i < initStates.size(); i++) { + curState = initStates.elementAt(i); if (this.tool.isGoodState(curState)) { for (int j = 0; j < this.invariants.length; j++) { if (!this.tool.isValid(this.invariants[j], curState)) { - // We get here because of invariant violation: - MP.printError(EC.TLC_INVARIANT_VIOLATED_INITIAL, + // We get here because of invariant violation. + return MP.printError(EC.TLC_INVARIANT_VIOLATED_INITIAL, new String[] { this.tool.getInvNames()[j], curState.toString() }); - return; } } } else { - MP.printError(EC.TLC_STATE_NOT_COMPLETELY_SPECIFIED_INITIAL, curState.toString()); - return; + return MP.printError(EC.TLC_STATE_NOT_COMPLETELY_SPECIFIED_INITIAL, curState.toString()); } } } catch (Exception e) { - // Assert.printStack(e); + final int errorCode; if (curState != null) { - MP.printError(EC.TLC_INITIAL_STATE, + errorCode = MP.printError(EC.TLC_INITIAL_STATE, new String[] { (e.getMessage() == null) ? e.toString() : e.getMessage(), curState.toString() }); } else { - MP.printError(EC.GENERAL, e); // LL changed call 7 April 2012 + errorCode = MP.printError(EC.GENERAL, e); // LL changed call 7 April 2012 } this.printSummary(); - return; + return errorCode; } - if (this.numOfGenStates == 0) { - MP.printError(EC.TLC_NO_STATES_SATISFYING_INIT); - return; + + if (this.numOfGenStates.longValue() == 0) { + return MP.printError(EC.TLC_NO_STATES_SATISFYING_INIT); } + // It appears deepNormalize brings the states into a canonical form to // speed up equality checks. - theInitStates.deepNormalize(); + initStates.deepNormalize(); - // Start progress report thread: - ProgressReport report = new ProgressReport(); + // + // Start progress report thread. + // + final ProgressReport report = new ProgressReport(); report.start(); - // Start simulating: - final StateVec stateTrace = new StateVec((int) traceDepth); - int idx = 0; - try { - // The two loops essentially do: - // a) Pick one of the initial states for as long as the bahavior's length is less than traceCnt - // b) Randomly pick an action to generate the successor states (more than 1) to the current initial state - // c) Check all of the generated successors for their validity - // d) Randomly pick a generated successor and make it curState - for (int traceCnt = 1; traceCnt <= this.traceNum; traceCnt++) { - this.aril = rng.getAril(); - stateTrace.clear(); - - // a) Randomly select a state from the set of init states. - curState = this.randomState(theInitStates); - boolean inConstraints = this.tool.isInModel(curState); - - for (int traceIdx = 0; traceIdx < this.traceDepth; traceIdx++) { - // Add the curState to the trace regardless of its inModel - // property. - stateTrace.addElement(curState); - - if (!inConstraints) { - break; - } - - // b) Get the curState's successor states - StateVec nextStates = this.randomNextStates(curState); - if (nextStates == null) { - if (this.checkDeadlock) { - // We get here because of deadlock: - this.printBehavior(EC.TLC_DEADLOCK_REACHED, null, curState, stateTrace); - if (!TLCGlobals.continuation) { - return; - } - } - break; - } - - // c) Check all generated next states before all but one are - // discarded - for (int i = 0; i < nextStates.size(); i++) { - this.numOfGenStates++; - TLCState state = nextStates.elementAt(i); + // + // Start simulating. + // + this.aril = rng.getAril(); + + // Start up multiple simulation worker threads, each with their own unique seed. + final Set<Integer> runningWorkers = new HashSet<>(); + for (int i = 0; i < this.workers.size(); i++) { + SimulationWorker worker = workers.get(i); + worker.start(initStates); + runningWorkers.add(i); + } - if (TLCGlobals.coverageInterval >= 0) { - ((TLCStateMutSource) state).addCounts(this.astCounts); - } + int errorCode = EC.NO_ERROR; + + // Continuously consume results from all worker threads. + while (true) { + final SimulationWorkerResult result = workerResultQueue.take(); - if (!this.tool.isGoodState(state)) { - this.printBehavior(EC.TLC_STATE_NOT_COMPLETELY_SPECIFIED_NEXT, null, state, stateTrace); - return; - } else { - try { - for (idx = 0; idx < this.invariants.length; idx++) { - if (!this.tool.isValid(this.invariants[idx], state)) { - // We get here because of invariant - // violation: - this.printBehavior(EC.TLC_INVARIANT_VIOLATED_BEHAVIOR, - new String[] { this.tool.getInvNames()[idx] }, state, stateTrace); - if (!TLCGlobals.continuation) { - return; - } - } - } - } catch (Exception e) { - // Assert.printStack(e); - this.printBehavior(EC.TLC_INVARIANT_EVALUATION_FAILED, - new String[] { this.tool.getInvNames()[idx], e.getMessage() }, state, stateTrace); - return; - } - - try { - for (idx = 0; idx < this.impliedActions.length; idx++) { - if (!this.tool.isValid(this.impliedActions[idx], curState, state)) { - // We get here because of implied-action - // violation: - - this.printBehavior(EC.TLC_ACTION_PROPERTY_VIOLATED_BEHAVIOR, - new String[] { this.tool.getImpliedActNames()[idx] }, state, stateTrace); - if (!TLCGlobals.continuation) { - return; - } - } - } - } catch (Exception e) { - // Assert.printStack(e); - this.printBehavior(EC.TLC_ACTION_PROPERTY_EVALUATION_FAILED, - new String[] { this.tool.getImpliedActNames()[idx], e.getMessage() }, state, - stateTrace); - return; - } - } + // If the result is an error, print it. + if (result.isError()) { + SimulationWorkerError error = result.error(); + + // We assume that if a worker threw an unexpected exception, there is a bug + // somewhere, so we print out the exception and terminate. In the case of a + // liveness error, which is reported as an exception, we also terminate. + if (error.exception != null) { + if (error.exception instanceof LiveException) { + // In case of a liveness error, there is no need to print out + // the behavior since the liveness checker should take care of that itself. + this.printSummary(); + errorCode = ((LiveException)error.exception).errorCode; + } else if (error.exception instanceof TLCRuntimeException) { + final TLCRuntimeException exception = (TLCRuntimeException)error.exception; + printBehavior(exception, error.state, error.stateTrace); + errorCode = exception.errorCode; + } else { + printBehavior(EC.GENERAL, new String[] { MP.ECGeneralMsg("", error.exception) }, error.state, + error.stateTrace); + errorCode = EC.GENERAL; } - // At this point all generated successor states have been checked for - // their respective validity (isGood/isValid/impliedActions/...). - // d) Then randomly select one of them and make it curState - // for the next iteration of the loop. - TLCState s1 = this.randomState(nextStates); - inConstraints = (this.tool.isInModel(s1) && this.tool.isInActions(curState, s1)); - curState = s1; + break; } - // Check if the current trace satisfies liveness properties. - liveCheck.checkTrace(stateTrace); - - // Write the trace out if desired. The trace is printed in the - // format of TLA module, so that it can be read by TLC again. - if (this.traceFile != null) { - String fileName = this.traceFile + traceCnt; - // TODO is it ok here? - PrintWriter pw = new PrintWriter(FileUtil.newBFOS(fileName)); - pw.println("---------------- MODULE " + fileName + " -----------------"); - for (idx = 0; idx < stateTrace.size(); idx++) { - pw.println("STATE_" + (idx + 1) + " == "); - pw.println(stateTrace.elementAt(idx) + "\n"); - } - pw.println("================================================="); - pw.close(); + // Print the trace for all other errors. + printBehavior(error); + + // For certain, "fatal" errors, we shut down all workers and terminate, + // regardless of the "continue" parameter, since these errors likely indicate a bug in the spec. + if (isNonContinuableError(error.errorCode)) { + errorCode = error.errorCode; + break; + } + + // If the 'continue' option is false, then we always terminate on the + // first error, shutting down all workers. Otherwise, we continue receiving + // results from the worker threads. + if (!TLCGlobals.continuation) { + errorCode = error.errorCode; + break; + } + + if (errorCode == EC.NO_ERROR) + { + errorCode = EC.GENERAL; } } - } catch (Throwable e) { - // Assert.printStack(e); - if (e instanceof LiveException) { - this.printSummary(); - } else { - // LL modified error message on 7 April 2012 - this.printBehavior(EC.GENERAL, new String[] { MP.ECGeneralMsg("", e) }, curState, stateTrace); + // If the result is OK, this indicates that the worker has terminated, so we + // make note of this. If all of the workers have terminated, there is no need to + // continue waiting for results, so we should terminate. + else { + runningWorkers.remove(result.workerId()); + if(runningWorkers.isEmpty()) { + break; + } } } + + // Shut down all workers. + this.shutdownAndJoinWorkers(workers); + + // Do a final progress report. + report.isRunning = false; + synchronized (report) { + report.notify(); + } + // Wait for the progress reporter thread to finish. + report.join(); + + return errorCode; + } + + + public final void printBehavior(final TLCRuntimeException exception, final TLCState state, final StateVec stateTrace) { + MP.printTLCRuntimeException(exception); + printBehavior(state, stateTrace); + } + + public final void printBehavior(SimulationWorkerError error) { + printBehavior(error.errorCode, error.parameters, error.state, error.stateTrace); } /** * Prints out the simulation behavior, in case of an error. (unless we're at * maximum depth, in which case don't!) */ - public final void printBehavior(int errorCode, String[] parameters, TLCState state, final StateVec stateTrace) { - + public final void printBehavior(final int errorCode, final String[] parameters, final TLCState state, final StateVec stateTrace) { MP.printError(errorCode, parameters); + printBehavior(state, stateTrace); + this.printSummary(); + } + + public final void printBehavior(final TLCState state, final StateVec stateTrace) { if (this.traceDepth == Long.MAX_VALUE) { MP.printMessage(EC.TLC_ERROR_STATE); StatePrinter.printState(state); } else { + if (!stateTrace.isLastElement(state)) { + // MAK 09/24/2019: this method is called with state being the stateTrace's + // last element or not. + stateTrace.addElement(state); + } + MP.printError(EC.TLC_BEHAVIOR_UP_TO_THIS_POINT); + // MAK 09/24/2019: For space reasons, TLCState does not store the state's action. + // This is why the loop below creates TLCStateInfo instances out of the pair cur + // -> last to print the action's name as part of the error trace. This is + // especially useful for Error-Trace Explorer in the Toolbox. TLCState lastState = null; + TLCStateInfo sinfo; + int cnt = 1; for (int i = 0; i < stateTrace.size(); i++) { - StatePrinter.printState(stateTrace.elementAt(i), lastState, i + 1); - lastState = stateTrace.elementAt(i); + final TLCState curState = stateTrace.elementAt(i); + if (lastState != null) { + sinfo = this.tool.getState(curState, lastState); + } else { + sinfo = new TLCStateInfo(curState, "<Initial predicate>"); + } + + // MAK 09/25/2019: It is possible for + // tlc2.tool.SimulationWorker.simulateRandomTrace() to produce traces with + // *non-terminal* stuttering steps, i.e. it might produce traces such + // as s0,s1,s1,s2,s3,s3,s3,...sN* (where sN* represents an infinite suffix of + // "terminal" stuttering steps). In other words, it produces traces s.t. + // a trace can contain finite (sub-)sequence of stuttering steps. + // The reason is that simulateRandomTrace with non-zero probability selects + // a stuttering steps as the current state's successor. Guarding against it + // would require to fingerprint states (i.e. check equality) for each successor + // state selected which is considered too expensive. + // A trace with finite stuttering can be reduced to a shorter - hence + // better readable - trace with only infinite stuttering. This check makes sure + // we get rid of the confusing Toolbox behavior that a trace with finite + // stuttering is implicitly reduced by breadth-first-search when trace + // expressions are evaluated. + if (lastState == null || curState.fingerPrint() != lastState.fingerPrint()) { + StatePrinter.printState(sinfo, lastState, cnt++); + } else { + assert Boolean.TRUE; + } + lastState = curState; } - StatePrinter.printState(state, null, stateTrace.size() + 1); } - this.printSummary(); } - /** - * This method returns a state that is randomly chosen from the set of - * states. It returns null if the set of states is empty. - */ - public final TLCState randomState(StateVec states) throws EvalException { - int len = states.size(); - if (len > 0) { - int index = (int) Math.floor(this.rng.nextDouble() * len); - return states.elementAt(index); + public IValue getLocalValue(int idx) { + for (SimulationWorker w : workers) { + return w.getLocalValue(idx); } return null; } - /* - * (non-Javadoc) - * @see tlc2.tool.Cancelable#setCancelFlag(boolean) - */ - public void setCancelFlag(boolean flag) { - this.isCancelled = flag; - } - - /** - * This method returns the set of next states generated by a randomly chosen - * action. It returns null if there is no possible next state. - */ - public final StateVec randomNextStates(TLCState state) { - int len = this.actions.length; - int index = (int) Math.floor(this.rng.nextDouble() * len); - int p = this.rng.nextPrime(); - for (int i = 0; i < len; i++) { - StateVec pstates = this.tool.getNextStates(this.actions[index], state); - if (!pstates.empty()) { - return pstates; - } - index = (index + p) % len; + public void setAllValues(int idx, IValue val) { + for (SimulationWorker w : workers) { + w.setLocalValue(idx, val); } - return null; } /** @@ -371,14 +423,13 @@ public class Simulator implements Cancelable { /* * This allows the toolbox to easily display the last set of state space - * statistics by putting them in the same form as all other progress - * statistics. + * statistics by putting them in the same form as all other progress statistics. */ if (TLCGlobals.tool) { - MP.printMessage(EC.TLC_PROGRESS_SIMU, String.valueOf(this.numOfGenStates)); + MP.printMessage(EC.TLC_PROGRESS_SIMU, String.valueOf(numOfGenStates.longValue())); } - MP.printMessage(EC.TLC_STATS_SIMU, new String[] { String.valueOf(this.numOfGenStates), + MP.printMessage(EC.TLC_STATS_SIMU, new String[] { String.valueOf(numOfGenStates.longValue()), String.valueOf(this.seed), String.valueOf(this.aril) }); } @@ -386,21 +437,8 @@ public class Simulator implements Cancelable { * Reports coverage */ public final void reportCoverage() { - if (TLCGlobals.coverageInterval >= 0) { - MP.printMessage(EC.TLC_COVERAGE_START); - ObjLongTable counts = this.tool.getPrimedLocs(); - ObjLongTable.Enumerator keys = this.astCounts.keys(); - Object key; - while ((key = keys.nextElement()) != null) { - String loc = ((SemanticNode) key).getLocation().toString(); - counts.add(loc, astCounts.get(key)); - } - Object[] skeys = counts.sortStringKeys(); - for (int i = 0; i < skeys.length; i++) { - long val = counts.get(skeys[i]); - MP.printMessage(EC.TLC_COVERAGE_VALUE, new String[] { skeys[i].toString(), String.valueOf(val) }); - } - MP.printMessage(EC.TLC_COVERAGE_END); + if (TLCGlobals.isCoverageEnabled()) { + CostModelCreator.report(this.tool, this.startTime ); } } @@ -408,15 +446,17 @@ public class Simulator implements Cancelable { * Reports progress information */ final class ProgressReport extends Thread { + + volatile boolean isRunning = true; + public void run() { int count = TLCGlobals.coverageInterval / TLCGlobals.progressInterval; try { - while (true) { + while (isRunning) { synchronized (this) { this.wait(TLCGlobals.progressInterval); } - MP.printMessage(EC.TLC_PROGRESS_SIMU, String.valueOf(numOfGenStates)); - + MP.printMessage(EC.TLC_PROGRESS_SIMU, String.valueOf(numOfGenStates.longValue())); if (count > 1) { count--; } else { @@ -430,4 +470,10 @@ public class Simulator implements Cancelable { } } } + + public void stop() { + for (SimulationWorker worker : workers) { + worker.interrupt(); + } + } } diff --git a/tlatools/src/tlc2/tool/Specs.java b/tlatools/src/tlc2/tool/Specs.java new file mode 100644 index 0000000000000000000000000000000000000000..4ff9e63c91c4bebaae73a8ca12a5f5bc86a357de --- /dev/null +++ b/tlatools/src/tlc2/tool/Specs.java @@ -0,0 +1,93 @@ +/******************************************************************************* + * Copyright (c) 2019 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool; + +import java.util.HashSet; +import java.util.Iterator; + +import tla2sany.semantic.ExprNode; +import tla2sany.semantic.LevelNode; +import tla2sany.semantic.OpDefNode; +import tla2sany.semantic.SubstInNode; +import tla2sany.semantic.SymbolNode; +import tlc2.util.Context; +import tlc2.util.List; +import tlc2.value.impl.LazyValue; + +public abstract class Specs { + + /** + * The level of the expression according to level checking. + * static method, does not change instance state + */ + public static int getLevel(LevelNode expr, Context c) + { + HashSet<SymbolNode> lpSet = expr.getLevelParams(); + if (lpSet.isEmpty()) + return expr.getLevel(); + + int level = expr.getLevel(); + Iterator<SymbolNode> iter = lpSet.iterator(); + while (iter.hasNext()) + { + SymbolNode param = (SymbolNode) iter.next(); + Object res = c.lookup(param, true); + if (res != null) + { + if (res instanceof LazyValue) + { + LazyValue lv = (LazyValue) res; + int plevel = getLevel((LevelNode) lv.expr, lv.con); + level = (plevel > level) ? plevel : level; + } else if (res instanceof OpDefNode) + { + int plevel = getLevel((LevelNode) res, c); + level = (plevel > level) ? plevel : level; + } + } + } + return level; + } + + /** + * Static method, does not change instance state + * @param expr + * @param subs + * @return + */ + public static final ExprNode addSubsts(ExprNode expr, List subs) + { + ExprNode res = expr; + + while (!subs.isEmpty()) + { + SubstInNode sn = (SubstInNode) subs.car(); + res = new SubstInNode(sn.stn, sn.getSubsts(), res, sn.getInstantiatingModule(), sn.getInstantiatedModule()); + subs = subs.cdr(); + } + return res; + } +} \ No newline at end of file diff --git a/tlatools/src/tlc2/tool/StateVec.java b/tlatools/src/tlc2/tool/StateVec.java index b31de9d6c166da9a1faa7a6d502f22cb62a12fa2..de75c3773f5e2f353b8f0f19f272273a3f0fa99a 100644 --- a/tlatools/src/tlc2/tool/StateVec.java +++ b/tlatools/src/tlc2/tool/StateVec.java @@ -5,12 +5,13 @@ package tlc2.tool; -import tla2sany.semantic.SemanticNode; +import java.util.LinkedList; + import tlc2.TLCGlobals; import tlc2.output.EC; -import tlc2.value.Value; +import tlc2.value.impl.RecordValue; +import tlc2.value.impl.Value; import util.Assert; -import util.UniqueString; /* * This class represents a TLA+ state vector. @@ -18,7 +19,7 @@ import util.UniqueString; * updates are used for improved performance and reduced * allocation. */ -public final class StateVec { +public final class StateVec implements IStateFunctor, INextStateFunctor { private TLCState v[]; private int size; @@ -49,6 +50,10 @@ public final class StateVec { public final int size() { return this.size; } + public boolean isEmpty() { + return this.size == 0; + } + public final void grow(int add) { int oldLen = this.v.length; if (oldLen >= TLCGlobals.setBound) { @@ -64,32 +69,35 @@ public final class StateVec { public final TLCState elementAt(int i) { return this.v[i]; } - public final void clear() { - this.size = 0; + public boolean isLastElement(final TLCState state) { + if (isEmpty()) { + return false; + } + return this.elementAt(size() - 1) == state; } - // Add the binding to all the variables - public final StateVec bind(UniqueString var, Value val, SemanticNode ast) { - for (int i = 0; i < this.size; i++) { - TLCState s = this.v[i]; - if (s.containsKey(var)) return null; - v[i] = s.bind(var, val, ast); - } - return this; + public TLCState first() { + return elementAt(0); } - // Bind a value in one of the states. - public final StateVec bindAt(int i, UniqueString var, Value val, SemanticNode ast) { - v[i] = v[i].bind(var, val, ast); - return this; + public final void clear() { + this.size = 0; } - + + /* (non-Javadoc) + * @see tlc2.tool.IStateFunction#addElement(tlc2.tool.TLCState) + */ public final StateVec addElement(TLCState state) { if (this.size >= this.v.length) { grow(1); } this.v[this.size++] = state; return this; } + @Override + public final StateVec addElement(TLCState predecessor, Action action, TLCState state) { + return addElement(state); + } + public final StateVec addElements(StateVec s1) { StateVec s0 = this; @@ -119,6 +127,14 @@ public final class StateVec { this.size--; } + public void removeAt(final int index) { + replaceAt(index, null); + } + + public void replaceAt(final int index, final TLCState state) { + this.v[index] = state; + } + public final StateVec copy() { TLCState[] res = new TLCState[this.size]; for (int i = 0; i < this.size; i++) { @@ -158,4 +174,33 @@ public final class StateVec { return sb.toString(); } + public final boolean contains(TLCState state) { + for (int i = 0; i < size; i++) { + if (this.v[i].fingerPrint() == state.fingerPrint()) { + return true; + } + } + return false; + } + + public final Value[] toRecords(final TLCState append) { + final Value[] values = new Value[size + 1]; + for (int i = 0; i < values.length; i++) { + values[i] = new RecordValue(v[i]); + } + values[values.length] = new RecordValue(append); + return values; + } + + public final Value[] toRecords(final TLCState from, final TLCState append) { + final LinkedList<RecordValue> res = new LinkedList<>(); + res.add(new RecordValue(append)); + for (int i = size - 1; i >= 0; i--) { + res.push(new RecordValue(v[i])); + if (from.fingerPrint() == v[i].fingerPrint()) { + break; + } + } + return res.toArray(new Value[res.size()]); + } } diff --git a/tlatools/src/tlc2/tool/TLAClass.java b/tlatools/src/tlc2/tool/TLAClass.java deleted file mode 100644 index 3f20e5eb442495857c407725493a94d4469fb74d..0000000000000000000000000000000000000000 --- a/tlatools/src/tlc2/tool/TLAClass.java +++ /dev/null @@ -1,72 +0,0 @@ -// Copyright (c) 2003 Compaq Corporation. All rights reserved. -// Portions Copyright (c) 2003 Microsoft Corporation. All rights reserved. -// Last modified on Mon 30 Apr 2007 at 13:18:28 PST by lamport -// modified on Mon Jan 29 16:21:11 PST 2001 by yuanyu - -package tlc2.tool; - -import tlc2.output.EC; -import util.Assert; - -/** - * - * @author Yuan Yu, Simon Zambrovski - * @version $Id$ - */ -public class TLAClass -{ - /* Load a class from a file. */ - private String pkg; - - public TLAClass(String pkg) - { - if (pkg.length() != 0 && pkg.charAt(pkg.length() - 1) != '.') - { - this.pkg = pkg + '.'; - } else - { - this.pkg = pkg; - } - } - - /** - * This method attempts to load the java class with the given name. - **/ - public synchronized Class loadClass(String name) - { - Class cl = null; - try - { - try - { - cl = Class.forName(name); - } catch (Exception e) - { /*SKIP*/ - } - if (cl == null) - { - try - { - cl = Class.forName(this.pkg + name); - } catch (Exception e) - { /*SKIP*/ - } - } - } catch (Throwable e) - { - Assert.fail(EC.TLC_ERROR_REPLACING_MODULES, new String[] { name, - (e.getMessage()==null)?e.toString():e.getMessage() }); - } - return cl; - } - - public static void main(String argv[]) - { - TLAClass tc = new TLAClass("tlc2.module"); - Class c = tc.loadClass("Strings"); // must set CLASSPATH correctly - System.err.println("c = " + c); - // Class c1 = tc.loadClass("Class"); - // System.err.println("c1 = " + c1); - } - -} diff --git a/tlatools/src/tlc2/tool/TLAPlusExecutor.java b/tlatools/src/tlc2/tool/TLAPlusExecutor.java new file mode 100644 index 0000000000000000000000000000000000000000..8647dde9374e6471230b1e9ead6d450ab269505f --- /dev/null +++ b/tlatools/src/tlc2/tool/TLAPlusExecutor.java @@ -0,0 +1,128 @@ +/******************************************************************************* + * Copyright (c) 2020 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool; + +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.locks.ReentrantLock; + +import tla2sany.semantic.SymbolNode; +import tlc2.tool.impl.FastTool; +import tlc2.util.Context; +import tlc2.value.IValue; +import util.SimpleFilenameToStream; + +public class TLAPlusExecutor { + + public class Mapping { + private final Action action; + private final Map<SymbolNode, IValue> params; + private final Map<String, SymbolNode> symbol2node; + + public Mapping(final Action a, final String... params) { + this.action = a; + this.params = new HashMap<>(); + this.symbol2node = new HashMap<>(); + for (String param : params) { + final SymbolNode node = action.con.lookupName(s -> s.getName().equals(param)); + this.symbol2node.put(param, node); + } + } + + public Mapping set(final String param, final IValue value) { + final SymbolNode node = this.symbol2node.get(param); + this.params.put(node, value); + return this; + } + + public Context getContext() { + // Replace TLC's values in ctx with the given ones by extending the pointer list + // of ctxs. + Context ctx = action.con; + for (Map.Entry<SymbolNode, IValue> entry : params.entrySet()) { + ctx = ctx.cons(entry.getKey(), entry.getValue()); + } + return ctx; + } + } + + private final ReentrantLock lock = new ReentrantLock(); + private final FastTool tool; + + private TLCState state; + + public TLAPlusExecutor(String spec, String config) { + this.tool = new FastTool(spec, config, new SimpleFilenameToStream()); + + // Initialize the TLA+ executor by generating the initial state. + this.state = this.tool.getInitStates().first(); + } + + public Mapping map(final String actionName, final String processNode, final IValue v, final String... params) { + final Action[] actions = tool.getActions(); + for (Action action : actions) { + if (action.getName().equals(actionName)) { + final Object lookup = action.con.lookup(s -> s.getName().equals(processNode)); + if (lookup != null) { + if (lookup.equals(v)) { + return new Mapping(action, params); + } + } + } + } + throw new RuntimeException("failed to create mapping"); + } + + public Object step(final Mapping m) throws Exception { + final Context ctx = m.getContext(); + lock.lock(); + try { + while (true) { + final StateVec nextStates = this.tool.getNextStates(m.action, ctx, state); + assert !nextStates.isEmpty(); + this.state = nextStates.first(); + + // Check invariants. + for (int k = 0; k < this.tool.getInvariants().length; k++) { + if (!tool.isValid(this.tool.getInvariants()[k], this.state)) { + throw new INextStateFunctor.InvariantViolatedException(); + } + } + + final Object result = this.state.execCallable(); + if (result != null) { + return result; + } + } + } finally { + lock.unlock(); + } + } + + public ReentrantLock getLock() { + return lock; + } +} diff --git a/tlatools/src/tlc2/tool/TLAPlusExecutorState.java b/tlatools/src/tlc2/tool/TLAPlusExecutorState.java new file mode 100644 index 0000000000000000000000000000000000000000..0c9434e003117b8c655f62c663098fd514a2743f --- /dev/null +++ b/tlatools/src/tlc2/tool/TLAPlusExecutorState.java @@ -0,0 +1,392 @@ +/******************************************************************************* + * Copyright (c) 2020 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool; + +import java.io.IOException; +import java.util.Comparator; +import java.util.Set; +import java.util.TreeSet; +import java.util.concurrent.Callable; + +import tla2sany.semantic.OpDeclNode; +import tla2sany.semantic.SemanticNode; +import tla2sany.semantic.SymbolNode; +import tlc2.TLCGlobals; +import tlc2.util.Context; +import tlc2.util.FP64; +import tlc2.value.IMVPerm; +import tlc2.value.IValue; +import tlc2.value.IValueInputStream; +import tlc2.value.IValueOutputStream; +import tlc2.value.Values; +import util.UniqueString; +import util.WrongInvocationException; + +public final class TLAPlusExecutorState extends TLCState { + + private Callable<?> callable; + + @Override + public Object execCallable() throws Exception { + if (callable != null) { + return callable.call(); + } + return null; + } + + @Override + public void setCallable(Callable<?> f) { + this.callable = f; + } + + //* Everything below this line is a verbatim - except for the type - copy of TLCStateMut. *// + + public static void setVariables(OpDeclNode[] variables) { + vars = variables; + Empty = new TLAPlusExecutorState(new IValue[vars.length]); + } + + public TLCState createEmpty() { + return new TLAPlusExecutorState(new IValue[vars.length]); + } + + protected TLAPlusExecutorState(final IValue[] vals) { + this.values = vals; + } + + private IValue values[]; + private static ITool mytool = null; + + /** + * If non-null, viewMap denotes the function to be applied to + * a state before its fingerprint is computed. + */ + private static SemanticNode viewMap = null; + + /** + * If non-null, perms denotes the set of permutations under the + * symmetry assumption. + */ + private static IMVPerm[] perms = null; + + public static void setTool(ITool tool) { + mytool = tool; + viewMap = tool.getViewSpec(); + perms = tool.getSymmetryPerms(); + } + + //TODO equals without hashcode! + public final boolean equals(Object obj) { + if (obj instanceof TLAPlusExecutorState) { + TLAPlusExecutorState state = (TLAPlusExecutorState)obj; + for (int i = 0; i < this.values.length; i++) { + if (this.values[i] == null) { + if (state.values[i] != null) return false; + } + else if (state.values[i] == null || + !this.values[i].equals(state.values[i])) { + return false; + } + } + return true; + } + return false; + } + + public final TLCState bind(UniqueString name, IValue value) { + // Note, tla2sany.semantic.OpApplNode.toString(Value) relies on this ordering. + int loc = name.getVarLoc(); + this.values[loc] = value; + return this; + } + + public final TLCState bind(SymbolNode id, IValue value) { + throw new WrongInvocationException("SharedTLCState.bind: This is a TLC bug."); + } + + public final TLCState unbind(UniqueString name) { + int loc = name.getVarLoc(); + this.values[loc] = null; + return this; + } + + public final IValue lookup(UniqueString var) { + int loc = var.getVarLoc(); + if (loc < 0) return null; + return this.values[loc]; + } + + public final boolean containsKey(UniqueString var) { + return (this.lookup(var) != null); + } + + public final TLCState copy() { + int len = this.values.length; + IValue[] vals = new IValue[len]; + for (int i = 0; i < len; i++) { + vals[i] = this.values[i]; + } + return new TLAPlusExecutorState(vals); + } + + public final TLCState deepCopy() { + int len = this.values.length; + IValue[] vals = new IValue[len]; + for (int i = 0; i < len; i++) { + IValue val = this.values[i]; + if (val != null) { + vals[i] = val.deepCopy(); + } + } + return new TLAPlusExecutorState(vals); + } + + public final StateVec addToVec(StateVec states) { + return states.addElement(this.copy()); + } + + public final void deepNormalize() { + for (int i = 0; i < this.values.length; i++) { + IValue val = this.values[i]; + if (val != null) { + val.deepNormalize(); + } + } + } + + /** + * This method returns the fingerprint of this state. We fingerprint + * the values in the state according to the order given by vars. + * This guarantees the same state has the same fingerprint. + * + * Since the values in this state can be shared by multiple threads + * via the state queue. They have to be normalized before adding to + * the state queue. We do that here. + */ + public final long fingerPrint() { + int sz = this.values.length; + + // TLC supports symmetry reduction. Symmetry reduction works by defining classes + // of symmetrically equivalent states for which TLC only checks a + // single representative of the equivalence class (orbit). E.g. in a two + // process mutual exclusion problem, the process ids are - most of the time - + // not relevant with regards to mutual exclusion: We don't care if process A or + // B is in the critical section as long as only a single process is in CS. Thus + // two states are symmetric that only differ in the process id variable value. + // Symmetry can also be observed graphically in the state graph s.t. subgraphs + // are isomorphic (Graph Isomorphism). Instead of enumerating the complete state + // graph, TLC enumerates one of the isomorphic subgraphs whose state correspond + // to the representatives. With respect to the corresponding Kripke structure M, + // the resulting Kripke M' is called the "quotient structure" (see "Exploiting + // Symmetry in Temporal Logic Model Checking" by Clarke et al). + // + // The definition of equivalence classes (orbits) is provided manually by the + // user at startup by defining 1 to n symmetry sets. Thus TLC has to find + // representative at runtime only which happens below. Given any state s, TLC + // evaluates rep(s) to find the lexicographically smallest state ss = rep(s) + // with regards to the variable values. The state ss is then fingerprint instead + // of s. + // + // Evaluating rep(s) - to reduce s to ss - requires to apply all permutations in + // the group this.perms (derived from the user-defined orbit). This is known as + // the constructive orbit problem and is NP-hard. The loop has O(|perms| * |this.values|) + // with |prems| = |symmetry set 1|! * |symmetry set 2|! * ... * |symmetry set n|. + // + // minVals is what is used to calculate/generate the fingerprint below. + // If this state is not the lexicographically smallest state ss, its current + // minVals will be replaced temporarily with the values of ss for the + // calculation of the fingerprint. + IValue[] minVals = this.values; + if (perms != null) { + IValue[] vals = new IValue[sz]; + // The following for loop converges to the smallest state ss under symmetry by + // looping over all permutations applying each. If the outcome turns out to be + // lexicographically smaller than the currently smallest, it replaces the + // current smallest. Once all permutations (perms) have been processed, we know + // we have found the smallest state. + NEXT_PERM: for (int i = 0; i < perms.length; i++) { + int cmp = 0; + // For each value in values succinctly permute the current value + // and compare it to its corresponding minValue in minVals. + for (int j = 0; j < sz; j++) { + vals[j] = this.values[j].permute(perms[i]); + if (cmp == 0) { + // Only compare unless an earlier compare has found a + // difference already (if a difference has been found + // earlier, still permute the remaining values of the + // state to fully permute all state values). + cmp = vals[j].compareTo(minVals[j]); + if (cmp > 0) { + // When cmp evaluates to >0, all subsequent + // applications of perms[i] for the remaining values + // won't make the resulting vals[] smaller than + // minVals. Thus, exit preemptively from the loop + // over vals. This works because perms is the cross + // product of all symmetry sets. + continue NEXT_PERM; + } + } + } + // cmp < 0 means the current state is part of a symmetry + // permutation set/group and not the "smallest" one. + if (cmp < 0) { + if (minVals == this.values) { + minVals = vals; + vals = new IValue[sz]; + } else { + IValue[] temp = minVals; + minVals = vals; + vals = temp; + } + } + } + } + // Fingerprint the state: + long fp = FP64.New(); + if (viewMap == null) { + for (int i = 0; i < sz; i++) { + fp = minVals[i].fingerPrint(fp); + } + if (this.values != minVals) { + for (int i = 0; i < sz; i++) { + this.values[i].deepNormalize(); + } + } + } else { + for (int i = 0; i < sz; i++) { + this.values[i].deepNormalize(); + } + TLAPlusExecutorState state = this; + if (minVals != this.values) { + state = new TLAPlusExecutorState(minVals); + } + IValue val = mytool.eval(viewMap, Context.Empty, state); + fp = val.fingerPrint(fp); + } + return fp; + } + + public final boolean allAssigned() { + int len = this.values.length; + for (int i = 0; i < len; i++) { + if (values[i] == null) return false; + } + return true; + } + + public final Set<OpDeclNode> getUnassigned() { + // Return sorted set (lexicographical). + final Set<OpDeclNode> unassignedVars = new TreeSet<OpDeclNode>(new Comparator<OpDeclNode>() { + @Override + public int compare(OpDeclNode o1, OpDeclNode o2) { + return o1.getName().toString().compareTo(o2.getName().toString()); + } + }); + int len = this.values.length; + for (int i = 0; i < len; i++) { + if (values[i] == null) { + unassignedVars.add(vars[i]); + } + } + return unassignedVars; + } + + public final void read(IValueInputStream vis) throws IOException { + super.read(vis); + int len = this.values.length; + for (int i = 0; i < len; i++) { + this.values[i] = vis.read(); + } + } + + public final void write(IValueOutputStream vos) throws IOException { + super.write(vos); + int len = this.values.length; + for (int i = 0; i < len; i++) { + this.values[i].write(vos); + } + } + + /* Returns a string representation of this state. */ + public final String toString() { + if (TLCGlobals.useView && viewMap != null) { + IValue val = mytool.eval(viewMap, Context.Empty, this); + return viewMap.toString(val); + } + StringBuffer result = new StringBuffer(); + int vlen = vars.length; + if (vlen == 1) { + UniqueString key = vars[0].getName(); + IValue val = this.lookup(key); + result.append(key.toString()); + result.append(" = "); + result.append(Values.ppr(val)); + result.append("\n"); + } + else { + for (int i = 0; i < vlen; i++) { + UniqueString key = vars[i].getName(); + IValue val = this.lookup(key); + result.append("/\\ "); + result.append(key.toString()); + result.append(" = "); + result.append(Values.ppr(val)); + result.append("\n"); + } + } + return result.toString(); + } + + /* Returns a string representation of this state. */ + public final String toString(TLCState lastState) { + StringBuffer result = new StringBuffer(); + TLAPlusExecutorState lstate = (TLAPlusExecutorState)lastState; + + int vlen = vars.length; + if (vlen == 1) { + UniqueString key = vars[0].getName(); + IValue val = this.lookup(key); + IValue lstateVal = lstate.lookup(key); + if (!lstateVal.equals(val)) { + result.append(key.toString()); + result.append(" = " + Values.ppr(val) + "\n"); + } + } + else { + for (int i = 0; i < vlen; i++) { + UniqueString key = vars[i].getName(); + IValue val = this.lookup(key); + IValue lstateVal = lstate.lookup(key); + if (!lstateVal.equals(val)) { + result.append("/\\ "); + result.append(key.toString()); + result.append(" = " + Values.ppr(val) + "\n"); + } + } + } + return result.toString(); + } +} diff --git a/tlatools/src/tlc2/tool/TLCState.java b/tlatools/src/tlc2/tool/TLCState.java index c7a4e96c2ed8b934e6f23b538589c0e8e4f4429e..67c5fa72575a904595e2b71ffa02dce1d861bc52 100644 --- a/tlatools/src/tlc2/tool/TLCState.java +++ b/tlatools/src/tlc2/tool/TLCState.java @@ -7,50 +7,69 @@ package tlc2.tool; import java.io.IOException; import java.io.Serializable; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.Callable; import tla2sany.semantic.OpDeclNode; -import tla2sany.semantic.SemanticNode; import tla2sany.semantic.SymbolNode; -import tlc2.value.Value; -import tlc2.value.ValueInputStream; -import tlc2.value.ValueOutputStream; +import tlc2.output.EC; +import tlc2.value.IValue; +import tlc2.value.IValueInputStream; +import tlc2.value.IValueOutputStream; +import util.Assert; import util.UniqueString; public abstract class TLCState implements Cloneable, Serializable { + public short workerId = Short.MAX_VALUE; // Must be set to a non-negative number. Valid worker ids \in [0,Short.MAX_VALUE] and start at 0. public long uid = -1; // Must be set to a non-negative number + // The level of an initial state is initialized with 1 to assert that + // TLCGet("level") in the first evaluation of the next-state relation equals 1. + // The successor states of initial states have level 2. During the evaluation + // of the initial *predicate* - which generates the initial states - the level + // is defined to be zero. + public static final int INIT_LEVEL = 1; + private int level = INIT_LEVEL; // Set by subclasses. Cannot set until we know what the variables are. public static TLCState Empty = null; // The state variables. - public static OpDeclNode[] vars = null; - - public static void setVariables(OpDeclNode[] variables) - { - vars = variables; - // SZ 10.04.2009: since this method is called exactly one from Spec#processSpec - // moved the call of UniqueString#setVariables to that place - - // UniqueString[] varNames = new UniqueString[variables.length]; - // for (int i = 0; i < varNames.length; i++) - // { - // varNames[i] = variables[i].getName(); - //} - //UniqueString.setVariables(varNames); - } + protected static OpDeclNode[] vars = null; - public void read(ValueInputStream vis) throws IOException { - this.uid = vis.readLongNat(); + public void read(IValueInputStream vis) throws IOException { + this.workerId = vis.readShortNat(); + this.uid = vis.readLongNat(); + this.level = vis.readShortNat(); + assert this.level >= 0; // Should never overflow. } - public void write(ValueOutputStream vos) throws IOException { - vos.writeLongNat(this.uid); - } + public void write(IValueOutputStream vos) throws IOException { + if (this.level > Short.MAX_VALUE) { + // The on-disk representation of TLCState limits the diameter/level to + // Short.MAX_VALUE whereas the in-memory rep supports int. The underlying + // assumption being that state spaces with a diameter beyond 32767 AND which + // require TLC to swap the state queue to disk are infeasible to check anyway. + // However, one can easily come up with a spec that corresponds to few, very long + // behaviors which can be kept in memory. + Assert.fail(EC.TLC_TRACE_TOO_LONG, this.toString()); + } + vos.writeShortNat(this.workerId); + vos.writeLongNat(this.uid); + vos.writeShortNat((short) this.level); + } - public abstract TLCState bind(UniqueString name, Value value, SemanticNode expr); - public abstract TLCState bind(SymbolNode id, Value value, SemanticNode expr); + public abstract TLCState bind(UniqueString name, IValue value); + public abstract TLCState bind(SymbolNode id, IValue value); public abstract TLCState unbind(UniqueString name); - public abstract Value lookup(UniqueString var); + /** + * Convenience method when performance doesn't matter. + */ + public IValue lookup(String var) { + return lookup(UniqueString.uniqueStringOf(var)); + } + public abstract IValue lookup(UniqueString var); public abstract boolean containsKey(UniqueString var); public abstract TLCState copy(); public abstract TLCState deepCopy(); @@ -58,9 +77,54 @@ public abstract class TLCState implements Cloneable, Serializable { public abstract void deepNormalize(); public abstract long fingerPrint(); public abstract boolean allAssigned(); + public abstract Set<OpDeclNode> getUnassigned(); public abstract TLCState createEmpty(); + + /** + * Returns a mapping of variable names to their assigned values in this state. + */ + public final Map<UniqueString, IValue> getVals() { + final Map<UniqueString, IValue> valMap = new HashMap<UniqueString, IValue>(); + for(int i = 0; i < vars.length; i++) { + UniqueString key = vars[i].getName(); + IValue val = this.lookup(key); + valMap.put(key, val); + } + return valMap; + } + + public final OpDeclNode[] getVars() { + return vars; + } + + public final void setPredecessor(final TLCState predecessor) { + // This method only keeps the level instead of the predecessor, because a) we + // don't need the predecessor and b) keeping predecessors would mean that we + // eventually have all states of the state graph in memory. + if (predecessor.getLevel() == Integer.MAX_VALUE) { + Assert.fail(EC.TLC_TRACE_TOO_LONG, this.toString()); + } + this.level = predecessor.getLevel() + 1; + } + public final int getLevel() { + return this.level; + } + + public final boolean isInitial() { + return this.level == INIT_LEVEL; + } + /* Returns a string representation of this state. */ public abstract String toString(); public abstract String toString(TLCState lastState); + + public Object execCallable() throws Exception { + // no-op - see TLAPlusExecutorState + return null; + } + + public void setCallable(Callable<?> cl) { + // no-op - see TLAPlusExecutorState + } } diff --git a/tlatools/src/tlc2/tool/TLCStateFun.java b/tlatools/src/tlc2/tool/TLCStateFun.java index 5c8c50c1831729e231b430f1d1b9510a56ae6a18..389ae6e07d45124130b674ca632c46d7974455da 100644 --- a/tlatools/src/tlc2/tool/TLCStateFun.java +++ b/tlatools/src/tlc2/tool/TLCStateFun.java @@ -6,13 +6,16 @@ package tlc2.tool; import java.io.IOException; +import java.util.HashSet; +import java.util.Set; -import tla2sany.semantic.SemanticNode; +import tla2sany.semantic.OpDeclNode; import tla2sany.semantic.SymbolNode; import tlc2.util.Context; -import tlc2.value.Value; -import tlc2.value.ValueInputStream; -import tlc2.value.ValueOutputStream; +import tlc2.value.IValue; +import tlc2.value.IValueInputStream; +import tlc2.value.IValueOutputStream; +import tlc2.value.impl.Value; import util.UniqueString; import util.WrongInvocationException; @@ -24,12 +27,12 @@ import util.WrongInvocationException; */ public final class TLCStateFun extends TLCState { private SymbolNode name; - private Value value; + private IValue value; private TLCStateFun next; public final static TLCState Empty = new TLCStateFun(null, null, null); - private TLCStateFun(SymbolNode name, Value value, TLCStateFun state) { + private TLCStateFun(SymbolNode name, IValue value, TLCStateFun state) { this.name = name; this.value = value; this.next = state; @@ -37,11 +40,11 @@ public final class TLCStateFun extends TLCState { public final TLCState createEmpty() { return Empty; } - public final TLCState bind(UniqueString name, Value value, SemanticNode expr) { + public final TLCState bind(UniqueString name, IValue value) { throw new WrongInvocationException("TLCStateFun.bind: This is a TLC bug."); } - public final TLCState bind(SymbolNode id, Value value, SemanticNode expr) { + public final TLCState bind(SymbolNode id, IValue value) { return new TLCStateFun(id, value, this); } @@ -49,7 +52,7 @@ public final class TLCStateFun extends TLCState { throw new WrongInvocationException("TLCStateFun.unbind: This is a TLC bug."); } - public final Value lookup(UniqueString var) { + public final IValue lookup(UniqueString var) { for (TLCStateFun cur = this; cur != Empty; cur = cur.next) { if (var == cur.name.getName()) return cur.value; } @@ -80,6 +83,8 @@ public final class TLCStateFun extends TLCState { } public final boolean allAssigned() { return true; } + + public final Set<OpDeclNode> getUnassigned() { return new HashSet<OpDeclNode>(); } public final Context addToContext(Context c) { Context c1 = c; @@ -93,11 +98,11 @@ public final class TLCStateFun extends TLCState { return states.addElement(this); } - public final void read(ValueInputStream vis) throws IOException { + public final void read(IValueInputStream vis) throws IOException { throw new WrongInvocationException("TLCStateFun.read: This is a TLC bug."); } - public final void write(ValueOutputStream vos) throws IOException { + public final void write(IValueOutputStream vos) throws IOException { throw new WrongInvocationException("TLCStateFun.write: This is a TLC bug."); } diff --git a/tlatools/src/tlc2/tool/TLCStateInfo.java b/tlatools/src/tlc2/tool/TLCStateInfo.java index e76fa69aca581122db6ee9f0baba32e153e13901..b2dc8216438a6335a7de146f98cc30acf389dff7 100644 --- a/tlatools/src/tlc2/tool/TLCStateInfo.java +++ b/tlatools/src/tlc2/tool/TLCStateInfo.java @@ -10,17 +10,49 @@ public class TLCStateInfo { public long stateNumber; public TLCState state; public Object info; + public Long fp; + + public TLCStateInfo(TLCState initialState) { + this.state = initialState; + this.info = "<Initial predicate>"; + this.stateNumber = 1; + this.fp = initialState.fingerPrint(); + } public TLCStateInfo(TLCState s, Object info) { this.state = s; this.info = info; } + public TLCStateInfo(TLCState s, String info, int stateNum, long fp) { + this(s, info); + stateNumber = stateNum; + this.fp = fp; + } + public final long fingerPrint() { - return this.state.fingerPrint(); + if (fp == null) { + fp = this.state.fingerPrint(); + } + return fp.longValue(); } public final String toString() { return this.state.toString(); } + + public boolean equals(Object other) { + if (other instanceof TLCStateInfo) { + TLCStateInfo sinfo = (TLCStateInfo) other; + return this.state.equals(sinfo.state); + } else if (other instanceof TLCState) { + TLCState state = (TLCState) other; + return this.state.equals(state); + } + return false; + } + + public int hashCode() { + return this.state.hashCode(); + } } diff --git a/tlatools/src/tlc2/tool/TLCStateMut.java b/tlatools/src/tlc2/tool/TLCStateMut.java index 985f3e638c6b0d0ba45d1eb755bb1b93b0661cdf..acf1645bf6f4767bb7b4877ba892f2f93a389cb0 100644 --- a/tlatools/src/tlc2/tool/TLCStateMut.java +++ b/tlatools/src/tlc2/tool/TLCStateMut.java @@ -7,16 +7,21 @@ package tlc2.tool; import java.io.IOException; import java.io.Serializable; +import java.util.Comparator; +import java.util.Set; +import java.util.TreeSet; +import tla2sany.semantic.OpDeclNode; import tla2sany.semantic.SemanticNode; import tla2sany.semantic.SymbolNode; import tlc2.TLCGlobals; import tlc2.util.Context; import tlc2.util.FP64; -import tlc2.value.MVPerm; -import tlc2.value.Value; -import tlc2.value.ValueInputStream; -import tlc2.value.ValueOutputStream; +import tlc2.value.IMVPerm; +import tlc2.value.IValue; +import tlc2.value.IValueInputStream; +import tlc2.value.IValueOutputStream; +import tlc2.value.Values; import util.UniqueString; import util.WrongInvocationException; @@ -29,8 +34,8 @@ import util.WrongInvocationException; * The viewMap was added by Rajeev Joshi. */ public final class TLCStateMut extends TLCState implements Cloneable, Serializable { - private Value values[]; - private static Tool mytool = null; + private IValue values[]; + private static ITool mytool = null; /** * If non-null, viewMap denotes the function to be applied to @@ -42,23 +47,39 @@ public final class TLCStateMut extends TLCState implements Cloneable, Serializab * If non-null, perms denotes the set of permutations under the * symmetry assumption. */ - private static MVPerm[] perms = null; + private static IMVPerm[] perms = null; - private TLCStateMut(Value[] vals) { this.values = vals; } + private TLCStateMut(IValue[] vals) { this.values = vals; } + + public static void setVariables(OpDeclNode[] variables) + { + vars = variables; + IValue[] vals = new IValue[vars.length]; + Empty = new TLCStateMut(vals); + + // SZ 10.04.2009: since this method is called exactly one from Spec#processSpec + // moved the call of UniqueString#setVariables to that place + + // UniqueString[] varNames = new UniqueString[variables.length]; + // for (int i = 0; i < varNames.length; i++) + // { + // varNames[i] = variables[i].getName(); + //} + //UniqueString.setVariables(varNames); + } - public static void init(Tool tool) { + public static void setTool(ITool tool) { mytool = tool; - Value[] vals = new Value[vars.length]; - Empty = new TLCStateMut(vals); viewMap = tool.getViewSpec(); perms = tool.getSymmetryPerms(); } public final TLCState createEmpty() { - Value[] vals = new Value[vars.length]; + IValue[] vals = new IValue[vars.length]; return new TLCStateMut(vals); } + //TODO equals without hashcode! public final boolean equals(Object obj) { if (obj instanceof TLCStateMut) { TLCStateMut state = (TLCStateMut)obj; @@ -76,13 +97,14 @@ public final class TLCStateMut extends TLCState implements Cloneable, Serializab return false; } - public final TLCState bind(UniqueString name, Value value, SemanticNode expr) { + public final TLCState bind(UniqueString name, IValue value) { + // Note, tla2sany.semantic.OpApplNode.toString(Value) relies on this ordering. int loc = name.getVarLoc(); this.values[loc] = value; return this; } - public final TLCState bind(SymbolNode id, Value value, SemanticNode expr) { + public final TLCState bind(SymbolNode id, IValue value) { throw new WrongInvocationException("TLCStateMut.bind: This is a TLC bug."); } @@ -92,7 +114,7 @@ public final class TLCStateMut extends TLCState implements Cloneable, Serializab return this; } - public final Value lookup(UniqueString var) { + public final IValue lookup(UniqueString var) { int loc = var.getVarLoc(); if (loc < 0) return null; return this.values[loc]; @@ -104,7 +126,7 @@ public final class TLCStateMut extends TLCState implements Cloneable, Serializab public final TLCState copy() { int len = this.values.length; - Value[] vals = new Value[len]; + IValue[] vals = new IValue[len]; for (int i = 0; i < len; i++) { vals[i] = this.values[i]; } @@ -113,9 +135,9 @@ public final class TLCStateMut extends TLCState implements Cloneable, Serializab public final TLCState deepCopy() { int len = this.values.length; - Value[] vals = new Value[len]; + IValue[] vals = new IValue[len]; for (int i = 0; i < len; i++) { - Value val = this.values[i]; + IValue val = this.values[i]; if (val != null) { vals[i] = val.deepCopy(); } @@ -129,7 +151,7 @@ public final class TLCStateMut extends TLCState implements Cloneable, Serializab public final void deepNormalize() { for (int i = 0; i < this.values.length; i++) { - Value val = this.values[i]; + IValue val = this.values[i]; if (val != null) { val.deepNormalize(); } @@ -145,59 +167,108 @@ public final class TLCStateMut extends TLCState implements Cloneable, Serializab * via the state queue. They have to be normalized before adding to * the state queue. We do that here. */ - public final long fingerPrint() { - int sz = this.values.length; + public final long fingerPrint() { + int sz = this.values.length; - Value[] minVals = this.values; - if (perms != null) { - Value[] vals = new Value[sz]; - // Find the "smallest" state under the symmetry permutations: - for (int i = 0; i < perms.length; i++) { - int cmp = 0; - for (int j = 0; j < sz; j++) { - vals[j] = this.values[j].permute(perms[i]); - if (cmp == 0) { - cmp = vals[j].compareTo(minVals[j]); - } + // TLC supports symmetry reduction. Symmetry reduction works by defining classes + // of symmetrically equivalent states for which TLC only checks a + // single representative of the equivalence class (orbit). E.g. in a two + // process mutual exclusion problem, the process ids are - most of the time - + // not relevant with regards to mutual exclusion: We don't care if process A or + // B is in the critical section as long as only a single process is in CS. Thus + // two states are symmetric that only differ in the process id variable value. + // Symmetry can also be observed graphically in the state graph s.t. subgraphs + // are isomorphic (Graph Isomorphism). Instead of enumerating the complete state + // graph, TLC enumerates one of the isomorphic subgraphs whose state correspond + // to the representatives. With respect to the corresponding Kripke structure M, + // the resulting Kripke M' is called the "quotient structure" (see "Exploiting + // Symmetry in Temporal Logic Model Checking" by Clarke et al). + // + // The definition of equivalence classes (orbits) is provided manually by the + // user at startup by defining 1 to n symmetry sets. Thus TLC has to find + // representative at runtime only which happens below. Given any state s, TLC + // evaluates rep(s) to find the lexicographically smallest state ss = rep(s) + // with regards to the variable values. The state ss is then fingerprint instead + // of s. + // + // Evaluating rep(s) - to reduce s to ss - requires to apply all permutations in + // the group this.perms (derived from the user-defined orbit). This is known as + // the constructive orbit problem and is NP-hard. The loop has O(|perms| * |this.values|) + // with |prems| = |symmetry set 1|! * |symmetry set 2|! * ... * |symmetry set n|. + // + // minVals is what is used to calculate/generate the fingerprint below. + // If this state is not the lexicographically smallest state ss, its current + // minVals will be replaced temporarily with the values of ss for the + // calculation of the fingerprint. + IValue[] minVals = this.values; + if (perms != null) { + IValue[] vals = new IValue[sz]; + // The following for loop converges to the smallest state ss under symmetry by + // looping over all permutations applying each. If the outcome turns out to be + // lexicographically smaller than the currently smallest, it replaces the + // current smallest. Once all permutations (perms) have been processed, we know + // we have found the smallest state. + NEXT_PERM: for (int i = 0; i < perms.length; i++) { + int cmp = 0; + // For each value in values succinctly permute the current value + // and compare it to its corresponding minValue in minVals. + for (int j = 0; j < sz; j++) { + vals[j] = this.values[j].permute(perms[i]); + if (cmp == 0) { + // Only compare unless an earlier compare has found a + // difference already (if a difference has been found + // earlier, still permute the remaining values of the + // state to fully permute all state values). + cmp = vals[j].compareTo(minVals[j]); + if (cmp > 0) { + // When cmp evaluates to >0, all subsequent + // applications of perms[i] for the remaining values + // won't make the resulting vals[] smaller than + // minVals. Thus, exit preemptively from the loop + // over vals. This works because perms is the cross + // product of all symmetry sets. + continue NEXT_PERM; + } + } + } + // cmp < 0 means the current state is part of a symmetry + // permutation set/group and not the "smallest" one. + if (cmp < 0) { + if (minVals == this.values) { + minVals = vals; + vals = new IValue[sz]; + } else { + IValue[] temp = minVals; + minVals = vals; + vals = temp; + } + } + } + } + // Fingerprint the state: + long fp = FP64.New(); + if (viewMap == null) { + for (int i = 0; i < sz; i++) { + fp = minVals[i].fingerPrint(fp); + } + if (this.values != minVals) { + for (int i = 0; i < sz; i++) { + this.values[i].deepNormalize(); + } + } + } else { + for (int i = 0; i < sz; i++) { + this.values[i].deepNormalize(); + } + TLCStateMut state = this; + if (minVals != this.values) { + state = new TLCStateMut(minVals); + } + IValue val = mytool.eval(viewMap, Context.Empty, state); + fp = val.fingerPrint(fp); + } + return fp; } - if (cmp < 0) { - if (minVals == this.values) { - minVals = vals; - vals = new Value[sz]; - } - else { - Value[] temp = minVals; - minVals = vals; - vals = temp; - } - } - } - } - // Fingerprint the state: - long fp = FP64.New(); - if (viewMap == null) { - for (int i = 0; i < sz; i++) { - fp = minVals[i].fingerPrint(fp); - } - if (this.values != minVals) { - for (int i = 0; i < sz; i++) { - this.values[i].deepNormalize(); - } - } - } - else { - for (int i = 0; i < sz; i++) { - this.values[i].deepNormalize(); - } - TLCStateMut state = this; - if (minVals != this.values) { - state = new TLCStateMut(minVals); - } - Value val = mytool.eval(viewMap, Context.Empty, state); - fp = val.fingerPrint(fp); - } - return fp; - } public final boolean allAssigned() { int len = this.values.length; @@ -206,8 +277,25 @@ public final class TLCStateMut extends TLCState implements Cloneable, Serializab } return true; } + + public final Set<OpDeclNode> getUnassigned() { + // Return sorted set (lexicographical). + final Set<OpDeclNode> unassignedVars = new TreeSet<OpDeclNode>(new Comparator<OpDeclNode>() { + @Override + public int compare(OpDeclNode o1, OpDeclNode o2) { + return o1.getName().toString().compareTo(o2.getName().toString()); + } + }); + int len = this.values.length; + for (int i = 0; i < len; i++) { + if (values[i] == null) { + unassignedVars.add(vars[i]); + } + } + return unassignedVars; + } - public final void read(ValueInputStream vis) throws IOException { + public final void read(IValueInputStream vis) throws IOException { super.read(vis); int len = this.values.length; for (int i = 0; i < len; i++) { @@ -215,37 +303,39 @@ public final class TLCStateMut extends TLCState implements Cloneable, Serializab } } - public final void write(ValueOutputStream vos) throws IOException { + public final void write(IValueOutputStream vos) throws IOException { super.write(vos); int len = this.values.length; for (int i = 0; i < len; i++) { - vos.write(this.values[i]); + this.values[i].write(vos); } } /* Returns a string representation of this state. */ public final String toString() { if (TLCGlobals.useView && viewMap != null) { - Value val = mytool.eval(viewMap, Context.Empty, this); - return Value.ppr(val.toString()); + IValue val = mytool.eval(viewMap, Context.Empty, this); + return viewMap.toString(val); } StringBuffer result = new StringBuffer(); int vlen = vars.length; if (vlen == 1) { UniqueString key = vars[0].getName(); - Value val = this.lookup(key); - String val_str = (val == null) ? "null" : Value.ppr(val.toString()); + IValue val = this.lookup(key); result.append(key.toString()); - result.append(" = " + val_str + "\n"); + result.append(" = "); + result.append(Values.ppr(val)); + result.append("\n"); } else { for (int i = 0; i < vlen; i++) { UniqueString key = vars[i].getName(); - Value val = this.lookup(key); - String val_str = (val == null) ? "null" : Value.ppr(val.toString()); + IValue val = this.lookup(key); result.append("/\\ "); result.append(key.toString()); - result.append(" = " + val_str + "\n"); + result.append(" = "); + result.append(Values.ppr(val)); + result.append("\n"); } } return result.toString(); @@ -259,24 +349,22 @@ public final class TLCStateMut extends TLCState implements Cloneable, Serializab int vlen = vars.length; if (vlen == 1) { UniqueString key = vars[0].getName(); - Value val = this.lookup(key); - Value lstateVal = lstate.lookup(key); + IValue val = this.lookup(key); + IValue lstateVal = lstate.lookup(key); if (!lstateVal.equals(val)) { - String val_str = (val == null) ? "null" : Value.ppr(val.toString()); result.append(key.toString()); - result.append(" = " + val_str + "\n"); + result.append(" = " + Values.ppr(val) + "\n"); } } else { for (int i = 0; i < vlen; i++) { UniqueString key = vars[i].getName(); - Value val = this.lookup(key); - Value lstateVal = lstate.lookup(key); + IValue val = this.lookup(key); + IValue lstateVal = lstate.lookup(key); if (!lstateVal.equals(val)) { - String val_str = (val == null) ? "null" : Value.ppr(val.toString()); result.append("/\\ "); result.append(key.toString()); - result.append(" = " + val_str + "\n"); + result.append(" = " + Values.ppr(val) + "\n"); } } } diff --git a/tlatools/src/tlc2/tool/TLCStateMutSource.java b/tlatools/src/tlc2/tool/TLCStateMutSource.java index 295fcd2e492f86d8688e244775699f73aa259d5e..8f9a2a18496afbccad7d89e7618bdf59d9a48a6e 100644 --- a/tlatools/src/tlc2/tool/TLCStateMutSource.java +++ b/tlatools/src/tlc2/tool/TLCStateMutSource.java @@ -1,319 +1,344 @@ -// Copyright (c) 2003 Compaq Corporation. All rights reserved. -// Portions Copyright (c) 2003 Microsoft Corporation. All rights reserved. -// Last modified on Mon 30 Apr 2007 at 15:30:02 PST by lamport -// modified on Fri Aug 24 15:08:38 PDT 2001 by yuanyu - -package tlc2.tool; - -import java.io.IOException; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; -import java.io.Serializable; - -import tla2sany.semantic.SemanticNode; -import tla2sany.semantic.SymbolNode; -import tlc2.TLCGlobals; -import tlc2.util.Context; -import tlc2.util.FP64; -import tlc2.util.ObjLongTable; -import tlc2.value.MVPerm; -import tlc2.value.Value; -import tlc2.value.ValueInputStream; -import tlc2.value.ValueOutputStream; -import util.UniqueString; -import util.WrongInvocationException; - -/** - * This class represents a TLA+ state, which simply is an assignment - * of explicit values to all the global variables. This is the - * mutable version, which means that in-place updates are used for - * improved performance and reduced allocation. - * - * The viewMap was added by Rajeev Joshi. - */ -public final class TLCStateMutSource extends TLCState -implements Cloneable, Serializable { - private Value[] values; - private SemanticNode[] asts; - private static Tool mytool = null; - - /** - * If non-null, viewMap denotes the function to be applied to - * a state before its fingerprint is computed. - */ - private static SemanticNode viewMap = null; - - /** - * If non-null, perms denotes the set of permutations under - * the symmetry assumption. - */ - private static MVPerm[] perms = null; - - private TLCStateMutSource(Value[] vals, SemanticNode[] asts) { - this.values = vals; - this.asts = asts; - } - - public static void init(Tool tool) { - mytool = tool; - Value[] vals = new Value[vars.length]; - SemanticNode[] snodes = new SemanticNode[vars.length]; - Empty = new TLCStateMutSource(vals, snodes); - viewMap = tool.getViewSpec(); - perms = tool.getSymmetryPerms(); - } - - public final TLCState createEmpty() { - int sz = vars.length; - return new TLCStateMutSource(new Value[sz], new SemanticNode[sz]); - } - - public final boolean equals(Object obj) { - if (obj instanceof TLCStateMutSource) { - TLCStateMutSource state = (TLCStateMutSource)obj; - for (int i = 0; i < this.values.length; i++) { - if (this.values[i] == null) { - if (state.values[i] != null) return false; - } - else if (state.values[i] == null || - !this.values[i].equals(state.values[i])) { - return false; - } - } - return true; - } - return false; - } - - public final TLCState bind(UniqueString name, Value value, SemanticNode ast) { - int loc = name.getVarLoc(); - this.values[loc] = value; - if (this.asts != null) { - this.asts[loc] = ast; - } - return this; - } - - public final TLCState bind(SymbolNode id, Value value, SemanticNode expr) { - throw new WrongInvocationException("TLCStateMutSource.bind: This is a TLC bug."); - } - - public final TLCState unbind(UniqueString name) { - int loc = name.getVarLoc(); - this.values[loc] = null; - this.asts[loc] = null; - return this; - } - - public final Value lookup(UniqueString var) { - int loc = var.getVarLoc(); - if (loc < 0) return null; - return this.values[loc]; - } - - public final boolean containsKey(UniqueString var) { - return (this.lookup(var) != null); - } - - public final TLCState copy() { - int len = this.values.length; - Value[] vals = new Value[len]; - SemanticNode[] astClone = new SemanticNode[len]; - for (int i = 0; i < len; i++) { - vals[i] = this.values[i]; - astClone[i] = this.asts[i]; - } - return new TLCStateMutSource(vals, astClone); - } - - public final TLCState deepCopy() { - int len = this.values.length; - Value[] vals = new Value[len]; - SemanticNode[] astClone = new SemanticNode[len]; - for (int i = 0; i < len; i++) { - Value val = this.values[i]; - if (val != null) { - vals[i] = val.deepCopy(); - astClone[i] = this.asts[i]; - } - } - return new TLCStateMutSource(vals, astClone); - } - - public final StateVec addToVec(StateVec states) { - return states.addElement(this.copy()); - } - - public final void deepNormalize() { - for (int i = 0; i < this.values.length; i++) { - Value val = this.values[i]; - if (val != null) { - val.deepNormalize(); - } - } - } - - private final void readObject(ObjectInputStream ois) - throws IOException, ClassNotFoundException { - this.values = (Value[])ois.readObject(); - this.asts = null; - } - - private final void writeObject(ObjectOutputStream oos) throws IOException { - oos.writeObject(this.values); - } - - /** - * This method returns the fingerprint of this state. We fingerprint - * the values in the state according to the order given by vars. - * This guarantees the same state has the same fingerprint. - * - * Since the values in this state can be shared by multiple threads - * via the state queue. They have to be normalized before adding to - * the state queue. We do that here. - */ - public final long fingerPrint() { - int sz = this.values.length; - - Value[] minVals = this.values; - if (perms != null) { - Value[] vals = new Value[sz]; - // Find the "smallest" state under the symmetry permutations: - for (int i = 0; i < perms.length; i++) { - int cmp = 0; - for (int j = 0; j < sz; j++) { - vals[j] = this.values[j].permute(perms[i]); - if (cmp == 0) { - cmp = vals[j].compareTo(minVals[j]); - } - } - if (cmp < 0) { - if (minVals == this.values) { - minVals = vals; - vals = new Value[sz]; - } - else { - Value[] temp = minVals; - minVals = vals; - vals = temp; - } - } - } - } - // Fingerprint the state: - long fp = FP64.New(); - if (viewMap == null) { - for (int i = 0; i < sz; i++) { - fp = minVals[i].fingerPrint(fp); - } - if (this.values != minVals) { - for (int i = 0; i < sz; i++) { - this.values[i].deepNormalize(); - } - } - } - else { - for (int i = 0; i < sz; i++) { - this.values[i].deepNormalize(); - } - TLCStateMutSource state = this; - if (minVals != this.values) { - state = new TLCStateMutSource(minVals, this.asts); - } - Value val = mytool.eval(viewMap, Context.Empty, state); - fp = val.fingerPrint(fp); - } - return fp; - } - - public final void addCounts(ObjLongTable counts) { - for (int i = 0; i < this.asts.length; i++) { - counts.add(this.asts[i], 1); - } - } - - public final boolean allAssigned() { - int len = this.values.length; - for (int i = 0; i < len; i++) { - if (values[i] == null) return false; - } - return true; - } - - public final void read(ValueInputStream vis) throws IOException { - super.read(vis); - int len = this.values.length; - for (int i = 0; i < len; i++) { - this.values[i] = vis.read(); - } - } - - public final void write(ValueOutputStream vos) throws IOException { - super.write(vos); - int len = this.values.length; - for (int i = 0; i < len; i++) { - vos.write(this.values[i]); - } - } - - /* Returns a string representation of this state. */ - public final String toString() { - if (TLCGlobals.useView && viewMap != null) { - Value val = mytool.eval(viewMap, Context.Empty, this); - return Value.ppr(val.toString()); - } - StringBuffer result = new StringBuffer(); - int vlen = vars.length; - if (vlen == 1) { - UniqueString key = vars[0].getName(); - Value val = lookup(key); - String val_str = (val == null) ? "null" : Value.ppr(val.toString()); - result.append(key.toString()); - result.append(" = " + val_str + "\n"); - } - else { - for (int i = 0; i < vlen; i++) { - UniqueString key = vars[i].getName(); - Value val = lookup(key); - String val_str = (val == null) ? "null" : Value.ppr(val.toString()); - result.append("/\\ "); - result.append(key.toString()); - result.append(" = " + val_str + "\n"); - } - } - return result.toString(); - } - - /* Returns a string representation of this state. */ - public final String toString(TLCState lastState) { - StringBuffer result = new StringBuffer(); - TLCStateMutSource lstate = (TLCStateMutSource)lastState; - - int vlen = vars.length; - if (vlen == 1) { - UniqueString key = vars[0].getName(); - Value val = this.lookup(key); - Value lstateVal = lstate.lookup(key); - if (val == null || !val.equals(lstateVal)) { - String val_str = (val == null) ? "null" : Value.ppr(val.toString()); - result.append(key.toString()); - result.append(" = " + val_str + "\n"); - } - } - else { - for (int i = 0; i < vlen; i++) { - UniqueString key = vars[i].getName(); - Value val = this.lookup(key); - Value lstateVal = lstate.lookup(key); - if (val == null || !val.equals(lstateVal)) { - String val_str = (val == null) ? "null" : Value.ppr(val.toString()); - result.append("/\\ "); - result.append(key.toString()); - result.append(" = " + val_str + "\n"); - } - } - } - return result.toString(); - } - -} +//// Copyright (c) 2003 Compaq Corporation. All rights reserved. +//// Portions Copyright (c) 2003 Microsoft Corporation. All rights reserved. +//// Last modified on Mon 30 Apr 2007 at 15:30:02 PST by lamport +//// modified on Fri Aug 24 15:08:38 PDT 2001 by yuanyu +// +//package tlc2.tool; +// +//import java.io.IOException; +//import java.io.ObjectInputStream; +//import java.io.ObjectOutputStream; +//import java.io.Serializable; +//import java.util.Comparator; +//import java.util.Set; +//import java.util.TreeSet; +// +//import tla2sany.semantic.OpDeclNode; +//import tla2sany.semantic.SemanticNode; +//import tla2sany.semantic.SymbolNode; +//import tlc2.TLCGlobals; +//import tlc2.tool.coverage.CostModel; +//import tlc2.util.Context; +//import tlc2.util.FP64; +//import tlc2.util.ObjLongTable; +//import tlc2.value.MVPerm; +//import tlc2.value.Value; +//import tlc2.value.ValueInputStream; +//import tlc2.value.ValueOutputStream; +//import util.UniqueString; +//import util.WrongInvocationException; +// +///** +// * This class represents a TLA+ state, which simply is an assignment +// * of explicit values to all the global variables. This is the +// * mutable version, which means that in-place updates are used for +// * improved performance and reduced allocation. +// * +// * The viewMap was added by Rajeev Joshi. +// */ +//public final class TLCStateMutSource extends TLCState +//implements Cloneable, Serializable { +// private Value[] values; +// private SemanticNode[] asts; +// private static Tool mytool = null; +// +// /** +// * If non-null, viewMap denotes the function to be applied to +// * a state before its fingerprint is computed. +// */ +// private static SemanticNode viewMap = null; +// +// /** +// * If non-null, perms denotes the set of permutations under +// * the symmetry assumption. +// */ +// private static MVPerm[] perms = null; +// +// private TLCStateMutSource(Value[] vals, SemanticNode[] asts) { +// this.values = vals; +// this.asts = asts; +// } +// +// public static void init(Tool tool) { +// mytool = tool; +// Value[] vals = new Value[vars.length]; +// SemanticNode[] snodes = new SemanticNode[vars.length]; +// Empty = new TLCStateMutSource(vals, snodes); +// viewMap = tool.getViewSpec(); +// perms = tool.getSymmetryPerms(); +// } +// +// public final TLCState createEmpty() { +// int sz = vars.length; +// return new TLCStateMutSource(new Value[sz], new SemanticNode[sz]); +// } +// +// public final boolean equals(Object obj) { +// if (obj instanceof TLCStateMutSource) { +// TLCStateMutSource state = (TLCStateMutSource)obj; +// for (int i = 0; i < this.values.length; i++) { +// if (this.values[i] == null) { +// if (state.values[i] != null) return false; +// } +// else if (state.values[i] == null || +// !this.values[i].equals(state.values[i])) { +// return false; +// } +// } +// return true; +// } +// return false; +// } +// +// public final TLCState bind(UniqueString name, Value value, SemanticNode ast, CostModel cm) { +// int loc = name.getVarLoc(); +// this.values[loc] = value; +// if (this.asts != null) { +// this.asts[loc] = ast; +// } +// return this; +// } +// +// public final TLCState bind(SymbolNode id, Value value, SemanticNode expr, CostModel cm) { +// throw new WrongInvocationException("TLCStateMutSource.bind: This is a TLC bug."); +// } +// +// public final TLCState unbind(UniqueString name) { +// int loc = name.getVarLoc(); +// this.values[loc] = null; +// this.asts[loc] = null; +// return this; +// } +// +// public final Value lookup(UniqueString var) { +// int loc = var.getVarLoc(); +// if (loc < 0) return null; +// return this.values[loc]; +// } +// +// public final boolean containsKey(UniqueString var) { +// return (this.lookup(var) != null); +// } +// +// public final TLCState copy() { +// int len = this.values.length; +// Value[] vals = new Value[len]; +// SemanticNode[] astClone = new SemanticNode[len]; +// for (int i = 0; i < len; i++) { +// vals[i] = this.values[i]; +// astClone[i] = this.asts[i]; +// } +// return new TLCStateMutSource(vals, astClone); +// } +// +// public final TLCState deepCopy() { +// int len = this.values.length; +// Value[] vals = new Value[len]; +// SemanticNode[] astClone = new SemanticNode[len]; +// for (int i = 0; i < len; i++) { +// Value val = this.values[i]; +// if (val != null) { +// vals[i] = val.deepCopy(); +// astClone[i] = this.asts[i]; +// } +// } +// return new TLCStateMutSource(vals, astClone); +// } +// +// public final StateVec addToVec(StateVec states) { +// return states.addElement(this.copy()); +// } +// +// public final void deepNormalize() { +// for (int i = 0; i < this.values.length; i++) { +// Value val = this.values[i]; +// if (val != null) { +// val.deepNormalize(); +// } +// } +// } +// +// private final void readObject(ObjectInputStream ois) +// throws IOException, ClassNotFoundException { +// this.values = (Value[])ois.readObject(); +// this.asts = null; +// } +// +// private final void writeObject(ObjectOutputStream oos) throws IOException { +// oos.writeObject(this.values); +// } +// +// /** +// * This method returns the fingerprint of this state. We fingerprint +// * the values in the state according to the order given by vars. +// * This guarantees the same state has the same fingerprint. +// * +// * Since the values in this state can be shared by multiple threads +// * via the state queue. They have to be normalized before adding to +// * the state queue. We do that here. +// * +// * @see TLCStateMut#fingerPrint() for a commented version of this +// * implementation. +// */ +// public final long fingerPrint() { +// int sz = this.values.length; +// +// Value[] minVals = this.values; +// if (perms != null) { +// Value[] vals = new Value[sz]; +// // Find the "smallest" state under the symmetry permutations: +// for (int i = 0; i < perms.length; i++) { +// int cmp = 0; +// for (int j = 0; j < sz; j++) { +// vals[j] = this.values[j].permute(perms[i]); +// if (cmp == 0) { +// cmp = vals[j].compareTo(minVals[j]); +// } +// } +// if (cmp < 0) { +// if (minVals == this.values) { +// minVals = vals; +// vals = new Value[sz]; +// } +// else { +// Value[] temp = minVals; +// minVals = vals; +// vals = temp; +// } +// } +// } +// } +// // Fingerprint the state: +// long fp = FP64.New(); +// if (viewMap == null) { +// for (int i = 0; i < sz; i++) { +// fp = minVals[i].fingerPrint(fp); +// } +// if (this.values != minVals) { +// for (int i = 0; i < sz; i++) { +// this.values[i].deepNormalize(); +// } +// } +// } +// else { +// for (int i = 0; i < sz; i++) { +// this.values[i].deepNormalize(); +// } +// TLCStateMutSource state = this; +// if (minVals != this.values) { +// state = new TLCStateMutSource(minVals, this.asts); +// } +// Value val = mytool.eval(viewMap, Context.Empty, state); +// fp = val.fingerPrint(fp); +// } +// return fp; +// } +// +// public final void addCounts(final ObjLongTable<SemanticNode> counts) { +// for (int i = 0; i < this.asts.length; i++) { +// counts.add(this.asts[i], 1); +// } +// } +// +// public final boolean allAssigned() { +// int len = this.values.length; +// for (int i = 0; i < len; i++) { +// if (values[i] == null) return false; +// } +// return true; +// } +// +// public final Set<OpDeclNode> getUnassigned() { +// // Return sorted set (lexicographical). +// final Set<OpDeclNode> unassignedVars = new TreeSet<OpDeclNode>(new Comparator<OpDeclNode>() { +// @Override +// public int compare(OpDeclNode o1, OpDeclNode o2) { +// return o1.getName().toString().compareTo(o2.getName().toString()); +// } +// }); +// int len = this.values.length; +// for (int i = 0; i < len; i++) { +// if (values[i] == null) { +// unassignedVars.add(vars[i]); +// } +// } +// return unassignedVars; +// } +// +// public final void read(ValueInputStream vis) throws IOException { +// super.read(vis); +// int len = this.values.length; +// for (int i = 0; i < len; i++) { +// this.values[i] = vis.read(); +// } +// } +// +// public final void write(ValueOutputStream vos) throws IOException { +// super.write(vos); +// int len = this.values.length; +// for (int i = 0; i < len; i++) { +// vos.write(this.values[i]); +// } +// } +// +// /* Returns a string representation of this state. */ +// public final String toString() { +// if (TLCGlobals.useView && viewMap != null) { +// Value val = mytool.eval(viewMap, Context.Empty, this); +// return viewMap.toString(val); +// } +// StringBuffer result = new StringBuffer(); +// int vlen = vars.length; +// if (vlen == 1) { +// UniqueString key = vars[0].getName(); +// Value val = lookup(key); +// result.append(key.toString()); +// result.append(" = "); +// result.append(Value.ppr(val)); +// result.append("\n"); +// } +// else { +// for (int i = 0; i < vlen; i++) { +// UniqueString key = vars[i].getName(); +// Value val = lookup(key); +// result.append("/\\ "); +// result.append(key.toString()); +// result.append(" = "); +// result.append(Value.ppr(val)); +// result.append("\n"); +// } +// } +// return result.toString(); +// } +// +// /* Returns a string representation of this state. */ +// public final String toString(TLCState lastState) { +// StringBuffer result = new StringBuffer(); +// TLCStateMutSource lstate = (TLCStateMutSource)lastState; +// +// int vlen = vars.length; +// if (vlen == 1) { +// UniqueString key = vars[0].getName(); +// Value val = this.lookup(key); +// Value lstateVal = lstate.lookup(key); +// if (val == null || !val.equals(lstateVal)) { +// result.append(key.toString()); +// result.append(" = " + Value.ppr(val) + "\n"); +// } +// } +// else { +// for (int i = 0; i < vlen; i++) { +// UniqueString key = vars[i].getName(); +// Value val = this.lookup(key); +// Value lstateVal = lstate.lookup(key); +// if (val == null || !val.equals(lstateVal)) { +// result.append("/\\ "); +// result.append(key.toString()); +// result.append(" = " + Value.ppr(val) + "\n"); +// } +// } +// } +// return result.toString(); +// } +// +//} \ No newline at end of file diff --git a/tlatools/src/tlc2/tool/TLCTrace.java b/tlatools/src/tlc2/tool/TLCTrace.java index 2f05a1842eacd7b43192850e3295b70367a8b315..157e37f27319bc9d116f4dce0cccb5e5ad491c78 100644 --- a/tlatools/src/tlc2/tool/TLCTrace.java +++ b/tlatools/src/tlc2/tool/TLCTrace.java @@ -10,155 +10,187 @@ import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.File; import java.io.IOException; -import java.util.ArrayList; -import java.util.Arrays; import java.util.HashMap; import java.util.Map; import tlc2.output.EC; import tlc2.output.MP; -import tlc2.output.OutputCollector; import tlc2.output.StatePrinter; import tlc2.util.BufferedRandomAccessFile; import tlc2.util.LongVec; +import tlc2.value.RandomEnumerableValues; import util.FileUtil; public class TLCTrace { - private static String filename; - private BufferedRandomAccessFile raf; - private long lastPtr; - private TraceApp tool; - - public TLCTrace(String metadir, String specFile, TraceApp tool) - throws IOException { - filename = metadir + FileUtil.separator + specFile + ".st"; - this.raf = new BufferedRandomAccessFile(filename, "rw"); - this.lastPtr = 1L; - this.tool = tool; - } - - /** - * @param fp A finger print of a state without a predecessor (init state) - * @return The new location (pointer) for the given finger print (state) - * @throws IOException - */ - public final synchronized long writeState(final long aFingerprint) - throws IOException { - return writeState(1, aFingerprint); - } - - /** - * @param predecessor The predecessor state - * @param fp A finger print - * @return The new location (pointer) for the given finger print (state) - * @throws IOException - */ - public final synchronized long writeState(final TLCState predecessor, final long aFingerprint) - throws IOException { - return writeState(predecessor.uid, aFingerprint); - } - - /** - * @param predecessorLoc The location of the state predecessor - * @param fp A finger print - * @return The new location (pointer) for the given finger print (state) - * @throws IOException - */ - private final synchronized long writeState(long predecessorLoc, long fp) - throws IOException { - //TODO Remove synchronization as all threads content for this lock - this.lastPtr = this.raf.getFilePointer(); - this.raf.writeLongNat(predecessorLoc); - this.raf.writeLong(fp); - return this.lastPtr; - } - - public final void close() throws IOException { - this.raf.close(); - } - - private synchronized long getPrev(long loc) throws IOException { - this.raf.seek(loc); - return this.raf.readLongNat(); - } - - private synchronized long getFP(long loc) throws IOException { - this.raf.seek(loc); - this.raf.readLongNat(); /*drop*/ - return this.raf.readLong(); - } - - /** - * Returns the level (monotonically increasing)! - * - * LL: The user has no real need of an accurate tree height. Breadth-first - * search is good because it provides the shortest possible error trace. - * Usually approximately breadth-first search is just as good because it - * makes little difference if the error trace isn't quite as short as - * possible. I believe that in most applications, after a short initial - * period, the height of the tree grows slowly. All workers are usually - * working on states of the same height except for brief periods when the - * height changes, and then the heights will differ by at most one. - * Reporting the height to the user gives him some information about how - * fast model checking is going. He will have no problem getting used to the - * idea that it's only an approximation. (I expect that few users even know - * what it means.) I'd like to make the reported value be monotonic because, - * if it's not, users may worry and people already have enough things in - * life to worry about. - * - * @see TLCTrace#getLevel() - */ - public final int getLevelForReporting() throws IOException { - final int calculatedLevel = getLevel(this.lastPtr); - if(calculatedLevel > previousLevel) { - previousLevel = calculatedLevel; + static final String EXT = ".st"; + protected static String filename; + private final BufferedRandomAccessFile raf; + private long lastPtr; + private TraceApp tool; + + public TLCTrace(String metadir, String specFile, TraceApp tool) throws IOException { + filename = metadir + FileUtil.separator + specFile + EXT; + this.raf = new BufferedRandomAccessFile(filename, "rw"); + this.lastPtr = 1L; + this.tool = tool; } - return previousLevel; - } - - /** - * Stores the previous level reported to guarantee that it is monotonic - */ - private int previousLevel; - - /** - * @see TLCTrace#getLevel(long) - */ - public final int getLevel() throws IOException { - // This assumption (lastPtr) only holds for the TLC in non-parallel mode. - // Generally the last line (logically a state) is not necessarily - // on the highest level of the state tree. This is only the case if - // states are explored strictly by breadth-first search. - return getLevel(this.lastPtr); - } - - /** - * @param startLoc The start location (pointer) from where the level (height) of the state tree should be calculated - * @return The level (height) of the state tree. - * @throws IOException - */ - public synchronized final int getLevel(long startLoc) throws IOException { - // keep current location - long currentFilePointer = this.raf.getFilePointer(); - - // calculate level/depth based on start location - int level = 0; + + /** + * @param fp + * A finger print of a state without a predecessor (init state) + * @return The new location (pointer) for the given finger print (state) + * @throws IOException + */ + public final synchronized long writeState(final long aFingerprint) throws IOException { + return writeState(1, aFingerprint); + } + + /** + * @param predecessor + * The predecessor state + * @param fp + * A finger print + * @return The new location (pointer) for the given finger print (state) + * @throws IOException + */ + public final synchronized long writeState(final TLCState predecessor, final long aFingerprint) throws IOException { + return writeState(predecessor.uid, aFingerprint); + } + + /** + * @param predecessorLoc + * The location of the state predecessor + * @param fp + * A finger print + * @return The new location (pointer) for the given finger print (state) + * @throws IOException + */ + private final synchronized long writeState(long predecessorLoc, long fp) throws IOException { + this.lastPtr = this.raf.getFilePointer(); + this.raf.writeLongNat(predecessorLoc); + this.raf.writeLong(fp); + return this.lastPtr; + } + + public void close() throws IOException { + this.raf.close(); + } + + private synchronized long getPrev(long loc) throws IOException { + this.raf.seek(loc); + return this.raf.readLongNat(); + } + + private synchronized long getFP(long loc) throws IOException { + this.raf.seek(loc); + this.raf.readLongNat(); /* drop */ + return this.raf.readLong(); + } + + /** + * Returns the level (monotonically increasing)! + * + * LL: The user has no real need of an accurate tree height. Breadth-first + * search is good because it provides the shortest possible error trace. + * Usually approximately breadth-first search is just as good because it + * makes little difference if the error trace isn't quite as short as + * possible. I believe that in most applications, after a short initial + * period, the height of the tree grows slowly. All workers are usually + * working on states of the same height except for brief periods when the + * height changes, and then the heights will differ by at most one. + * Reporting the height to the user gives him some information about how + * fast model checking is going. He will have no problem getting used to the + * idea that it's only an approximation. (I expect that few users even know + * what it means.) I'd like to make the reported value be monotonic because, + * if it's not, users may worry and people already have enough things in + * life to worry about. + * + * @see TLCTrace#getLevel() + */ + public synchronized int getLevelForReporting() throws IOException { + final int calculatedLevel = getLevel(this.lastPtr); + if (calculatedLevel > previousLevel) { + previousLevel = calculatedLevel; + } + return previousLevel; + } + + /** + * Stores the previous level reported to guarantee that it is monotonic + */ + private int previousLevel; + + /** + * @see TLCTrace#getLevel(long) + * @return 1 to the length of the longest behavior found so far. + */ + public int getLevel() throws IOException { + // This assumption (lastPtr) only holds for the TLC in non-parallel mode. + // Generally the last line (logically a state) is not necessarily + // on the highest level of the state tree, which is only true if + // states are explored with strict breadth-first search. + // + // The (execution) trace is a forest of one to n trees, where each path + // in the forest represents the order in which states have been generated + // by the workers. + // The algorithm, with which the diameter is approximated from the trace, + // is pretty simple; too simple. The trace is constantly written to the .st + // file where each "record" in the file is a link from a successor state to + // its predecessor state. Thus, the link is a position in the trace file + // where the predecessor state - actually its fingerprint - is stored. At + // the end of the trace file, there are up to m leaf records for which no + // successors have been appended (yet, assuming there are any). + // + // Once the workers have terminated, TLC traverses the trace from a leaf record + // back to a root record. This height is what is reported as the diameter. + // The selection, from what leaf record TLC starts the traversal, is based on + // the last record inserted into the trace file. If this record is one with a + // low height (because its corresponding worker waited most of the time), the + // diameter will thus be underreport. If, on the other hand, the last record + // happens to be one with a large height, the diameter will be overreported. + // + // The selection of the leaf record is the source of the algorithm's + // non-determinism. With a single worker, the last record in the trace is + // always the same which always corresponds to the longest behavior found so + // far (strict BFS). This invariant does not hold with multiple workers. + // + // Obviously, with multiple workers the approximation of the diameter will + // improve with the size of the state graph. Assuming a well-shaped state graph, + // we can argue that the approximation is good enough and document, that its + // value can be anything from 1 to the longest behavior found so far. + return getLevel(this.lastPtr); + } + + /** + * @param startLoc + * The start location (pointer) from where the level (height) of + * the path in the execution tree should be calculated. + * @return The level (height) of the path in the execution tree (the trace) + * starting at startLoc. + * @throws IOException + */ + public synchronized final int getLevel(long startLoc) throws IOException { + // keep current location + long currentFilePointer = this.raf.getFilePointer(); + + // calculate level/depth based on start location + int level = 0; for (long predecessorLoc = startLoc; predecessorLoc != 1; predecessorLoc = this .getPrev(predecessorLoc)) { - level++; - } - - // rewind to current location - this.raf.seek(currentFilePointer); - return level; - } - - /** - * @return All states in the trace file - * @throws IOException - */ - public final TLCStateInfo[] getTrace() throws IOException { + level++; + } + + // rewind to current location + this.raf.seek(currentFilePointer); + return level; + } + + /** + * @return All states in the trace file + * @throws IOException + */ + public final TLCStateInfo[] getTrace() throws IOException { final Map<Long, TLCStateInfo> locToState = new HashMap<Long, TLCStateInfo>(); synchronized (this) { @@ -167,16 +199,16 @@ public class TLCTrace { long length = this.raf.length(); // go to first byte this.raf.seek(0); - + // read init state - this.raf.readLongNat(); /* drop predecessor of init state*/ + this.raf.readLongNat(); /* drop predecessor of init state */ TLCStateInfo state = this.tool.getState(this.raf.readLong()); locToState.put(0L, state); - - for (long location = 12; location < length; location+=12) { + + for (long location = 12; location < length; location += 12) { final long predecessorLocation = this.raf.readLongNat(); final long fp = this.raf.readLong(); - + // read predecessor from map final TLCStateInfo predecessor = locToState.get(predecessorLocation); @@ -186,7 +218,7 @@ public class TLCTrace { // chain to predecessor state.predecessorState = predecessor; state.stateNumber = location / 12; - + // store in map locToState.put(location, state); } @@ -196,242 +228,290 @@ public class TLCTrace { this.raf.seek(curLoc); } } - + return locToState.values().toArray(new TLCStateInfo[locToState.size()]); - } - - /** - * @param loc The start location (pointer) from where the trace should be computed - * @param included true if the start location state should be included - * @return An array of predecessor states - * @throws IOException - */ - public final TLCStateInfo[] getTrace(long loc, boolean included) - throws IOException { - LongVec fps = new LongVec(); - - synchronized(this) { - long curLoc = this.raf.getFilePointer(); - long loc1 = (included) ? loc : this.getPrev(loc); - for (long ploc = loc1; ploc != 1; ploc = this.getPrev(ploc)) { - fps.addElement(this.getFP(ploc)); - } - this.raf.seek(curLoc); - } - - int stateNum = 0; - int len = fps.size(); - TLCStateInfo[] res = new TLCStateInfo[len]; - if (len > 0) { - long fp = fps.elementAt(len-1); - TLCStateInfo sinfo = this.tool.getState(fp); - if (sinfo == null) - { - MP.printError(EC.TLC_FAILED_TO_RECOVER_INIT); - MP.printError(EC.TLC_BUG, "1"); - System.exit(1); - } - res[stateNum++] = sinfo; - for (int i = len - 2; i >= 0; i--) { - fp = fps.elementAt(i); - sinfo = this.tool.getState(fp, sinfo.state); - if (sinfo == null) { - /* - * The following error message is misleading, because it's triggered - * when TLC can't find a non-initial state from its fingerprint - * when it's generating an error trace. LL 7 Mar 2012 - */ - MP.printError(EC.TLC_FAILED_TO_RECOVER_INIT); - MP.printError(EC.TLC_BUG, "2"); - System.exit(1); } - res[stateNum++] = sinfo; - } - } - return res; - } - - /** - * Write out a sequence of states that reaches s2 from an initial - * state, according to the spec. s2 is a next state of s1. - * - * @param s1 may not be null. - * @param s2 may be null. - * @throws IOException - * @throws WorkerException - */ - public synchronized final void printTrace(final TLCState s1, final TLCState s2) - throws IOException, WorkerException - { - ArrayList<TLCStateInfo> trace = new ArrayList<TLCStateInfo>(); // collecting the whole error trace - - MP.printError(EC.TLC_BEHAVIOR_UP_TO_THIS_POINT); - // Print the prefix leading to s1: - long loc1 = s1.uid; - TLCState lastState = null; - TLCStateInfo[] prefix = this.getTrace(loc1, false); - int idx = 0; - while (idx < prefix.length) - { - StatePrinter.printState(prefix[idx], lastState, idx+1); - lastState = prefix[idx].state; - trace.add(prefix[idx]); - idx++; - } - - // Print s1: - TLCStateInfo sinfo; - if (prefix.length == 0) { - sinfo = this.tool.getState(s1.fingerPrint()); - if (sinfo == null) - { - MP.printError(EC.TLC_FAILED_TO_RECOVER_INIT); - MP.printError(EC.TLC_BUG, "3"); - System.exit(1); - } - } - else - { - TLCState s0 = prefix[prefix.length-1].state; - sinfo = this.tool.getState(s1.fingerPrint(), s0); - if (sinfo == null) - { - MP.printError(EC.TLC_FAILED_TO_RECOVER_INIT); - MP.printError(EC.TLC_BUG, "4"); - StatePrinter.printState(s1); - System.exit(1); - } - } - if (s2 == null) - { - lastState = null; - } - StatePrinter.printState(sinfo, lastState, ++idx); - lastState = sinfo.state; - trace.add(sinfo); - - // Print s2: - if (s2 != null) { - sinfo = this.tool.getState(s2, s1); - if (sinfo == null) - { - MP.printError(EC.TLC_FAILED_TO_RECOVER_INIT); - MP.printError(EC.TLC_BUG, "5"); - StatePrinter.printState(s2); - System.exit(1); - } - StatePrinter.printState(sinfo, null, ++idx); - trace.add(sinfo); - } - OutputCollector.setTrace(trace); - } - - - /** - * Returns a sequence of states that reaches, but excludes the - * state with fingerprint fp. - */ - @SuppressWarnings("unused") - private final TLCStateInfo[] printPrefix(long fp) throws IOException { - // First, find the location for fp: - this.raf.seek(0); - this.raf.readLongNat(); /*drop*/ - - while (this.raf.readLong() != fp) { - this.raf.readLongNat(); /*drop*/ - } - - // Print the states corresponding to the fps: - TLCState lastState = null; - TLCStateInfo[] prefix = this.getTrace(this.lastPtr, false); - int idx = 0; - while (idx < prefix.length) { - StatePrinter.printState(prefix[idx], lastState, idx+1); - lastState = prefix[idx].state; - idx++; - } - return prefix; - } - - /* Checkpoint. */ - public synchronized final void beginChkpt() throws IOException { - this.raf.flush(); - // SZ Feb 24, 2009: FileUtil introduced - DataOutputStream dos = FileUtil.newDFOS(filename + ".tmp"); - dos.writeLong(this.raf.getFilePointer()); - dos.writeLong(this.lastPtr); - dos.close(); - } - - public final void commitChkpt() throws IOException { - File oldChkpt = new File(filename + ".chkpt"); - File newChkpt = new File(filename + ".tmp"); - if ((oldChkpt.exists() && !oldChkpt.delete()) || - !newChkpt.renameTo(oldChkpt)) { - throw new IOException("Trace.commitChkpt: cannot delete " + oldChkpt); - } - } - - public final void recover() throws IOException { - // SZ Feb 24, 2009: FileUtil introduced - DataInputStream dis = FileUtil.newDFIS(filename + ".chkpt"); - long filePos = dis.readLong(); - this.lastPtr = dis.readLong(); - dis.close(); - this.raf.seek(filePos); - } - - public static String getFilename() { return filename; } - - public static long getRecoverPtr() throws IOException { - // SZ Feb 24, 2009: FileUtil introduced - DataInputStream dis = FileUtil.newDFIS(filename + ".chkpt"); - long res = dis.readLong(); - dis.close(); - return res; - } - - @SuppressWarnings("unused") - private long[] addBlock(long fp[], long prev[]) throws IOException { - // Reuse prev. - for (int i = 0; i < fp.length; i++) { - prev[i] = this.writeState(prev[i], fp[i]); - } - return prev; - } - - public synchronized final Enumerator elements() throws IOException { - return new Enumerator(); - } - - final class Enumerator { - long len; - BufferedRandomAccessFile enumRaf; - - Enumerator() throws IOException { - this.len = raf.length(); - this.enumRaf = new BufferedRandomAccessFile(filename, "r"); - } - - final void reset(long pos) throws IOException { - this.len = raf.length(); - if (pos == -1) { - pos = this.enumRaf.getFilePointer(); - } - this.enumRaf = new BufferedRandomAccessFile(filename, "r"); - this.enumRaf.seek(pos); - } - - final long nextPos() { - long fpos = this.enumRaf.getFilePointer(); - if (fpos < this.len) { return fpos; } - return -1; - } - - final long nextFP() throws IOException { - this.enumRaf.readLongNat(); /*drop*/ - return this.enumRaf.readLong(); - } - } + + /** + * @param loc + * The start location (pointer) from where the trace should be + * computed + * @param included + * true if the start location state should be included + * @return An array of predecessor states + * @throws IOException + */ + protected TLCStateInfo[] getTrace(long loc, boolean included) throws IOException { + LongVec fps = new LongVec(); + + // Starting at the given start fingerprint (which is the end of the + // trace from the point of the initial states), the chain of + // predecessors fingerprints are reconstructed from the trace file up to + // the initial state. + synchronized (this) { + long curLoc = this.raf.getFilePointer(); + long loc1 = (included) ? loc : this.getPrev(loc); + for (long ploc = loc1; ploc != 1; ploc = this.getPrev(ploc)) { + fps.addElement(this.getFP(ploc)); + } + this.raf.seek(curLoc); + } + + return getTrace(fps); + } + + /** + * This method is *not* safe to call multiple times iff the spec being checked + * consumed randomness, ie. TLC!RandomElement or through the Randomization + * module. In other words, such specs are incompatible with TLC's -continue + * mode. + * <p> + * To implement this correctly, state space exploration would either have to + * halt while the fingerprints are resolved to TLCStates below or ITool has + * to offer additional API s.t. the seed of RandomEnumerableValues gets + * passed as part of the method call. + */ + protected final TLCStateInfo[] getTrace(final LongVec fps) { + return getTrace(null, fps); + } + + protected final TLCStateInfo[] getTrace(TLCStateInfo sinfo, final LongVec fps) { + // Re-Initialize the rng with the seed value recorded and used during the model + // checking phase. Otherwise, we won't be able to reconstruct the error trace + // because the set of initial states is likely to be different. + // This is only necessary though, if TLCGlobals.enumFraction was < 1 during + // the generation of inits. + RandomEnumerableValues.reset(); + + // The vector of fingerprints is now being followed forward from the + // initial state (which is the last state in the long vector), to the + // end state. + // + // At each fingerprint of the sequence, the equivalent state gets + // reconstructed. For the initial state it's just the fingerprint, for + // successor states the predecessor p to the successor state s and the + // fingerprint that are passed to Tool. Tool generates *all* next states + // of p and throws away all except the one that has a matching + // fingerprint. + int stateNum = 0; + final int len = fps.size(); + final TLCStateInfo[] res = new TLCStateInfo[len]; + if (len > 0) { + if (sinfo == null) { + // Recreate initial state from its fingerprint. + final long fp = fps.elementAt(fps.size() - 1); + sinfo = this.tool.getState(fp); + } + // Recover successor states from its predecessor and its fingerprint. + res[stateNum++] = sinfo; + for (int i = len - 2; i >= 0; i--) { + long fp = fps.elementAt(i); + sinfo = this.tool.getState(fp, sinfo.state); + if (sinfo == null) { + /* + * The following error message is misleading, because it's triggered when TLC + * can't find a non-initial state from its fingerprint when it's generating an + * error trace. LL 7 Mar 2012 + */ + MP.printError(EC.TLC_FAILED_TO_RECOVER_INIT); + MP.printError(EC.TLC_BUG, "2 " + Long.toString(fp)); + System.exit(1); + } + res[stateNum++] = sinfo; + } + } + return res; + } + + /** + * Write out a sequence of states that reaches s2 from an initial state, + * according to the spec. s2 is a next state of s1. + * + * @param s1 + * may not be null. + * @param s2 + * may be null. + * @throws IOException + * @throws WorkerException + */ + public void printTrace(final TLCState s1, final TLCState s2) throws IOException, WorkerException { + printTrace(s1, s2, getTrace(s1.uid, false)); + } + + protected synchronized final void printTrace(final TLCState s1, final TLCState s2, final TLCStateInfo[] prefix) + throws IOException, WorkerException { + if (s1.isInitial()) { + // Do not recreate the potentially expensive error trace - e.g. when the set of + // initial states is huge such as during inductive invariant checking. Instead + // use the two states s1 and s2 directly. + MP.printError(EC.TLC_BEHAVIOR_UP_TO_THIS_POINT); + StatePrinter.printState(new TLCStateInfo(s1)); + if (s2 != null) { + // Create TLCStateInfo instance to include corresponding action in output. + StatePrinter.printState(this.tool.getState(s2, s1), s1, 2); + } + return; + } + + MP.printError(EC.TLC_BEHAVIOR_UP_TO_THIS_POINT); + // Print the prefix leading to s1: + TLCState lastState = null; + int idx = 0; + while (idx < prefix.length) { + StatePrinter.printState(prefix[idx], lastState, idx + 1); + lastState = prefix[idx].state; + idx++; + } + + // Print s1: + TLCStateInfo sinfo; + // If the prefix is of length zero, s1 is an initial state. If the + // prefix has elements, use the last state in prefix as the predecessor + // to s1. + if (prefix.length == 0) { + sinfo = this.tool.getState(s1.fingerPrint()); + if (sinfo == null) { + MP.printError(EC.TLC_FAILED_TO_RECOVER_INIT); + MP.printError(EC.TLC_BUG, "3"); + System.exit(1); + } + } else { + TLCState s0 = prefix[prefix.length - 1].state; + sinfo = this.tool.getState(s1.fingerPrint(), s0); + if (sinfo == null) { + MP.printError(EC.TLC_FAILED_TO_RECOVER_INIT); + MP.printError(EC.TLC_BUG, "4"); + StatePrinter.printState(s1); + System.exit(1); + } + } + if (s2 == null) { + lastState = null; + } + StatePrinter.printState(sinfo, lastState, ++idx); + lastState = sinfo.state; + + // Print s2: + // The predecessor to s2 is s1. + if (s2 != null) { + sinfo = this.tool.getState(s2, s1); + if (sinfo == null) { + MP.printError(EC.TLC_FAILED_TO_RECOVER_INIT); + MP.printError(EC.TLC_BUG, "5"); + StatePrinter.printState(s2); + System.exit(1); + } + StatePrinter.printState(sinfo, null, ++idx); + } + } + + /** + * Returns a sequence of states that reaches, but excludes the state with + * fingerprint fp. + */ + @SuppressWarnings("unused") + private final TLCStateInfo[] printPrefix(long fp) throws IOException { + // First, find the location for fp: + this.raf.seek(0); + this.raf.readLongNat(); /* drop */ + + while (this.raf.readLong() != fp) { + this.raf.readLongNat(); /* drop */ + } + + // Print the states corresponding to the fps: + TLCState lastState = null; + TLCStateInfo[] prefix = this.getTrace(this.lastPtr, false); + int idx = 0; + while (idx < prefix.length) { + StatePrinter.printState(prefix[idx], lastState, idx + 1); + lastState = prefix[idx].state; + idx++; + } + return prefix; + } + + /* Checkpoint. */ + public synchronized void beginChkpt() throws IOException { + this.raf.flush(); + // SZ Feb 24, 2009: FileUtil introduced + DataOutputStream dos = FileUtil.newDFOS(filename + ".tmp"); + dos.writeLong(this.raf.getFilePointer()); + dos.writeLong(this.lastPtr); + dos.close(); + } + + public void commitChkpt() throws IOException { + File oldChkpt = new File(filename + ".chkpt"); + File newChkpt = new File(filename + ".tmp"); + if ((oldChkpt.exists() && !oldChkpt.delete()) || !newChkpt.renameTo(oldChkpt)) { + throw new IOException("Trace.commitChkpt: cannot delete " + oldChkpt); + } + } + + public void recover() throws IOException { + // SZ Feb 24, 2009: FileUtil introduced + DataInputStream dis = FileUtil.newDFIS(filename + ".chkpt"); + long filePos = dis.readLong(); + this.lastPtr = dis.readLong(); + dis.close(); + this.raf.seek(filePos); + } + + @SuppressWarnings("unused") + private long[] addBlock(long fp[], long prev[]) throws IOException { + // Reuse prev. + for (int i = 0; i < fp.length; i++) { + prev[i] = this.writeState(prev[i], fp[i]); + } + return prev; + } + + public synchronized Enumerator elements() throws IOException { + return new TLCTraceEnumerator(); + } + + public interface Enumerator { + long nextPos(); + long nextFP() throws IOException; + void close() throws IOException; + void reset(long i) throws IOException; + } + + public class TLCTraceEnumerator implements Enumerator { + long len; + BufferedRandomAccessFile enumRaf; + + TLCTraceEnumerator() throws IOException { + this.len = raf.length(); + this.enumRaf = new BufferedRandomAccessFile(filename, "r"); + } + + public final void reset(long pos) throws IOException { + this.len = raf.length(); + if (pos == -1) { + pos = this.enumRaf.getFilePointer(); + } + this.enumRaf = new BufferedRandomAccessFile(filename, "r"); + this.enumRaf.seek(pos); + } + + public final long nextPos() { + long fpos = this.enumRaf.getFilePointer(); + if (fpos < this.len) { + return fpos; + } + return -1L; + } + + public final long nextFP() throws IOException { + this.enumRaf.readLongNat(); /* drop */ + return this.enumRaf.readLong(); + } + + public final void close() throws IOException { + this.enumRaf.close(); + } + } } diff --git a/tlatools/src/tlc2/tool/Tool.java b/tlatools/src/tlc2/tool/Tool.java deleted file mode 100644 index 594fbc36dfdc032ce84823b761d35939004b6724..0000000000000000000000000000000000000000 --- a/tlatools/src/tlc2/tool/Tool.java +++ /dev/null @@ -1,2990 +0,0 @@ -// Copyright (c) 2003 Compaq Corporation. All rights reserved. -// Portions Copyright (c) 2003 Microsoft Corporation. All rights reserved. -// Last modified on Thu 2 Aug 2007 at 10:25:48 PST by lamport -// modified on Fri Jan 4 22:46:57 PST 2002 by yuanyu - -package tlc2.tool; - -import tla2sany.modanalyzer.SpecObj; -import tla2sany.semantic.ExprNode; -import tla2sany.semantic.ExprOrOpArgNode; -import tla2sany.semantic.FormalParamNode; -import tla2sany.semantic.LabelNode; -import tla2sany.semantic.LetInNode; -import tla2sany.semantic.ModuleNode; -import tla2sany.semantic.OpApplNode; -import tla2sany.semantic.OpArgNode; -import tla2sany.semantic.OpDeclNode; -import tla2sany.semantic.OpDefNode; -import tla2sany.semantic.SemanticNode; -import tla2sany.semantic.Subst; -import tla2sany.semantic.SubstInNode; -import tla2sany.semantic.APSubstInNode; -import tla2sany.semantic.SymbolNode; -import tla2sany.semantic.ThmOrAssumpDefNode; -import tlc2.TLCGlobals; -import tlc2.output.EC; -import tlc2.output.MP; -import tlc2.util.Context; -import tlc2.util.Vect; -import tlc2.value.Applicable; -import tlc2.value.BoolValue; -import tlc2.value.Enumerable; -import tlc2.value.FcnLambdaValue; -import tlc2.value.FcnParams; -import tlc2.value.FcnRcdValue; -import tlc2.value.LazyValue; -import tlc2.value.MVPerm; -import tlc2.value.MethodValue; -import tlc2.value.OpLambdaValue; -import tlc2.value.OpValue; -import tlc2.value.RecordValue; -import tlc2.value.Reducible; -import tlc2.value.SetCapValue; -import tlc2.value.SetCupValue; -import tlc2.value.SetDiffValue; -import tlc2.value.SetEnumValue; -import tlc2.value.SetOfFcnsValue; -import tlc2.value.SetOfRcdsValue; -import tlc2.value.SetOfTuplesValue; -import tlc2.value.SetPredValue; -import tlc2.value.StringValue; -import tlc2.value.SubsetValue; -import tlc2.value.TupleValue; -import tlc2.value.UnionValue; -import tlc2.value.Value; -import tlc2.value.ValueConstants; -import tlc2.value.ValueEnumeration; -import tlc2.value.ValueExcept; -import tlc2.value.ValueVec; -import util.Assert; -import util.FilenameToStream; -import util.UniqueString; - -/** - * This class provides useful methods for tools like model checker - * and simulator. - * - * It's instance serves as a spec handle - * This is one of two places in TLC, where not all messages are retrieved from the message printer, - * but constructed just here in the code. - * - * @version $Id$ - */ -public class Tool - extends Spec - implements ValueConstants, ToolGlobals, TraceApp -{ - protected Action[] actions; // the list of TLA actions. - private CallStack callStack; // the call stack. - - private Vect actionVec = new Vect(10); - - /** - * Creates a new tool handle - * @param specDir - * @param specFile - * @param configFile - */ - public Tool(String specDir, String specFile, String configFile, FilenameToStream resolver) - { - super(specDir, specFile, configFile, resolver); - this.actions = null; - this.callStack = null; - } - - /** - * Initialization. Any Tool object must call it before doing anything. - * @param spec - <code>null</code> or a filled spec object from previous SANY run - */ - public final SpecObj init(boolean preprocess, SpecObj spec) - { - - // Parse and process this spec. - // It takes care of all overrides. - // SZ Feb 20, 2009: added spec reference, - // if not null it is just used instead of re-parsing - SpecObj processSpec = super.processSpec(spec); - - // Initialize state. - if (TLCGlobals.coverageInterval >= 0) { - TLCStateMutSource.init(this); - } - else { - TLCStateMut.init(this); - } - - // Pre-evaluate all the definitions in the spec that are constants. - if (preprocess) { - this.processConstantDefns(); - } - - // Finally, process the config file. - super.processConfig(); - - return processSpec; - } - - public final void setCallStack() - { - this.callStack = new CallStack(); - } - - public final CallStack getCallStack() - { - return this.callStack; - } - - /** - * This method returns the set of all possible actions of the - * spec, and sets the actions field of this object. In fact, we - * could simply treat the next predicate as one "giant" action. - * But for efficiency, we preprocess the next state predicate by - * splitting it into a set of actions for the maximum prefix - * of disjunction and existential quantification. - */ - public final Action[] getActions() { - if (this.actions == null) { - Action next = this.getNextStateSpec(); - if (next == null) { - this.actions = new Action[0]; - } - else { - this.getActions(next.pred, next.con); - int sz = this.actionVec.size(); - this.actions = new Action[sz]; - for (int i = 0; i < sz; i++) { - this.actions[i] = (Action)this.actionVec.elementAt(i); - } - } - } - return this.actions; - } - - - - private final void getActions(SemanticNode next, Context con) { - switch (next.getKind()) { - case OpApplKind: - { - OpApplNode next1 = (OpApplNode)next; - this.getActionsAppl(next1, con); - return; - } - case LetInKind: - { - LetInNode next1 = (LetInNode)next; - this.getActions(next1.getBody(), con); - return; - } - case SubstInKind: - { - SubstInNode next1 = (SubstInNode)next; - Subst[] substs = next1.getSubsts(); - if (substs.length == 0) { - this.getActions(next1.getBody(), con); - } - else { - Action action = new Action(next1, con); - this.actionVec.addElement(action); - } - return; - } - - // Added by LL on 13 Nov 2009 to handle theorem and assumption names. - case APSubstInKind: - { - APSubstInNode next1 = (APSubstInNode)next; - Subst[] substs = next1.getSubsts(); - if (substs.length == 0) { - this.getActions(next1.getBody(), con); - } - else { - Action action = new Action(next1, con); - this.actionVec.addElement(action); - } - return; - } - - /*********************************************************************** - * LabelKind class added by LL on 13 Jun 2007. * - ***********************************************************************/ - case LabelKind: - { - LabelNode next1 = (LabelNode)next; - this.getActions(next1.getBody(), con); - return; - } - default: - { - Assert.fail("The next state relation is not a boolean expression.\n" + next); - } - } - } - - private final void getActionsAppl(OpApplNode next, Context con) { - ExprOrOpArgNode[] args = next.getArgs(); - SymbolNode opNode = next.getOperator(); - int opcode = BuiltInOPs.getOpCode(opNode.getName()); - - if (opcode == 0) { - Object val = this.lookup(opNode, con, false); - - if (val instanceof OpDefNode) { - OpDefNode opDef = (OpDefNode)val; - opcode = BuiltInOPs.getOpCode(opDef.getName()); - if (opcode == 0) { - try { - FormalParamNode[] formals = opDef.getParams(); - int alen = args.length; - int argLevel = 0; - for (int i = 0; i < alen; i++) { - argLevel = args[i].getLevel(); - if (argLevel != 0) break; - } - if (argLevel == 0) { - Context con1 = con; - for (int i = 0; i < alen; i++) { - Value aval = this.eval(args[i], con, TLCState.Empty); - con1 = con1.cons(formals[i], aval); - } - this.getActions(opDef.getBody(), con1); - return; - } - } - catch (Throwable e) { /*SKIP*/ } - } - } - if (opcode == 0) { - Action action = new Action(next, con); - this.actionVec.addElement(action); - return; - } - } - - switch (opcode) { - case OPCODE_be: // BoundedExists - { - int cnt = this.actionVec.size(); - try { - ContextEnumerator Enum = - this.contexts(next, con, TLCState.Empty, TLCState.Empty, EvalControl.Clear); - Context econ; - while ((econ = Enum.nextElement()) != null) { - this.getActions(args[0], econ); - } - } - catch (Throwable e) { - Action action = new Action(next, con); - this.actionVec.removeAll(cnt); - this.actionVec.addElement(action); - } - return; - } - case OPCODE_dl: // DisjList - case OPCODE_lor: - { - for (int i = 0; i < args.length; i++) { - this.getActions(args[i], con); - } - return; - } - default: - { - // We handle all the other builtin operators here. - Action action = new Action(next, con); - this.actionVec.addElement(action); - return; - } - } - } - - /* - * This method returns the set of possible initial states that - * satisfies the initial state predicate. Initial state predicate - * can be under-specified. Too many possible initial states will - * probably make tools like TLC useless. - */ - public final StateVec getInitStates() { - Vect init = this.getInitStateSpec(); - ActionItemList acts = ActionItemList.Empty; - StateVec initStates = new StateVec(0); - for (int i = 1; i < init.size(); i++) { - Action elem = (Action)init.elementAt(i); - acts = acts.cons(elem.pred, elem.con, -1); - } - if (init.size() != 0) { - Action elem = (Action)init.elementAt(0); - TLCState ps = TLCState.Empty.createEmpty(); - this.getInitStates(elem.pred, acts, elem.con, ps, initStates); - } - return initStates; - } - - /* Create the state specified by pred. */ - public final TLCState makeState(SemanticNode pred) { - ActionItemList acts = ActionItemList.Empty; - TLCState ps = TLCState.Empty.createEmpty(); - StateVec states = new StateVec(0); - this.getInitStates(pred, acts, Context.Empty, ps, states); - if (states.size() != 1) { - Assert.fail("The predicate does not specify a unique state." + pred); - } - TLCState state = states.elementAt(0); - if (!this.isGoodState(state)) { - Assert.fail("The state specified by the predicate is not complete." + pred); - } - return state; - } - - private final void getInitStates(SemanticNode init, ActionItemList acts, - Context c, TLCState ps, StateVec states) { - switch (init.getKind()) { - case OpApplKind: - { - OpApplNode init1 = (OpApplNode)init; - this.getInitStatesAppl(init1, acts, c, ps, states); - return; - } - case LetInKind: - { - LetInNode init1 = (LetInNode)init; - this.getInitStates(init1.getBody(), acts, c, ps, states); - return; - } - case SubstInKind: - { - SubstInNode init1 = (SubstInNode)init; - Subst[] subs = init1.getSubsts(); - Context c1 = c; - for (int i = 0; i < subs.length; i++) { - Subst sub = subs[i]; - c1 = c1.cons(sub.getOp(), this.getVal(sub.getExpr(), c, false)); - } - this.getInitStates(init1.getBody(), acts, c1, ps, states); - return; - } - - // Added by LL on 13 Nov 2009 to handle theorem and assumption names. - case APSubstInKind: - { - APSubstInNode init1 = (APSubstInNode)init; - Subst[] subs = init1.getSubsts(); - Context c1 = c; - for (int i = 0; i < subs.length; i++) { - Subst sub = subs[i]; - c1 = c1.cons(sub.getOp(), this.getVal(sub.getExpr(), c, false)); - } - this.getInitStates(init1.getBody(), acts, c1, ps, states); - return; - } - - - /*********************************************************************** - * LabelKind class added by LL on 13 Jun 2007. * - ***********************************************************************/ - case LabelKind: - { - LabelNode init1 = (LabelNode)init; - this.getInitStates(init1.getBody(), acts, c, ps, states); - return; - } - default: - { - Assert.fail("The init state relation is not a boolean expression.\n" + init); - } - } - } - - private final void getInitStates(ActionItemList acts, TLCState ps, StateVec states) { - if (acts.isEmpty()) { - states.addElement(ps.copy()); - } - else { - // Assert.check(act.kind > 0 || act.kind == -1); - ActionItemList acts1 = acts.cdr(); - this.getInitStates(acts.carPred(), acts1, acts.carContext(), ps, states); - } - } - - private final void getInitStatesAppl(OpApplNode init, ActionItemList acts, - Context c, TLCState ps, StateVec states) { - ExprOrOpArgNode[] args = init.getArgs(); - int alen = args.length; - SymbolNode opNode = init.getOperator(); - int opcode = BuiltInOPs.getOpCode(opNode.getName()); - - if (opcode == 0) { - // This is a user-defined operator with one exception: it may - // be substed by a builtin operator. This special case occurs - // when the lookup returns an OpDef with opcode # 0. - Object val = this.lookup(opNode, c, ps, false); - - if (val instanceof OpDefNode) { - OpDefNode opDef = (OpDefNode)val; - opcode = BuiltInOPs.getOpCode(opDef.getName()); - if (opcode == 0) { - // Context c1 = this.getOpContext(opDef, args, c, false); - Context c1 = this.getOpContext(opDef, args, c, true); - this.getInitStates(opDef.getBody(), acts, c1, ps, states); - return; - } - } - // Added 13 Nov 2009 by LL to fix Yuan's fix. - /********************************************************************* - * Modified on 23 October 2012 by LL to work if ThmOrAssumpDefNode * - * imported with parameterized instantiation. * - *********************************************************************/ - if (val instanceof ThmOrAssumpDefNode) { - ThmOrAssumpDefNode opDef = (ThmOrAssumpDefNode)val; - opcode = BuiltInOPs.getOpCode(opDef.getName()); - Context c1 = this.getOpContext(opDef, args, c, true); - this.getInitStates(opDef.getBody(), acts, c1, ps, states); - return; - } - - if (val instanceof LazyValue) { - LazyValue lv = (LazyValue)val; - if (lv.val == null || lv.val == ValUndef) { - this.getInitStates(lv.expr, acts, lv.con, ps, states); - return; - } - val = lv.val; - } - - Object bval = val; - if (alen == 0) { - if (val instanceof MethodValue) { - bval = ((MethodValue)val).apply(EmptyArgs, EvalControl.Clear); - } - } - else { - if (val instanceof OpValue) { - Applicable opVal = (Applicable)val; - Value[] argVals = new Value[alen]; - // evaluate the actuals: - for (int i = 0; i < alen; i++) { - argVals[i] = this.eval(args[i], c, ps); - } - // apply the operator: - bval = opVal.apply(argVals, EvalControl.Clear); - } - } - - if (opcode == 0) - { - if (!(bval instanceof BoolValue)) - { - Assert.fail(EC.TLC_EXPECTED_EXPRESSION_IN_COMPUTING, new String[] { "initial states", "boolean", - bval.toString(), init.toString() }); - } - if (((BoolValue) bval).val) - { - this.getInitStates(acts, ps, states); - } - return; - } - } - - switch (opcode) { - case OPCODE_dl: // DisjList - case OPCODE_lor: - { - for (int i = 0; i < alen; i++) { - this.getInitStates(args[i], acts, c, ps, states); - } - return; - } - case OPCODE_cl: // ConjList - case OPCODE_land: - { - for (int i = alen-1; i > 0; i--) { - acts = acts.cons(args[i], c, i); - } - this.getInitStates(args[0], acts, c, ps, states); - return; - } - case OPCODE_be: // BoundedExists - { - SemanticNode body = args[0]; - ContextEnumerator Enum = this.contexts(init, c, ps, TLCState.Empty, EvalControl.Clear); - Context c1; - while ((c1 = Enum.nextElement()) != null) { - this.getInitStates(body, acts, c1, ps, states); - } - return; - } - case OPCODE_bf: // BoundedForall - { - SemanticNode body = args[0]; - ContextEnumerator Enum = this.contexts(init, c, ps, TLCState.Empty, EvalControl.Clear); - Context c1 = Enum.nextElement(); - if (c1 == null) { - this.getInitStates(acts, ps, states); - } - else { - ActionItemList acts1 = acts; - Context c2; - while ((c2 = Enum.nextElement()) != null) { - acts1 = acts1.cons(body, c2, -1); - } - this.getInitStates(body, acts1, c1, ps, states); - } - return; - } - case OPCODE_ite: // IfThenElse - { - Value guard = this.eval(args[0], c, ps); - if (!(guard instanceof BoolValue)) { - Assert.fail("In computing initial states, a non-boolean expression (" + - guard.getKindString() + ") was used as the condition " + - "of an IF.\n" + init); - } - int idx = (((BoolValue)guard).val) ? 1 : 2; - this.getInitStates(args[idx], acts, c, ps, states); - return; - } - case OPCODE_case: // Case - { - SemanticNode other = null; - for (int i = 0; i < alen; i++) { - OpApplNode pair = (OpApplNode)args[i]; - ExprOrOpArgNode[] pairArgs = pair.getArgs(); - if (pairArgs[0] == null) { - other = pairArgs[1]; - } - else { - Value bval = this.eval(pairArgs[0], c, ps); - if (!(bval instanceof BoolValue)) { - Assert.fail("In computing initial states, a non-boolean expression (" + - bval.getKindString() + ") was used as a guard condition" + - " of a CASE.\n" + pairArgs[1]); - } - if (((BoolValue)bval).val) { - this.getInitStates(pairArgs[1], acts, c, ps, states); - return; - } - } - } - if (other == null) { - Assert.fail("In computing initial states, TLC encountered a CASE with no" + - " conditions true.\n" + init); - } - this.getInitStates(other, acts, c, ps, states); - return; - } - case OPCODE_fa: // FcnApply - { - Value fval = this.eval(args[0], c, ps); - if (fval instanceof FcnLambdaValue) { - FcnLambdaValue fcn = (FcnLambdaValue)fval; - if (fcn.fcnRcd == null) { - Context c1 = this.getFcnContext(fcn, args, c, ps, TLCState.Empty, EvalControl.Clear); - this.getInitStates(fcn.body, acts, c1, ps, states); - return; - } - fval = fcn.fcnRcd; - } - else if (!(fval instanceof Applicable)) { - Assert.fail("In computing initial states, a non-function (" + - fval.getKindString() + ") was applied as a function.\n" + init); - } - Applicable fcn = (Applicable) fval; - Value argVal = this.eval(args[1], c, ps); - Value bval = fcn.apply(argVal, EvalControl.Clear); - if (!(bval instanceof BoolValue)) - { - Assert.fail(EC.TLC_EXPECTED_EXPRESSION_IN_COMPUTING2, new String[] { "initial states", "boolean", - init.toString() }); - } - if (((BoolValue)bval).val) { - this.getInitStates(acts, ps, states); - } - return; - } - case OPCODE_eq: - { - SymbolNode var = this.getVar(args[0], c, false); - if (var == null || var.getName().getVarLoc() < 0) { - Value bval = this.eval(init, c, ps); - if (!((BoolValue)bval).val) return; - } - else { - UniqueString varName = var.getName(); - Value lval = ps.lookup(varName); - Value rval = this.eval(args[1], c, ps); - if (lval == null) { - ps = ps.bind(varName, rval, init); - this.getInitStates(acts, ps, states); - ps.unbind(varName); - return; - } - else { - if (!lval.equals(rval)) return; - } - } - this.getInitStates(acts, ps, states); - return; - } - case OPCODE_in: - { - SymbolNode var = this.getVar(args[0], c, false); - if (var == null || var.getName().getVarLoc() < 0) { - Value bval = this.eval(init, c, ps); - if (!((BoolValue)bval).val) return; - } - else { - UniqueString varName = var.getName(); - Value lval = ps.lookup(varName); - Value rval = this.eval(args[1], c, ps); - if (lval == null) { - if (!(rval instanceof Enumerable)) { - Assert.fail("In computing initial states, the right side of \\IN" + - " is not enumerable.\n" + init); - } - ValueEnumeration Enum = ((Enumerable)rval).elements(); - Value elem; - while ((elem = Enum.nextElement()) != null) { - ps.bind(varName, elem, init); - this.getInitStates(acts, ps, states); - ps.unbind(varName); - } - return; - } - else { - if (!rval.member(lval)) return; - } - } - this.getInitStates(acts, ps, states); - return; - } - case OPCODE_implies: - { - Value lval = this.eval(args[0], c, ps); - if (!(lval instanceof BoolValue)) { - Assert.fail("In computing initial states of a predicate of form" + - " P => Q, P was " + lval.getKindString() + "\n." + init); - } - if (((BoolValue)lval).val) { - this.getInitStates(args[1], acts, c, ps, states); - } - else { - this.getInitStates(acts, ps, states); - } - return; - } - // The following case added by LL on 13 Nov 2009 to handle subexpression names. - case OPCODE_nop: - { - this.getInitStates(args[0], acts, c, ps, states); - return; - } - default: - { - // For all the other builtin operators, simply evaluate: - Value bval = this.eval(init, c, ps); - if (!(bval instanceof BoolValue)) { - - Assert.fail("In computing initial states, TLC expected a boolean expression," + - "\nbut instead found " + bval + ".\n" + init); - } - if (((BoolValue)bval).val) { - this.getInitStates(acts, ps, states); - } - return; - } - } - } - - /** - * This method returns the set of next states when taking the action - * in the given state. - */ - public final StateVec getNextStates(Action action, TLCState state) { - ActionItemList acts = ActionItemList.Empty; - TLCState s1 = TLCState.Empty.createEmpty(); - StateVec nss = new StateVec(0); - this.getNextStates(action.pred, acts, action.con, state, s1, nss); - return nss; - } - - private final TLCState getNextStates(SemanticNode pred, ActionItemList acts, Context c, - TLCState s0, TLCState s1, StateVec nss) { - switch (pred.getKind()) { - case OpApplKind: - { - OpApplNode pred1 = (OpApplNode)pred; - return this.getNextStatesAppl(pred1, acts, c, s0, s1, nss); - } - case LetInKind: - { - LetInNode pred1 = (LetInNode)pred; - return this.getNextStates(pred1.getBody(), acts, c, s0, s1, nss); - } - case SubstInKind: - { - SubstInNode pred1 = (SubstInNode)pred; - Subst[] subs = pred1.getSubsts(); - int slen = subs.length; - Context c1 = c; - for (int i = 0; i < slen; i++) { - Subst sub = subs[i]; - c1 = c1.cons(sub.getOp(), this.getVal(sub.getExpr(), c, false)); - } - return this.getNextStates(pred1.getBody(), acts, c1, s0, s1, nss); - } - - // Added by LL on 13 Nov 2009 to handle theorem and assumption names. - case APSubstInKind: - { - APSubstInNode pred1 = (APSubstInNode)pred; - Subst[] subs = pred1.getSubsts(); - int slen = subs.length; - Context c1 = c; - for (int i = 0; i < slen; i++) { - Subst sub = subs[i]; - c1 = c1.cons(sub.getOp(), this.getVal(sub.getExpr(), c, false)); - } - return this.getNextStates(pred1.getBody(), acts, c1, s0, s1, nss); - } - - - /*********************************************************************** - * LabelKind class added by LL on 13 Jun 2007. * - ***********************************************************************/ - case LabelKind: - { - LabelNode pred1 = (LabelNode)pred; - return this.getNextStates(pred1.getBody(), acts, c, s0, s1, nss); - } - default: - { - Assert.fail("The next state relation is not a boolean expression.\n" + pred); - } - } - return s1; - } - - private final TLCState getNextStates(ActionItemList acts, TLCState s0, TLCState s1, - StateVec nss) { - TLCState resState = s1; - - if (acts.isEmpty()) { - nss.addElement(s1); - resState = s1.copy(); - } - else { - int kind = acts.carKind(); - SemanticNode pred = acts.carPred(); - Context c = acts.carContext(); - ActionItemList acts1 = acts.cdr(); - if (kind > 0) { - if (this.callStack != null) this.callStack.push(pred); - resState = this.getNextStates(pred, acts1, c, s0, s1, nss); - if (this.callStack != null) this.callStack.pop(); - } - else if (kind == -1) { - resState = this.getNextStates(pred, acts1, c, s0, s1, nss); - } - else if (kind == -2) { - resState = this.processUnchanged(pred, acts1, c, s0, s1, nss); - } - else { - Value v1 = this.eval(pred, c, s0); - Value v2 = this.eval(pred, c, s1); - if (!v1.equals(v2)) { - resState = this.getNextStates(acts1, s0, s1, nss); - } - } - } - - return resState; - } - - private final TLCState getNextStatesAppl(OpApplNode pred, ActionItemList acts, Context c, - TLCState s0, TLCState s1, StateVec nss) { - ExprOrOpArgNode[] args = pred.getArgs(); - int alen = args.length; - SymbolNode opNode = pred.getOperator(); - int opcode = BuiltInOPs.getOpCode(opNode.getName()); - - if (opcode == 0) { - // This is a user-defined operator with one exception: it may - // be substed by a builtin operator. This special case occurs - // when the lookup returns an OpDef with opcode # 0. - Object val = this.lookup(opNode, c, s0, false); - - if (val instanceof OpDefNode) { - OpDefNode opDef = (OpDefNode)val; - opcode = BuiltInOPs.getOpCode(opDef.getName()); - if (opcode == 0) { - // Context c1 = this.getOpContext(opDef, args, c, false); - Context c1 = this.getOpContext(opDef, args, c, true); - return this.getNextStates(opDef.getBody(), acts, c1, s0, s1, nss); - } - } - - // Added by LL 13 Nov 2009 to fix Yuan's fix - /********************************************************************* - * Modified on 23 October 2012 by LL to work if ThmOrAssumpDefNode * - * imported with parameterized instantiation. * - *********************************************************************/ - if (val instanceof ThmOrAssumpDefNode) { - ThmOrAssumpDefNode opDef = (ThmOrAssumpDefNode)val; - Context c1 = this.getOpContext(opDef, args, c, true); - return this.getNextStates(opDef.getBody(), acts, c1, s0, s1, nss); - } - - if (val instanceof LazyValue) { - LazyValue lv = (LazyValue)val; - if (lv.val == null || lv.val == ValUndef) { - return this.getNextStates(lv.expr, acts, lv.con, s0, s1, nss); - } - val = lv.val; - } - - Object bval = val; - if (alen == 0) { - if (val instanceof MethodValue) { - bval = ((MethodValue)val).apply(EmptyArgs, EvalControl.Clear); - } - } - else { - if (val instanceof OpValue) { - Applicable opVal = (Applicable)val; - Value[] argVals = new Value[alen]; - // evaluate the actuals: - for (int i = 0; i < alen; i++) { - argVals[i] = this.eval(args[i], c, s0, s1, EvalControl.Clear); - } - // apply the operator: - bval = opVal.apply(argVals, EvalControl.Clear); - } - } - - if (opcode == 0) - { - if (!(bval instanceof BoolValue)) - { - Assert.fail(EC.TLC_EXPECTED_EXPRESSION_IN_COMPUTING, new String[] { "next states", "boolean", - bval.toString(), pred.toString() }); - } - if (((BoolValue) bval).val) - { - return this.getNextStates(acts, s0, s1, nss); - } - return s1; - } - } - - TLCState resState = s1; - switch (opcode) { - case OPCODE_cl: // ConjList - case OPCODE_land: - { - ActionItemList acts1 = acts; - for (int i = alen - 1; i > 0; i--) { - acts1 = acts1.cons(args[i], c, i); - } - if (this.callStack != null) this.callStack.push(args[0]); - resState = this.getNextStates(args[0], acts1, c, s0, s1, nss); - if (this.callStack != null) this.callStack.pop(); - return resState; - } - case OPCODE_dl: // DisjList - case OPCODE_lor: - { - for (int i = 0; i < alen; i++) { - if (this.callStack != null) this.callStack.push(args[i]); - resState = this.getNextStates(args[i], acts, c, s0, resState, nss); - if (this.callStack != null) this.callStack.pop(); - } - return resState; - } - case OPCODE_be: // BoundedExists - { - SemanticNode body = args[0]; - ContextEnumerator Enum = this.contexts(pred, c, s0, s1, EvalControl.Clear); - Context c1; - while ((c1 = Enum.nextElement()) != null) { - resState = this.getNextStates(body, acts, c1, s0, resState, nss); - } - return resState; - } - case OPCODE_bf: // BoundedForall - { - SemanticNode body = args[0]; - ContextEnumerator Enum = this.contexts(pred, c, s0, s1, EvalControl.Clear); - Context c1 = Enum.nextElement(); - if (c1 == null) { - resState = this.getNextStates(acts, s0, s1, nss); - } - else { - ActionItemList acts1 = acts; - Context c2; - while ((c2 = Enum.nextElement()) != null) { - acts1 = acts1.cons(body, c2, -1); - } - resState = this.getNextStates(body, acts1, c1, s0, s1, nss); - } - return resState; - } - case OPCODE_fa: // FcnApply - { - Value fval = this.eval(args[0], c, s0, s1, EvalControl.KeepLazy); - if (fval instanceof FcnLambdaValue) { - FcnLambdaValue fcn = (FcnLambdaValue)fval; - if (fcn.fcnRcd == null) { - Context c1 = this.getFcnContext(fcn, args, c, s0, s1, EvalControl.Clear); - return this.getNextStates(fcn.body, acts, c1, s0, s1, nss); - } - fval = fcn.fcnRcd; - } - if (!(fval instanceof Applicable)) { - Assert.fail("In computing next states, a non-function (" + - fval.getKindString() + ") was applied as a function.\n" + pred); - } - Applicable fcn = (Applicable)fval; - Value argVal = this.eval(args[1], c, s0, s1, EvalControl.Clear); - Value bval = fcn.apply(argVal, EvalControl.Clear); - if (!(bval instanceof BoolValue)) { - Assert.fail(EC.TLC_EXPECTED_EXPRESSION_IN_COMPUTING2, new String[] { "next states", "boolean", - pred.toString() }); - } - if (((BoolValue)bval).val) { - return this.getNextStates(acts, s0, s1, nss); - } - return resState; - } - case OPCODE_aa: // AngleAct <A>_e - { - ActionItemList acts1 = acts.cons(args[1], c, -3); - return this.getNextStates(args[0], acts1, c, s0, s1, nss); - } - case OPCODE_sa: // [A]_e - { - /* The following two lines of code did not work, and were changed by - * YuanYu to mimic the way \/ works. Change made - * 11 Mar 2009, with LL sitting next to him. - */ - // this.getNextStates(args[0], acts, c, s0, s1, nss); - // return this.processUnchanged(args[1], acts, c, s0, s1, nss); - resState = this.getNextStates(args[0], acts, c, s0, resState, nss); - return this.processUnchanged(args[1], acts, c, s0, resState, nss); - } - case OPCODE_ite: // IfThenElse - { - Value guard = this.eval(args[0], c, s0, s1, EvalControl.Clear); - if (!(guard instanceof BoolValue)) { - Assert.fail("In computing next states, a non-boolean expression (" + - guard.getKindString() + ") was used as the condition of" + - " an IF." + pred); - } - if (((BoolValue)guard).val) { - return this.getNextStates(args[1], acts, c, s0, s1, nss); - } - else { - return this.getNextStates(args[2], acts, c, s0, s1, nss); - } - } - case OPCODE_case: // Case - { - SemanticNode other = null; - for (int i = 0; i < alen; i++) { - OpApplNode pair = (OpApplNode)args[i]; - ExprOrOpArgNode[] pairArgs = pair.getArgs(); - if (pairArgs[0] == null) { - other = pairArgs[1]; - } - else { - Value bval = this.eval(pairArgs[0], c, s0, s1, EvalControl.Clear); - if (!(bval instanceof BoolValue)) { - Assert.fail("In computing next states, a non-boolean expression (" + - bval.getKindString() + ") was used as a guard condition" + - " of a CASE.\n" + pairArgs[1]); - } - if (((BoolValue)bval).val) { - return this.getNextStates(pairArgs[1], acts, c, s0, s1, nss); - } - } - } - if (other == null) { - Assert.fail("In computing next states, TLC encountered a CASE with no" + - " conditions true.\n" + pred); - } - return this.getNextStates(other, acts, c, s0, s1, nss); - } - case OPCODE_eq: - { - SymbolNode var = this.getPrimedVar(args[0], c, false); - // Assert.check(var.getName().getVarLoc() >= 0); - if (var == null) { - Value bval = this.eval(pred, c, s0, s1, EvalControl.Clear); - if (!((BoolValue)bval).val) { - return resState; - } - } - else { - UniqueString varName = var.getName(); - Value lval = s1.lookup(varName); - Value rval = this.eval(args[1], c, s0, s1, EvalControl.Clear); - if (lval == null) { - resState.bind(varName, rval, pred); - resState = this.getNextStates(acts, s0, resState, nss); - resState.unbind(varName); - return resState; - } - else if (!lval.equals(rval)) { - return resState; - } - } - return this.getNextStates(acts, s0, s1, nss); - } - case OPCODE_in: - { - SymbolNode var = this.getPrimedVar(args[0], c, false); - // Assert.check(var.getName().getVarLoc() >= 0); - if (var == null) { - Value bval = this.eval(pred, c, s0, s1, EvalControl.Clear); - if (!((BoolValue)bval).val) { - return resState; - } - } - else { - UniqueString varName = var.getName(); - Value lval = s1.lookup(varName); - Value rval = this.eval(args[1], c, s0, s1, EvalControl.Clear); - if (lval == null) { - if (!(rval instanceof Enumerable)) { - Assert.fail("In computing next states, the right side of \\IN" + - " is not enumerable.\n" + pred); - } - ValueEnumeration Enum = ((Enumerable)rval).elements(); - Value elem; - while ((elem = Enum.nextElement()) != null) { - resState.bind(varName, elem, pred); - resState = this.getNextStates(acts, s0, resState, nss); - resState.unbind(varName); - } - return resState; - } - else if (!rval.member(lval)) { - return resState; - } - } - return this.getNextStates(acts, s0, s1, nss); - } - case OPCODE_implies: - { - Value bval = this.eval(args[0], c, s0, s1, EvalControl.Clear); - if (!(bval instanceof BoolValue)) { - Assert.fail("In computing next states of a predicate of the form" + - " P => Q, P was\n" + bval.getKindString() + ".\n" + pred); - } - if (((BoolValue)bval).val) { - return this.getNextStates(args[1], acts, c, s0, s1, nss); - } - else { - return this.getNextStates(acts, s0, s1, nss); - } - } - case OPCODE_unchanged: - { - return this.processUnchanged(args[0], acts, c, s0, s1, nss); - } - case OPCODE_cdot: - { - Assert.fail("The current version of TLC does not support action composition."); - /*** - TLCState s01 = TLCStateFun.Empty; - StateVec iss = new StateVec(0); - this.getNextStates(args[0], ActionItemList.Empty, c, s0, s01, iss); - int sz = iss.size(); - for (int i = 0; i < sz; i++) { - s01 = iss.elementAt(i); - this.getNextStates(args[1], acts, c, s01, s1, nss); - } - ***/ - return s1; - } - // The following case added by LL on 13 Nov 2009 to handle subexpression names. - case OPCODE_nop: - { - return this.getNextStates(args[0], acts, c, s0, s1, nss); - } - default: - { - // We handle all the other builtin operators here. - Value bval = this.eval(pred, c, s0, s1, EvalControl.Clear); - if (!(bval instanceof BoolValue)) { - Assert.fail(EC.TLC_EXPECTED_EXPRESSION_IN_COMPUTING, new String[] { "next states", "boolean", - bval.toString(), pred.toString() }); - } - if (((BoolValue)bval).val) { - resState = this.getNextStates(acts, s0, s1, nss); - } - return resState; - } - } - } - - private final TLCState processUnchanged(SemanticNode expr, ActionItemList acts, Context c, - TLCState s0, TLCState s1, StateVec nss) { - SymbolNode var = this.getVar(expr, c, false); - TLCState resState = s1; - if (var != null) { - // expr is a state variable: - UniqueString varName = var.getName(); - Value val0 = s0.lookup(varName); - Value val1 = s1.lookup(varName); - if (val1 == null) { - resState.bind(varName, val0, expr); - resState = this.getNextStates(acts, s0, resState, nss); - resState.unbind(varName); - } - else if (val0.equals(val1)) { - resState = this.getNextStates(acts, s0, s1, nss); - } - else { - MP.printWarning(EC.TLC_UNCHANGED_VARIABLE_CHANGED, new String[]{varName.toString(), expr.toString()}); - } - return resState; - } - - if (expr instanceof OpApplNode) { - OpApplNode expr1 = (OpApplNode)expr; - ExprOrOpArgNode[] args = expr1.getArgs(); - int alen = args.length; - SymbolNode opNode = expr1.getOperator(); - UniqueString opName = opNode.getName(); - int opcode = BuiltInOPs.getOpCode(opName); - - if (opcode == OPCODE_tup) { - // a tuple: - if (alen != 0) { - ActionItemList acts1 = acts; - for (int i = alen-1; i > 0; i--) { - acts1 = acts1.cons(args[i], c, -2); - } - return this.processUnchanged(args[0], acts1, c, s0, s1, nss); - } - return this.getNextStates(acts, s0, s1, nss); - } - - if (opcode == 0 && alen == 0) { - // a 0-arity operator: - Object val = this.lookup(opNode, c, false); - - if (val instanceof OpDefNode) { - return this.processUnchanged(((OpDefNode)val).getBody(), acts, c, s0, s1, nss); - } - else if (val instanceof LazyValue) { - LazyValue lv = (LazyValue)val; - return this.processUnchanged(lv.expr, acts, lv.con, s0, s1, nss); - } - else { - Assert.fail("In computing next states, TLC found the identifier\n" + - opName + " undefined in an UNCHANGED expression at\n" + expr); - } - return this.getNextStates(acts, s0, s1, nss); - } - } - - Value v0 = this.eval(expr, c, s0); - Value v1 = this.eval(expr, c, s1, null, EvalControl.Clear); - if (v0.equals(v1)) { - resState = this.getNextStates(acts, s0, s1, nss); - } - return resState; - } - - /* Special version of eval for state expressions. */ - public final Value eval(SemanticNode expr, Context c, TLCState s0) { - return this.eval(expr, c, s0, TLCState.Empty, EvalControl.Clear); - } - - /* - * This method evaluates the expression expr in the given context, - * current state, and partial next state. - */ - public final Value eval(SemanticNode expr, Context c, TLCState s0, - TLCState s1, int control) { - switch (expr.getKind()) { - /*********************************************************************** - * LabelKind class added by LL on 13 Jun 2007. * - ***********************************************************************/ - case LabelKind: - { - LabelNode expr1 = (LabelNode) expr; - return this.eval( expr1.getBody(), c, s0, s1, control) ; - } - case OpApplKind: - { - OpApplNode expr1 = (OpApplNode)expr; - return this.evalAppl(expr1, c, s0, s1, control); - } - case LetInKind: - { - LetInNode expr1 = (LetInNode)expr; - OpDefNode[] letDefs = expr1.getLets(); - int letLen = letDefs.length; - Context c1 = c; - for (int i = 0; i < letLen; i++) { - OpDefNode opDef = letDefs[i]; - if (opDef.getArity() == 0) { - Value rhs = new LazyValue(opDef.getBody(), c1); - c1 = c1.cons(opDef, rhs); - } - } - return this.eval(expr1.getBody(), c1, s0, s1, control); - } - case SubstInKind: - { - SubstInNode expr1 = (SubstInNode)expr; - Subst[] subs = expr1.getSubsts(); - int slen = subs.length; - Context c1 = c; - for (int i = 0; i < slen; i++) { - Subst sub = subs[i]; - c1 = c1.cons(sub.getOp(), this.getVal(sub.getExpr(), c, true)); - } - return this.eval(expr1.getBody(), c1, s0, s1, control); - } - - // Added by LL on 13 Nov 2009 to handle theorem and assumption names. - case APSubstInKind: - { - APSubstInNode expr1 = (APSubstInNode)expr; - Subst[] subs = expr1.getSubsts(); - int slen = subs.length; - Context c1 = c; - for (int i = 0; i < slen; i++) { - Subst sub = subs[i]; - c1 = c1.cons(sub.getOp(), this.getVal(sub.getExpr(), c, true)); - } - return this.eval(expr1.getBody(), c1, s0, s1, control); - } - - - case NumeralKind: - case DecimalKind: - case StringKind: - { - return Value.getValue(expr); - } - case AtNodeKind: - { - return (Value)c.lookup(EXCEPT_AT); - } - case OpArgKind: - { - OpArgNode expr1 = (OpArgNode)expr; - SymbolNode opNode = expr1.getOp(); - Object val = this.lookup(opNode, c, false); - - if (val instanceof OpDefNode) { - return new OpLambdaValue((OpDefNode)val, this, c, s0, s1); - } - return (Value)val; - } - default: - { - Assert.fail("Attempted to evaluate an expression that cannot be evaluated.\n" + - expr); - return null; // make compiler happy - } - } - } - - public final Value evalAppl(OpApplNode expr, Context c, TLCState s0, - TLCState s1, int control) { - ExprOrOpArgNode[] args = expr.getArgs(); - SymbolNode opNode = expr.getOperator(); - int opcode = BuiltInOPs.getOpCode(opNode.getName()); - - if (opcode == 0) { - // This is a user-defined operator with one exception: it may - // be substed by a builtin operator. This special case occurs - // when the lookup returns an OpDef with opcode # 0. - if (this.callStack != null) this.callStack.push(expr); - Object val = this.lookup(opNode, c, s0, EvalControl.isPrimed(control)); - - // First, unlazy if it is a lazy value. We cannot use the cached - // value when s1 == null or isEnabled(control). - if (val instanceof LazyValue) { - LazyValue lv = (LazyValue)val; - if (s1 == null || - lv.val == ValUndef || - EvalControl.isEnabled(control)) { - val = this.eval(lv.expr, lv.con, s0, s1, control); - } - else { - if (lv.val == null) { - lv.val = this.eval(lv.expr, lv.con, s0, s1, control); - } - val = lv.val; - } - } - - Value res = null; - if (val instanceof OpDefNode) { - OpDefNode opDef = (OpDefNode)val; - opcode = BuiltInOPs.getOpCode(opDef.getName()); - if (opcode == 0) { - Context c1 = this.getOpContext(opDef, args, c, true); - res = this.eval(opDef.getBody(), c1, s0, s1, control); - } - } - else if (val instanceof Value) { - res = (Value)val; - int alen = args.length; - if (alen == 0) { - if (val instanceof MethodValue) { - res = ((MethodValue)val).apply(EmptyArgs, EvalControl.Clear); - } - } - else { - if (val instanceof OpValue) { - Applicable opVal = (Applicable)val; - Value[] argVals = new Value[alen]; - // evaluate the actuals: - for (int i = 0; i < alen; i++) { - argVals[i] = this.eval(args[i], c, s0, s1, control); - } - // apply the operator: - res = opVal.apply(argVals, control); - } - } - } - /********************************************************************* - * The following added by Yuan Yu on 13 Nov 2009 to allow theorem an * - * assumption names to be used as expressions. * - * * - * Modified on 23 October 2012 by LL to work if ThmOrAssumpDefNode * - * imported with parameterized instantiation. * - *********************************************************************/ - else if (val instanceof ThmOrAssumpDefNode) { -// Assert.fail("Trying to evaluate the theorem or assumption name `" -// + opNode.getName() + "'. \nUse `" + opNode.getName() -// + "!:' instead.\n" +expr); - ThmOrAssumpDefNode opDef = (ThmOrAssumpDefNode) val ; - Context c1 = this.getOpContext(opDef, args, c, true); - return this.eval(opDef.getBody(), c1, s0, s1, control); - } - else { - Assert.fail("In evaluation, the identifier " + opNode.getName() + " is either" + - " undefined or not an operator.\n" + expr); - } - if (this.callStack != null) this.callStack.pop(); - if (opcode == 0) return res; - } - - switch (opcode) { - case OPCODE_bc: // BoundedChoose - { - SemanticNode pred = args[0]; - SemanticNode inExpr = expr.getBdedQuantBounds()[0]; - Value inVal = this.eval(inExpr, c, s0, s1, control); - if (!(inVal instanceof Enumerable)) { - Assert.fail("Attempted to compute the value of an expression of\n" + - "form CHOOSE x \\in S: P, but S was not enumerable.\n" + expr); - } - - // To fix Bugzilla Bug 279 : TLC bug caused by TLC's not preserving the semantics of CHOOSE, - // the statement - // - // inVal.normalize(); - // - // was replaced by the following by LL on 7 Mar 2012. This fix has not yet received - // the blessing of Yuan Yu, so it should be considered to be provisional. - // - Value convertedVal = SetEnumValue.convert(inVal); - if (convertedVal != null) { - inVal = convertedVal; - } else { - inVal.normalize(); - } - // end of fix. - - ValueEnumeration enumSet = ((Enumerable)inVal).elements(); - FormalParamNode[] bvars = expr.getBdedQuantSymbolLists()[0]; - boolean isTuple = expr.isBdedQuantATuple()[0]; - if (isTuple) { - // Identifier tuple case: - int cnt = bvars.length; - Value val; - while ((val = enumSet.nextElement()) != null) { - TupleValue tv = TupleValue.convert(val); - if (tv == null || tv.size() != cnt) { - Assert.fail("Attempted to compute the value of an expression of form\n" + - "CHOOSE <<x1, ... , xN>> \\in S: P, but S was not a set\n" + - "of N-tuples.\n" + expr); - } - Context c1 = c; - for (int i = 0; i < cnt; i++) { - c1 = c1.cons(bvars[i], tv.elems[i]); - } - Value bval = this.eval(pred, c1, s0, s1, control); - if (!(bval instanceof BoolValue)) { - Assert.fail(EC.TLC_EXPECTED_VALUE, new String[]{"boolean", expr.toString()}); - } - if (((BoolValue)bval).val) return val; - } - } - else { - // Simple identifier case: - SymbolNode name = bvars[0]; - Value val; - while ((val = enumSet.nextElement()) != null) { - Context c1 = c.cons(name, val); - Value bval = this.eval(pred, c1, s0, s1, control); - if (!(bval instanceof BoolValue)) { - Assert.fail(EC.TLC_EXPECTED_VALUE, new String[]{"boolean", expr.toString()}); - } - if (((BoolValue)bval).val) return val; - } - } - Assert.fail("Attempted to compute the value of an expression of form\n" + - "CHOOSE x \\in S: P, but no element of S satisfied P.\n" + expr); - return null; // make compiler happy - } - case OPCODE_be: // BoundedExists - { - ContextEnumerator Enum = this.contexts(expr, c, s0, s1, control); - SemanticNode body = args[0]; - Context c1; - while ((c1 = Enum.nextElement()) != null) { - Value bval = this.eval(body, c1, s0, s1, control); - if (!(bval instanceof BoolValue)) { - Assert.fail(EC.TLC_EXPECTED_VALUE, new String[]{"boolean", expr.toString()}); - } - if (((BoolValue)bval).val) return ValTrue; - } - return ValFalse; - } - case OPCODE_bf: // BoundedForall - { - ContextEnumerator Enum = this.contexts(expr, c, s0, s1, control); - SemanticNode body = args[0]; - Context c1; - while ((c1 = Enum.nextElement()) != null) { - Value bval = this.eval(body, c1, s0, s1, control); - if (!(bval instanceof BoolValue)) { - Assert.fail(EC.TLC_EXPECTED_VALUE, new String[]{"boolean", expr.toString()}); - } - if (!((BoolValue)bval).val) return ValFalse; - } - return ValTrue; - } - case OPCODE_case: // Case - { - int alen = args.length; - SemanticNode other = null; - for (int i = 0; i < alen; i++) { - OpApplNode pairNode = (OpApplNode)args[i]; - ExprOrOpArgNode[] pairArgs = pairNode.getArgs(); - if (pairArgs[0] == null) { - other = pairArgs[1]; - } - else { - Value bval = this.eval(pairArgs[0], c, s0, s1, control); - if (!(bval instanceof BoolValue)) { - Assert.fail("A non-boolean expression (" + bval.getKindString() + - ") was used as a condition of a CASE. " + pairArgs[0]); - } - if (((BoolValue)bval).val) { - return this.eval(pairArgs[1], c, s0, s1, control); - } - } - } - if (other == null) { - Assert.fail("Attempted to evaluate a CASE with no conditions true.\n" + expr); - } - return this.eval(other, c, s0, s1, control); - } - case OPCODE_cp: // CartesianProd - { - int alen = args.length; - Value[] sets = new Value[alen]; - for (int i = 0; i < alen; i++) { - sets[i] = this.eval(args[i], c, s0, s1, control); - } - return new SetOfTuplesValue(sets); - } - case OPCODE_cl: // ConjList - { - int alen = args.length; - for (int i = 0; i < alen; i++) { - if (this.callStack != null) this.callStack.push(args[i]); - Value bval = this.eval(args[i], c, s0, s1, control); - if (!(bval instanceof BoolValue)) { - Assert.fail("A non-boolean expression (" + bval.getKindString() + - ") was used as a formula in a conjunction.\n" + args[i]); - } - if (this.callStack != null) this.callStack.pop(); - if (!((BoolValue)bval).val) return ValFalse; - } - return ValTrue; - } - case OPCODE_dl: // DisjList - { - int alen = args.length; - for (int i = 0; i < alen; i++) { - if (this.callStack != null) this.callStack.push(args[i]); - Value bval = this.eval(args[i], c, s0, s1, control); - if (!(bval instanceof BoolValue)) { - Assert.fail("A non-boolean expression (" + bval.getKindString() + - ") was used as a formula in a disjunction.\n" + args[i]); - } - if (this.callStack != null) this.callStack.pop(); - if (((BoolValue)bval).val) return ValTrue; - } - return ValFalse; - } - case OPCODE_exc: // Except - { - int alen = args.length; - Value result = this.eval(args[0], c, s0, s1, control); - // SZ: variable not used ValueExcept[] expts = new ValueExcept[alen-1]; - for (int i = 1; i < alen; i++) { - OpApplNode pairNode = (OpApplNode)args[i]; - ExprOrOpArgNode[] pairArgs = pairNode.getArgs(); - SemanticNode[] cmpts = ((OpApplNode)pairArgs[0]).getArgs(); - - Value[] lhs = new Value[cmpts.length]; - for (int j = 0; j < lhs.length; j++) { - lhs[j] = this.eval(cmpts[j], c, s0, s1, control); - } - Value atVal = result.select(lhs); - if (atVal == null) { - // Do nothing but warn: - MP.printWarning(EC.TLC_EXCEPT_APPLIED_TO_UNKNOWN_FIELD, new String[]{args[0].toString()}); - } - else { - Context c1 = c.cons(EXCEPT_AT, atVal); - Value rhs = this.eval(pairArgs[1], c1, s0, s1, control); - ValueExcept vex = new ValueExcept(lhs, rhs); - result = result.takeExcept(vex); - } - } - return result; - } - case OPCODE_fa: // FcnApply - { - Value result = null; - if (this.callStack != null) this.callStack.push(expr); - Value fval = this.eval(args[0], c, s0, s1, EvalControl.setKeepLazy(control)); - if ((fval instanceof FcnRcdValue) || - (fval instanceof FcnLambdaValue)) { - Applicable fcn = (Applicable)fval; - Value argVal = this.eval(args[1], c, s0, s1, control); - result = fcn.apply(argVal, control); - } - else if ((fval instanceof TupleValue) || - (fval instanceof RecordValue)) { - Applicable fcn = (Applicable)fval; - if (args.length != 2) { - Assert.fail("Attempted to evaluate an expression of form f[e1, ... , eN]" + - "\nwith f a tuple or record and N > 1.\n" + expr); - } - Value aval = this.eval(args[1], c, s0, s1, control); - result = fcn.apply(aval, control); - } - else { - Assert.fail("A non-function (" + fval.getKindString() + ") was applied" + - " as a function.\n" + expr); - } - if (this.callStack != null) this.callStack.pop(); - return result; - } - case OPCODE_fc: // FcnConstructor - case OPCODE_nrfs: // NonRecursiveFcnSpec - case OPCODE_rfs: // RecursiveFcnSpec - { - FormalParamNode[][] formals = expr.getBdedQuantSymbolLists(); - boolean[] isTuples = expr.isBdedQuantATuple(); - ExprNode[] domains = expr.getBdedQuantBounds(); - - Value[] dvals = new Value[domains.length]; - boolean isFcnRcd = true; - for (int i = 0; i < dvals.length; i++) { - dvals[i] = this.eval(domains[i], c, s0, s1, control); - isFcnRcd = isFcnRcd && (dvals[i] instanceof Reducible); - } - FcnParams params = new FcnParams(formals, isTuples, dvals); - - SemanticNode fbody = args[0]; - FcnLambdaValue fval = new FcnLambdaValue(params, fbody, this, c, s0, s1, control); - if (opcode == OPCODE_rfs) { - SymbolNode fname = expr.getUnbdedQuantSymbols()[0]; - fval.makeRecursive(fname); - isFcnRcd = false; - } - if (isFcnRcd && !EvalControl.isKeepLazy(control)) { - return fval.toFcnRcd(); - } - return fval; - } - case OPCODE_ite: // IfThenElse - { - Value bval = this.eval(args[0], c, s0, s1, control); - if (!(bval instanceof BoolValue)) { - Assert.fail("A non-boolean expression (" + bval.getKindString() + - ") was used as the condition of an IF.\n" + expr); - } - if (((BoolValue)bval).val) { - return this.eval(args[1], c, s0, s1, control); - } - return this.eval(args[2], c, s0, s1, control); - } - case OPCODE_rc: // RcdConstructor - { - int alen = args.length; - UniqueString[] names = new UniqueString[alen]; - Value[] vals = new Value[alen]; - for (int i = 0; i < alen; i++) { - OpApplNode pairNode = (OpApplNode)args[i]; - ExprOrOpArgNode[] pair = pairNode.getArgs(); - names[i] = ((StringValue)Value.getValue(pair[0])).getVal(); - vals[i] = this.eval(pair[1], c, s0, s1, control); - } - return new RecordValue(names, vals, false); - } - case OPCODE_rs: // RcdSelect - { - Value rval = this.eval(args[0], c, s0, s1, control); - Value sval = Value.getValue(args[1]); - if (rval instanceof RecordValue) { - Value result = ((RecordValue)rval).select(sval); - if (result == null) { - Assert.fail("Attempted to select nonexistent field " + sval + " from the" + - " record\n" + Value.ppr(rval.toString()) + "\n" + expr); - } - return result; - } - else { - FcnRcdValue fcn = FcnRcdValue.convert(rval); - if (fcn == null) { - Assert.fail("Attempted to select field " + sval + " from a non-record" + - " value " + Value.ppr(rval.toString()) + "\n" + expr); - } - return fcn.apply(sval, control); - } - } - case OPCODE_se: // SetEnumerate - { - int alen = args.length; - ValueVec vals = new ValueVec(alen); - for (int i = 0; i < alen; i++) { - vals.addElement(this.eval(args[i], c, s0, s1, control)); - } - return new SetEnumValue(vals, false); - } - case OPCODE_soa: // SetOfAll: {e(x) : x \in S} - { - ValueVec vals = new ValueVec(); - ContextEnumerator Enum = this.contexts(expr, c, s0, s1, control); - SemanticNode body = args[0]; - Context c1; - while ((c1 = Enum.nextElement()) != null) { - Value val = this.eval(body, c1, s0, s1, control); - vals.addElement(val); - // vals.addElement1(val); - } - return new SetEnumValue(vals, false); - } - case OPCODE_sor: // SetOfRcds - { - int alen = args.length; - UniqueString names[] = new UniqueString[alen]; - Value vals[] = new Value[alen]; - for (int i = 0; i < alen; i++) { - OpApplNode pairNode = (OpApplNode)args[i]; - ExprOrOpArgNode[] pair = pairNode.getArgs(); - names[i] = ((StringValue)Value.getValue(pair[0])).getVal(); - vals[i] = this.eval(pair[1], c, s0, s1, control); - } - return new SetOfRcdsValue(names, vals, false); - } - case OPCODE_sof: // SetOfFcns - { - Value lhs = this.eval(args[0], c, s0, s1, control); - Value rhs = this.eval(args[1], c, s0, s1, control); - return new SetOfFcnsValue(lhs, rhs); - } - case OPCODE_sso: // SubsetOf - { - SemanticNode pred = args[0]; - SemanticNode inExpr = expr.getBdedQuantBounds()[0]; - Value inVal = this.eval(inExpr, c, s0, s1, control); - boolean isTuple = expr.isBdedQuantATuple()[0]; - FormalParamNode[] bvars = expr.getBdedQuantSymbolLists()[0]; - if (inVal instanceof Reducible) { - ValueVec vals = new ValueVec(); - ValueEnumeration enumSet = ((Enumerable)inVal).elements(); - Value elem; - if (isTuple) { - while ((elem = enumSet.nextElement()) != null) { - Context c1 = c; - Value[] tuple = ((TupleValue)elem).elems; - for (int i = 0; i < bvars.length; i++) { - c1 = c1.cons(bvars[i], tuple[i]); - } - Value bval = this.eval(pred, c1, s0, s1, control); - if (!(bval instanceof BoolValue)) { - Assert.fail("Attempted to evaluate an expression of form {x \\in S : P(x)}" + - " when P was " + bval.getKindString() + ".\n" + pred); - } - if (((BoolValue)bval).val) { - vals.addElement(elem); - } - } - } - else { - SymbolNode idName = bvars[0]; - while ((elem = enumSet.nextElement()) != null) { - Context c1 = c.cons(idName, elem); - Value bval = this.eval(pred, c1, s0, s1, control); - if (!(bval instanceof BoolValue)) { - Assert.fail("Attempted to evaluate an expression of form {x \\in S : P(x)}" + - " when P was " + bval.getKindString() + ".\n" + pred); - } - if (((BoolValue)bval).val) { - vals.addElement(elem); - } - } - } - return new SetEnumValue(vals, inVal.isNormalized()); - } - else if (isTuple) { - return new SetPredValue(bvars, inVal, pred, this, c, s0, s1, control); - } - else { - return new SetPredValue(bvars[0], inVal, pred, this, c, s0, s1, control); - } - } - case OPCODE_tup: // Tuple - { - int alen = args.length; - Value[] vals = new Value[alen]; - for (int i = 0; i < alen; i++) { - vals[i] = this.eval(args[i], c, s0, s1, control); - } - return new TupleValue(vals); - } - case OPCODE_uc: // UnboundedChoose - { - Assert.fail("TLC attempted to evaluate an unbounded CHOOSE.\n" + - "Make sure that the expression is of form CHOOSE x \\in S: P(x).\n" + - expr); - return null; // make compiler happy - } - case OPCODE_ue: // UnboundedExists - { - Assert.fail("TLC attempted to evaluate an unbounded \\E.\n" + - "Make sure that the expression is of form \\E x \\in S: P(x).\n" + - expr); - return null; // make compiler happy - } - case OPCODE_uf: // UnboundedForall - { - Assert.fail("TLC attempted to evaluate an unbounded \\A.\n" + - "Make sure that the expression is of form \\A x \\in S: P(x).\n" + - expr); - return null; // make compiler happy - } - case OPCODE_lnot: - { - Value arg = this.eval(args[0], c, s0, s1, control); - if (!(arg instanceof BoolValue)) { - Assert.fail("Attempted to apply the operator ~ to a non-boolean\n(" + - arg.getKindString() + ")\n" + expr); - } - return (((BoolValue)arg).val) ? ValFalse : ValTrue; - } - case OPCODE_subset: - { - Value arg = this.eval(args[0], c, s0, s1, control); - return new SubsetValue(arg); - } - case OPCODE_union: - { - Value arg = this.eval(args[0], c, s0, s1, control); - return UnionValue.union(arg); - } - case OPCODE_domain: - { - Value arg = this.eval(args[0], c, s0, s1, control); - if (!(arg instanceof Applicable)) { - Assert.fail("Attempted to apply the operator DOMAIN to a non-function\n(" + - arg.getKindString() + ")\n" + expr); - } - return ((Applicable)arg).getDomain(); - } - case OPCODE_enabled: - { - TLCState sfun = TLCStateFun.Empty; - Context c1 = Context.branch(c); - sfun = this.enabled(args[0], ActionItemList.Empty, c1, s0, sfun); - return (sfun != null) ? ValTrue : ValFalse; - } - case OPCODE_eq: - { - Value arg1 = this.eval(args[0], c, s0, s1, control); - Value arg2 = this.eval(args[1], c, s0, s1, control); - return (arg1.equals(arg2)) ? ValTrue : ValFalse; - } - case OPCODE_land: - { - Value arg1 = this.eval(args[0], c, s0, s1, control); - if (!(arg1 instanceof BoolValue)) { - Assert.fail("Attempted to evaluate an expression of form P /\\ Q" + - " when P was\n" + arg1.getKindString() + ".\n" + expr); - } - if (((BoolValue)arg1).val) { - Value arg2 = this.eval(args[1], c, s0, s1, control); - if (!(arg2 instanceof BoolValue)) { - Assert.fail("Attempted to evaluate an expression of form P /\\ Q" + - " when Q was\n" + arg2.getKindString() + ".\n" + expr); - } - return arg2; - } - return ValFalse; - } - case OPCODE_lor: - { - Value arg1 = this.eval(args[0], c, s0, s1, control); - if (!(arg1 instanceof BoolValue)) { - Assert.fail("Attempted to evaluate an expression of form P \\/ Q" + - " when P was\n" + arg1.getKindString() + ".\n" + expr); - } - if (((BoolValue)arg1).val) return ValTrue; - Value arg2 = this.eval(args[1], c, s0, s1, control); - if (!(arg2 instanceof BoolValue)) { - Assert.fail("Attempted to evaluate an expression of form P \\/ Q" + - " when Q was\n" + arg2.getKindString() + ".\n" + expr); - } - return arg2; - } - case OPCODE_implies: - { - Value arg1 = this.eval(args[0], c, s0, s1, control); - if (!(arg1 instanceof BoolValue)) { - Assert.fail("Attempted to evaluate an expression of form P => Q" + - " when P was\n" + arg1.getKindString() + ".\n" + expr); - } - if (((BoolValue)arg1).val) { - Value arg2 = this.eval(args[1], c, s0, s1, control); - if (!(arg2 instanceof BoolValue)) { - Assert.fail("Attempted to evaluate an expression of form P => Q" + - " when Q was\n" + arg2.getKindString() + ".\n" + expr); - } - return arg2; - } - return ValTrue; - } - case OPCODE_equiv: - { - Value arg1 = this.eval(args[0], c, s0, s1, control); - Value arg2 = this.eval(args[1], c, s0, s1, control); - if (!(arg1 instanceof BoolValue) || !(arg2 instanceof BoolValue)) { - Assert.fail("Attempted to evaluate an expression of form P <=> Q" + - " when P or Q was not a boolean.\n" + expr); - } - BoolValue bval1 = (BoolValue)arg1; - BoolValue bval2 = (BoolValue)arg2; - return (bval1.val == bval2.val) ? ValTrue : ValFalse; - } - case OPCODE_noteq: - { - Value arg1 = this.eval(args[0], c, s0, s1, control); - Value arg2 = this.eval(args[1], c, s0, s1, control); - return arg1.equals(arg2) ? ValFalse : ValTrue; - } - case OPCODE_subseteq: - { - Value arg1 = this.eval(args[0], c, s0, s1, control); - Value arg2 = this.eval(args[1], c, s0, s1, control); - if (!(arg1 instanceof Enumerable)) { - Assert.fail("Attempted to evaluate an expression of form S \\subseteq T," + - " but S was not enumerable.\n" + expr); - } - ValueEnumeration Enum = ((Enumerable)arg1).elements(); - Value elem; - while ((elem = Enum.nextElement()) != null) { - if (!arg2.member(elem)) return ValFalse; - } - return ValTrue; - } - case OPCODE_in: - { - Value arg1 = this.eval(args[0], c, s0, s1, control); - Value arg2 = this.eval(args[1], c, s0, s1, control); - return (arg2.member(arg1)) ? ValTrue : ValFalse; - } - case OPCODE_notin: - { - Value arg1 = this.eval(args[0], c, s0, s1, control); - Value arg2 = this.eval(args[1], c, s0, s1, control); - return (arg2.member(arg1)) ? ValFalse : ValTrue; - } - case OPCODE_setdiff: - { - Value arg1 = this.eval(args[0], c, s0, s1, control); - Value arg2 = this.eval(args[1], c, s0, s1, control); - if (arg1 instanceof Reducible) { - return ((Reducible)arg1).diff(arg2); - } - return new SetDiffValue(arg1, arg2); - } - case OPCODE_cap: - { - Value arg1 = this.eval(args[0], c, s0, s1, control); - Value arg2 = this.eval(args[1], c, s0, s1, control); - if (arg1 instanceof Reducible) { - return ((Reducible)arg1).cap(arg2); - } - else if (arg2 instanceof Reducible) { - return ((Reducible)arg2).cap(arg1); - } - return new SetCapValue(arg1, arg2); - } - - case OPCODE_nop: - /********************************************************************* - * Added by LL on 2 Aug 2007. * - *********************************************************************/ - { return eval(args[0], c, s0, s1, control) ; - } - - case OPCODE_cup: - { - Value arg1 = this.eval(args[0], c, s0, s1, control); - Value arg2 = this.eval(args[1], c, s0, s1, control); - if (arg1 instanceof Reducible) { - return ((Reducible)arg1).cup(arg2); - } - else if (arg2 instanceof Reducible) { - return ((Reducible)arg2).cup(arg1); - } - return new SetCupValue(arg1, arg2); - } - case OPCODE_prime: - { - if (EvalControl.isEnabled(control)) { - // We are now in primed and enabled. - return this.eval(args[0], c, s1, null, EvalControl.setPrimed(control)); - } - return this.eval(args[0], c, s1, null, control); - } - case OPCODE_unchanged: - { - Value v0 = this.eval(args[0], c, s0, TLCState.Empty, control); - if (EvalControl.isEnabled(control)) { - // We are now in primed and enabled. - control = EvalControl.setPrimed(control); - } - Value v1 = this.eval(args[0], c, s1, null, control); - return (v0.equals(v1)) ? ValTrue : ValFalse; - } - case OPCODE_aa: // <A>_e - { - Value res = this.eval(args[0], c, s0, s1, control); - if (!(res instanceof BoolValue)) { - Assert.fail("Attempted to evaluate an expression of form <A>_e," + - " but A was not a boolean.\n" + expr); - } - if (!((BoolValue)res).val) return ValFalse; - Value v0 = this.eval(args[1], c, s0, TLCState.Empty, control); - if (EvalControl.isEnabled(control)) { - // We are now in primed and enabled. - control = EvalControl.setPrimed(control); - } - Value v1 = this.eval(args[1], c, s1, null, control); - return v0.equals(v1) ? ValFalse : ValTrue; - } - case OPCODE_sa: // [A]_e - { - Value res = this.eval(args[0], c, s0, s1, control); - if (!(res instanceof BoolValue)) { - Assert.fail("Attempted to evaluate an expression of form [A]_e," + - " but A was not a boolean.\n" + expr); - } - if (((BoolValue)res).val) return ValTrue; - Value v0 = this.eval(args[1], c, s0, TLCState.Empty, control); - if (EvalControl.isEnabled(control)) { - // We are now in primed and enabled. - control = EvalControl.setPrimed(control); - } - Value v1 = this.eval(args[1], c, s1, null, control); - return (v0.equals(v1)) ? ValTrue : ValFalse; - } - case OPCODE_cdot: - { - Assert.fail("The current version of TLC does not support action composition."); - /*** - TLCState s01 = TLCStateFun.Empty; - StateVec iss = new StateVec(0); - this.getNextStates(args[0], ActionItemList.Empty, c, s0, s01, iss); - int sz = iss.size(); - for (int i = 0; i < sz; i++) { - s01 = iss.elementAt(i); - this.eval(args[1], c, s01, s1, control); - } - ***/ - return null; // make compiler happy - } - case OPCODE_sf: // SF - { - Assert.fail(EC.TLC_ENCOUNTERED_FORMULA_IN_PREDICATE, new String[]{"SF", expr.toString()}); - return null; // make compiler happy - } - case OPCODE_wf: // WF - { - Assert.fail(EC.TLC_ENCOUNTERED_FORMULA_IN_PREDICATE, new String[]{"WF", expr.toString()}); - return null; // make compiler happy - } - case OPCODE_te: // TemporalExists - { - Assert.fail(EC.TLC_ENCOUNTERED_FORMULA_IN_PREDICATE, new String[]{"\\EE", expr.toString()}); - return null; // make compiler happy - } - case OPCODE_tf: // TemporalForAll - { - Assert.fail(EC.TLC_ENCOUNTERED_FORMULA_IN_PREDICATE, new String[]{"\\AA", expr.toString()}); - return null; // make compiler happy - } - case OPCODE_leadto: - { - Assert.fail(EC.TLC_ENCOUNTERED_FORMULA_IN_PREDICATE, new String[]{"a ~> b", expr.toString()}); - return null; // make compiler happy - } - case OPCODE_arrow: - { - Assert.fail(EC.TLC_ENCOUNTERED_FORMULA_IN_PREDICATE, new String[]{"a -+-> formula", expr.toString()}); - return null; // make compiler happy - } - case OPCODE_box: - { - Assert.fail(EC.TLC_ENCOUNTERED_FORMULA_IN_PREDICATE, new String[]{"[]A", expr.toString()}); - return null; // make compiler happy - } - case OPCODE_diamond: - { - Assert.fail(EC.TLC_ENCOUNTERED_FORMULA_IN_PREDICATE, new String[]{"<>A", expr.toString()}); - return null; // make compiler happy - } - - default: - { - Assert.fail("TLC BUG: could not evaluate this expression.\n" + expr); - return null; - } - } - } - - /** - * This method determines if the argument is a valid state. A state - * is good iff it assigns legal explicit values to all the global - * state variables. - */ - public final boolean isGoodState(TLCState state) { - return state.allAssigned(); - } - - /* This method determines if a state satisfies the model constraints. */ - public final boolean isInModel(TLCState state) throws EvalException { - ExprNode[] constrs = this.getModelConstraints(); - for (int i = 0; i < constrs.length; i++) { - Value bval = this.eval(constrs[i], Context.Empty, state); - if (!(bval instanceof BoolValue)) { - Assert.fail(EC.TLC_EXPECTED_VALUE, new String[]{"boolean", constrs[i].toString()}); - } - if (!((BoolValue)bval).val) return false; - } - return true; - } - - /* This method determines if a pair of states satisfy the action constraints. */ - public final boolean isInActions(TLCState s1, TLCState s2) throws EvalException { - ExprNode[] constrs = this.getActionConstraints(); - for (int i = 0; i < constrs.length; i++) { - Value bval = this.eval(constrs[i], Context.Empty, s1, s2, EvalControl.Clear); - if (!(bval instanceof BoolValue)) { - Assert.fail(EC.TLC_EXPECTED_VALUE, new String[]{"boolean", constrs[i].toString()}); - } - if (!((BoolValue)bval).val) return false; - } - return true; - } - - /** - * This method determines if an action is enabled in the given state. - * More precisely, it determines if (act.pred /\ (sub' # sub)) is - * enabled in the state s and context act.con. - */ - public final TLCState enabled(SemanticNode pred, ActionItemList acts, - Context c, TLCState s0, TLCState s1) { - switch (pred.getKind()) { - case OpApplKind: - { - OpApplNode pred1 = (OpApplNode)pred; - return this.enabledAppl(pred1, acts, c, s0, s1); - } - case LetInKind: - { - LetInNode pred1 = (LetInNode)pred; - OpDefNode[] letDefs = pred1.getLets(); - Context c1 = c; - for (int i = 0; i < letDefs.length; i++) { - OpDefNode opDef = letDefs[i]; - if (opDef.getArity() == 0) { - Value rhs = new LazyValue(opDef.getBody(), c1); - c1 = c1.cons(opDef, rhs); - } - } - return this.enabled(pred1.getBody(), acts, c1, s0, s1); - } - case SubstInKind: - { - SubstInNode pred1 = (SubstInNode)pred; - Subst[] subs = pred1.getSubsts(); - int slen = subs.length; - Context c1 = c; - for (int i = 0; i < slen; i++) { - Subst sub = subs[i]; - c1 = c1.cons(sub.getOp(), this.getVal(sub.getExpr(), c, false)); - } - return this.enabled(pred1.getBody(), acts, c1, s0, s1); - } - - // Added by LL on 13 Nov 2009 to handle theorem and assumption names. - case APSubstInKind: - { - APSubstInNode pred1 = (APSubstInNode)pred; - Subst[] subs = pred1.getSubsts(); - int slen = subs.length; - Context c1 = c; - for (int i = 0; i < slen; i++) { - Subst sub = subs[i]; - c1 = c1.cons(sub.getOp(), this.getVal(sub.getExpr(), c, false)); - } - return this.enabled(pred1.getBody(), acts, c1, s0, s1); - } - - - /*********************************************************************** - * LabelKind class added by LL on 13 Jun 2007. * - ***********************************************************************/ - case LabelKind: - { - LabelNode pred1 = (LabelNode)pred; - return this.enabled(pred1.getBody(), acts, c, s0, s1); - } - default: - { - // We should not compute enabled on anything else. - Assert.fail("Attempted to compute ENABLED on a non-boolean expression.\n" + pred); - return null; // make compiler happy - } - } - } - - private final TLCState enabled(ActionItemList acts, TLCState s0, TLCState s1) { - if (acts.isEmpty()) return s1; - - int kind = acts.carKind(); - SemanticNode pred = acts.carPred(); - Context c = acts.carContext(); - ActionItemList acts1 = acts.cdr(); - if (kind > 0) { - if (this.callStack != null) this.callStack.push(acts.carPred()); - TLCState res = this.enabled(pred, acts1, c, s0, s1); - if (this.callStack != null) this.callStack.pop(); - return res; - } - else if (kind == -1) { - return this.enabled(pred, acts1, c, s0, s1); - } - if (kind == -2) { - return this.enabledUnchanged(pred, acts1, c, s0, s1); - } - - Value v1 = this.eval(pred, c, s0, TLCState.Empty, EvalControl.Enabled); - // We are now in ENABLED and primed state. - Value v2 = this.eval(pred, c, s1, null, EvalControl.Primed); - - if (v1.equals(v2)) return null; - return this.enabled(acts1, s0, s1); - } - - private final TLCState enabledAppl(OpApplNode pred, ActionItemList acts, Context c, TLCState s0, TLCState s1) - { - ExprOrOpArgNode[] args = pred.getArgs(); - int alen = args.length; - SymbolNode opNode = pred.getOperator(); - int opcode = BuiltInOPs.getOpCode(opNode.getName()); - - if (opcode == 0) - { - // This is a user-defined operator with one exception: it may - // be substed by a builtin operator. This special case occurs - // when the lookup returns an OpDef with opcode # 0. - Object val = this.lookup(opNode, c, s0, false); - - if (val instanceof OpDefNode) - { - OpDefNode opDef = (OpDefNode) val; - opcode = BuiltInOPs.getOpCode(opDef.getName()); - if (opcode == 0) - { - // Context c1 = this.getOpContext(opDef, args, c, false); - Context c1 = this.getOpContext(opDef, args, c, true); - return this.enabled(opDef.getBody(), acts, c1, s0, s1); - } - } - - - // Added 13 Nov 2009 by LL to handle theorem or assumption names - /********************************************************************* - * Modified on 23 October 2012 by LL to work if ThmOrAssumpDefNode * - * imported with parameterized instantiation. * - *********************************************************************/ - if (val instanceof ThmOrAssumpDefNode) - { - ThmOrAssumpDefNode opDef = (ThmOrAssumpDefNode) val; - Context c1 = this.getOpContext(opDef, args, c, true); - return this.enabled(opDef.getBody(), acts, c1, s0, s1); - } - - - if (val instanceof LazyValue) - { - LazyValue lv = (LazyValue) val; - return this.enabled(lv.expr, acts, lv.con, s0, s1); - } - - Object bval = val; - if (alen == 0) - { - if (val instanceof MethodValue) - { - bval = ((MethodValue) val).apply(EmptyArgs, EvalControl.Clear); - } - } else - { - if (val instanceof OpValue) - { - Applicable op = (Applicable) val; - Value[] argVals = new Value[alen]; - // evaluate the actuals: - for (int i = 0; i < alen; i++) - { - argVals[i] = this.eval(args[i], c, s0, s1, EvalControl.Enabled); - } - // apply the operator: - bval = op.apply(argVals, EvalControl.Enabled); - } - } - - if (opcode == 0) - { - if (!(bval instanceof BoolValue)) - { - Assert.fail(EC.TLC_EXPECTED_EXPRESSION_IN_COMPUTING, new String[] { "ENABLED", "boolean", - bval.toString(), pred.toString() }); - } - if (((BoolValue) bval).val) - { - return this.enabled(acts, s0, s1); - } - return null; - } - } - - switch (opcode) { - case OPCODE_aa: // AngleAct <A>_e - { - ActionItemList acts1 = acts.cons(args[1], c, -3); - return this.enabled(args[0], acts1, c, s0, s1); - } - case OPCODE_be: // BoundedExists - { - SemanticNode body = args[0]; - ContextEnumerator Enum = this.contexts(pred, c, s0, s1, EvalControl.Enabled); - Context c1; - while ((c1 = Enum.nextElement()) != null) - { - TLCState s2 = this.enabled(body, acts, c1, s0, s1); - if (s2 != null) - return s2; - } - return null; - } - case OPCODE_bf: // BoundedForall - { - SemanticNode body = args[0]; - ContextEnumerator Enum = this.contexts(pred, c, s0, s1, EvalControl.Enabled); - Context c1 = Enum.nextElement(); - if (c1 == null) - { - return this.enabled(acts, s0, s1); - } - ActionItemList acts1 = acts; - Context c2; - while ((c2 = Enum.nextElement()) != null) - { - acts1 = acts1.cons(body, c2, -1); - } - return this.enabled(body, acts1, c1, s0, s1); - } - case OPCODE_case: // Case - { - SemanticNode other = null; - for (int i = 0; i < alen; i++) - { - OpApplNode pair = (OpApplNode) args[i]; - ExprOrOpArgNode[] pairArgs = pair.getArgs(); - if (pairArgs[0] == null) - { - other = pairArgs[1]; - } else - { - Value bval = this.eval(pairArgs[0], c, s0, s1, EvalControl.Enabled); - if (!(bval instanceof BoolValue)) - { - Assert.fail("In computing ENABLED, a non-boolean expression(" + bval.getKindString() - + ") was used as a guard condition" + " of a CASE.\n" + pairArgs[1]); - } - if (((BoolValue) bval).val) - { - return this.enabled(pairArgs[1], acts, c, s0, s1); - } - } - } - if (other == null) - { - Assert.fail("In computing ENABLED, TLC encountered a CASE with no" + " conditions true.\n" + pred); - } - return this.enabled(other, acts, c, s0, s1); - } - case OPCODE_cl: // ConjList - case OPCODE_land: { - ActionItemList acts1 = acts; - for (int i = alen - 1; i > 0; i--) - { - acts1 = acts1.cons(args[i], c, i); - } - if (this.callStack != null) - this.callStack.push(args[0]); - TLCState res = this.enabled(args[0], acts1, c, s0, s1); - if (this.callStack != null) - this.callStack.pop(); - return res; - } - case OPCODE_dl: // DisjList - case OPCODE_lor: { - for (int i = 0; i < alen; i++) - { - if (this.callStack != null) - this.callStack.push(args[i]); - TLCState s2 = this.enabled(args[i], acts, c, s0, s1); - if (this.callStack != null) - this.callStack.pop(); - if (s2 != null) - return s2; - } - return null; - } - case OPCODE_fa: // FcnApply - { - Value fval = this.eval(args[0], c, s0, s1, EvalControl.setKeepLazy(EvalControl.Enabled)); - if (fval instanceof FcnLambdaValue) - { - FcnLambdaValue fcn = (FcnLambdaValue) fval; - if (fcn.fcnRcd == null) - { - Context c1 = this.getFcnContext(fcn, args, c, s0, s1, EvalControl.Enabled); - return this.enabled(fcn.body, acts, c1, s0, s1); - } - fval = fcn.fcnRcd; - } - if (fval instanceof Applicable) - { - Applicable fcn = (Applicable) fval; - Value argVal = this.eval(args[1], c, s0, s1, EvalControl.Enabled); - Value bval = fcn.apply(argVal, EvalControl.Enabled); - if (!(bval instanceof BoolValue)) - { - Assert.fail(EC.TLC_EXPECTED_EXPRESSION_IN_COMPUTING2, new String[] { "ENABLED", "boolean", - pred.toString() }); - - } - if (!((BoolValue) bval).val) - return null; - } else - { - Assert.fail("In computing ENABLED, a non-function (" + fval.getKindString() - + ") was applied as a function.\n" + pred); - } - return this.enabled(acts, s0, s1); - } - case OPCODE_ite: // IfThenElse - { - Value guard = this.eval(args[0], c, s0, s1, EvalControl.Enabled); - if (!(guard instanceof BoolValue)) - { - Assert.fail("In computing ENABLED, a non-boolean expression(" + guard.getKindString() - + ") was used as the guard condition" + " of an IF.\n" + pred); - } - int idx = (((BoolValue) guard).val) ? 1 : 2; - return this.enabled(args[idx], acts, c, s0, s1); - } - case OPCODE_sa: // SquareAct [A]_e - { - TLCState s2 = this.enabled(args[0], acts, c, s0, s1); - if (s2 != null) - return s2; - return this.enabledUnchanged(args[1], acts, c, s0, s1); - } - case OPCODE_te: // TemporalExists - case OPCODE_tf: // TemporalForAll - { - Assert.fail("In computing ENABLED, TLC encountered temporal quantifier.\n" + pred); - return null; // make compiler happy - } - case OPCODE_uc: // UnboundedChoose - { - Assert.fail("In computing ENABLED, TLC encountered unbounded CHOOSE. " - + "Make sure that the expression is of form CHOOSE x \\in S: P(x).\n" + pred); - return null; // make compiler happy - } - case OPCODE_ue: // UnboundedExists - { - Assert.fail("In computing ENABLED, TLC encountered unbounded quantifier. " - + "Make sure that the expression is of form \\E x \\in S: P(x).\n" + pred); - return null; // make compiler happy - } - case OPCODE_uf: // UnboundedForall - { - Assert.fail("In computing ENABLED, TLC encountered unbounded quantifier. " - + "Make sure that the expression is of form \\A x \\in S: P(x).\n" + pred); - return null; // make compiler happy - } - case OPCODE_sf: // SF - { - Assert.fail(EC.TLC_ENABLED_WRONG_FORMULA, new String[]{ "SF", pred.toString()}); - return null; // make compiler happy - } - case OPCODE_wf: // WF - { - Assert.fail(EC.TLC_ENABLED_WRONG_FORMULA, new String[] { "WF", pred.toString() }); - return null; // make compiler happy - } - case OPCODE_box: { - Assert.fail(EC.TLC_ENABLED_WRONG_FORMULA, new String[] { "[]", pred.toString() }); - return null; // make compiler happy - } - case OPCODE_diamond: { - Assert.fail(EC.TLC_ENABLED_WRONG_FORMULA, new String[] { "<>", pred.toString() }); - return null; // make compiler happy - } - case OPCODE_unchanged: { - return this.enabledUnchanged(args[0], acts, c, s0, s1); - } - case OPCODE_eq: { - SymbolNode var = this.getPrimedVar(args[0], c, true); - if (var == null) - { - Value bval = this.eval(pred, c, s0, s1, EvalControl.Enabled); - if (!((BoolValue) bval).val) - return null; - } else - { - UniqueString varName = var.getName(); - Value lval = s1.lookup(varName); - Value rval = this.eval(args[1], c, s0, s1, EvalControl.Enabled); - if (lval == null) - { - TLCState s2 = s1.bind(var, rval, pred); - return this.enabled(acts, s0, s2); - } else - { - if (!lval.equals(rval)) - return null; - } - } - return this.enabled(acts, s0, s1); - } - case OPCODE_implies: { - Value bval = this.eval(args[0], c, s0, s1, EvalControl.Enabled); - if (!(bval instanceof BoolValue)) - { - Assert.fail("While computing ENABLED of an expression of the form" + " P => Q, P was " - + bval.getKindString() + ".\n" + pred); - } - if (((BoolValue) bval).val) - { - return this.enabled(args[1], acts, c, s0, s1); - } - return this.enabled(acts, s0, s1); - } - case OPCODE_cdot: { - Assert.fail("The current version of TLC does not support action composition."); - /*** - TLCState s01 = TLCStateFun.Empty; - StateVec iss = new StateVec(0); - this.getNextStates(args[0], ActionItemList.Empty, c, s0, s01, iss); - int sz = iss.size(); - for (int i = 0; i < sz; i++) { - s01 = iss.elementAt(i); - TLCState s2 = this.enabled(args[1], acts, c, s01, s1); - if (s2 != null) return s2; - } - ***/ - return null; // make compiler happy - } - case OPCODE_leadto: { - Assert.fail("In computing ENABLED, TLC encountered a temporal formula" + " (a ~> b).\n" + pred); - return null; // make compiler happy - } - case OPCODE_arrow: { - Assert.fail("In computing ENABLED, TLC encountered a temporal formula" + " (a -+-> formula).\n" + pred); - return null; // make compiler happy - } - case OPCODE_in: { - SymbolNode var = this.getPrimedVar(args[0], c, true); - if (var == null) - { - Value bval = this.eval(pred, c, s0, s1, EvalControl.Enabled); - if (!((BoolValue) bval).val) - return null; - } else - { - UniqueString varName = var.getName(); - Value lval = s1.lookup(varName); - Value rval = this.eval(args[1], c, s0, s1, EvalControl.Enabled); - if (lval == null) - { - if (!(rval instanceof Enumerable)) - { - Assert.fail("The right side of \\IN is not enumerable.\n" + pred); - } - ValueEnumeration Enum = ((Enumerable) rval).elements(); - Value val; - while ((val = Enum.nextElement()) != null) - { - TLCState s2 = s1.bind(var, val, pred); - s2 = this.enabled(acts, s0, s2); - if (s2 != null) - return s2; - } - return null; - } else - { - if (!rval.member(lval)) - return null; - } - } - return this.enabled(acts, s0, s1); - } - // The following case added by LL on 13 Nov 2009 to handle subexpression names. - case OPCODE_nop: - { - return this.enabled(args[0], acts, c, s0, s1); - } - - default: { - // We handle all the other builtin operators here. - Value bval = this.eval(pred, c, s0, s1, EvalControl.Enabled); - if (!(bval instanceof BoolValue)) - { - Assert.fail(EC.TLC_EXPECTED_EXPRESSION_IN_COMPUTING, new String[] { "ENABLED", "boolean", - bval.toString(), pred.toString() }); - } - if (((BoolValue) bval).val) - { - return this.enabled(acts, s0, s1); - } - return null; - } - } - } - - private final TLCState enabledUnchanged(SemanticNode expr, ActionItemList acts, - Context c, TLCState s0, TLCState s1) { - SymbolNode var = this.getVar(expr, c, true); - if (var != null) { - // a state variable: - UniqueString varName = var.getName(); - Value v0 = this.eval(expr, c, s0, s1, EvalControl.Enabled); - Value v1 = s1.lookup(varName); - if (v1 == null) { - s1 = s1.bind(var, v0, expr); - return this.enabled(acts, s0, s1); - } - if (v1.equals(v0)) { - return this.enabled(acts, s0, s1); - } - MP.printWarning(EC.TLC_UNCHANGED_VARIABLE_CHANGED, new String[]{varName.toString() , expr.toString()}); - return null; - } - - if (expr instanceof OpApplNode) { - OpApplNode expr1 = (OpApplNode)expr; - ExprOrOpArgNode[] args = expr1.getArgs(); - int alen = args.length; - SymbolNode opNode = expr1.getOperator(); - UniqueString opName = opNode.getName(); - int opcode = BuiltInOPs.getOpCode(opName); - - if (opcode == OPCODE_tup) { - // a tuple: - if (alen != 0) { - ActionItemList acts1 = acts; - for (int i = 1; i < alen; i++) { - acts1 = acts1.cons(args[i], c, -2); - } - return this.enabledUnchanged(args[0], acts1, c, s0, s1); - } - return this.enabled(acts, s0, s1); - } - - if (opcode == 0 && alen == 0) { - // a 0-arity operator: - Object val = this.lookup(opNode, c, false); - - if (val instanceof LazyValue) { - LazyValue lv = (LazyValue)val; - return this.enabledUnchanged(lv.expr, acts, lv.con, s0, s1); - } - else if (val instanceof OpDefNode) { - return this.enabledUnchanged(((OpDefNode)val).getBody(), acts, c, s0, s1); - } - else if (val == null) { - Assert.fail("In computing ENABLED, TLC found the undefined identifier\n" + - opName + " in an UNCHANGED expression at\n" + expr); - } - return this.enabled(acts, s0, s1); - } - } - - Value v0 = this.eval(expr, c, s0, TLCState.Empty, EvalControl.Enabled); - Value v1 = this.eval(expr, c, s1, TLCState.Empty, EvalControl.Primed); - if (!v0.equals(v1)) return null; - return this.enabled(acts, s0, s1); - } - - /* This method determines if the action predicate is valid in (s0, s1). */ - public final boolean isValid(Action act, TLCState s0, TLCState s1) { - Value val = this.eval(act.pred, act.con, s0, s1, EvalControl.Clear); - if (!(val instanceof BoolValue)) { - Assert.fail(EC.TLC_EXPECTED_VALUE, new String[]{"boolean", act.pred.toString()}); - } - return ((BoolValue)val).val; - } - - /* Returns true iff the predicate is valid in the state. */ - public final boolean isValid(Action act, TLCState state) { - return this.isValid(act, state, TLCState.Empty); - } - - /* Returns true iff the predicate is valid in the state. */ - public final boolean isValid(Action act) { - return this.isValid(act, TLCState.Empty, TLCState.Empty); - } - - public final boolean isValid(ExprNode expr) { - Value val = this.eval(expr, Context.Empty, TLCState.Empty); - if (!(val instanceof BoolValue)) { - Assert.fail(EC.TLC_EXPECTED_VALUE, new String[]{"boolean", expr.toString()}); - } - return ((BoolValue)val).val; - } - - /* Reconstruct the initial state whose fingerprint is fp. */ - public final TLCStateInfo getState(long fp) { - StateVec initStates = this.getInitStates(); - for (int i = 0; i < initStates.size(); i++) { - TLCState state = initStates.elementAt(i); - long nfp = state.fingerPrint(); - if (fp == nfp) { - String info = "<Initial predicate>"; - return new TLCStateInfo(state, info); - } - } - return null; - } - - /* Reconstruct the next state of state s whose fingerprint is fp. */ - public final TLCStateInfo getState(long fp, TLCState s) { - for (int i = 0; i < this.actions.length; i++) { - Action curAction = this.actions[i]; - StateVec nextStates = this.getNextStates(curAction, s); - for (int j = 0; j < nextStates.size(); j++) { - TLCState state = nextStates.elementAt(j); - long nfp = state.fingerPrint(); - if (fp == nfp) { - return new TLCStateInfo(state, curAction.getLocation()); - } - } - } - return null; - } - - /* Reconstruct the info for s1. */ - public final TLCStateInfo getState(TLCState s1, TLCState s) { - for (int i = 0; i < this.actions.length; i++) { - Action curAction = this.actions[i]; - StateVec nextStates = this.getNextStates(curAction, s); - for (int j = 0; j < nextStates.size(); j++) { - TLCState state = nextStates.elementAt(j); - if (s1.equals(state)) { - return new TLCStateInfo(state, curAction.getLocation()); - } - } - } - return null; - } - - /* Return the set of all permutations under the symmetry assumption. */ - public final MVPerm[] getSymmetryPerms() { - String name = this.config.getSymmetry(); - if (name.length() == 0) return null; - Object symm = this.defns.get(name); - if (symm == null) { - Assert.fail(EC.TLC_CONFIG_SPECIFIED_NOT_DEFINED, new String[] { "symmetry function", name}); - } - if (!(symm instanceof OpDefNode)) { - Assert.fail("The symmetry function " + name + " must specify a set of permutations."); - } - Value fcns = this.eval(((OpDefNode)symm).getBody(), Context.Empty, TLCState.Empty); - if (!(fcns instanceof Enumerable)) { - Assert.fail("The symmetry operator must specify a set of functions."); - } - ValueEnumeration Enum = ((Enumerable)fcns).elements(); - return MVPerm.permutationSubgroup(Enum); - } - - public final Context getFcnContext(FcnLambdaValue fcn, ExprOrOpArgNode[] args, - Context c, TLCState s0, TLCState s1, - int control) { - Context fcon = fcn.con; - int plen = fcn.params.length(); - FormalParamNode[][] formals = fcn.params.formals; - Value[] domains = fcn.params.domains; - boolean[] isTuples = fcn.params.isTuples; - Value argVal = this.eval(args[1], c, s0, s1, control); - - if (plen == 1) { - if (!domains[0].member(argVal)) { - Assert.fail("In applying the function\n" + Value.ppr(fcn.toString()) + - ",\nthe first argument is:\n" + Value.ppr(argVal.toString()) + - "which is not in its domain.\n" + args[0]); - } - if (isTuples[0]) { - FormalParamNode[] ids = formals[0]; - TupleValue tv = TupleValue.convert(argVal); - if (tv == null || argVal.size() != ids.length) { - Assert.fail("In applying the function\n" + Value.ppr(this.toString()) + - ",\nthe argument is:\n" + Value.ppr(argVal.toString()) + - "which does not match its formal parameter.\n" + args[0]); - } - Value[] elems = tv.elems; - for (int i = 0; i < ids.length; i++) { - fcon = fcon.cons(ids[i], elems[i]); - } - } - else { - fcon = fcon.cons(formals[0][0], argVal); - } - } - else { - TupleValue tv = TupleValue.convert(argVal); - if (tv == null) { - Assert.fail("Attempted to apply a function to an argument not in its" + - " domain.\n" + args[0]); - } - int argn = 0; - Value[] elems = tv.elems; - for (int i = 0; i < formals.length; i++) { - FormalParamNode[] ids = formals[i]; - Value domain = domains[i]; - if (isTuples[i]) { - if (!domain.member(elems[argn])) { - Assert.fail("In applying the function\n" + Value.ppr(fcn.toString()) + - ",\nthe argument number " + (argn+1) + " is:\n" + - Value.ppr(elems[argn].toString()) + - "\nwhich is not in its domain.\n" + args[0]); - } - TupleValue tv1 = TupleValue.convert(elems[argn++]); - if (tv1 == null || tv1.size() != ids.length) { - Assert.fail("In applying the function\n" + Value.ppr(fcn.toString()) + - ",\nthe argument number " + argn + " is:\n" + - Value.ppr(elems[argn-1].toString()) + - "which does not match its formal parameter.\n" + args[0]); - } - Value[] avals = tv1.elems; - for (int j = 0; j < ids.length; j++) { - fcon = fcon.cons(ids[j], avals[j]); - } - } - else { - for (int j = 0; j < ids.length; j++) { - if (!domain.member(elems[argn])) { - Assert.fail("In applying the function\n" + Value.ppr(fcn.toString()) + - ",\nthe argument number " + (argn+1) + " is:\n" + - Value.ppr(elems[argn].toString()) + - "which is not in its domain.\n" + args[0]); - } - fcon = fcon.cons(ids[j], elems[argn++]); - } - } - } - } - return fcon; - } - - /* A context enumerator for an operator application. */ - public final ContextEnumerator contexts(OpApplNode appl, Context c, TLCState s0, - TLCState s1, int control) { - FormalParamNode[][] formals = appl.getBdedQuantSymbolLists(); - boolean[] isTuples = appl.isBdedQuantATuple(); - ExprNode[] domains = appl.getBdedQuantBounds(); - - int flen = formals.length; - int alen = 0; - for (int i = 0; i < flen; i++) { - alen += (isTuples[i]) ? 1 : formals[i].length; - } - Object[] vars = new Object[alen]; - ValueEnumeration[] enums = new ValueEnumeration[alen]; - int idx = 0; - for (int i = 0; i < flen; i++) { - Value boundSet = this.eval(domains[i], c, s0, s1, control); - if (!(boundSet instanceof Enumerable)) { - Assert.fail("TLC encountered a non-enumerable quantifier bound\n" + - Value.ppr(boundSet.toString()) + ".\n" + domains[i]); - } - FormalParamNode[] farg = formals[i]; - if (isTuples[i]) { - vars[idx] = farg; - enums[idx++] = ((Enumerable)boundSet).elements(); - } - else { - for (int j = 0; j < farg.length; j++) { - vars[idx] = farg[j]; - enums[idx++] = ((Enumerable)boundSet).elements(); - } - } - } - return new ContextEnumerator(vars, enums, c); - } - - /** - * This method converts every definition that is constant into TLC - * value. By doing this, TLC avoids evaluating the same expression - * multiple times. - * - * The method runs for every module in the module tables. - * - * Modified by LL on 23 July 2013 so it is not run for modules that are - * instantiated and have parameters (CONSTANT or VARIABLE declarations) - */ - private void processConstantDefns() { - ModuleNode[] mods = this.moduleTbl.getModuleNodes(); - for (int i = 0; i < mods.length; i++) { - if ( (! mods[i].isInstantiated()) - || ( (mods[i].getConstantDecls().length == 0) - && (mods[i].getVariableDecls().length == 0) ) ) { - this.processConstantDefns(mods[i]); - } - } - } - - /** - * Converts the constant definitions in the corresponding value for the - * module -- that is, it "converts" (which seems to mean calling deepNormalize) - * the values substituted for the declared constants. On 17 Mar 2012 it was - * modified by LL to evaluate the OpDefNode when a defined operator is substituted - * for an ordinary declared constant (not a declared operator constant). Without this - * evaluation, the definition gets re-evaluated every time TLC evaluates the declared - * constant. LL also added a check that an operator substituted for the declared - * constant also has the correct arity. - * - * @param mod the module to run on - */ - private void processConstantDefns(ModuleNode mod) { - - // run for constant definitions - OpDeclNode[] consts = mod.getConstantDecls(); - for (int i = 0; i < consts.length; i++) { - Object val = consts[i].getToolObject(TLCGlobals.ToolId); - if (val != null && val instanceof Value) { - ((Value)val).deepNormalize(); - // System.err.println(consts[i].getName() + ": " + val); - } // The following else clause was added by LL on 17 March 2012. - else if (val != null && val instanceof OpDefNode) { - OpDefNode opDef = (OpDefNode) val; - // The following check logically belongs in Spec.processSpec, but it's not there. - // So, LL just added it here. This error cannot occur when running TLC from - // the Toolbox. - Assert.check(opDef.getArity() == consts[i].getArity(), - EC.TLC_CONFIG_WRONG_SUBSTITUTION_NUMBER_OF_ARGS, - new String[] {consts[i].getName().toString(), opDef.getName().toString()}); - - if (opDef.getArity() == 0) { - Value defVal = this.eval(opDef.getBody(), Context.Empty, TLCState.Empty); - defVal.deepNormalize(); - consts[i].setToolObject(TLCGlobals.ToolId, defVal); - } - } - } - - // run for constant operator definitions - OpDefNode[] opDefs = mod.getOpDefs(); - for (int i = 0; i < opDefs.length; i++) { - OpDefNode opDef = opDefs[i]; - - // The following variable evaluate and its value added by LL on 24 July 2013 - // to prevent pre-evaluation of a definition from an EXTENDS of a module that - // is also instantiated. - ModuleNode moduleNode = opDef.getOriginallyDefinedInModuleNode() ; - boolean evaluate = (moduleNode == null) - || (! moduleNode.isInstantiated()) - || ( (moduleNode.getConstantDecls().length == 0) - && (moduleNode.getVariableDecls().length == 0) ) ; - - if (evaluate && opDef.getArity() == 0) { - Object realDef = this.lookup(opDef, Context.Empty, false); - if (realDef instanceof OpDefNode) { - opDef = (OpDefNode)realDef; - if (this.getLevelBound(opDef.getBody(), Context.Empty) == 0) { - try { - UniqueString opName = opDef.getName(); - // System.err.println(opName); - Value val = this.eval(opDef.getBody(), Context.Empty, TLCState.Empty); - val.deepNormalize(); - // System.err.println(opName + ": " + val); - opDef.setToolObject(TLCGlobals.ToolId, val); - Object def = this.defns.get(opName); - if (def == opDef) { - this.defns.put(opName, val); - } - } - catch (Throwable e) { - // Assert.printStack(e); - } - } - } - } - } - - // run for all inner modules - ModuleNode[] imods = mod.getInnerModules(); - for (int i = 0; i < imods.length; i++) { - this.processConstantDefns(imods[i]); - } - } - -} diff --git a/tlatools/src/tlc2/tool/ToolGlobals.java b/tlatools/src/tlc2/tool/ToolGlobals.java index 90fe0a8a45eb9ed741168dff078dfb23a3fc1388..2d6fa5ed720ffe8903351a1677360bb0d3e080ba 100644 --- a/tlatools/src/tlc2/tool/ToolGlobals.java +++ b/tlatools/src/tlc2/tool/ToolGlobals.java @@ -7,7 +7,6 @@ package tlc2.tool; import tla2sany.semantic.ASTConstants; import tla2sany.semantic.OpDefNode; -import tlc2.value.Value; import util.UniqueString; public interface ToolGlobals extends ASTConstants { @@ -15,8 +14,6 @@ public interface ToolGlobals extends ASTConstants { * This interface provides useful globals for the implementation * of the tools. */ - - public static final Value[] EmptyArgs = new Value[0]; // SZ 11.04.2009: changed the method to the equivalent public static final OpDefNode EXCEPT_AT = new OpDefNode(UniqueString.uniqueStringOf("@")); diff --git a/tlatools/src/tlc2/tool/Worker.java b/tlatools/src/tlc2/tool/Worker.java index 3b98529ead182a74fc13c6b90bf1f0f5fc473a86..43d76b3320b5157f1d357d52f94591f583c00c31 100644 --- a/tlatools/src/tlc2/tool/Worker.java +++ b/tlatools/src/tlc2/tool/Worker.java @@ -5,53 +5,69 @@ package tlc2.tool; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.File; +import java.io.IOException; + +import tlc2.TLCGlobals; import tlc2.output.EC; import tlc2.output.MP; +import tlc2.tool.fp.FPSet; +import tlc2.tool.impl.FastTool; import tlc2.tool.queue.IStateQueue; +import tlc2.util.BufferedRandomAccessFile; +import tlc2.util.IStateWriter; import tlc2.util.IdThread; -import tlc2.util.ObjLongTable; -import tlc2.value.Value; +import tlc2.util.SetOfStates; +import tlc2.util.statistics.FixedSizedBucketStatistics; +import tlc2.util.statistics.IBucketStatistics; +import util.Assert.TLCRuntimeException; +import util.FileUtil; +import util.WrongInvocationException; + +public final class Worker extends IdThread implements IWorker, INextStateFunctor { -public class Worker extends IdThread implements IWorker { + protected static final boolean coverage = TLCGlobals.isCoverageEnabled(); + private static final int INITIAL_CAPACITY = 16; /** * Multi-threading helps only when running on multiprocessors. TLC can * pretty much eat up all the cycles of a processor running single threaded. * We expect to get linear speedup with respect to the number of processors. */ - private ModelChecker tlc; - private IStateQueue squeue; - private ObjLongTable astCounts; - private Value[] localValues; + private final ModelChecker tlc; + private final FastTool tool; + private final IStateQueue squeue; + private final FPSet theFPSet; + private final IStateWriter allStateWriter; + private final IBucketStatistics outDegree; + private final String filename; + private final BufferedRandomAccessFile raf; + private final boolean checkDeadlock; + + private long lastPtr; + private long statesGenerated; + private int unseenSuccessorStates = 0; + private volatile int maxLevel = 0; // SZ Feb 20, 2009: changed due to super type introduction - public Worker(int id, AbstractChecker tlc) { + public Worker(int id, AbstractChecker tlc, String metadir, String specFile) throws IOException { super(id); // SZ 12.04.2009: added thread name this.setName("TLC Worker " + id); this.tlc = (ModelChecker) tlc; + this.checkLiveness = this.tlc.checkLiveness; + this.checkDeadlock = this.tlc.checkDeadlock; + this.tool = (FastTool) this.tlc.tool; this.squeue = this.tlc.theStateQueue; - this.astCounts = new ObjLongTable(10); - this.localValues = new Value[4]; + this.theFPSet = this.tlc.theFPSet; + this.allStateWriter = this.tlc.allStateWriter; + this.outDegree = new FixedSizedBucketStatistics(this.getName(), 32); // maximum outdegree of 32 appears sufficient for now. this.setName("TLCWorkerThread-" + String.format("%03d", id)); - } - - public final ObjLongTable getCounts() { return this.astCounts; } - - public Value getLocalValue(int idx) { - if (idx < this.localValues.length) { - return this.localValues[idx]; - } - return null; - } - public void setLocalValue(int idx, Value val) { - if (idx >= this.localValues.length) { - Value[] vals = new Value[idx + 1]; - System.arraycopy(this.localValues, 0, vals, 0, this.localValues.length); - this.localValues = vals; - } - this.localValues[idx] = val; + this.filename = metadir + FileUtil.separator + specFile + "-" + myGetId(); + this.raf = new BufferedRandomAccessFile(filename + TLCTrace.EXT, "rw"); } /** @@ -59,11 +75,11 @@ public class Worker extends IdThread implements IWorker { * possible next states of the state, checks the invariants, and * updates the state set and state queue. */ - public final void run() { + public void run() { TLCState curState = null; try { while (true) { - curState = (TLCState) this.squeue.sDequeue(); + curState = this.squeue.sDequeue(); if (curState == null) { synchronized (this.tlc) { this.tlc.setDone(); @@ -72,16 +88,44 @@ public class Worker extends IdThread implements IWorker { this.squeue.finishAll(); return; } - if (this.tlc.doNext(curState, this.astCounts)) - return; + setCurrentState(curState); + + if (this.checkLiveness) { + // Allocate iff liveness is checked. + setOfStates = createSetOfStates(); + } + + final long preNext = this.statesGenerated; + try { + this.tool.getNextStates(this, curState); + } catch (TLCRuntimeException | EvalException e) { + // The next-state relation couldn't be evaluated. + this.tlc.doNextFailed(curState, null, e); + } + + if (this.checkDeadlock && preNext == this.statesGenerated) { + // A deadlock is defined as a state without (seen or unseen) successor + // states. In other words, evaluating the next-state relation for a state + // yields no states. + this.tlc.doNextSetErr(curState, null, false, EC.TLC_DEADLOCK_REACHED, null); + } + + // Finally, add curState into the behavior graph for liveness checking: + if (this.checkLiveness) + { + doNextCheckLiveness(curState, setOfStates); + } + + this.outDegree.addSample(unseenSuccessorStates); + unseenSuccessorStates = 0; } } catch (Throwable e) { // Something bad happened. Quit ... // Assert.printStack(e); + resetCurrentState(); synchronized (this.tlc) { - if (this.tlc.setErrState(curState, null, true)) { - MP.printError(EC.GENERAL, e); // LL changed call 7 April - // 2012 + if (this.tlc.setErrState(curState, null, true, EC.GENERAL)) { + MP.printError(EC.GENERAL, e); // LL changed call 7 April 2012 } this.squeue.finishAll(); this.tlc.notify(); @@ -89,4 +133,332 @@ public class Worker extends IdThread implements IWorker { return; } } + + /* Liveness */ + + private int multiplier = 1; + + private SetOfStates setOfStates; + + private final boolean checkLiveness; + + private final void doNextCheckLiveness(TLCState curState, SetOfStates liveNextStates) throws IOException { + final long curStateFP = curState.fingerPrint(); + + // Add the stuttering step: + liveNextStates.put(curStateFP, curState); + this.tlc.allStateWriter.writeState(curState, curState, true, IStateWriter.Visualization.STUTTERING); + + this.tlc.liveCheck.addNextState(tlc.tool, curState, curStateFP, liveNextStates); + + if (liveNextStates.capacity() > (multiplier * INITIAL_CAPACITY)) { + // Increase initial size for as long as the set has to grow + multiplier++; + } + } + + private final SetOfStates createSetOfStates() { + return new SetOfStates(multiplier * INITIAL_CAPACITY); + } + + /* Statistics */ + + final void incrementStatesGenerated(long l) { + this.statesGenerated += l; + } + + final long getStatesGenerated() { + return this.statesGenerated; + } + + public final IBucketStatistics getOutDegree() { + return this.outDegree; + } + + public final int getMaxLevel() { + return maxLevel; + } + + final void setLevel(int level) { + maxLevel = level; + } + + /* Maintain trace file (to reconstruct error-trace) */ + + /* + * Synchronize reads and writes to read a consistent union of all trace file + * fragments when one worker W wants to create the counter-example. Otherwise, we + * might not be able to correctly trace the path from state to an initial state. + * The W thread holds ModelChecker.this. The other workers might either: a) Wait + * on IStateQueue#sDequeue (waiting for a new state to be read from disk or + * added to the queue) b) Wait on ModelChecker.this (because they also found + * another counter-example but are blocked until we are done printing it) c) + * Wait on ModelChecker.this in Worker#run because the state queue is empty and + * they which to terminate. d) Run state space exploration The on-disk file of + * each worker's trace fragment is potentially inconsistent because the worker's + * cache in BufferedRandomAccessFile hasn't been flushed out. + */ + + public final synchronized void writeState(final TLCState initialState, final long fp) throws IOException { + // Write initial state to trace file. + this.lastPtr = this.raf.getFilePointer(); + this.raf.writeLongNat(1L); + this.raf.writeShortNat(myGetId()); + this.raf.writeLong(fp); + + // Add predecessor pointer to success state. + initialState.workerId = (short) myGetId(); + initialState.uid = this.lastPtr; + } + + public final synchronized void writeState(final TLCState curState, final long sucStateFp, final TLCState sucState) throws IOException { + // Keep track of maximum diameter. + maxLevel = Math.max(curState.getLevel() + 1, maxLevel); + + // Write to trace file. + this.lastPtr = this.raf.getFilePointer(); + this.raf.writeLongNat(curState.uid); + this.raf.writeShortNat(curState.workerId); + this.raf.writeLong(sucStateFp); + + // Add predecessor pointer to success state. + sucState.workerId = (short) myGetId(); + sucState.uid = this.lastPtr; + + sucState.setPredecessor(curState); + + unseenSuccessorStates++; + +// System.err.println(String.format("<<%s, %s>>: pred=<<%s, %s>>, %s -> %s", myGetId(), this.lastPtr, +// curState.uid, curState.workerId, +// curState.fingerPrint(), sucStateFp)); + } + + // Read from previously written (see writeState) trace file. + public final synchronized ConcurrentTLCTrace.Record readStateRecord(final long ptr) throws IOException { + // Remember current tip of the file before we rewind. + this.raf.mark(); + + // rewind to position we want to read from. + this.raf.seek(ptr); + + final long prev = this.raf.readLongNat(); + assert 0 <= ptr; + + final int worker = this.raf.readShortNat(); + assert 0 <= worker && worker < tlc.workers.length; + + final long fp = this.raf.readLong(); + assert tlc.theFPSet.contains(fp); + + // forward/go back back to tip of file. + // This is only necessary iff TLC runs with '-continue'. In other words, state + // space exploration continues after an error trace has been written. + this.raf.seek(this.raf.getMark()); + + return new ConcurrentTLCTrace.Record(prev, worker, fp); + } + + /* Checkpointing */ + + public final synchronized void beginChkpt() throws IOException { + this.raf.flush(); + final DataOutputStream dos = FileUtil.newDFOS(filename + ".tmp"); + dos.writeLong(this.raf.getFilePointer()); + dos.writeLong(this.lastPtr); + dos.close(); + } + + public final synchronized void commitChkpt() throws IOException { + final File oldChkpt = new File(filename + ".chkpt"); + final File newChkpt = new File(filename + ".tmp"); + if ((oldChkpt.exists() && !oldChkpt.delete()) || !newChkpt.renameTo(oldChkpt)) { + throw new IOException("Trace.commitChkpt: cannot delete " + oldChkpt); + } + } + + public final void recover() throws IOException { + final DataInputStream dis = FileUtil.newDFIS(filename + ".chkpt"); + final long filePos = dis.readLong(); + this.lastPtr = dis.readLong(); + dis.close(); + this.raf.seek(filePos); + } + + /* Enumerator */ + + public final Enumerator elements() throws IOException { + return new Enumerator(); + } + + public class Enumerator { + + private final long len; + private final BufferedRandomAccessFile enumRaf; + + Enumerator() throws IOException { + this.len = raf.getFilePointer(); + this.enumRaf = new BufferedRandomAccessFile(filename + TLCTrace.EXT, "r"); + } + + public boolean hasMoreFP() { + final long fpos = this.enumRaf.getFilePointer(); + if (fpos < this.len) { + return true; + } + return false; + } + + public long nextFP() throws IOException { + this.enumRaf.readLongNat(); /* drop */ + this.enumRaf.readShortNat(); /* drop */ + return this.enumRaf.readLong(); + } + + public void close() throws IOException { + this.enumRaf.close(); + } + } + + //**************************************************************// + + @Override + public final Object addElement(final TLCState state) { + throw new WrongInvocationException("tlc2.tool.Worker.addElement(TLCState) should not be called"); + } + + @Override + public final Object addElement(final TLCState curState, final Action action, final TLCState succState) { + if (coverage) { action.cm.incInvocations(); } + this.statesGenerated++; + + try { + if (!this.tool.isGoodState(succState)) { + this.tlc.doNextSetErr(curState, succState, action); + throw new InvariantViolatedException(); + } + + // Check if state is excluded by a state or action constraint. + final boolean inModel = (this.tool.isInModel(succState) && this.tool.isInActions(curState, succState)); + + // Check if state is new or has been seen earlier. + boolean unseen = true; + if (inModel) { + unseen = !isSeenState(curState, succState, action); + } + + // Check if succState violates any invariant: + if (unseen) { + if (this.doNextCheckInvariants(curState, succState)) { + throw new InvariantViolatedException(); + } + } + + // Check if the state violates any implied action. We need to do it + // even if succState is not new. + if (this.doNextCheckImplied(curState, succState)) { + throw new InvariantViolatedException(); + } + + if (inModel && unseen) { + // The state is inModel, unseen and neither invariants + // nor implied actions are violated. It is thus eligible + // for further processing by other workers. + this.squeue.sEnqueue(succState); + } + return this; + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + private final boolean isSeenState(final TLCState curState, final TLCState succState, final Action action) + throws IOException { + final long fp = succState.fingerPrint(); + final boolean seen = this.theFPSet.put(fp); + // Write out succState when needed: + this.allStateWriter.writeState(curState, succState, !seen, action); + if (!seen) { + // Write succState to trace only if it satisfies the + // model constraints. Do not enqueue it yet, but wait + // for implied actions and invariants to be checked. + // Those checks - if violated - will cause model checking + // to terminate. Thus we cannot let concurrent workers start + // exploring this new state. Conversely, the state has to + // be in the trace in case either invariant or implied action + // checks want to print the trace. + this.writeState(curState, fp, succState); + if (coverage) { action.cm.incSecondary(); } + } + // For liveness checking: + if (this.checkLiveness) + { + this.setOfStates.put(fp, succState); + } + return seen; + } + + private final boolean doNextCheckInvariants(final TLCState curState, final TLCState succState) throws IOException, WorkerException, Exception { + int k = 0; + try + { + for (k = 0; k < this.tool.getInvariants().length; k++) + { + if (!tool.isValid(this.tool.getInvariants()[k], succState)) + { + // We get here because of invariant violation: + if (TLCGlobals.continuation) { + synchronized (this.tlc) + { + MP.printError(EC.TLC_INVARIANT_VIOLATED_BEHAVIOR, + this.tool.getInvNames()[k]); + this.tlc.trace.printTrace(curState, succState); + return false; + } + } else { + return this.tlc.doNextSetErr(curState, succState, false, + EC.TLC_INVARIANT_VIOLATED_BEHAVIOR, this.tool.getInvNames()[k]); + } + } + } + } catch (Exception e) + { + this.tlc.doNextEvalFailed(curState, succState, EC.TLC_INVARIANT_EVALUATION_FAILED, + this.tool.getInvNames()[k], e); + } + return false; + } + + private final boolean doNextCheckImplied(final TLCState curState, final TLCState succState) throws IOException, WorkerException, Exception { + int k = 0; + try + { + for (k = 0; k < this.tool.getImpliedActions().length; k++) + { + if (!tool.isValid(this.tool.getImpliedActions()[k], curState, succState)) + { + // We get here because of implied-action violation: + if (TLCGlobals.continuation) + { + synchronized (this.tlc) + { + MP.printError(EC.TLC_ACTION_PROPERTY_VIOLATED_BEHAVIOR, this.tool + .getImpliedActNames()[k]); + this.tlc.trace.printTrace(curState, succState); + return false; + } + } else { + return this.tlc.doNextSetErr(curState, succState, false, + EC.TLC_ACTION_PROPERTY_VIOLATED_BEHAVIOR, + this.tool.getImpliedActNames()[k]); + } + } + } + } catch (Exception e) + { + this.tlc.doNextEvalFailed(curState, succState, EC.TLC_ACTION_PROPERTY_EVALUATION_FAILED, + this.tool.getImpliedActNames()[k], e); + } + return false; + } } diff --git a/tlatools/src/tlc2/tool/coverage/ActionWrapper.java b/tlatools/src/tlc2/tool/coverage/ActionWrapper.java new file mode 100644 index 0000000000000000000000000000000000000000..ead40c50e5947345682f5c02c0a32472b4e85edf --- /dev/null +++ b/tlatools/src/tlc2/tool/coverage/ActionWrapper.java @@ -0,0 +1,169 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.coverage; + +import tla2sany.semantic.LetInNode; +import tla2sany.semantic.SemanticNode; +import tla2sany.semantic.SubstInNode; +import tla2sany.st.Location; +import tlc2.output.EC; +import tlc2.output.MP; +import tlc2.tool.Action; + +public final class ActionWrapper extends CostModelNode { + + public enum Relation { + INIT, NEXT, PROP; + } + + private final Action action; + private final Relation relation; + + public ActionWrapper(final Action action, Relation rel) { + this.action = action; + this.relation = rel; + } + + /* (non-Javadoc) + * @see tlc2.tool.coverage.CostModelNode#getLocation() + */ + @Override + protected Location getLocation() { + if (this.action.isDeclared()) { + return this.action.getDeclaration(); + } + return this.action.pred.getLocation(); + } + + private String printLocation() { + if (!this.action.isDeclared()) { + // Safeguard for legacy cases if any. + return this.action.toString(); + } + // Determine if the mapping from the action's name/identifier/declaration to the + // action's definition is 1:1 or 1:N. + // + // Act == /\ x = 23 + // /\ x' = 42 + // vs + // Act == \/ /\ x = 23 + // /\ x' = 42 + // \/ /\ x = 123 + // /\ x' = 4711 + // or + // Act == (x = 23 /\ x' = 42) \/ (x = 123 /\ x' = 4711) + // + // For a 1:1 mapping this prints just the location of Act. For a 1:N mapping it + // prints the location of Act _and_ the location (in shortened form) of the actual + // disjunct. + final Location declaration = this.action.getDeclaration(); + final Location definition = this.action.getOpDef().getBody().getLocation(); + final Location actual = this.action.pred.getLocation(); + if (definition.equals(actual)) { + // 1:1 + return String.format("<%s %s>", this.action.getName(), declaration); + } else { + // 1:N + return String.format("<%s %s (%s %s %s %s)>", this.action.getName(), declaration, actual.beginLine(), + actual.beginColumn(), actual.endLine(), actual.endColumn()); + } + } + + /* (non-Javadoc) + * @see tlc2.tool.coverage.CostModelNode#getRoot() + */ + @Override + public CostModelNode getRoot() { + return this; + } + + /* (non-Javadoc) + * @see tlc2.tool.CostModel#get(tla2sany.semantic.SemanticNode) + */ + @Override + public final CostModel get(final SemanticNode eon) { + // returns this instance in case no match is found in children. As a result, the + // CostModel will be incorrect which is not as severe as running into an NPE. + if (eon instanceof SubstInNode) { + final SubstInNode sin = (SubstInNode) eon; + return this.children.getOrDefault(sin.getBody(), this); + } else if (eon instanceof LetInNode) { + final LetInNode lin = (LetInNode) eon; + return this.children.getOrDefault(lin.getBody(), this); + } + return this.children.getOrDefault(eon, this); + } + + /* (non-Javadoc) + * @see tlc2.tool.coverage.CostModelNode#getNode() + */ + @Override + SemanticNode getNode() { + return action.pred; + } + + /* (non-Javadoc) + * @see tlc2.tool.coverage.CostModelNode#isRoot() + */ + @Override + boolean isRoot() { + return true; + } + + /* (non-Javadoc) + * @see tlc2.tool.coverage.CostModel#report() + */ + public CostModel report() { + // Report count for action itself. + if (relation == Relation.PROP) { + assert getEvalCount() == 0L && this.secondary.getCount() == 0L; + MP.printMessage(EC.TLC_COVERAGE_PROPERTY, new String[] { printLocation() }); + } else if (relation == Relation.INIT) { + // TODO Eventually coverage for init and next should consistently report states + // found and distinct states into the same counters. + MP.printMessage(EC.TLC_COVERAGE_INIT, new String[] { printLocation(), String.valueOf(getEvalCount()), + String.valueOf(getEvalCount() + this.secondary.getCount()) }); + } else { + MP.printMessage(EC.TLC_COVERAGE_NEXT, new String[] { printLocation(), + String.valueOf(this.secondary.getCount()), String.valueOf(getEvalCount()) }); + } + + // An action has single child which is the OpApplNodeWrapper with the OpApplNode + // for this OpDefNode unless the action's pred is a substitution or a let/in expr. + assert !(this.action.pred instanceof SubstInNode || this.action.pred instanceof LetInNode) + ? this.children.size() == 1 + : !this.children.isEmpty(); + // Let children report. + // Could disable here if decided to implement action-only coverage at the TLC + // level (see org.lamport.tla.toolbox.tool.tlc.model.Model.Coverage). + this.children.values().forEach(c -> c.report()); + return this; + } + + public boolean is(Relation r) { + return r == this.relation; + } +} diff --git a/tlatools/src/tlc2/tool/coverage/CostModel.java b/tlatools/src/tlc2/tool/coverage/CostModel.java new file mode 100644 index 0000000000000000000000000000000000000000..cdfbaf6c7415447c394315e1e87ceb71909e78c5 --- /dev/null +++ b/tlatools/src/tlc2/tool/coverage/CostModel.java @@ -0,0 +1,101 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.coverage; + +import tla2sany.semantic.SemanticNode; + +public interface CostModel { + + CostModel DO_NOT_RECORD = new CostModel() { + + @Override + public final CostModel report() { + // no-op + return this; + } + + @Override + public final CostModel get(final SemanticNode sn) { + return this; + } + + @Override + public final CostModel getRoot() { + return this; + } + + @Override + public final CostModel incInvocations() { + // no-op + return this; + } + + @Override + public final CostModel incInvocations(final long value) { + // no-op + return this; + } + + @Override + public final CostModel incSecondary() { + // no-op + return this; + } + + @Override + public final CostModel incSecondary(long value) { + // no-op + return null; + } + + @Override + public final CostModel getAndIncrement(SemanticNode eon) { + // no-op + return this; + } + + @Override + public final String toString() { + return "DO_NOT_RECORD"; + } + }; + + CostModel incInvocations(); + + CostModel incInvocations(final long value); + + CostModel incSecondary(); + + CostModel incSecondary(final long value); + + CostModel report(); + + CostModel get(final SemanticNode sn); + + CostModel getAndIncrement(final SemanticNode eon); + + CostModel getRoot(); +} diff --git a/tlatools/src/tlc2/tool/coverage/CostModelCreator.java b/tlatools/src/tlc2/tool/coverage/CostModelCreator.java new file mode 100644 index 0000000000000000000000000000000000000000..a70c7187074f80a4da80e9272abb24e913fbd13f --- /dev/null +++ b/tlatools/src/tlc2/tool/coverage/CostModelCreator.java @@ -0,0 +1,465 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.coverage; + +import static tlc2.tool.ToolGlobals.OPCODE_unchanged; + +import java.util.ArrayDeque; +import java.util.Arrays; +import java.util.Comparator; +import java.util.Deque; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.TreeSet; + +import tla2sany.explorer.ExploreNode; +import tla2sany.explorer.ExplorerVisitor; +import tla2sany.semantic.ExprNode; +import tla2sany.semantic.ExprOrOpArgNode; +import tla2sany.semantic.LetInNode; +import tla2sany.semantic.OpApplNode; +import tla2sany.semantic.OpDefNode; +import tla2sany.semantic.SemanticNode; +import tla2sany.semantic.Subst; +import tla2sany.semantic.SubstInNode; +import tla2sany.semantic.SymbolNode; +import tlc2.output.EC; +import tlc2.output.MP; +import tlc2.tool.Action; +import tlc2.tool.ITool; +import tlc2.tool.coverage.ActionWrapper.Relation; +import tlc2.util.Context; +import tlc2.util.ObjLongTable; +import tlc2.util.Vect; + +/** + * <h1>Why a CostModel:</h1> Why a CostModelCreator to traverses the semantic + * graph to create a forest of OpApplNode trees (rooted at an action)?<br> + * The semantic graph is no fit to store cost metrics. This is due to the fact + * that the semantic graph has only a single node for each OpDefNode and thus + * OpApplNode even when an OpApplNode is invoked from different actions and thus + * different call trees. If we were to store costs inside the semantic graph, it + * would thus not be possible to show costs per action. Therefore, + * CostModelCreate creates a tree for each action whose subtree is the set of + * OpApplNodes reachable from this particular action. + * <p> + * A CostModel tree of an action gets traversed by Tool in lockstep (except that + * the tree only contains OpApplNodes thus <code>return this</code> in + * OpApplNodeWrapper#get) when Tool traverses the semantic graph to evaluate an + * action. + * <p> + * As part of the work on the CostModel, the ExplorerVisitor received an extension + * to export the semantic graph into dot notation, which can be rendered with + * GraphViz: + * <code>java -cp tla2tools.jartla2sany.SANY -d ATLA+Spec.tla dot</code> It + * optionally includes line numbers if the system property + * <code>tla2sany.explorer.DotExplorerVisitor.includeLineNumbers=true</code> + * is set. + * <p> + * ----------------------------------------------------------------------------- + * <h2>Note on performance:</h2> Spec/Tool and Value turn isCoverageEnabled into + * constants. This should help the runtime to optimize away most calls to + * CostModel unless coverage is enabled. + * <p> + * Macro-benchmarks on a real-world PlusCal [1] model show a 2% performance drop + * in terms of throughput due to the addition of the "if (coverage) {...}" calls + * when coverage is turned off (it is in the range of %40 when turned on). In + * other words, the CostModel data collector has a non-zero overhead. The + * reasons are unknown and further investigations have been abandoned. Instead, + * the problem has been side-stepped by refactorings in ModelChecker and Tool. + * [1] + * https://bitbucket.org/parvmor/tarjanconcurrentscc/src/unionfind/specifications/MCBloemenSCC.tla + * altered to terminate TLC after five minutes with TLCSet("exit",...) + * <p> + * The refactorings essentially decomposed large methods into smaller ones (see + * git history for commits). This allows the runtime to emit more efficient code + * s.t. hot methods can be inlined to reduce invocations. The net gain is in the + * range of 5%; enough to make up for the 2% drop introduced by the CostModel + * collector.<br> + * Methods were identified by: + * <code>java -XX:+PrintCompilation -XX:+UnlockDiagnosticVMOptions -XX:+PrintInlining + * -jar tla2tools.jar -workers 1 | grep "hot method too big"</code> + * <p> + * A far more elegant implementation and zero-overhead solution of the CostModel + * collector uses aspect-programming (based on e.g. AspectJ used in other places + * of TLC). This approach has been prototyped in CostModelAspect.java. The + * aspects in CostModelAspect are woven into TLC by AspectJ at the defined + * pointcuts when needed.<br> + * Weaving can either be done at compile-time (CTW) or launch-time (LTW) where + * LTW is preferable in our case to only load the CostModel code when a user + * enables it. Unfortunately, benchmarks revealed a huge performance drops in + * the order of a magnitude with LTW. Inspections of the generated bytecode with + * JitWatch did not reveal the root cause for this major performance drop. As a + * side-effect, LTW would require TLC to include the AspectJ runtime - and even + * bigger in size - the ASM bytecode instrumentation. Users would also have to + * pass the -javaagent parameter to TLC. In other words, using coverage wouldn't + * be as transparent to the user as it is today. + * <p> + * Experiments with CTW - compared to LTW - resulted in much better performance + * with coverage enabled. Still, the overhead of CTW with coverage disabled was + * non-negligible. We therefore went as far as hacking the class-loading in + * TLC.java to load vanilla TLC with coverage off and instrumented code with + * coverage on. However, we considered this approach to be too hack-ish and + * abandoned it in favor of the aforementioned refactorings (it also broke the + * model.ModelInJarfeature). It additionally increased the size of tla2tools.jar + * two-fold.<br> + * Generally it appears as if the code generation in AspectJ is not optimized to + * emit the most efficient code. Most pointcuts result in superfluous object + * allocations inside the generated bytecode - to pass along method parameters - + * for *every* method invocation. Future versions of AspectJ might produce more + * efficient code though. + * <p> + * ----------------------------------------------------------------------------- + * Changing tla2sany.semantic.Generator to generate a forest of call trees - + * s.t. there is one OpDefNode & OpApplNode instance per call tree instead of + * one global graph - appears to be the implementation with minimal overhead. We + * did not follow up on this idea however, because of the inherent risk of + * introducing subtle side-effect into the semantics of TLA+ expression + * evaluation. An optimization - to only generate a forest when coverage is + * enabled - that uses dynamic proxies (JDK dynamic proxies, CGLib, ASM) proved + * impossible unless the complete class hierarchy of SemanticNode gets + * refactored into a hierarchy with interfaces (proxies can practically only be + * generated for interfaces) + */ +public class CostModelCreator extends ExplorerVisitor { + + private final Deque<CostModelNode> stack = new ArrayDeque<>(); + private final Map<ExprOrOpArgNode, Subst> substs = new HashMap<>(); + private final Map<OpApplNode, Set<OpApplNodeWrapper>> node2Wrapper = new HashMap<>(); + private final Set<OpDefNode> opDefNodes = new HashSet<>(); + // Set of OpDefNodes occurring in LetIns and their OpApplNodes. + private final Map<ExprNode, ExprNode> letIns = new HashMap<>(); + // OpAppNode does not implement equals/hashCode which causes problem when added + // to sets or maps. E.g. for a test, an OpApplNode instance belonging to + // Sequences.tla showed up in coverage output. + private final Set<OpApplNodeWrapper> nodes = new HashSet<>(); + private final ITool tool; + + private ActionWrapper root; + private Context ctx = Context.Empty; + + private CostModelCreator(final SemanticNode root, final ITool tool) { + this.tool = tool; + this.stack.push(new RecursiveOpApplNodeWrapper()); + root.walkGraph(new CoverageHashTable(opDefNodes), this); + } + + // root cannot be type OpApplNode but has to be SemanticNode (see Test216). + private CostModelCreator(final ITool tool) { + this.tool = tool; + // MAK 10/08/2018: Annotate OApplNodes in the semantic tree that correspond to + // primed vars. It is unclear why OpApplNodes do not get marked as primed when + // instantiated. The logic in Tool#getPrimedLocs is too obscure to tell. + final ObjLongTable<SemanticNode>.Enumerator<SemanticNode> keys = tool.getPrimedLocs().keys(); + SemanticNode sn; + while ((sn = keys.nextElement()) != null) { + this.nodes.add(new OpApplNodeWrapper((OpApplNode) sn, null)); + } + } + + private CostModel getCM(final Action act, final ActionWrapper.Relation relation) { + this.substs.clear(); + this.node2Wrapper.clear(); + this.opDefNodes.clear(); + this.letIns.clear(); + this.stack.clear(); + this.ctx = Context.Empty; + + this.root = new ActionWrapper(act, relation); + this.stack.push(root); + act.pred.walkGraph(new CoverageHashTable(opDefNodes), this); + + assert this.stack.peek().isRoot(); + return this.stack.peek().getRoot(); + } + + @Override + public void preVisit(final ExploreNode exploreNode) { + if (exploreNode instanceof OpApplNode) { + final OpApplNode opApplNode = (OpApplNode) exploreNode; + if (opApplNode.isStandardModule()) { + return; + } + + final OpApplNodeWrapper oan; + if (opApplNode.hasOpcode(OPCODE_unchanged)) { + oan = new UnchangedOpApplNodeWrapper(opApplNode, this.root); + } else { + oan = new OpApplNodeWrapper(opApplNode, this.root); + } + + if (nodes.contains(oan)) { + oan.setPrimed(); + } + + // A (recursive) function definition nested in LetIn: + // LET F[n \in S] == e + // IN F[...] + // with e either built from F or not. + if (letIns.containsKey(opApplNode)) { + // At the visit of the LETIN node in the walk over the semantic graph we stored + // the mapping from the LET part to the IN part in this.lets (see LetInNode below). + // Here, we add the LET parts(s) to the lets of the IN part if it is found on + // the stack (this is more involved because we have to find the OANWrappers and + // not just the OANs). + final ExprNode in = letIns.get(opApplNode); + for (CostModelNode cmn : stack) { + final SemanticNode node = cmn.getNode(); + if (node == in && cmn instanceof OpApplNodeWrapper) { + // addLets instead of addChild because lets can be added multiple times + // whereas addChild asserts a child to be added only once. + ((OpApplNodeWrapper) cmn).addLets(oan); + } + } + } + + // CONSTANT operators (including definition overrides...) + final SymbolNode operator = opApplNode.getOperator(); + final Object val = tool.lookup(operator); + if (val instanceof OpDefNode && operator != val) { // second conjunct bc lookup returns operator when nothing else found. + final OpDefNode odn = (OpDefNode) val; + final ExprNode body = odn.getBody(); + if (body instanceof OpApplNode) { + final CostModelCreator substitution = new CostModelCreator(body, tool); + oan.addChild((OpApplNodeWrapper) substitution.getModel()); + } + } + + // RECURSIVE + if (operator instanceof OpDefNode) { + final OpDefNode odn = (OpDefNode) operator; + if (odn.getInRecursive()) { + final OpApplNodeWrapper recursive = (OpApplNodeWrapper) stack.stream() + .filter(w -> w.getNode() != null && w.getNode() instanceof OpApplNode + && ((OpApplNode) w.getNode()).getOperator() == odn) + .findFirst().orElse(null); + if (recursive != null) { + oan.setRecursive(recursive); + } + } + } + + // Higher-order operators/Operators as arguments (LAMBDA, ...) + // + // line X: Foo(Op(_), S) == \A s \in S: Op(s) + // line ?: ... + // line Y: Bar == Foo(LAMBDA e: e..., {1,2,3}) + // + // This is the most involved part of CMC: The task is to make the OANW + // corresponding to the RHS of the LAMBDA expression on line Y a child of Op(s) + // on line X. However, the graph exploration is DFS which means that we haven't + // seen the LAMBDA on line Y when we are at the Op(s) on line X and we've + // (mostly) forgotten about Op(s) when we see the LAMBDA. ToolImpl - as part of + // its DFS over the semantic graph - passes a context along which gets extended + // or *branched*. Here, we cannot pass a Context along the decent but instead + // keep a single, global context. + // + // The global context does not create a problem with regards to correctness, but + // can lead to long context chains for larger specifications. Therefore, only + // extend the context when opApplNode.argsContainOpArgNodes() is true, i.e. when + // one or more arguments of an operator are also operators (such as a LAMBDA). + // Without this safeguard, the time to create the CostModel for the SchedMono + // specification took approximately 60 seconds. With the safeguard, it is down + // to a second or two. + // + // To summarize, this is a clutch that has been hacked to work good enough! + // + // if-branches 1., 2., and 3. below are evaluated in three distinct + // invocation of outer preVisit for different ExploreNodes. + if (tool != null && operator instanceof OpDefNode && opApplNode.hasOpcode(0) + && opApplNode.argsContainOpArgNodes()) { + // 1) Maintain Context for all OpApplNode iff one or more of its args are of + // type OpArgNode. This is more restrictive than Tool. + final OpDefNode odn = (OpDefNode) operator; + if (odn.hasOpcode(0) && !odn.isStandardModule()) { + this.ctx = tool.getOpContext(odn, opApplNode.getArgs(), ctx, false); + } + } + final Object lookup = this.ctx.lookup(opApplNode.getOperator()); + if (lookup instanceof OpDefNode) { + // 2) Context has an entry for the given body where body is 'LAMBDA e: e...' and + // oan is 'Op(s)'. Remember for later. + final ExprNode body = ((OpDefNode) lookup).getBody(); + if (body instanceof OpApplNode) { + // Design choice: + // Might as well store the mapping from body to oan via + // body#setToolObject(tla2sany.semantic.FrontEnd.getToolId(), oan) instead of in + // node2Wrapper. However, node2Wrapper can be gc'ed after the CostModel has been + // created and before state space exploration. + this.node2Wrapper.computeIfAbsent((OpApplNode) body, key -> new HashSet<>()).add(oan); + } + } + if (this.node2Wrapper.containsKey(opApplNode)) { + // 3) Now it's later. Connect w and oan where + // w is 'Op(s)' and oan is 'LAMBDA e: e...' + this.node2Wrapper.get(opApplNode).forEach(w -> w.addChild(oan)); + } + // End of Higher-order operators/Operators as arguments (LAMBDA, ...) + + // Substitutions + if (this.substs.containsKey(exploreNode)) { + final Subst subst = this.substs.get(exploreNode); + assert subst.getExpr() == oan.getNode(); + subst.setCM(oan); + } + + final CostModelNode parent = stack.peek(); + parent.addChild(oan.setLevel(parent.getLevel() + 1)); + stack.push(oan); + } else if (exploreNode instanceof SubstInNode) { + final SubstInNode sin = (SubstInNode) exploreNode; + final Subst[] substs = sin.getSubsts(); + for (Subst subst : substs) { + this.substs.put(subst.getExpr(), subst); + } + } else if (exploreNode instanceof LetInNode) { + final LetInNode lin = (LetInNode) exploreNode; + for (OpDefNode opDefNode : lin.getLets()) { + letIns.put(opDefNode.getBody(), lin.getBody()); + } + } else if (exploreNode instanceof OpDefNode) { + //TODO Might suffice to just keep RECURSIVE ones. + opDefNodes.add((OpDefNode) exploreNode); + } + } + + @Override + public void postVisit(final ExploreNode exploreNode) { + if (exploreNode instanceof OpApplNode) { + if (((OpApplNode) exploreNode).isStandardModule()) { + return; + } + final CostModelNode pop = stack.pop(); + assert pop.getNode() == exploreNode; + } else if (exploreNode instanceof OpDefNode) { + final boolean removed = opDefNodes.remove((OpDefNode) exploreNode); + assert removed; + } + } + + private CostModel getModel() { + assert this.stack.peek().isRoot(); + return this.stack.peek().getRoot(); + } + + public static final void create(final ITool tool) { + final CostModelCreator collector = new CostModelCreator(tool); + + // TODO Start from the ModuleNode similar to how the Explorer works. It is + // unclear how to lookup the corresponding subtree in the global CM graph + // in getNextState and getInitStates of the model checker. + final Vect<Action> init = tool.getInitStateSpec(); + for (int i = 0; i < init.size(); i++) { + final Action initAction = init.elementAt(i); + initAction.cm = collector.getCM(initAction, Relation.INIT); + } + + final Map<SemanticNode, CostModel> cms = new HashMap<>(); + for (Action nextAction : tool.getActions()) { + if (cms.containsKey(nextAction.pred)) { + nextAction.cm = cms.get(nextAction.pred); + } else { + nextAction.cm = collector.getCM(nextAction, Relation.NEXT); + cms.put(nextAction.pred, nextAction.cm); + } + } + + for (Action invariant : tool.getInvariants()) { + invariant.cm = collector.getCM(invariant, Relation.PROP); + } + + // https://github.com/tlaplus/tlaplus/issues/413#issuecomment-577304602 + if (Boolean.getBoolean(CostModelCreator.class.getName() + ".implied")) { + for (Action impliedInits : tool.getImpliedInits()) { + impliedInits.cm = collector.getCM(impliedInits, Relation.PROP); + } + for (Action impliedActions : tool.getImpliedActions()) { + impliedActions.cm = collector.getCM(impliedActions, Relation.PROP); + } + } + } + + public static void report(final ITool tool, final long startTime) { + MP.printMessage(EC.TLC_COVERAGE_START); + final Vect<Action> init = tool.getInitStateSpec(); + for (int i = 0; i < init.size(); i++) { + final Action initAction = init.elementAt(i); + initAction.cm.report(); + } + + // Order next-state actions based on location to print in order of location. + // Note that Action[] actions may contain action instances with identical + // location which is the case for actions that are evaluated in the scope of a + // Context, i.e. \E s \in ProcSet: action(s) \/ ... + // However, actions with identical location share the ActionWrapper instance + // which is why we can non-deterministically choose to report one of it without + // producing bogus results (see CostModelCreator.preVisit(ExploreNode) above). + final Action[] actions = tool.getActions(); + final Set<CostModel> reported = new HashSet<>(); + final Set<Action> sortedActions = new TreeSet<>(new Comparator<Action>() { + @Override + public int compare(Action o1, Action o2) { + return o1.pred.getLocation().compareTo(o2.pred.getLocation()); + } + }); + sortedActions.addAll(Arrays.asList(actions)); + for (Action action : sortedActions) { + if (!reported.contains(action.cm)) { + action.cm.report(); + reported.add(action.cm); + } + } + + for (Action invariant : tool.getInvariants()) { + //TODO May need to be ordered similar to next-state actions above. + invariant.cm.report(); + } + + // https://github.com/tlaplus/tlaplus/issues/413#issuecomment-577304602 + if (Boolean.getBoolean(CostModelCreator.class.getName() + ".implied")) { + for (Action impliedInits : tool.getImpliedInits()) { + impliedInits.cm.report(); + } + for (Action impliedActions : tool.getImpliedActions()) { + impliedActions.cm.report(); + } + } + + // Notify users about the performance overhead related to coverage collection + // after N minutes of model checking. The assumption is that a user has little + // interest in coverage for a large (long-running) model anyway. In the future + // it is hopefully possible to switch from profiling to sampling to relax the + // performance overhead of coverage and cost statistics. + final long l = System.currentTimeMillis() - startTime; + if (l > (5L * 60L * 1000L)) { + MP.printMessage(EC.TLC_COVERAGE_END_OVERHEAD); + } else { + MP.printMessage(EC.TLC_COVERAGE_END); + } + } +} diff --git a/tlatools/src/tlc2/tool/coverage/CostModelNode.java b/tlatools/src/tlc2/tool/coverage/CostModelNode.java new file mode 100644 index 0000000000000000000000000000000000000000..7b4700eb5bfc9b2482b11bca912e39c7965f349f --- /dev/null +++ b/tlatools/src/tlc2/tool/coverage/CostModelNode.java @@ -0,0 +1,106 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.coverage; + +import java.util.LinkedHashMap; +import java.util.Map; + +import tla2sany.semantic.SemanticNode; +import tla2sany.st.Location; +import tlc2.TLCGlobals; +import tlc2.util.statistics.CounterStatistic; + +public abstract class CostModelNode implements CostModel { + + // children has to preserve order to later traverse tree in the module location + // order when reporting coverage. Thus, use LinkedHashMap here. + protected final Map<SemanticNode, CostModelNode> children = new LinkedHashMap<>(); + + protected final CounterStatistic stats = CounterStatistic.getInstance(() -> TLCGlobals.isCoverageEnabled()); + protected final CounterStatistic secondary = CounterStatistic.getInstance(() -> TLCGlobals.isCoverageEnabled()); + + // ---------------- Statistics ---------------- // + + protected long getEvalCount() { + return this.stats.getCount(); + } + + protected long getSecondary() { + return this.secondary.getCount(); + } + + protected abstract Location getLocation(); + + // -- --// + + void addChild(final CostModelNode child) { + final boolean newlyInserted = this.children.put(child.getNode(), child) == null; + assert newlyInserted; + } + + abstract SemanticNode getNode(); + + @Override + public abstract CostModelNode getRoot(); + + boolean isRoot() { + return false; + } + + int getLevel() { + return 0; + } + + // -- -- // + + @Override + public final CostModel getAndIncrement(final SemanticNode eon) { + return get(eon).incInvocations(); + } + + @Override + public final CostModel incInvocations(long size) { + this.stats.add(size); + return this; + } + + @Override + public final CostModel incInvocations() { + this.stats.increment(); + return this; + } + + public final CostModel incSecondary() { + this.secondary.increment(); + return this; + } + + @Override + public final CostModel incSecondary(final long value) { + this.secondary.add(value); + return this; + } +} diff --git a/tlatools/src/tlc2/tool/coverage/CoverageHashTable.java b/tlatools/src/tlc2/tool/coverage/CoverageHashTable.java new file mode 100644 index 0000000000000000000000000000000000000000..c4b3fede60cd40bd3a2f452e564d9eaa6f8cabdf --- /dev/null +++ b/tlatools/src/tlc2/tool/coverage/CoverageHashTable.java @@ -0,0 +1,58 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.coverage; + +import java.util.Set; + +import tla2sany.explorer.ExploreNode; +import tla2sany.semantic.OpDefNode; + +@SuppressWarnings("serial") +class CoverageHashTable extends java.util.Hashtable<Integer, ExploreNode> { + private final Set<OpDefNode> nodes; + + public CoverageHashTable(final Set<OpDefNode> nodes) { + this.nodes = nodes; + } + + @Override + public ExploreNode get(Object key) { + // Return null here to visit an OpDefNode D multiple times if D is "called" from + // multiple OpApplNodes. However, stop endless recursion if D is a RECURSIVE + // operator. + final ExploreNode v = super.get(key); + if (v instanceof OpDefNode) { + final OpDefNode odn = (OpDefNode) v; + if (odn.getInRecursive()) { + if (nodes.contains(odn)) { + // RECURSIVE operators + return v; + } + } + } + return null; + } +} \ No newline at end of file diff --git a/tlatools/src/tlc2/tool/coverage/OpApplNodeWrapper.java b/tlatools/src/tlc2/tool/coverage/OpApplNodeWrapper.java new file mode 100644 index 0000000000000000000000000000000000000000..ac2233cf32037dc7f55d5e58f2ba07b7eaacb50a --- /dev/null +++ b/tlatools/src/tlc2/tool/coverage/OpApplNodeWrapper.java @@ -0,0 +1,343 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.coverage; + +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Set; + +import tla2sany.semantic.OpApplNode; +import tla2sany.semantic.SemanticNode; +import tla2sany.st.Location; +import tlc2.TLCGlobals; +import tlc2.output.EC; +import tlc2.output.MP; + +public class OpApplNodeWrapper extends CostModelNode implements Comparable<OpApplNodeWrapper>, CostModel { + + private final Set<Long> childCounts = new HashSet<>(); + private final CostModelNode root; + private final OpApplNode node; + // Periodic coverage reporting executes concurrently with the evaluation of the + // init and next-state relation. Traversing the CostModel trees to collect the + // individual eval counts creates thus an inconsistent snapshot. To reduce the + // inconsistency, freeze the eval count for all tree nodes on the first tree + // traversal while the child counts are calculated. The snapshot is still + // inconsistent from the perspective of the evaluation, but at least the + // reporting in print (eval counts reported to parent - childCounts - and eval + // counts printed is consistent. Alternatively, evaluation of init and next + // could be suspended for the duration of the snapshot, but that seems overkill. + private long snapshotEvalCount = 0; + private boolean primed = false; + private int level; + private OpApplNodeWrapper recursive; + protected final Map<SemanticNode, CostModelNode> lets = new LinkedHashMap<>(); + + OpApplNodeWrapper(OpApplNode node, CostModelNode root) { + super(); + this.node = node; + this.root = root; + this.level = 0; + } + + // For unit testing only. + OpApplNodeWrapper() { + this(null, null); + } + + // For unit testing only. + OpApplNodeWrapper(OpApplNode node, long samples) { + this(node, null); + this.incInvocations(samples); + } + + // ---------------- Identity... ---------------- // + + @Override + public int compareTo(OpApplNodeWrapper arg0) { + return this.getLocation().compareTo(arg0.getLocation()); + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((node.getLocation() == null) ? 0 : node.getLocation().hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + OpApplNodeWrapper other = (OpApplNodeWrapper) obj; + if (node.getLocation() == null) { + if (other.node.getLocation() != null) + return false; + } else if (!node.getLocation().equals(other.node.getLocation())) { + return false; + } + return true; + + } + + @Override + public String toString() { + if (this.node == null) { + return "root"; + } + return node.toString(); + } + + // ---------------- ---------------- // + + @Override + protected Location getLocation() { + return this.node != null ? this.node.getLocation() : Location.nullLoc; + } + + public OpApplNode getNode() { + return this.node; + } + + public boolean isRoot() { + return this.node == null; + } + + // ---------------- Parent <> Child ---------------- // + + public OpApplNodeWrapper addLets(OpApplNodeWrapper lets) { + this.lets.put(lets.getNode(), lets); + return this; + } + + public OpApplNodeWrapper setRecursive(OpApplNodeWrapper recursive) { + assert this.recursive == null; + this.recursive = recursive; + return this; + } + + @Override + public CostModelNode getRoot() { + assert this.root instanceof ActionWrapper; + return this.root; + } + + private final Set<Integer> seen = new HashSet<>(); + + @Override + public final CostModelNode get(final SemanticNode eon) { + if (eon == this.node || !(eon instanceof OpApplNode)) { + return this; + } + + CostModelNode child = children.get(eon); + if (child != null) { + return child; + } + + if (recursive != null) { + child = recursive.children.get(eon); + if (child != null) { + return child; + } + } + + if (lets != null) { + child = lets.get(eon); + if (child != null) { + return child; + } + } + + // TODO Not all places in Tool lookup the correct CM yet. This should only be an + // engineering effort though. + if (seen.add(eon.myUID)) { + //...only report it once to not spam the Toolbox console. + MP.printMessage(EC.TLC_COVERAGE_MISMATCH, new String[] { eon.toString(), this.toString() }); + } + return this; + } + + // ---------------- Level ---------------- // + + public int getLevel() { + return this.level; + } + + public OpApplNodeWrapper setLevel(int level) { + this.level = level; + return this; + } + + // ---------------- Primed ---------------- // + + public OpApplNodeWrapper setPrimed() { + assert !isPrimed(); + this.primed = true; + return this; + } + + protected boolean isPrimed() { + return this.primed; + } + + // ---------------- Print ---------------- // + + protected long getEvalCount(Calculate fresh) { + if (fresh == Calculate.FRESH) { + return super.getEvalCount(); + } else { + return snapshotEvalCount; + } + } + + public CostModel report() { + print(0, Calculate.FRESH); + return this; + } + + protected void print(int level, final Calculate fresh) { + final Set<Long> collectedEvalCounts = new HashSet<>(); + this.collectChildren(collectedEvalCounts, fresh); + if (collectedEvalCounts.isEmpty()) { + // Subtree has nothing to report. + if (getEvalCount(fresh) == 0l && !isPrimed()) { + // ..this node neither. + return; + } else { + printSelf(level++); + return; // Do not invoke printSelf(...) again below. + } + } + + if (collectedEvalCounts.size() == 1) { + final long count = getCount(collectedEvalCounts); + + if (count < getEvalCount(fresh)) { + // Cannot collapse subtree because inconsistent with this node. + printSelf(level++); + printChildren(level); + return; + } + if (!isPrimed() && getEvalCount(fresh) == 0l && count != 0l) { + // Collapse consistent subtree into this node unless this node is primed. + printSelf(level++, count); + return; + } + if (getEvalCount(fresh) == count && count == 0l) { + if (isPrimed()) { + printSelf(level++); + } + // Have a primed in subtree. + printChildren(level); + return; + } + if (getEvalCount(fresh) == count) { + // Have a primed in subtree. + printSelf(level++); + return; + } + } + + // Subtree is inconsistent and needs to report itself. + if (getEvalCount(fresh) > 0 || isPrimed()) { + printSelf(level++); + } + printChildren(level); + } + + private long getCount(Set<Long> collectWeights) { + assert collectWeights.size() == 1; + for (Long l : collectWeights) { + return l; + } + return -1l; // make compiler happy + } + + protected void printChildren(final int level) { + for (CostModelNode cmn : children.values()) { + ((OpApplNodeWrapper) cmn).print(level, Calculate.CACHED); + } + } + + protected void printSelf(final int level, final long count) { + MP.printMessage(EC.TLC_COVERAGE_VALUE, new String[] { + indentBegin(level, TLCGlobals.coverageIndent, getLocation().toString()), String.valueOf(count) }); + } + + protected void printSelf(final int level, final long count, final long cost) { + MP.printMessage(EC.TLC_COVERAGE_VALUE_COST, + new String[] { indentBegin(level, TLCGlobals.coverageIndent, getLocation().toString()), + String.valueOf(count), String.valueOf(cost) }); + } + + protected void printSelf(final int level) { + if (getSecondary() > 0) { + printSelf(level, getEvalCount(), getSecondary()); + } else { + printSelf(level, getEvalCount()); + } + } + + protected static String indentBegin(final int n, final char c, final String str) { + assert n >= 0; + final String whitespaces = new String(new char[n]).replace('\0', c); + return whitespaces + str; + } + + // ---------------- Child counts ---------------- // + + protected enum Calculate { + FRESH, CACHED; + } + + protected void collectChildren(final Set<Long> result, Calculate c) { + for (CostModelNode cmn : children.values()) { + ((OpApplNodeWrapper) cmn).collectAndFreezeEvalCounts(result, c); + } + } + + protected void collectAndFreezeEvalCounts(final Set<Long> result, final Calculate c) { + if (c == Calculate.FRESH) { + snapshotEvalCount = this.getEvalCount(c); + childCounts.clear(); + if (snapshotEvalCount > 0 || this.isPrimed()) { + childCounts.add(snapshotEvalCount); + } + collectChildren(childCounts, c); + } + result.addAll(childCounts); + } +} \ No newline at end of file diff --git a/tlatools/src/tlc2/tool/coverage/RecursiveOpApplNodeWrapper.java b/tlatools/src/tlc2/tool/coverage/RecursiveOpApplNodeWrapper.java new file mode 100644 index 0000000000000000000000000000000000000000..98ab4bcfa7c5d5adad94e569b4b2989e4b54cc79 --- /dev/null +++ b/tlatools/src/tlc2/tool/coverage/RecursiveOpApplNodeWrapper.java @@ -0,0 +1,45 @@ +/******************************************************************************* + * Copyright (c) Nov 20, 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.coverage; + +public final class RecursiveOpApplNodeWrapper extends OpApplNodeWrapper { + + RecursiveOpApplNodeWrapper() { + super(null, null); + } + + @Override + public boolean isRoot() { + assert this.getNode() == null; + return true; + } + + @Override + public CostModelNode getRoot() { + assert this.children.size() == 1; + return this.children.values().iterator().next(); + } +} diff --git a/tlatools/src/tlc2/tool/coverage/UnchangedOpApplNodeWrapper.java b/tlatools/src/tlc2/tool/coverage/UnchangedOpApplNodeWrapper.java new file mode 100644 index 0000000000000000000000000000000000000000..58a02229e49c932a39d6148c85fc2bf0af0bff46 --- /dev/null +++ b/tlatools/src/tlc2/tool/coverage/UnchangedOpApplNodeWrapper.java @@ -0,0 +1,56 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.coverage; + +import java.util.HashSet; +import java.util.Set; + +import tla2sany.semantic.OpApplNode; + +public class UnchangedOpApplNodeWrapper extends OpApplNodeWrapper { + + public UnchangedOpApplNodeWrapper(OpApplNode opApplNode, ActionWrapper root) { + super(opApplNode, root); + } + + @Override + protected boolean isPrimed() { + return true; + } + + @Override + protected void print(int level, final Calculate fresh) { + final Set<Long> collectedEvalCounts = new HashSet<>(); + this.collectChildren(collectedEvalCounts, fresh); + collectedEvalCounts.remove(0L); + if (collectedEvalCounts.isEmpty()) { + printSelf(level++); + return; + } else { + printSelf(level, Math.max(getEvalCount(), collectedEvalCounts.iterator().next())); + } + } +} diff --git a/tlatools/src/tlc2/tool/distributed/DistApp.java b/tlatools/src/tlc2/tool/distributed/DistApp.java index da2df940491f058d6df1803f87466c8574c95b1e..2f02277a4f47f4c4d5f78443b23307e2870e5bd5 100644 --- a/tlatools/src/tlc2/tool/distributed/DistApp.java +++ b/tlatools/src/tlc2/tool/distributed/DistApp.java @@ -7,6 +7,7 @@ package tlc2.tool.distributed; import java.rmi.RemoteException; +import tlc2.tool.IStateFunctor; import tlc2.tool.TLCState; import tlc2.tool.TLCStateInfo; import tlc2.tool.TraceApp; @@ -14,7 +15,6 @@ import tlc2.tool.WorkerException; /** * @author Simon Zambrovski - * @version $Id$ */ public abstract class DistApp implements TraceApp { @@ -35,7 +35,7 @@ public abstract class DistApp implements TraceApp { // Returns a list of initial states. // TLCServer - public abstract TLCState[] getInitStates() throws WorkerException; + public abstract void getInitStates(IStateFunctor functor); // Returns a list of successor states of the state s. / // TLCServer diff --git a/tlatools/src/tlc2/tool/distributed/RMIFilenameToStreamResolver.java b/tlatools/src/tlc2/tool/distributed/RMIFilenameToStreamResolver.java index f2160bcac1874f4537a2b74e88f136604bef932f..be69d22bde492547e6a0faf7544aac68da2aea73 100644 --- a/tlatools/src/tlc2/tool/distributed/RMIFilenameToStreamResolver.java +++ b/tlatools/src/tlc2/tool/distributed/RMIFilenameToStreamResolver.java @@ -52,7 +52,7 @@ public class RMIFilenameToStreamResolver implements FilenameToStream { // read the file from the server // strip off path - final String name = new File(filename).getName(); + final String name = new TLAFile(filename, this).getName(); File file = fileCache.get(name); // not in cache @@ -102,7 +102,7 @@ public class RMIFilenameToStreamResolver implements FilenameToStream { } private File writeToNewTempFile(String name, byte[] bs) { - final File f = new File(rndPrefix + File.separator + name); + final File f = new TLAFile(rndPrefix + File.separator + name, this); f.deleteOnExit(); FileOutputStream outputStream = null; diff --git a/tlatools/src/tlc2/tool/distributed/TLCApp.java b/tlatools/src/tlc2/tool/distributed/TLCApp.java index 84c45fad11a51057e1ce1c46cf0397e32de8411c..7e2bce787f2552c5c06000015e48b267431e916e 100644 --- a/tlatools/src/tlc2/tool/distributed/TLCApp.java +++ b/tlatools/src/tlc2/tool/distributed/TLCApp.java @@ -2,33 +2,33 @@ // Portions Copyright (c) 2003 Microsoft Corporation. All rights reserved. // Last modified on Mon 30 Apr 2007 at 15:29:57 PST by lamport // modified on Fri Jul 27 10:47:59 PDT 2001 by yuanyu - package tlc2.tool.distributed; import java.io.File; import java.io.IOException; +import java.util.List; import model.InJarFilenameToStream; import model.ModelInJar; import tlc2.TLCGlobals; import tlc2.tool.Action; +import tlc2.tool.IStateFunctor; +import tlc2.tool.ITool; import tlc2.tool.StateVec; import tlc2.tool.TLCState; import tlc2.tool.TLCStateInfo; -import tlc2.tool.Tool; import tlc2.tool.WorkerException; import tlc2.tool.fp.FPSet; import tlc2.tool.fp.FPSetConfiguration; +import tlc2.tool.impl.CallStackTool; +import tlc2.tool.impl.FastTool; import tlc2.util.FP64; -import tlc2.value.Value; import util.FileUtil; import util.FilenameToStream; +import util.TLAConstants; import util.ToolIO; import util.UniqueString; -/** - * @version $Id$ - */ public class TLCApp extends DistApp { private String config; @@ -36,25 +36,25 @@ public class TLCApp extends DistApp { /* Constructors */ public TLCApp(String specFile, String configFile, boolean deadlock, String fromChkpt, FPSetConfiguration fpSetConfig) throws IOException { - this(specFile, configFile, deadlock, true, null); + this(specFile, configFile, deadlock, null); this.fromChkpt = fromChkpt; - this.metadir = FileUtil.makeMetaDir(this.tool.specDir, fromChkpt); + this.metadir = FileUtil.makeMetaDir(this.tool.getSpecDir(), fromChkpt); this.fpSetConfig = fpSetConfig; } public TLCApp(String specFile, String configFile, boolean deadlock, String fromChkpt, FPSetConfiguration fpSetConfig, FilenameToStream fts) throws IOException { - this(specFile, configFile, deadlock, true, fts); + this(specFile, configFile, deadlock, fts); this.fromChkpt = fromChkpt; - this.metadir = FileUtil.makeMetaDir(this.tool.specDir, fromChkpt); + this.metadir = FileUtil.makeMetaDir(this.tool.getSpecDir(), fromChkpt); this.fpSetConfig = fpSetConfig; } // TODO too many constructors redefinitions, replace with this(..) calls public TLCApp(String specFile, String configFile, - Boolean deadlock, Boolean preprocess, FilenameToStream fts) throws IOException { + Boolean deadlock, FilenameToStream fts) throws IOException { // get the spec dir from the spec file int lastSep = specFile.lastIndexOf(File.separatorChar); @@ -64,15 +64,11 @@ public class TLCApp extends DistApp { this.config = configFile; - // TODO NameResolver - this.tool = new Tool(specDir, specFile, configFile, fts); - // SZ Feb 24, 2009: setup the user directory - ToolIO.setUserDir(specDir); - + this.checkDeadlock = deadlock.booleanValue(); - this.preprocess = preprocess.booleanValue(); - // SZ Feb 20, 2009: added null reference to SpecObj - this.tool.init(this.preprocess, null); + this.preprocess = true; + this.tool = new FastTool(specDir, specFile, configFile, fts); + this.impliedInits = this.tool.getImpliedInits(); this.invariants = this.tool.getInvariants(); this.impliedActions = this.tool.getImpliedActions(); @@ -80,13 +76,13 @@ public class TLCApp extends DistApp { } /* Fields */ - public Tool tool; + public ITool tool; public Action[] invariants; // the invariants to be checked public Action[] impliedInits; // the implied-inits to be checked public Action[] impliedActions; // the implied-actions to be checked public Action[] actions; // the subactions private boolean checkDeadlock; // check deadlock? - private boolean preprocess; // preprocess? + private final boolean preprocess; // preprocess? private String fromChkpt = null; // recover from this checkpoint private String metadir = null; // the directory pathname for metadata private FPSetConfiguration fpSetConfig; @@ -109,14 +105,14 @@ public class TLCApp extends DistApp { * @see tlc2.tool.distributed.DistApp#getFileName() */ public final String getFileName() { - return this.tool.rootFile; + return this.tool.getRootFile(); } /* (non-Javadoc) * @see tlc2.tool.distributed.DistApp#getSpecDir() */ public String getSpecDir() { - return this.tool.specDir; + return this.tool.getSpecDir(); } /* (non-Javadoc) @@ -139,23 +135,16 @@ public class TLCApp extends DistApp { public final boolean canRecover() { return this.fromChkpt != null; } + + public List<File> getModuleFiles() { + return this.tool.getModuleFiles(new InJarFilenameToStream(ModelInJar.PATH)); + } /* (non-Javadoc) - * @see tlc2.tool.distributed.DistApp#getInitStates() + * @see tlc2.tool.distributed.DistApp#getInitStates(tlc2.tool.IStateFunctor) */ - public final TLCState[] getInitStates() throws WorkerException { - StateVec theInitStates = this.tool.getInitStates(); - TLCState[] res = new TLCState[theInitStates.size()]; - for (int i = 0; i < theInitStates.size(); i++) { - TLCState curState = theInitStates.elementAt(i); - if (!this.tool.isGoodState(curState)) { - String msg = "Error: Initial state is not completely specified by the" - + " initial predicate."; - throw new WorkerException(msg, curState, null, false); - } - res[i] = (TLCState) curState; - } - return res; + public final void getInitStates(IStateFunctor functor) { + this.tool.getInitStates(functor); } /* (non-Javadoc) @@ -183,6 +172,10 @@ public class TLCApp extends DistApp { + " the next-state action."; throw new WorkerException(msg, curState, succState, false); } + // isInModel, isInAction and invariant and implied action checks are + // carried out later in TLCWorker#getNextStates(..) and below in + // checkState(State, State) when it is know that the states are new + // (after the call to the fingerprint set). res[i] = succState; } return res; @@ -268,16 +261,14 @@ public class TLCApp extends DistApp { * @see tlc2.tool.distributed.DistApp#setCallStack() */ public final void setCallStack() { - this.tool.setCallStack(); + this.tool = new CallStackTool(this.tool); } /* (non-Javadoc) * @see tlc2.tool.distributed.DistApp#printCallStack() */ public final String printCallStack() { - // SZ Jul 10, 2009: check if this is ok - // changed the method signature - return this.tool.getCallStack().toString(); + return this.tool.toString(); } @SuppressWarnings("deprecation") @@ -296,9 +287,9 @@ public class TLCApp extends DistApp { index++; if (index < args.length) { configFile = args[index]; - int len = configFile.length(); - if (configFile.startsWith(".cfg", len - 4)) { - configFile = configFile.substring(0, len - 4); + if (configFile.endsWith(TLAConstants.Files.CONFIG_EXTENSION)) { + configFile = configFile.substring(0, + (configFile.length() - TLAConstants.Files.CONFIG_EXTENSION.length())); } index++; } else { @@ -340,12 +331,18 @@ public class TLCApp extends DistApp { index++; if (index < args.length) { try { - TLCGlobals.coverageInterval = Integer - .parseInt(args[index]) * 1000; - if (TLCGlobals.coverageInterval < 0) { - printErrorMsg("Error: expect a nonnegative integer for -coverage option."); - return null; - } + // Coverage reporting is broken is distributed TLC. Thus + // warn the user and continue ignoring the parameter. + // Consume its value though. + ToolIO.out.println( + "Warning: coverage reporting not supported in distributed TLC, ignoring -coverage " + + args[index] + " parameter."); +// TLCGlobals.coverageInterval = Integer +// .parseInt(args[index]) * 1000; +// if (TLCGlobals.coverageInterval < 0) { +// printErrorMsg("Error: expect a nonnegative integer for -coverage option."); +// return null; +// } index++; } catch (Exception e) { printErrorMsg("Error: An integer for coverage report interval required." @@ -358,7 +355,7 @@ public class TLCApp extends DistApp { } } else if (args[index].equals("-terse")) { index++; - Value.expand = false; + TLCGlobals.expand = false; } else if (args[index].equals("-nowarning")) { index++; TLCGlobals.warn = false; @@ -497,9 +494,8 @@ public class TLCApp extends DistApp { return null; } specFile = args[index++]; - int len = specFile.length(); - if (specFile.startsWith(".tla", len - 4)) { - specFile = specFile.substring(0, len - 4); + if (specFile.endsWith(TLAConstants.Files.TLA_EXTENSION)) { + specFile = specFile.substring(0, (specFile.length() - TLAConstants.Files.TLA_EXTENSION.length())); } } } @@ -509,12 +505,13 @@ public class TLCApp extends DistApp { // indicator to check the in-jar model/ folder for a spec. // If a spec is found, use it instead. if (ModelInJar.hasModel()) { + ModelInJar.loadProperties(); // Reads result.mail.address and so on. TLCGlobals.tool = true; // always run in Tool mode (to parse output by Toolbox later) TLCGlobals.chkptDuration = 0; // never use checkpoints with distributed TLC (highly inefficient) FP64.Init(fpIndex); FilenameToStream resolver = new InJarFilenameToStream(ModelInJar.PATH); - return new TLCApp("MC", "MC", deadlock, fromChkpt, - fpSetConfig, resolver); + return new TLCApp(TLAConstants.Files.MODEL_CHECK_FILE_BASENAME, TLAConstants.Files.MODEL_CHECK_FILE_BASENAME, + deadlock, fromChkpt, fpSetConfig, resolver); } printErrorMsg("Error: Missing input TLA+ module."); diff --git a/tlatools/src/tlc2/tool/distributed/TLCServer.java b/tlatools/src/tlc2/tool/distributed/TLCServer.java index 51649a13303d0f1f668f38d2de5d3cd8d9c1fe64..e6b199d3bd03d0b0622f0c00aedef9ef5d41252a 100644 --- a/tlatools/src/tlc2/tool/distributed/TLCServer.java +++ b/tlatools/src/tlc2/tool/distributed/TLCServer.java @@ -10,30 +10,36 @@ import java.io.FileInputStream; import java.io.IOException; import java.net.InetAddress; import java.net.URI; +import java.rmi.AccessException; +import java.rmi.ConnectException; import java.rmi.NoSuchObjectException; import java.rmi.NotBoundException; import java.rmi.RemoteException; +import java.rmi.ServerException; import java.rmi.registry.LocateRegistry; import java.rmi.registry.Registry; import java.rmi.server.UnicastRemoteObject; +import java.util.ArrayList; import java.util.ConcurrentModificationException; +import java.util.List; import java.util.Map; import java.util.Map.Entry; -import java.util.Set; -import java.util.SortedSet; -import java.util.TreeSet; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.atomic.AtomicLong; +import model.InJarFilenameToStream; +import model.ModelInJar; +import tlc2.TLC; import tlc2.TLCGlobals; import tlc2.output.EC; import tlc2.output.MP; +import tlc2.tool.EvalException; +import tlc2.tool.IStateFunctor; import tlc2.tool.ModelChecker; import tlc2.tool.TLCState; import tlc2.tool.TLCTrace; -import tlc2.tool.WorkerException; import tlc2.tool.distributed.fp.FPSetManager; import tlc2.tool.distributed.fp.FPSetRMI; import tlc2.tool.distributed.fp.IFPSetManager; @@ -48,15 +54,24 @@ import tlc2.tool.queue.DiskStateQueue; import tlc2.tool.queue.IStateQueue; import tlc2.util.FP64; import util.Assert; +import util.Assert.TLCRuntimeException; import util.FileUtil; import util.MailSender; -import util.SimpleFilenameToStream; import util.UniqueString; @SuppressWarnings("serial") public class TLCServer extends UnicastRemoteObject implements TLCServerRMI, InternRMI { + /** + * Name by which {@link FPSetRMI} lookup the {@link TLCServer} (master). + */ + public static final String SERVER_NAME = "TLCServer"; + /** + * Name by which {@link TLCWorker} lookup the {@link TLCServer} (master). + */ + public static final String SERVER_WORKER_NAME = SERVER_NAME + "WORKER"; + /** * Prefix master and worker heavy workload threads with this prefix and an incrementing counter to * make the threads identifiable in jmx2munin statistics, which uses simple string matching. @@ -187,7 +202,7 @@ public class TLCServer extends UnicastRemoteObject implements TLCServerRMI, final FPSet fpSet = FPSetFactory.getFPSet(work.getFPSetConfiguration()); fpSet.init(1, metadir, work.getFileName()); return new NonDistributedFPSetManager(fpSet, InetAddress.getLocalHost() - .getCanonicalHostName()); + .getCanonicalHostName(), trace); } /* (non-Javadoc) @@ -274,7 +289,7 @@ public class TLCServer extends UnicastRemoteObject implements TLCServerRMI, * leaving the TimerTask running. This occurs by design if the TimerTask * has already been marked for execution) * - * @see https://bugzilla.tlaplus.net/show_bug.cgi?id=216 + * @see Bug #216 in general/bugzilla/index.html */ if (worker != null) { MP.printMessage(EC.TLC_DISTRIBUTED_WORKER_DEREGISTERED, thread.getUri().toString()); @@ -360,40 +375,17 @@ public class TLCServer extends UnicastRemoteObject implements TLCServerRMI, } /** - * @throws Exception + * @throws Throwable */ - private final Set<Long> doInit() throws Exception { - final SortedSet<Long> set = new TreeSet<Long>(); - TLCState curState = null; - try { - TLCState[] initStates = work.getInitStates(); - for (int i = 0; i < initStates.length; i++) { - curState = initStates[i]; - boolean inConstraints = work.isInModel(curState); - boolean seen = false; - if (inConstraints) { - long fp = curState.fingerPrint(); - seen = !set.add(fp); - if (!seen) { - initStates[i].uid = trace.writeState(fp); - stateQueue.enqueue(initStates[i]); - } - } - if (!inConstraints || !seen) { - work.checkState(null, curState); - } - } - } catch (Exception e) { - this.errState = curState; - this.keepCallStack = true; - if (e instanceof WorkerException) { - this.errState = ((WorkerException) e).state2; - this.keepCallStack = ((WorkerException) e).keepCallStack; - } - this.done = true; - throw e; + private final void doInit() throws Throwable { + final DoInitFunctor functor = new DoInitFunctor(); + work.getInitStates(functor); + + // Iff one of the init states' checks violates any properties, the + // functor will record it. + if (functor.e != null) { + throw functor.e; } - return set; } /** @@ -415,6 +407,8 @@ public class TLCServer extends UnicastRemoteObject implements TLCServerRMI, * @throws NotBoundException */ protected void modelCheck() throws IOException, InterruptedException, NotBoundException { + final long startTime = System.currentTimeMillis(); + /* * Before we initialize the server, we check if recovery is requested */ @@ -427,68 +421,89 @@ public class TLCServer extends UnicastRemoteObject implements TLCServerRMI, String.valueOf(stateQueue.size())}); recovered = true; } + + // Create the central naming authority that is used by _all_ nodes + String hostname = InetAddress.getLocalHost().getHostName(); + Registry rg = LocateRegistry.createRegistry(Port); + rg.rebind(SERVER_NAME, this); + + // First register TLCSERVER with RMI and only then wait for all FPSets + // to become registered. This only waits if we use distributed + // fingerprint set (FPSet) servers which have to partition the + // distributed hash table (fingerprint space) prior to starting model + // checking. + waitForFPSetManager(); /* * Start initializing the server by calculating the init state(s) */ - - //TODO if init states is huge, this might go OOM - Set<Long> initFPs = new TreeSet<Long>(); if (!recovered) { // Initialize with the initial states: try { MP.printMessage(EC.TLC_COMPUTING_INIT); - initFPs = doInit(); + doInit(); MP.printMessage(EC.TLC_INIT_GENERATED1, new String[] { String.valueOf(stateQueue.size()), "(s)" }); } catch (Throwable e) { // Assert.printStack(e); done = true; - // LL modified error message on 7 April 2012 - MP.printError(EC.GENERAL, "initializing the server", e); // LL changed call 7 April 2012 - if (errState != null) { - MP.printMessage(EC.TLC_INITIAL_STATE, "While working on the initial state: " + errState); - } - // We redo the work on the error state, recording the call - // stack. - work.setCallStack(); - try { - initFPs = doInit(); - } catch (Throwable e1) { - MP.printError(EC.GENERAL, "evaluating the nested" // LL changed call 7 April 2012 - + "\nexpressions at the following positions:\n" - + work.printCallStack(), e); + + // Distributed TLC does not support TLCGet/TLCSet operator. It + // would require synchronization among all (distributed) + // workers. In distributed mode, it is of limited use anyway. + if (e instanceof EvalException + && ((EvalException) e).getErrorCode() == EC.TLC_MODULE_TLCGET_UNDEFINED + && (((EvalException) e).getMessage().contains("TLCSet") + || ((EvalException) e).getMessage().contains("TLCGet")) + || (e instanceof TLCRuntimeException && ((TLCRuntimeException) e).errorCode == EC.TLC_MODULE_VALUE_JAVA_METHOD_OVERRIDE)) { + MP.printError(EC.TLC_FEATURE_UNSUPPORTED, + "TLCSet & TLCGet operators not supported by distributed TLC."); + } else { + String msg = e.getMessage(); + if (msg == null) { + msg = e.toString(); + } + if (!this.hasNoErrors()) { + MP.printError(EC.TLC_INITIAL_STATE, new String[] { msg, this.errState.toString() }); + } else { + MP.printError(EC.GENERAL, msg); + } + // We redo the work on the error state, recording the call + // stack. + work.setCallStack(); + try { + doInit(); + } catch (Throwable e1) { + // Assert.printStack(e); + MP.printError(EC.TLC_NESTED_EXPRESSION, work.printCallStack()); + } } } } if (done) { + printSummary(1, 0, stateQueue.size(), fpSetManager.size(), false); + MP.printMessage(EC.TLC_FINISHED, + TLC.convertRuntimeToHumanReadable(System.currentTimeMillis() - startTime)); + es.shutdown(); // clean up before exit: close(false); return; } - - // Create the central naming authority that is used by _all_ nodes - String hostname = InetAddress.getLocalHost().getHostName(); - Registry rg = LocateRegistry.createRegistry(Port); - rg.rebind("TLCServer", this); - MP.printMessage(EC.TLC_DISTRIBUTED_SERVER_RUNNING, hostname); - - // First register TLCSERVER with RMI and only then wait for all FPSets - // to become registered. This only waits if we use distributed - // fingerprint set (FPSet) servers which have to partition the - // distributed hash table (fingerprint space) prior to starting model - // checking. - waitForFPSetManager(); - - // Add the init state(s) to the local FPSet or distributed servers - for (Long fp : initFPs) { - fpSetManager.put(fp); - } + // Init states have been computed successfully which marks the point in + // time where workers can start generating and exploring next states. + rg.rebind(SERVER_WORKER_NAME, this); + /* * This marks the end of the master and FPSet server initialization. * Model checking can start now. + * Print the startup message now, because the Toolbox is supposed to + * show that it's waiting for workers to connect. If the messaage + * gets printed earlier, it's replaced by EC.TLC_INIT_GENERATED1 + * right away and the user can consequently miss that TLCServer is + * running. */ + MP.printMessage(EC.TLC_DISTRIBUTED_SERVER_RUNNING, hostname); // Model checking results to be collected after model checking has finished long oldNumOfGenStates = 0; @@ -515,9 +530,13 @@ public class TLCServer extends UnicastRemoteObject implements TLCServerRMI, distinctStatesPerMinute = (long) ((fpSetSize - oldFPSetSize) / factor); // print to system.out - MP.printMessage(EC.TLC_PROGRESS_STATS, new String[] { String.valueOf(trace.getLevelForReporting()), - String.valueOf(numOfGenStates), String.valueOf(fpSetSize), - String.valueOf(getNewStates()), String.valueOf(statesPerMinute), String.valueOf(distinctStatesPerMinute) }); + MP.printMessage(EC.TLC_PROGRESS_STATS, new String[] { + String.valueOf(trace.getLevelForReporting()), + MP.format(numOfGenStates), + MP.format(fpSetSize), + MP.format(getNewStates()), + MP.format(statesPerMinute), + MP.format(distinctStatesPerMinute) }); // Make the TLCServer main thread sleep for one report interval wait(REPORT_INTERVAL); @@ -562,6 +581,14 @@ public class TLCServer extends UnicastRemoteObject implements TLCServerRMI, } catch (NoSuchObjectException e) { // worker might have been lost in the meantime MP.printWarning(EC.GENERAL, "Ignoring attempt to exit dead worker"); + } catch (ConnectException e) { + // worker might have been lost in the meantime + MP.printWarning(EC.GENERAL, "Ignoring attempt to exit dead worker"); + } catch (ServerException e) { + // worker might have been lost in the meantime + MP.printWarning(EC.GENERAL, "Ignoring attempt to exit dead worker"); + } finally { + threadsToWorkers.remove(thread); } } @@ -581,9 +608,9 @@ public class TLCServer extends UnicastRemoteObject implements TLCServerRMI, // Postprocessing: if (hasNoErrors()) { // We get here because the checking has succeeded. - final double actualProb = fpSetManager.checkFPs(); + final long actualDistance = fpSetManager.checkFPs(); final long statesSeen = fpSetManager.getStatesSeen(); - ModelChecker.reportSuccess(finalNumberOfDistinctStates, actualProb, statesSeen); + ModelChecker.reportSuccess(finalNumberOfDistinctStates, actualDistance, statesSeen); } else if (keepCallStack) { // We redo the work on the error state, recording the call stack. work.setCallStack(); @@ -591,14 +618,16 @@ public class TLCServer extends UnicastRemoteObject implements TLCServerRMI, // Finally print the results printSummary(level, statesGenerated, statesLeftInQueue, finalNumberOfDistinctStates, hasNoErrors()); - MP.printMessage(EC.TLC_FINISHED); + MP.printMessage(EC.TLC_FINISHED, + TLC.convertRuntimeToHumanReadable(System.currentTimeMillis() - startTime)); MP.flush(); // Close trace and (distributed) _FPSet_ servers! close(hasNoErrors()); // dispose RMI leftovers - rg.unbind("TLCServer"); + rg.unbind(SERVER_WORKER_NAME); + rg.unbind(SERVER_NAME); UnicastRemoteObject.unexportObject(this, false); } @@ -654,9 +683,12 @@ public class TLCServer extends UnicastRemoteObject implements TLCServerRMI, public static final void printSummary(int level, long statesGenerated, long statesLeftInQueue, long distinctStates, boolean success) throws IOException { if (TLCGlobals.tool) { - MP.printMessage(EC.TLC_PROGRESS_STATS, new String[] { String.valueOf(level), - String.valueOf(statesGenerated), String.valueOf(distinctStates), - String.valueOf(statesLeftInQueue), "0", "0" }); + MP.printMessage(EC.TLC_PROGRESS_STATS, new String[] { + String.valueOf(level), + MP.format(statesGenerated), + MP.format(distinctStates), + MP.format(statesLeftInQueue), + "0", "0" }); } MP.printMessage(EC.TLC_STATS, new String[] { String.valueOf(statesGenerated), @@ -666,15 +698,23 @@ public class TLCServer extends UnicastRemoteObject implements TLCServerRMI, } } - public static void main(String argv[]) { - MP.printMessage(EC.GENERAL, "TLC Server " + TLCGlobals.versionOfTLC); + public static void main(String[] argv) { + // Print version before MailSender has been created. Oh well, it misses + // the version output. + MP.printMessage(EC.TLC_VERSION, "TLC Server " + TLCGlobals.versionOfTLC); TLCStandardMBean tlcServerMXWrapper = TLCStandardMBean.getNullTLCStandardMBean(); MailSender mail = null; TLCServer server = null; + TLCApp app = null; try { TLCGlobals.setNumWorkers(0); - final TLCApp app = TLCApp.create(argv); - mail = new MailSender(app.getFileName()); + // Create MS before TLCApp to capture the parsing output. + mail = new MailSender(); + + app = TLCApp.create(argv); + mail.setModelName(System.getProperty(MailSender.MODEL_NAME, app.getFileName())); + mail.setSpecName(System.getProperty(MailSender.SPEC_NAME, app.getFileName())); + if (expectedFPSetCount > 0) { server = new DistributedFPSetTLCServer(app, expectedFPSetCount); } else { @@ -703,18 +743,29 @@ public class TLCServer extends UnicastRemoteObject implements TLCServerRMI, } } } finally { + if (!server.es.isShutdown()) { + server.es.shutdownNow(); + } tlcServerMXWrapper.unregister(); - boolean send = mail.send(); - // In case sending the mail has failed we treat this as an error. - // This is needed when TLC runs on another host and email is - // the only means for the user to get access to the model checking - // results. - // With distributed TLC and CloudDistributedTLCJob in particular, - // the cloud node is immediately turned off once the TLC process has - // finished. If we were to shutdown the node even when sending out - // the email has failed, the result would be lost. - if (!send) { - System.exit(1); + // When creation of TLCApp fails, we get here as well. + if (mail != null) { + List<File> files = new ArrayList<File>(); + if (app != null) { + files = app.getModuleFiles(); + } + boolean send = mail.send(files); + // In case sending the mail has failed we treat this as an error. + // This is needed when TLC runs on another host and email is + // the only means for the user to get access to the model checking + // results. + // With distributed TLC and CloudDistributedTLCJob in particular, + // the cloud node is immediately turned off once the TLC process has + // finished. If we were to shutdown the node even when sending out + // the email has failed, the result would be lost. + if (!send) { + MP.printMessage(EC.GENERAL, "Sending result mail failed."); + System.exit(1); + } } } } @@ -767,7 +818,7 @@ public class TLCServer extends UnicastRemoteObject implements TLCServerRMI, String name = new File(file).getName(); // Resolve all - File f = new SimpleFilenameToStream().resolve(name); + File f = new InJarFilenameToStream(ModelInJar.PATH).resolve(name); return read(f); } @@ -807,6 +858,41 @@ public class TLCServer extends UnicastRemoteObject implements TLCServerRMI, return buffer; } + private class DoInitFunctor implements IStateFunctor { + + private Throwable e; + + /* (non-Javadoc) + * @see tlc2.tool.IStateFunctor#addElement(tlc2.tool.TLCState) + */ + public Object addElement(final TLCState curState) { + if (e != null) { + return curState; + } + + try { + final boolean inConstraints = work.isInModel(curState); + boolean seen = false; + if (inConstraints) { + long fp = curState.fingerPrint(); + seen = fpSetManager.put(fp); + if (!seen) { + curState.uid = trace.writeState(fp); + stateQueue.enqueue(curState); + } + } + if (!inConstraints || !seen) { + work.checkState(null, curState); + } + } catch (Exception e) { + if (setErrState(curState, true)) { + this.e = e; + } + } + + return curState; + } + } /** * Tries to exit all connected workers @@ -823,6 +909,24 @@ public class TLCServer extends UnicastRemoteObject implements TLCServerRMI, * @see java.lang.Runnable#run() */ public void run() { + if (server.threadsToWorkers.isEmpty()) { + // Nothing to be done here. + return; + } + + try { + // No need to attempt to exit workers if the server itself + // isn't registered any longer. It won't be able to connect to + // workers anyway. + LocateRegistry.getRegistry(Port).lookup(SERVER_NAME); + } catch (AccessException e1) { + return; + } catch (RemoteException e1) { + return; + } catch (NotBoundException e1) { + return; + } + for (TLCWorkerRMI worker : server.threadsToWorkers.values()) { try { worker.exit(); diff --git a/tlatools/src/tlc2/tool/distributed/TLCServerThread.java b/tlatools/src/tlc2/tool/distributed/TLCServerThread.java index fa66f1b53e44cfa5decaf5d2f5cdc8a9bbeab0f7..226ab2592043a4240949b3987562ed59fe4bc4ac 100644 --- a/tlatools/src/tlc2/tool/distributed/TLCServerThread.java +++ b/tlatools/src/tlc2/tool/distributed/TLCServerThread.java @@ -255,13 +255,14 @@ public class TLCServerThread extends IdThread { state2 = ((WorkerException) e).state2; } if (this.tlcServer.setErrState(state1, true)) { - MP.printError(EC.GENERAL, e); if (state1 != null) { try { this.tlcServer.trace.printTrace(state1, state2); } catch (Exception e1) { MP.printError(EC.GENERAL, e1); } + } else { + MP.printError(EC.GENERAL, e); } stateQueue.finishAll(); synchronized (this.tlcServer) { @@ -318,9 +319,9 @@ public class TLCServerThread extends IdThread { keepAliveTimer.cancel(); // This call has to be idempotent, otherwise we see bugs as in - // https://bugzilla.tlaplus.net/show_bug.cgi?id=234 + // Bug #234 in general/bugzilla/index.html // - // Prevent second invocation of worker de-registration which stamps from + // Prevent second invocation of worker de-registration which stems from // a race condition between the TimerTask (that periodically checks // server aliveness) and the exception handling that kicks in when the // run() method catches an RMI exception. diff --git a/tlatools/src/tlc2/tool/distributed/TLCWorker.java b/tlatools/src/tlc2/tool/distributed/TLCWorker.java index 14d486ffd417a279f279da0bf9d770467c05d3ea..b4cbeca16890a016137f0ce7bf0aab8f446fc9f6 100644 --- a/tlatools/src/tlc2/tool/distributed/TLCWorker.java +++ b/tlatools/src/tlc2/tool/distributed/TLCWorker.java @@ -14,6 +14,7 @@ import java.net.UnknownHostException; import java.rmi.ConnectException; import java.rmi.Naming; import java.rmi.NoSuchObjectException; +import java.rmi.NotBoundException; import java.rmi.RemoteException; import java.rmi.server.UnicastRemoteObject; import java.util.Date; @@ -305,8 +306,8 @@ public class TLCWorker extends UnicastRemoteObject implements TLCWorkerRMI { cdl = new CountDownLatch(numCores); try { - String url = "//" + serverName + ":" + TLCServer.Port - + "/TLCServer"; + final String url = "//" + serverName + ":" + TLCServer.Port + + "/" + TLCServer.SERVER_WORKER_NAME; // try to repeatedly connect to the server until it becomes available int i = 1; @@ -331,7 +332,30 @@ public class TLCWorker extends UnicastRemoteObject implements TLCWorkerRMI { // how to handle throw e; } + } catch (NotBoundException e) { + // Registry is available but no object by "TLCServer". This + // happens when TLCServer makes it registry available but + // has't registered itself yet. + long sleep = (long) Math.sqrt(i); + ToolIO.out.println("Server " + serverName + " reachable but not ready yet, sleeping " + sleep + + "s for server to come online..."); + Thread.sleep(sleep * 1000); + i *= 2; } + // It is vital to *not* catch NoRouteToHostException or + // UnknownHostException. TLCWorker is supposed to terminate when + // either of the two is thrown. The rational is that the while + // loop could be going while TLCServer is busy generating a huge + // set of init states. Close to completion, it finds a violating + // state and terminates. In case of cloud distributed TLC + // (see CloudDistributedTLCJob), the host/vm running the master + // immediately shuts down. That is when the NoRouteToHostException + // will make sure that the set of TLCWorkers will terminate the VM + // and shutdown the vm too. There is obviously the chance that + // another vm gets the IP of the former master assigned and boots + // up during a sleep period above. Even though I do not know what + // holding period IP address have across the various cloud providers, + // I find this rather unlikely. } long irredPoly = server.getIrredPolyForFP(); @@ -348,8 +372,7 @@ public class TLCWorker extends UnicastRemoteObject implements TLCWorkerRMI { fts.setTLCServer(server); DistApp work = new TLCApp(server.getSpecFileName(), - server.getConfigFileName(), server.getCheckDeadlock(), - server.getPreprocess(), fts); + server.getConfigFileName(), server.getCheckDeadlock(), fts); final IFPSetManager fpSetManager = server.getFPSetManager(); diff --git a/tlatools/src/tlc2/tool/distributed/fp/DistributedFPSet.java b/tlatools/src/tlc2/tool/distributed/fp/DistributedFPSet.java index 9017beb6e4ed1f6058deb58c4803838a6346c8d5..4653408b73fa68e3cec3f86a4c1542017fac7774 100644 --- a/tlatools/src/tlc2/tool/distributed/fp/DistributedFPSet.java +++ b/tlatools/src/tlc2/tool/distributed/fp/DistributedFPSet.java @@ -115,9 +115,11 @@ public class DistributedFPSet { private static TLCServerRMI lookupTLCServer(final String serverName) throws MalformedURLException, RemoteException, NotBoundException, InterruptedException { String url = "//" + serverName + ":" + TLCServer.Port - + "/TLCServer"; + + "/" + TLCServer.SERVER_NAME; - // try to repeatedly connect to the server until it becomes available + // try to repeatedly connect to the server until it becomes available. + // see similar while loop in + // tlc2.tool.distributed.TLCWorker.main(String[]) for more comments. int i = 1; while(true) { try { @@ -138,6 +140,15 @@ public class DistributedFPSet { // how to handle throw e; } + } catch (NotBoundException e) { + // Registry is available but no object by "TLCServer". This + // happens when TLCServer makes it registry available but + // has't registered itself yet. + long sleep = (long) Math.sqrt(i); + ToolIO.out.println("Server " + serverName + " reachable but not ready yet, sleeping " + sleep + + "s for server to come online..."); + Thread.sleep(sleep * 1000); + i *= 2; } } } diff --git a/tlatools/src/tlc2/tool/distributed/fp/DynamicFPSetManager.java b/tlatools/src/tlc2/tool/distributed/fp/DynamicFPSetManager.java index e37d6334a5c2aa7d5c88f9ef3d7fba8ea2c25ad0..af1f3d10a398a7b798bc1ebe95f33c39fa5fe2a0 100644 --- a/tlatools/src/tlc2/tool/distributed/fp/DynamicFPSetManager.java +++ b/tlatools/src/tlc2/tool/distributed/fp/DynamicFPSetManager.java @@ -21,6 +21,12 @@ public class DynamicFPSetManager extends FPSetManager implements Serializable { public DynamicFPSetManager(int expectedNumOfServers) throws RemoteException { super(); + // If expectedNumOfServers is no power of two, a subset of FPSet + // servers will act as passive backups. Each time a FPSet from + // the active set is lost, all workers switch over to the next + // available passive one on a list. The list has to be known + // prior to when the workers start (it's possible to send out + // list updates, but it hasn't been implemented). this.expectedNumOfServers = expectedNumOfServers; // Guard against invalid values @@ -36,7 +42,7 @@ public class DynamicFPSetManager extends FPSetManager implements Serializable { } // Zero upper bits of mask which won't be used when addressing the - // fingerprint servers anyway. + // fingerprint servers anyway. See IFPSetManager#getFPSetIndex. this.mask = (1L << log) - 1L; } diff --git a/tlatools/src/tlc2/tool/distributed/fp/FPSetManager.java b/tlatools/src/tlc2/tool/distributed/fp/FPSetManager.java index 28537d9d0db2386a5f9a91e692032d7478a20eb7..582ff06b056529c0c7ceace497ec4e7eaa80bf83 100644 --- a/tlatools/src/tlc2/tool/distributed/fp/FPSetManager.java +++ b/tlatools/src/tlc2/tool/distributed/fp/FPSetManager.java @@ -29,6 +29,7 @@ import java.util.logging.Logger; import tlc2.output.EC; import tlc2.tool.distributed.fp.callable.BitVectorWrapper; import tlc2.tool.distributed.fp.callable.CheckFPsCallable; +import tlc2.tool.distributed.fp.callable.CheckInvariantCallable; import tlc2.tool.distributed.fp.callable.ContainsBlockCallable; import tlc2.tool.distributed.fp.callable.PutBlockCallable; import tlc2.util.BitVector; @@ -206,16 +207,11 @@ public abstract class FPSetManager implements IFPSetManager { return hostname; } - protected int getIndex(long fp) { - long l = fp & mask; - return (int) (l % this.fpSets.size()); - } - /* (non-Javadoc) * @see tlc2.tool.distributed.IFPSetManager#put(long) */ public boolean put(long fp) { - int fpIdx = getIndex(fp); + int fpIdx = getFPSetIndex(fp); while (true) { try { return this.fpSets.get(fpIdx).put(fp); @@ -236,7 +232,7 @@ public abstract class FPSetManager implements IFPSetManager { * @see tlc2.tool.distributed.IFPSetManager#contains(long) */ public boolean contains(long fp) { - int fpIdx = getIndex(fp); + int fpIdx = getFPSetIndex(fp); while (true) { try { return this.fpSets.get(fpIdx).contains(fp); @@ -425,19 +421,19 @@ public abstract class FPSetManager implements IFPSetManager { /* (non-Javadoc) * @see tlc2.tool.distributed.IFPSetManager#checkFPs() */ - public double checkFPs() { + public long checkFPs() { final int len = this.fpSets.size(); // Instantiation of a thread pool here is fine, as long as checkFPs is only called seldomly. final ExecutorService executorService = Executors.newFixedThreadPool(len); try { // Start checkFP on all FPSets concurrently // (checkFPs scans the full set sequentially!) - final CompletionService<Double> ecs = new ExecutorCompletionService<Double>(executorService); + final CompletionService<Long> ecs = new ExecutorCompletionService<Long>(executorService); for (int i = 0; i < len; i++) { ecs.submit(new CheckFPsCallable(fpSets.get(i).getFpset())); } // Return minimum value - double res = Double.MAX_VALUE; + long res = Long.MAX_VALUE; for (int i = 0; i < len; i++) { try { res = Math.min(res, ecs.take().get()); @@ -458,6 +454,43 @@ public abstract class FPSetManager implements IFPSetManager { } } + /* (non-Javadoc) + * @see tlc2.tool.distributed.fp.IFPSetManager#checkInvariant() + */ + public boolean checkInvariant() { + final int len = this.fpSets.size(); + // Instantiation of a thread pool here is fine, as long as checkFPs is only called seldomly. + final ExecutorService executorService = Executors.newFixedThreadPool(len); + try { + // Start checkFP on all FPSets concurrently + // (checkFPs scans the full set sequentially!) + final CompletionService<Boolean> ecs = new ExecutorCompletionService<Boolean>(executorService); + for (int i = 0; i < len; i++) { + ecs.submit(new CheckInvariantCallable(fpSets.get(i).getFpset())); + } + // Return minimum value + for (int i = 0; i < len; i++) { + try { + if(!ecs.take().get()) { + return false; + } + } catch (InterruptedException e) { + // not expected to happen, could return an approximation + // if happens (but fail fast for the moment). + e.printStackTrace(); + } catch (ExecutionException e) { + // not expected to happen, could return an approximation + // if happens (but fail fast for the moment). + e.printStackTrace(); + } + } + return true; + } finally { + // Always shutdown the executor service + executorService.shutdown(); + } + } + /* (non-Javadoc) * @see tlc2.tool.distributed.IFPSetManager#size() */ diff --git a/tlatools/src/tlc2/tool/distributed/fp/FPSetRMI.java b/tlatools/src/tlc2/tool/distributed/fp/FPSetRMI.java index bd9ef02490b1b389567f95c4714750c76c8d1b66..b44d0e5200af17117ef6a26ccde5b5081e38590a 100644 --- a/tlatools/src/tlc2/tool/distributed/fp/FPSetRMI.java +++ b/tlatools/src/tlc2/tool/distributed/fp/FPSetRMI.java @@ -8,6 +8,7 @@ import java.io.IOException; import java.rmi.Remote; import java.rmi.RemoteException; +import tlc2.tool.TLCTrace; import tlc2.tool.fp.FPSet; import tlc2.util.BitVector; import tlc2.util.LongVec; @@ -32,8 +33,13 @@ public interface FPSetRMI extends Remote { * This distance reflects the probability of a fingerprint * collision. */ - double checkFPs() throws IOException; - + long checkFPs() throws IOException; + + /** + * @see FPSet#checkInvariant() + */ + boolean checkInvariant() throws IOException; + /** * Disposes this {@link FPSet}. It cannot be used afterwards anymore. * @@ -97,7 +103,7 @@ public interface FPSetRMI extends Remote { */ BitVector putBlock(LongVec fpv) throws IOException; - void recover() throws IOException; + void recover(TLCTrace trace) throws IOException; void recover(String filename) throws IOException; diff --git a/tlatools/src/tlc2/tool/distributed/fp/IFPSetManager.java b/tlatools/src/tlc2/tool/distributed/fp/IFPSetManager.java index 480f8cf24a44bc5b5e33a60d5de23907d3b52825..11299ec9f16a4d0d838f8d3a0b6d40adba4f7fd6 100644 --- a/tlatools/src/tlc2/tool/distributed/fp/IFPSetManager.java +++ b/tlatools/src/tlc2/tool/distributed/fp/IFPSetManager.java @@ -6,6 +6,9 @@ import java.io.Serializable; import java.util.concurrent.ExecutorService; import tlc2.tool.distributed.fp.FPSetManager.FPSets; +import tlc2.tool.fp.FPSet; +import tlc2.tool.fp.FPSetConfiguration; +import tlc2.tool.fp.MSBDiskFPSet; import tlc2.util.BitVector; import tlc2.util.LongVec; @@ -17,8 +20,13 @@ public interface IFPSetManager extends Serializable { /** * @see FPSetRMI#checkFPs() */ - double checkFPs(); + long checkFPs(); + /** + * @see FPSetRMI#checkInvariant() + */ + boolean checkInvariant(); + /** */ void checkpoint(String fname) throws InterruptedException, IOException; @@ -62,13 +70,27 @@ public interface IFPSetManager extends Serializable { BitVector[] containsBlock(LongVec[] fps, ExecutorService executorService); /** - * The index address of the {@link FPSetRMI} corresponding with the given + * The index of the {@link FPSetRMI} corresponding with the given * fingerprint in this {@link IFPSetManager}. It is used by worker nodes to * pre-sort the fingerprints in {@link LongVec} according to the index of - * the {@link FPSetRMI} responsible for the partition of the fingerprint - * space. - */ - int getFPSetIndex(long fp); + * the {@link FPSetRMI} responsible for the subset of the fingerprint space. + * + * @param fp + * The fingerprint for which the index should be calculated. + * @return The index of the {@link FPSet} that is assigned this subset of + * the fingerprint space. + * <p> + * Assignment is based on the least significant bits. This selection + * of fingerprint bits is important. Selecting the most significant + * bits of a fingerprint to assign an {@link FPSet} would violate + * the invariant of MSB-based {@link FPSet}s such as + * {@link MSBDiskFPSet}. They assume that the + * {@link FPSetConfiguration#getFpBits()} are fixed. FPSetManager + * may dynamically reassign additional subsets of the fingerprint + * space to an existing {@link FPSet}, when the previously assigned + * {@link FPSet} is lost due to a network or hardware failure. + */ + int getFPSetIndex(final long fp); /** * @see FPSetRMI#getStatesSeen() diff --git a/tlatools/src/tlc2/tool/distributed/fp/NonDistributedFPSetManager.java b/tlatools/src/tlc2/tool/distributed/fp/NonDistributedFPSetManager.java index 51f205333f53a1a7fca2354b296b763c16fd9bde..659b064cac2da203497b82283244245adcc7e630 100644 --- a/tlatools/src/tlc2/tool/distributed/fp/NonDistributedFPSetManager.java +++ b/tlatools/src/tlc2/tool/distributed/fp/NonDistributedFPSetManager.java @@ -6,6 +6,7 @@ import java.util.concurrent.ExecutorService; import tlc2.output.EC; import tlc2.output.MP; +import tlc2.tool.TLCTrace; import tlc2.util.BitVector; import tlc2.util.LongVec; @@ -14,11 +15,13 @@ public class NonDistributedFPSetManager implements IFPSetManager { private final FPSetRMI fpSet; private final String hostname; + private final transient TLCTrace trace; // Do not serialize trace and send it over the wire. Recovery executes on the master, not on the workers. public NonDistributedFPSetManager(final FPSetRMI fpSet, - final String hostname) throws IOException { + final String hostname, TLCTrace trace) throws IOException { this.fpSet = fpSet; this.hostname = hostname; + this.trace = trace; } /* (non-Javadoc) @@ -137,7 +140,7 @@ public class NonDistributedFPSetManager implements IFPSetManager { /* (non-Javadoc) * @see tlc2.tool.distributed.fp.FPSetManager#checkFPs() */ - public double checkFPs() { + public long checkFPs() { try { return this.fpSet.checkFPs(); } catch (IOException e) { @@ -147,6 +150,19 @@ public class NonDistributedFPSetManager implements IFPSetManager { } } + /* (non-Javadoc) + * @see tlc2.tool.distributed.fp.IFPSetManager#checkInvariant() + */ + public boolean checkInvariant() { + try { + return this.fpSet.checkInvariant(); + } catch (IOException e) { + // not expected to happen + MP.printError(EC.GENERAL, e); + return false; + } + } + /* (non-Javadoc) * @see tlc2.tool.distributed.fp.FPSetManager#size() */ @@ -198,7 +214,7 @@ public class NonDistributedFPSetManager implements IFPSetManager { * @see tlc2.tool.distributed.fp.FPSetManager#recover(java.lang.String) */ public void recover(String fname) throws InterruptedException, IOException { - this.fpSet.recover(); + this.fpSet.recover(trace); } /* (non-Javadoc) diff --git a/tlatools/src/tlc2/tool/distributed/fp/callable/CheckFPsCallable.java b/tlatools/src/tlc2/tool/distributed/fp/callable/CheckFPsCallable.java index 3b9871414e63dc67b1dc8026ca97e2522180c045..c07e2be4aaf8a02c6fd916f2fdf7a1eabf87f2d1 100644 --- a/tlatools/src/tlc2/tool/distributed/fp/callable/CheckFPsCallable.java +++ b/tlatools/src/tlc2/tool/distributed/fp/callable/CheckFPsCallable.java @@ -8,7 +8,7 @@ import tlc2.output.EC; import tlc2.output.MP; import tlc2.tool.distributed.fp.FPSetRMI; -public class CheckFPsCallable implements Callable<Double> { +public class CheckFPsCallable implements Callable<Long> { private final FPSetRMI fpSetRMI; public CheckFPsCallable(FPSetRMI fpSetRMI) { @@ -18,7 +18,7 @@ public class CheckFPsCallable implements Callable<Double> { /* (non-Javadoc) * @see java.util.concurrent.Callable#call() */ - public Double call() throws Exception { + public Long call() throws Exception { try { return fpSetRMI.checkFPs(); } catch (IOException e) { @@ -26,7 +26,7 @@ public class CheckFPsCallable implements Callable<Double> { MP.printError(EC.GENERAL, e); // return max value to indicate to caller that the result is // incorrect. - return Double.MAX_VALUE; + return Long.MAX_VALUE; } } } \ No newline at end of file diff --git a/tlatools/src/tlc2/tool/distributed/fp/callable/CheckInvariantCallable.java b/tlatools/src/tlc2/tool/distributed/fp/callable/CheckInvariantCallable.java new file mode 100644 index 0000000000000000000000000000000000000000..d1f7282b0151ea328115bc3c33bb32d76894acbe --- /dev/null +++ b/tlatools/src/tlc2/tool/distributed/fp/callable/CheckInvariantCallable.java @@ -0,0 +1,32 @@ +// Copyright (c) 2016 Markus Alexander Kuppe. All rights reserved. +package tlc2.tool.distributed.fp.callable; + +import java.io.IOException; +import java.util.concurrent.Callable; + +import tlc2.output.EC; +import tlc2.output.MP; +import tlc2.tool.distributed.fp.FPSetRMI; + +public class CheckInvariantCallable implements Callable<Boolean> { + private final FPSetRMI fpSetRMI; + + public CheckInvariantCallable(FPSetRMI fpSetRMI) { + this.fpSetRMI = fpSetRMI; + } + + /* (non-Javadoc) + * @see java.util.concurrent.Callable#call() + */ + public Boolean call() throws Exception { + try { + return fpSetRMI.checkInvariant(); + } catch (IOException e) { + // not expected to happen. + MP.printError(EC.GENERAL, e); + // return max value to indicate to caller that the result is + // incorrect. + return false; + } + } +} \ No newline at end of file diff --git a/tlatools/src/tlc2/tool/distributed/management/TLCServerMXWrapper.java b/tlatools/src/tlc2/tool/distributed/management/TLCServerMXWrapper.java index b91b47ba4a3ed2c95ab158043b25533446b4d189..420999af8669c2202f1ba4adea3cd0469c5573ac 100644 --- a/tlatools/src/tlc2/tool/distributed/management/TLCServerMXWrapper.java +++ b/tlatools/src/tlc2/tool/distributed/management/TLCServerMXWrapper.java @@ -3,10 +3,12 @@ package tlc2.tool.distributed.management; import java.io.IOException; +import java.rmi.RemoteException; import javax.management.NotCompliantMBeanException; import tlc2.TLCGlobals; +import tlc2.tool.TLCState; import tlc2.tool.distributed.TLCServer; import tlc2.tool.distributed.fp.IFPSetManager; import tlc2.tool.management.TLCStandardMBean; @@ -105,4 +107,89 @@ public class TLCServerMXWrapper extends TLCStandardMBean implements TLCStatistic public long getAverageBlockCnt() { return tlcServer.getAverageBlockCnt(); } + + /* (non-Javadoc) + * @see tlc2.tool.distributed.management.TLCStatisticsMXBean#getRuntimeRatio() + */ + public double getRuntimeRatio() { + // Distributed TLC does not support liveness checking + return 0d; + } + + /* (non-Javadoc) + * @see tlc2.tool.distributed.management.TLCStatisticsMXBean#liveCheck() + */ + public void liveCheck() { + // Distributed TLC does not support liveness checking + } + + /* (non-Javadoc) + * @see tlc2.tool.distributed.management.TLCStatisticsMXBean#getCurrentState() + */ + public String getCurrentState() { + final TLCState state = tlcServer.stateQueue.sPeek(); + if (state != null) { + return state.toString(); + } + return "N/A"; + } + + /* (non-Javadoc) + * @see tlc2.tool.distributed.management.TLCStatisticsMXBean#getSpecName() + */ + public String getSpecName() { + if (tlcServer.isRunning()) { + try { + return tlcServer.getSpecFileName(); + } catch (RemoteException e) { + e.printStackTrace(); + } + } + return "N/A"; + } + + /* (non-Javadoc) + * @see tlc2.tool.distributed.management.TLCStatisticsMXBean#getModelName() + */ + public String getModelName() { + if (tlcServer.isRunning()) { + try { + return tlcServer.getConfigFileName(); + } catch (RemoteException e) { + e.printStackTrace(); + } + } + return "N/A"; + } + + /* (non-Javadoc) + * @see tlc2.tool.distributed.management.TLCStatisticsMXBean#stop() + */ + public void stop() { + synchronized (tlcServer) { + tlcServer.setDone(); + tlcServer.stateQueue.finishAll(); + tlcServer.notifyAll(); + } + } + + /* (non-Javadoc) + * @see tlc2.tool.distributed.management.TLCStatisticsMXBean#suspend() + */ + @Override + public void suspend() { + synchronized (tlcServer) { + tlcServer.stateQueue.suspendAll(); + } + } + + /* (non-Javadoc) + * @see tlc2.tool.distributed.management.TLCStatisticsMXBean#resume() + */ + @Override + public void resume() { + synchronized (tlcServer) { + tlcServer.stateQueue.resumeAll(); + } + } } diff --git a/tlatools/src/tlc2/tool/distributed/management/TLCStatisticsMXBean.java b/tlatools/src/tlc2/tool/distributed/management/TLCStatisticsMXBean.java index ed30f3929999f1e37965cce63f65a8ce8e2867e3..d49f008b71d90a5a857464dd626225a689def856 100644 --- a/tlatools/src/tlc2/tool/distributed/management/TLCStatisticsMXBean.java +++ b/tlatools/src/tlc2/tool/distributed/management/TLCStatisticsMXBean.java @@ -2,15 +2,24 @@ package tlc2.tool.distributed.management; +import tlc2.tool.ModelChecker; +import tlc2.tool.TLCState; + /** * @author Markus Alexander Kuppe */ -/** - * @author markus - * - */ public interface TLCStatisticsMXBean { + /** + * @return The version of TLC. + */ + String getVersion(); + + /** + * @return The code revision corresponding to this version of TLC. + */ + String getRevision(); + /** * @return The amount of states generated (non-distinct). * {@link TLCStatisticsMXBean#getStatesGenerated()} >= @@ -57,4 +66,46 @@ public interface TLCStatisticsMXBean { * Creates a checkpoint next time possible */ void checkpoint(); + + /** + * @return The ratio between time dedicated to safety and liveness checking. + */ + double getRuntimeRatio(); + + /** + * Force new progress interval to check liveness + */ + void liveCheck(); + + /** + * The string representation of a {@link TLCState} the {@link ModelChecker} + * has recently checked. + */ + String getCurrentState(); + + /** + * @return The name of the spec currently being checked by TLC. + */ + String getSpecName(); + + /** + * @return The name of the model curreclty being checked by TLC. + */ + String getModelName(); + + /** + * Force TLC to stop model checking. + */ + void stop(); + + /** + * Suspend model checking until resume is called. + */ + void suspend(); + + /** + * Resumes model checking after a suspend. Do not resume an running model checker. + * It could interfere with model checking. + */ + void resume(); } diff --git a/tlatools/src/tlc2/tool/fp/DiskFPSet.java b/tlatools/src/tlc2/tool/fp/DiskFPSet.java index a6bc57708194b2d172dd0a70812ea52aa31bc24c..f032773b46e25ddf4f5794a4fba8313fd9503d80 100644 --- a/tlatools/src/tlc2/tool/fp/DiskFPSet.java +++ b/tlatools/src/tlc2/tool/fp/DiskFPSet.java @@ -9,11 +9,9 @@ import java.io.IOException; import java.io.RandomAccessFile; import java.net.InetAddress; import java.rmi.RemoteException; -import java.util.Arrays; +import java.text.DecimalFormat; import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicLong; -import java.util.concurrent.locks.Lock; -import java.util.logging.Level; +import java.util.concurrent.atomic.LongAdder; import java.util.logging.Logger; import javax.management.NotCompliantMBeanException; @@ -21,11 +19,11 @@ import javax.management.NotCompliantMBeanException; import tlc2.output.EC; import tlc2.output.MP; import tlc2.tool.TLCTrace; +import tlc2.tool.TLCTrace.Enumerator; import tlc2.tool.fp.management.DiskFPSetMXWrapper; import tlc2.tool.management.TLCStandardMBean; import tlc2.util.BufferedRandomAccessFile; import tlc2.util.IdThread; -import tlc2.util.Striped; import util.Assert; import util.FileUtil; @@ -59,8 +57,11 @@ import util.FileUtil; @SuppressWarnings("serial") public abstract class DiskFPSet extends FPSet implements FPSetStatistic { - private final static Logger LOGGER = Logger.getLogger(DiskFPSet.class.getName()); + protected final static Logger LOGGER = Logger.getLogger(DiskFPSet.class.getName()); + protected static final long MARK_FLUSHED = 0x8000000000000000L; + protected static final long FLUSHED_MASK = 0x7FFFFFFFFFFFFFFFL; + // fields /** * upper bound on "tblCnt" @@ -76,24 +77,6 @@ public abstract class DiskFPSet extends FPSet implements FPSetStatistic { */ protected String fpFilename; protected String tmpFilename; - - /** - * Number of locks in the striped lock (#StripeLocks = 2^LogLockCnt).<br> - * Theoretically best performance should be seen with on lock per bucket in - * the primary hash table. An some point though (not yet measured), this - * performance benefit is probably eaten up by the memory consumption of the - * striped lock {@link DiskFPSet#rwLock} itself, which reduces the memory - * available to the hash set. - */ - protected static final int LogLockCnt = Integer.getInteger(DiskFPSet.class.getName() + ".logLockCnt", 10); - /** - * protects n memory buckets - */ - protected final Striped rwLock; - /** - * Is (1 << LogLockCnt) and exposed here for subclasses - */ - protected final int lockCnt; /** * Number of entries on disk. This is equivalent to the current number of fingerprints stored on disk. * @see DiskFPSet#getFileCnt() @@ -111,13 +94,13 @@ public abstract class DiskFPSet extends FPSet implements FPSetStatistic { * number of entries in "tbl". This is equivalent to the current number of fingerprints stored in in-memory cache/index. * @see DiskFPSet#getTblCnt() */ - protected AtomicLong tblCnt; - + protected LongAdder tblCnt; + // http://concurrencyfreaks.blogspot.de/2013/09/longadder-is-not-sequentially-consistent.html /** * Number of used slots in tbl by a bucket * @see DiskFPSet#getTblLoad() */ - protected long tblLoad; + protected LongAdder tblLoad; /** * Number of allocated bucket slots across the complete index table. tblCnt will always <= bucketCnt; @@ -143,16 +126,16 @@ public abstract class DiskFPSet extends FPSet implements FPSetStatistic { protected long[] index; // statistics - private AtomicLong memHitCnt = new AtomicLong(0); - private AtomicLong diskLookupCnt = new AtomicLong(0); - private AtomicLong diskHitCnt = new AtomicLong(0); - private AtomicLong diskWriteCnt = new AtomicLong(0); - private AtomicLong diskSeekCnt = new AtomicLong(0); - private AtomicLong diskSeekCache = new AtomicLong(0); + protected LongAdder memHitCnt = new LongAdder(); + protected LongAdder diskHitCnt = new LongAdder(); + private LongAdder diskLookupCnt = new LongAdder(); + protected LongAdder diskWriteCnt = new LongAdder(); + private LongAdder diskSeekCnt = new LongAdder(); + private LongAdder diskSeekCache = new LongAdder(); // indicate how many cp or disk grow in put(long) has occurred private int checkPointMark; - private int growDiskMark; + protected int growDiskMark; /** * The load factor and initial capacity for the hashtable. @@ -161,7 +144,7 @@ public abstract class DiskFPSet extends FPSet implements FPSetStatistic { static final int InitialBucketCapacity = (1 << LogMaxLoad); /* Number of fingerprints per braf buffer. */ - public static final int NumEntriesPerPage = 8192 / LongSize; + public static final int NumEntriesPerPage = 8192 / (int) LongSize; /** * This is (assumed to be) the auxiliary storage for a fingerprint that need @@ -179,7 +162,7 @@ public abstract class DiskFPSet extends FPSet implements FPSetStatistic { * Accumulated wall clock time it has taken to flush this {@link FPSet} to * disk */ - private long flushTime = 0L; + protected long flushTime = 0L; /** * @@ -201,15 +184,14 @@ public abstract class DiskFPSet extends FPSet implements FPSetStatistic { */ protected DiskFPSet(final FPSetConfiguration fpSetConfig) throws RemoteException { super(fpSetConfig); - this.lockCnt = 1 << LogLockCnt; //TODO come up with a more dynamic value for stripes that takes tblCapacity into account - this.rwLock = Striped.readWriteLock(lockCnt); this.maxTblCnt = fpSetConfig.getMemoryInFingerprintCnt(); if (maxTblCnt <= 0) { throw new IllegalArgumentException("Negative or zero upper storage limit"); } this.fileCnt = 0; - this.tblCnt = new AtomicLong(0); + this.tblCnt = new LongAdder(); + this.tblLoad = new LongAdder(); this.flusherChosen = new AtomicBoolean(false); this.index = null; @@ -229,7 +211,7 @@ public abstract class DiskFPSet extends FPSet implements FPSetStatistic { /* (non-Javadoc) * @see tlc2.tool.fp.FPSet#init(int, java.lang.String, java.lang.String) */ - public void init(int numThreads, String aMetadir, String filename) + public FPSet init(int numThreads, String aMetadir, String filename) throws IOException { // Make it possible to pass in alternative location for the .fp and @@ -284,13 +266,14 @@ public abstract class DiskFPSet extends FPSet implements FPSetStatistic { new String[] { this.fpFilename, e.getMessage() }); throw new IOException(message); } + return this; } /* (non-Javadoc) * @see tlc2.tool.fp.FPSet#size() */ public long size() { - return this.tblCnt.get() + this.fileCnt; + return this.getTblCnt() + this.fileCnt; } public abstract long sizeof(); @@ -329,106 +312,6 @@ public abstract class DiskFPSet extends FPSet implements FPSetStatistic { } } - /* (non-Javadoc) - * @see tlc2.tool.fp.FPSet#put(long) - * - * 0 and {@link Long#MIN_VALUE} always return false - * - * Locking is as follows: - * - * Acquire mem read lock - * Acquire disk read lock - * Release mem read lock - * - * Acquire mem read/write lock - * Release disk read lock // interleaved - * insert into mem - * Acquire disk write lock (might cause potential writer to wait() which releases mem read lock (monitor)) - * flushToDisk - * Release disk write lock - * Release mem read lock - * - * asserts: - * - Exclusive access to disk and memory for a writer - * - */ - public final boolean put(long fp) throws IOException { - fp = checkValid(fp); - // zeros the msb - long fp0 = fp & 0x7FFFFFFFFFFFFFFFL; - - final Lock readLock = rwLock.getAt(getLockIndex(fp0)).readLock(); - readLock.lock(); - // First, look in in-memory buffer - if (this.memLookup(fp0)) { - readLock.unlock(); - this.memHitCnt.getAndIncrement(); - return true; - } - - // blocks => wait() if disk is being re-written - // (means the current thread returns rwLock monitor) - // Why not return monitor first and then acquire read lock? - // => prevent deadlock by acquiring threads in same order? - - // next, look on disk - boolean diskHit = this.diskLookup(fp0); - - // In event of disk hit, return - if (diskHit) { - readLock.unlock(); - this.diskHitCnt.getAndIncrement(); - return true; - } - - readLock.unlock(); - - // Another writer could write the same fingerprint here if it gets - // interleaved. This is no problem though, because memInsert again - // checks existence for fp to be inserted - - final Lock w = rwLock.getAt(getLockIndex(fp0)).writeLock(); - w.lock(); - - // if disk lookup failed, add to memory buffer - if (this.memInsert(fp0)) { - w.unlock(); - this.memHitCnt.getAndIncrement(); - return true; - } - - // test if buffer is full && block until there are no more readers - if (needsDiskFlush() && this.flusherChosen.compareAndSet(false, true)) { - - // statistics - growDiskMark++; - long timestamp = System.currentTimeMillis(); - - // acquire _all_ write locks - rwLock.acquireAllLocks(); - - // flush memory entries to disk - flusher.flushTable(); - - // release _all_ write locks - rwLock.releaseAllLocks(); - - // reset forceFlush to false - forceFlush = false; - - // finish writing - this.flusherChosen.set(false); - - long l = System.currentTimeMillis() - timestamp; - flushTime += l; - - LOGGER.log(Level.FINE, "Flushed disk {0} {1}. tine, in {2} sec", new Object[] { - ((DiskFPSetMXWrapper) diskFPSetMXWrapper).getObjectName(), getGrowDiskMark(), l}); - } - w.unlock(); - return false; - } - /** * @return true iff the current in-memory buffer has to be flushed to disk * to make room. @@ -439,42 +322,9 @@ public abstract class DiskFPSet extends FPSet implements FPSetStatistic { // A) the FP distribution causes the index tbl to be unevenly populated. // B) the FP distribution reassembles linear fill-up/down which // causes tblCnt * buckets with initial load factor to be allocated. - return (this.tblCnt.get() >= this.maxTblCnt) || forceFlush ; + return (this.getTblCnt() >= this.maxTblCnt) || forceFlush ; } - /* (non-Javadoc) - * @see tlc2.tool.fp.FPSet#contains(long) - * - * 0 and {@link Long#MIN_VALUE} always return false - */ - public final boolean contains(long fp) throws IOException { - fp = checkValid(fp); - // zeros the msb - long fp0 = fp & 0x7FFFFFFFFFFFFFFFL; - final Lock readLock = this.rwLock.getAt(getLockIndex(fp0)).readLock(); - readLock.lock(); - // First, look in in-memory buffer - if (this.memLookup(fp0)) { - readLock.unlock(); - this.memHitCnt.getAndIncrement(); - return true; - } - - // block if disk is being re-written - // next, look on disk - boolean diskHit = this.diskLookup(fp0); - // increment while still locked - if(diskHit) { - diskHitCnt.getAndIncrement(); - } - - // end read; add to memory buffer if necessary - readLock.unlock(); - return diskHit; - } - - protected abstract int getLockIndex(long fp); - /** * Checks if the given fingerprint has a value that can be correctly stored * by this FPSet @@ -503,9 +353,16 @@ public abstract class DiskFPSet extends FPSet implements FPSetStatistic { /** * Return "true" if "fp" is contained in the hash table; otherwise, insert * it and return "false". Precondition: msb(fp) = 0 + * @throws IOException */ - abstract boolean memInsert(long fp); + abstract boolean memInsert(long fp) throws IOException; + /** + * Locks and unlocks tbl + */ + abstract void acquireTblWriteLock(); + abstract void releaseTblWriteLock(); + /** * Look on disk for the fingerprint "fp". This method requires that * "this.rwLock" has been acquired for reading by the caller. @@ -513,11 +370,12 @@ public abstract class DiskFPSet extends FPSet implements FPSetStatistic { * @return true iff fp is on disk */ final boolean diskLookup(long fp) throws IOException { - if (this.index == null) + if (this.index == null) { return false; + } // Increment disk lookup counter - this.diskLookupCnt.getAndIncrement(); + this.diskLookupCnt.increment(); // search in index for position to seek to // do interpolated binary search @@ -527,11 +385,13 @@ public abstract class DiskFPSet extends FPSet implements FPSetStatistic { long hiVal = this.index[hiPage]; // Test boundary cases (if not inside interval) - if (fp < loVal || fp > hiVal) + if (fp < loVal || fp > hiVal) { return false; - if (fp == hiVal) // why not check loVal? memLookup would have found it already! + } + if (fp == hiVal) {// why not check loVal? memLookup would have found it already! return true; - double dfp = (double) fp; + } + final double dfp = (double) fp; // a) find disk page that would potentially contain the fp. this.index contains // the first fp of each disk page @@ -543,10 +403,10 @@ public abstract class DiskFPSet extends FPSet implements FPSetStatistic { * * loVal <= fp < hiVal exists x: loPage < x < hiPage */ - double dhi = (double) hiPage; - double dlo = (double) loPage; - double dhiVal = (double) hiVal; - double dloVal = (double) loVal; + final double dhi = (double) hiPage; + final double dlo = (double) loPage; + final double dhiVal = (double) hiVal; + final double dloVal = (double) loVal; int midPage = (loPage + 1) + (int) ((dhi - dlo - 1.0) * (dfp - dloVal) / (dhiVal - dloVal)); @@ -555,7 +415,7 @@ public abstract class DiskFPSet extends FPSet implements FPSetStatistic { Assert.check(loPage < midPage && midPage < hiPage, EC.SYSTEM_INDEX_ERROR); - long v = this.index[midPage]; + final long v = this.index[midPage]; if (fp < v) { hiPage = midPage; hiVal = v; @@ -567,6 +427,11 @@ public abstract class DiskFPSet extends FPSet implements FPSetStatistic { return true; } } + return diskLookupBinarySearch(fp, indexLength, loPage, hiPage, loVal, hiVal, dfp); + } + + private final boolean diskLookupBinarySearch(final long fp, final int indexLength, final int loPage, final int hiPage, long loVal, long hiVal, + final double dfp) throws IOException { // no page is in between loPage and hiPage at this point Assert.check(hiPage == loPage + 1, EC.SYSTEM_INDEX_ERROR); @@ -580,18 +445,11 @@ public abstract class DiskFPSet extends FPSet implements FPSetStatistic { try { // b0) open file for reading that is associated with current thread BufferedRandomAccessFile raf; - int id = IdThread.GetId(this.braf.length); + final int id = IdThread.GetId(this.braf.length); if (id < this.braf.length) { raf = this.braf[id]; } else { - synchronized (this.brafPool) { - if (this.poolIndex < this.brafPool.length) { - raf = this.brafPool[this.poolIndex++]; - } else { - raf = new BufferedRandomAccessFile( - this.fpFilename, "r"); - } - } + raf = poolOpen(); } // b1) do interpolated binary search on disk page determined by a) @@ -609,11 +467,11 @@ public abstract class DiskFPSet extends FPSet implements FPSetStatistic { // midEntry calculation done on logical indices, // addressing done on bytes, thus convert to long-addressing (* LongSize) if (raf.seeek(midEntry * LongSize)) { - diskSeekCnt.getAndIncrement(); + diskSeekCnt.increment(); } else { - diskSeekCache.getAndIncrement(); + diskSeekCache.increment(); } - long v = raf.readLong(); + final long v = raf.readLong(); if (fp < v) { hiEntry = midEntry; @@ -628,15 +486,9 @@ public abstract class DiskFPSet extends FPSet implements FPSetStatistic { } // b2) done doing disk search -> close file (finally candidate? => not really because if we exit with error, TLC exits) if (id >= this.braf.length) { - synchronized (this.brafPool) { - if (this.poolIndex > 0) { - this.brafPool[--this.poolIndex] = raf; - } else { - raf.close(); - } - } + poolClose(raf); } - } catch (IOException e) { + } catch (final IOException e) { if(midEntry * LongSize < 0) { // LL modified error message on 7 April 2012 MP.printError(EC.GENERAL, new String[]{"looking up a fingerprint, and" + @@ -649,6 +501,27 @@ public abstract class DiskFPSet extends FPSet implements FPSetStatistic { return diskHit; } + private final BufferedRandomAccessFile poolOpen() throws IOException { + synchronized (this.brafPool) { + if (this.poolIndex < this.brafPool.length) { + return this.brafPool[this.poolIndex++]; + } else { + return new BufferedRandomAccessFile( + this.fpFilename, "r"); + } + } + } + + private final void poolClose(final BufferedRandomAccessFile raf) throws IOException { + synchronized (this.brafPool) { + if (this.poolIndex > 0) { + this.brafPool[--this.poolIndex] = raf; + } else { + raf.close(); + } + } + } + /** * Calculates a mid entry where to divide the interval * @@ -683,7 +556,7 @@ public abstract class DiskFPSet extends FPSet implements FPSetStatistic { protected final void writeFP(RandomAccessFile outRAF, long fp) throws IOException { outRAF.writeLong(fp); - diskWriteCnt.getAndIncrement(); + diskWriteCnt.increment(); // update in-memory index file if (this.counter == 0) { this.index[this.currIndex++] = fp; @@ -697,6 +570,7 @@ public abstract class DiskFPSet extends FPSet implements FPSetStatistic { * @return The new required length for the {@link DiskFPSet#index} */ protected int calculateIndexLen(final long buffLen) { + // +2L because we always need the lo and hi bounds for the index. long indexLen = ((this.fileCnt + buffLen - 1L) / (long) NumEntriesPerPage) + 2L; //TODO this can cause a NegativeArraySizeException if fileCnt becomes sufficiently large @@ -730,7 +604,8 @@ public abstract class DiskFPSet extends FPSet implements FPSetStatistic { /* (non-Javadoc) * @see tlc2.tool.fp.FPSet#exit(boolean) */ - public final void exit(boolean cleanup) throws IOException { + public void exit(boolean cleanup) throws IOException { + super.exit(cleanup); if (cleanup) { // Delete the metadata directory: FileUtil.deleteDir(this.metadir, true); @@ -744,13 +619,13 @@ public abstract class DiskFPSet extends FPSet implements FPSetStatistic { /* (non-Javadoc) * @see tlc2.tool.fp.FPSet#checkFPs() */ - public final double checkFPs() throws IOException { + public long checkFPs() throws IOException { // It seems pointless to acquire the locks when checkFPs is only - // executed after model checking has finished. Sill lock the disk + // executed after model checking has finished. Lock the disk // fingerprint sets though. Acquiring the locks is cheap. - rwLock.acquireAllLocks(); + acquireTblWriteLock(); flusher.flushTable(); - rwLock.releaseAllLocks(); + releaseTblWriteLock(); RandomAccessFile braf = new BufferedRandomAccessFile( this.fpFilename, "r"); @@ -768,30 +643,30 @@ public abstract class DiskFPSet extends FPSet implements FPSetStatistic { } } braf.close(); - return (1.0 / dis); + return dis; } /* (non-Javadoc) * @see tlc2.tool.fp.FPSet#beginChkpt(java.lang.String) */ - public final void beginChkpt(String fname) throws IOException { + public void beginChkpt(String fname) throws IOException { this.flusherChosen.set(true); - rwLock.acquireAllLocks(); + acquireTblWriteLock(); flusher.flushTable(); FileUtil.copyFile(this.fpFilename, this.getChkptName(fname, "tmp")); checkPointMark++; - rwLock.releaseAllLocks(); + releaseTblWriteLock(); this.flusherChosen.set(false); } /* (non-Javadoc) * @see tlc2.tool.fp.FPSet#commitChkpt(java.lang.String) */ - public final void commitChkpt(String fname) throws IOException { + public void commitChkpt(String fname) throws IOException { File oldChkpt = new File(this.getChkptName(fname, "chkpt")); File newChkpt = new File(this.getChkptName(fname, "tmp")); if (!newChkpt.renameTo(oldChkpt)) { @@ -803,7 +678,7 @@ public abstract class DiskFPSet extends FPSet implements FPSetStatistic { /* (non-Javadoc) * @see tlc2.tool.fp.FPSet#recover(java.lang.String) */ - public final void recover(String fname) throws IOException { + public void recover(String fname) throws IOException { RandomAccessFile chkptRAF = new BufferedRandomAccessFile( this.getChkptName(fname, "chkpt"), "r"); RandomAccessFile currRAF = new BufferedRandomAccessFile( @@ -867,79 +742,52 @@ public abstract class DiskFPSet extends FPSet implements FPSetStatistic { // the fingerprints from the TLCTrace file. Not from its own .fp file. } - private long[] recoveryBuff = null; - private int recoveryIdx = -1; - - /* (non-Javadoc) - * @see tlc2.tool.fp.FPSet#prepareRecovery() - */ - public final void prepareRecovery() throws IOException { - // First close all "this.braf" and "this.brafPool" objects on currName: - for (int i = 0; i < this.braf.length; i++) { - this.braf[i].close(); - } - for (int i = 0; i < this.brafPool.length; i++) { - this.brafPool[i].close(); - } - - recoveryBuff = new long[1 << 21]; - recoveryIdx = 0; - } - /* (non-Javadoc) * @see tlc2.tool.fp.FPSet#recoverFP(long) */ public final void recoverFP(long fp) throws IOException { - recoveryBuff[recoveryIdx++] = (fp & 0x7FFFFFFFFFFFFFFFL); - if (recoveryIdx == recoveryBuff.length) { - Arrays.sort(recoveryBuff, 0, recoveryIdx); - flusher.mergeNewEntries(recoveryBuff, recoveryIdx); - recoveryIdx = 0; - } - } - - /* (non-Javadoc) - * @see tlc2.tool.fp.FPSet#completeRecovery() - */ - public final void completeRecovery() throws IOException { - Arrays.sort(recoveryBuff, 0, recoveryIdx); - flusher.mergeNewEntries(recoveryBuff, recoveryIdx); - recoveryBuff = null; - recoveryIdx = -1; - - // Reopen a BufferedRAF for each thread - for (int i = 0; i < this.braf.length; i++) { - this.braf[i] = new BufferedRandomAccessFile(this.fpFilename, - "r"); - } - for (int i = 0; i < this.brafPool.length; i++) { - this.brafPool[i] = new BufferedRandomAccessFile( - this.fpFilename, "r"); + // This implementation used to group n fingerprints into a sorted + // in-memory page. Pages were subsequently merged on-disk directly, + // creating the on-disk storage file for DiskFPSets. + // + // The new algorithm simply "replays" the fingerprints found in the + // trace file. It's biggest disadvantage is a performance penalty it + // pays because it doesn't group fingerprints. On the other hand, it has + // advantages over the old algorithm: + // + // - Simplified logic/code + // - No need for a long[] recovery buffer + // - TLC runs with a warm in-memory fingerprint cache + // - With large amounts of available fingerprint set memory, the .fp + // file might actually never be written. This means that the FPSet never + // has to go to disk during contains/put which yields a better overall + // runtime performance. + // + // TODO Use original on-disk merge if it is known that the fingerprints + // won't fit into memory anyway. + + // The code below is put(long) stripped from synchronization and + // statistics code to speed up recovery. Thus, recovery relys on + // exclusive access to the fingerprint set, which it has during + // recovery. + long fp0 = fp & FLUSHED_MASK; + boolean unique = !this.memInsert(fp0); + Assert.check(unique, EC.SYSTEM_CHECKPOINT_RECOVERY_CORRUPT, ""); + if (needsDiskFlush()) { + this.flusher.flushTable(); } - this.poolIndex = 0; } - /* (non-Javadoc) * @see tlc2.tool.fp.FPSet#recover() */ - public final void recover() throws IOException { - this.prepareRecovery(); - - long recoverPtr = TLCTrace.getRecoverPtr(); - @SuppressWarnings("resource") - RandomAccessFile braf = new BufferedRandomAccessFile( - TLCTrace.getFilename(), "r"); - while (braf.getFilePointer() < recoverPtr) { - // drop readLongNat - if (braf.readInt() < 0) - braf.readInt(); - - long fp = braf.readLong(); + public final void recover(TLCTrace trace) throws IOException { + final Enumerator elements = trace.elements(); + while (elements.nextPos() != -1) { + long fp = elements.nextFP(); this.recoverFP(fp); } - - this.completeRecovery(); + elements.close(); } private String getChkptName(String fname, String name) { @@ -950,7 +798,7 @@ public abstract class DiskFPSet extends FPSet implements FPSetStatistic { * @see tlc2.tool.fp.FPSet#checkInvariant() */ public boolean checkInvariant() throws IOException { - rwLock.acquireAllLocks(); + acquireTblWriteLock(); flusher.flushTable(); // No need for any lock here final RandomAccessFile braf = new BufferedRandomAccessFile( this.fpFilename, "r"); @@ -968,7 +816,7 @@ public abstract class DiskFPSet extends FPSet implements FPSetStatistic { } } finally { braf.close(); - rwLock.releaseAllLocks(); + releaseTblWriteLock(); } return true; } @@ -1017,14 +865,14 @@ public abstract class DiskFPSet extends FPSet implements FPSetStatistic { * {@link DiskFPSet#getTblLoad()} <= {@link DiskFPSet#getTblCnt()} */ public long getTblLoad() { - return tblLoad; + return tblLoad.sum(); } /** * @return the amount of fingerprints stored in memory. This is less or equal to {@link DiskFPSet#getTblCnt()} depending on if there collision buckets exist. */ public long getTblCnt() { - return tblCnt.get(); + return tblCnt.sum(); } /** @@ -1045,42 +893,42 @@ public abstract class DiskFPSet extends FPSet implements FPSetStatistic { * @return the diskLookupCnt */ public long getDiskLookupCnt() { - return diskLookupCnt.get(); + return diskLookupCnt.sum(); } /** * @return the diskHitCnt */ public long getMemHitCnt() { - return memHitCnt.get(); + return memHitCnt.sum(); } /** * @return the diskHitCnt */ public long getDiskHitCnt() { - return diskHitCnt.get(); + return diskHitCnt.sum(); } /** * @return the diskWriteCnt */ public long getDiskWriteCnt() { - return diskWriteCnt.get(); + return diskWriteCnt.sum(); } /** * @return the diskSeekCnt */ public long getDiskSeekCnt() { - return diskSeekCnt.get(); + return diskSeekCnt.sum(); } /** * @return the diskSeekCache */ public long getDiskSeekCache() { - return diskSeekCache.get(); + return diskSeekCache.sum(); } /** @@ -1108,6 +956,13 @@ public abstract class DiskFPSet extends FPSet implements FPSetStatistic { forceFlush = true; } + /** + * @return The (static) number of locks used to guard the set. + */ + public int getLockCnt() { + return 0; + } + /** * @return The technical maximum of readers/writers this {@link DiskFPSet} * can handle. It doesn't show the actual numbers of active clients. @@ -1118,23 +973,6 @@ public abstract class DiskFPSet extends FPSet implements FPSetStatistic { return this.braf.length + this.brafPool.length; } - /** - * @return The amount of elements in the {@link DiskFPSet#collisionBucket} - * if the {@link DiskFPSet} has a collisionBucket. -1L otherwise. - */ - public long getCollisionBucketCnt() { - return -1L; - } - - /** - * @return The proportional size of the collision bucket compared to the - * size of the set or <code>-1d</code> if implementation does not - * use a collision bucket. Domain is [0, 1]. - */ - public double getCollisionRatio() { - return -1d; - } - /** * The load factor is a measure of how full the (primary) in-memory hash * table is. @@ -1144,7 +982,7 @@ public abstract class DiskFPSet extends FPSet implements FPSetStatistic { * load factor, <code>-1d</code> is returned. */ public double getLoadFactor() { - return this.tblCnt.doubleValue() / (double) this.maxTblCnt; + return ((double) this.getTblCnt()) / (double) this.maxTblCnt; } // /** @@ -1206,7 +1044,7 @@ public abstract class DiskFPSet extends FPSet implements FPSetStatistic { * for writing by the caller, and that the mutex "this.rwLock" is also held. */ void flushTable() throws IOException { - if (tblCnt.get() == 0) + if (getTblCnt() == 0) return; prepareTable(); @@ -1228,9 +1066,9 @@ public abstract class DiskFPSet extends FPSet implements FPSetStatistic { throw new IOException(msg); } - tblCnt.set(0); + tblCnt.reset(); bucketsCapacity = 0; - tblLoad = 0; + tblLoad.reset(); } /** @@ -1239,18 +1077,17 @@ public abstract class DiskFPSet extends FPSet implements FPSetStatistic { * associated with "this.rwLock" must be held, as must the mutex * "this.rwLock" itself. */ - private final void mergeNewEntries() throws IOException { + protected void mergeNewEntries() throws IOException { // Implementation Note: Unfortunately, because the RandomAccessFile // class (and hence, the BufferedRandomAccessFile class) does not // provide a way to re-use an existing RandomAccessFile object on // a different file, this implementation must close all existing // files and re-allocate new BufferedRandomAccessFile objects. - - // close existing files (except brafPool[0]) for (int i = 0; i < braf.length; i++) { - braf[i].close(); + // Seek readers to zero position. + braf[i].seek(0L); } - for (int i = 1; i < brafPool.length; i++) { + for (int i = 0; i < brafPool.length; i++) { brafPool[i].close(); } @@ -1258,53 +1095,120 @@ public abstract class DiskFPSet extends FPSet implements FPSetStatistic { File tmpFile = new File(tmpFilename); tmpFile.delete(); RandomAccessFile tmpRAF = new BufferedRandomAccessFile(tmpFile, "rw"); - RandomAccessFile raf = brafPool[0]; - raf.seek(0); + tmpRAF.setLength((getTblCnt() + fileCnt) * FPSet.LongSize); // merge - mergeNewEntries(raf, tmpRAF); - + mergeNewEntries(braf, tmpRAF); + // clean up - raf.close(); + for (int i = 0; i < braf.length; i++) { + // close existing files (except brafPool[0]) + braf[i].close(); + } tmpRAF.close(); - String realName = fpFilename; - File currFile = new File(realName); - currFile.delete(); - boolean status = tmpFile.renameTo(currFile); - Assert.check(status, EC.SYSTEM_UNABLE_NOT_RENAME_FILE); + try { + FileUtil.replaceFile(tmpFilename, fpFilename); + } catch (IOException e) { + Assert.fail(EC.SYSTEM_UNABLE_NOT_RENAME_FILE, e); + } // reopen a BufferedRAF for each thread for (int i = 0; i < braf.length; i++) { // Better way would be to provide method BRAF.open - braf[i] = new BufferedRandomAccessFile(realName, "r"); + braf[i] = new BufferedRandomAccessFile(fpFilename, "r"); } for (int i = 0; i < brafPool.length; i++) { // Better way would be to provide method BRAF.open - brafPool[i] = new BufferedRandomAccessFile(realName, "r"); + brafPool[i] = new BufferedRandomAccessFile(fpFilename, "r"); } + + // Verify disk file is sorted. + assert checkFile(braf[0], index, fileCnt); + poolIndex = 0; } + + protected abstract void mergeNewEntries(BufferedRandomAccessFile[] inRAFs, RandomAccessFile outRAF) throws IOException; + + } + + private static boolean checkFile(BufferedRandomAccessFile braf, long[] index, long elements) throws IOException { + final long fileLen = braf.length(); + if (fileLen / LongSize != elements) { + return false; + } + final long ptr = braf.getFilePointer(); + long predecessor = Long.MIN_VALUE; + if (fileLen > 0) { + predecessor = braf.readLong(); + if (predecessor != index[0]) { + return false; + } + while (braf.getFilePointer() < fileLen) { + long l = braf.readLong(); + if (predecessor >= l) { + return false; + } + predecessor = l; + } + } + braf.seek(ptr); + return predecessor == index[index.length - 1]; + } + + /* + * Helper to read a fingerprint file (.fp) front to end and check for inconsistencies. + */ + + @SuppressWarnings("resource") + public static void main(String[] args) throws IOException { + if (args.length == 1 && !args[0].equals("")) { + + final BufferedRandomAccessFile braf = new BufferedRandomAccessFile(new File(args[0]), "r"); + + final long elements = braf.length() / FPSet.LongSize; + final DecimalFormat df = new DecimalFormat("###,###.###"); + System.out.println(String.format("About to scan %s elements.", df.format(elements))); + + long elem = 0L; + for (long i = 0; i < elements; i++) { + final long l = braf.readLong(); + if (l < elem) { + System.err.println( + String.format("Inconsistent elements %s at pos %s < %s at pos %s.", elem, i - 1L, l, i)); + } + elem = l; + if (i > 0 && i % 100000000L == 0L) { + System.out.println(String.format("Scanned %s elements.", df.format(i))); + } + } + } else if (args.length == 2 && !args[0].equals("") && !args[1].equals("")) { + final BufferedRandomAccessFile superset = new BufferedRandomAccessFile(new File(args[0]), "r"); + final BufferedRandomAccessFile subset = new BufferedRandomAccessFile(new File(args[1]), "r"); + + final long elements = subset.length() / FPSet.LongSize; + final long fileLen = superset.length(); + + OUTER: for (long i = 0; i < elements; i++) { + final long l = subset.readLong(); + while (superset.getFilePointer() < fileLen) { + final long m = superset.readLong(); + if (l == m) { + continue OUTER; + } else if (m > l) { + System.err + .println(String.format("Inconsistent element in superset %s not in superset at pos %s.", + m, superset.getFilePointer())); + } + } + System.err.println( + String.format("Element in subset %s not in superset at pos %s.", l, subset.getFilePointer())); + } - public final void mergeNewEntries(long[] buff, int buffLen) - throws IOException { - // create temporary file - File tmpFile = new File(tmpFilename); - tmpFile.delete(); - RandomAccessFile tmpRAF = new BufferedRandomAccessFile(tmpFile, "rw"); - File currFile = new File(fpFilename); - RandomAccessFile currRAF = new BufferedRandomAccessFile(currFile, "r"); - - // merge - this.mergeNewEntries(currRAF, tmpRAF); - - // clean up - currRAF.close(); - tmpRAF.close(); - currFile.delete(); - boolean status = tmpFile.renameTo(currFile); - Assert.check(status, EC.SYSTEM_UNABLE_NOT_RENAME_FILE); + System.out.println("Finished scanning files."); + } else { + System.err.println("Usage: DiskFPSet file.fp OR superset.fp subset.fp"); + System.exit(1); } - - protected abstract void mergeNewEntries(RandomAccessFile inRAF, RandomAccessFile outRAF) throws IOException; } } diff --git a/tlatools/src/tlc2/tool/fp/FPSet.java b/tlatools/src/tlc2/tool/fp/FPSet.java index 126d46e2a791076171c1f0ee8555eeee682a2a25..8bf0987e10c8b2aa16fb5e5b356f9b7d409f1329 100644 --- a/tlatools/src/tlc2/tool/fp/FPSet.java +++ b/tlatools/src/tlc2/tool/fp/FPSet.java @@ -10,6 +10,8 @@ import java.rmi.NoSuchObjectException; import java.rmi.RemoteException; import java.rmi.server.UnicastRemoteObject; +import tlc2.tool.TLCTrace; +import tlc2.tool.distributed.fp.DistributedFPSet; import tlc2.tool.distributed.fp.FPSetRMI; import tlc2.util.BitVector; import tlc2.util.LongVec; @@ -26,7 +28,7 @@ public abstract class FPSet extends UnicastRemoteObject implements FPSetRMI /** * Size of a Java long in bytes */ - protected static final int LongSize = 8; + protected static final long LongSize = 8; /** * Counts the amount of states passed to the containsBlock method @@ -46,7 +48,11 @@ public abstract class FPSet extends UnicastRemoteObject implements FPSetRMI * override this method as necessary. This method must be called * after the constructor but before any of the other methods below. */ - public abstract void init(int numThreads, String metadir, String filename) throws IOException; + public abstract FPSet init(int numThreads, String metadir, String filename) throws IOException; + + public void incWorkers(int num) { + // subclasses may override + } /* Returns the number of fingerprints in this set. */ /* (non-Javadoc) @@ -81,12 +87,21 @@ public abstract class FPSet extends UnicastRemoteObject implements FPSetRMI /* (non-Javadoc) * @see tlc2.tool.distributed.fp.FPSetRMI#exit(boolean) */ - public abstract void exit(boolean cleanup) throws IOException; + public void exit(boolean cleanup) throws IOException { + // If DistributedFPSet is running, signal termination and wake it up. + // This is necessary when a SecurityManager intercepts System.exit(int) + // calls which has the side effect that DistributedFPSet's reporting + // loop does not terminate and keeps going forever. + DistributedFPSet.shutdown(); + synchronized (this) { + this.notify(); + } + } /* (non-Javadoc) * @see tlc2.tool.distributed.fp.FPSetRMI#checkFPs() */ - public abstract double checkFPs() throws IOException; + public abstract long checkFPs() throws IOException; /* (non-Javadoc) * @see tlc2.tool.distributed.fp.FPSetRMI#beginChkpt() @@ -101,14 +116,10 @@ public abstract class FPSet extends UnicastRemoteObject implements FPSetRMI /* (non-Javadoc) * @see tlc2.tool.distributed.fp.FPSetRMI#recover() */ - public abstract void recover() throws IOException; + public abstract void recover(TLCTrace trace) throws IOException; public abstract void recoverFP(long fp) throws IOException; - public abstract void prepareRecovery() throws IOException; - - public abstract void completeRecovery() throws IOException; - /* The set of checkpoint methods for remote checkpointing. */ /* (non-Javadoc) * @see tlc2.tool.distributed.fp.FPSetRMI#beginChkpt(java.lang.String) diff --git a/tlatools/src/tlc2/tool/fp/FPSetConfiguration.java b/tlatools/src/tlc2/tool/fp/FPSetConfiguration.java index a79e8152b97aae155f952b28eba31f54b4022fd7..47473d729533406cf09b211f7b1ff73d387cc0ae 100644 --- a/tlatools/src/tlc2/tool/fp/FPSetConfiguration.java +++ b/tlatools/src/tlc2/tool/fp/FPSetConfiguration.java @@ -4,13 +4,18 @@ package tlc2.tool.fp; import java.io.Serializable; import tlc2.output.EC; +import tlc2.tool.distributed.fp.FPSetManager.FPSets; import util.Assert; import util.TLCRuntime; @SuppressWarnings("serial") public class FPSetConfiguration implements Serializable { - protected int fpBits = 0; + /** + * N most significant bits used as address bits by a MultiFPSet. + */ + protected int fpBits = 1; + protected long memoryInBytes = -1L; protected double ratio; protected String implementation; @@ -21,22 +26,45 @@ public class FPSetConfiguration implements Serializable { } public FPSetConfiguration(Double aRatio) { - this.ratio = aRatio; // Read the implementation class from the System properties instead of // the cmd line. Right now I'm reluctant to expose the impl class as a // cmd line parameter and carry it forth forever. - this.implementation = System.getProperty(FPSetFactory.IMPL_PROPERTY, - FPSetFactory.getImplementationDefault()); + this(aRatio, System.getProperty(FPSetFactory.IMPL_PROPERTY, + FPSetFactory.getImplementationDefault())); + } + + public FPSetConfiguration(Double aRatio, String implementation) { + this.ratio = aRatio; + this.implementation = implementation; } public boolean allowsNesting() { return getFpBits() > 0; } + /** + * @return The number of most significant bits that must not be used by an + * FPSet to calculate its index on. The bits are used by a + * {@link MultiFPSet} to address individual {@link FPSets}. + * Consequently, for an individual {@link FPSet} these bits are + * fixed (always either zero or one for all fingerprints that the + * {@link FPSet} sees). + */ public int getFpBits() { + if (fpBits == 0 && FPSetFactory.isDiskFPSet(implementation)) { + // DiskFPSets always require two instances. A single DiskFPSet + // essentially only uses 63. fingerprint bits and thus increases the + // likelihood of hash collisions. + fpBits = 1; + } return fpBits; } + public void setFpBits(int fpBits) { + Assert.check(FPSet.isValid(fpBits), EC.GENERAL); + this.fpBits = fpBits; + } + public long getMemoryInBytes() { final TLCRuntime instance = TLCRuntime.getInstance(); @@ -73,10 +101,6 @@ public class FPSetConfiguration implements Serializable { return 1 << getFpBits(); } - public int getPrefixBits() { - return getFpBits(); - } - public void setRatio(double aRatio) { // Allowing aRatio to be 0.0 makes little sense semantically, but we // accept it anyway and let TLCRuntime deal with it. @@ -84,11 +108,6 @@ public class FPSetConfiguration implements Serializable { this.ratio = aRatio; } - public void setFpBits(int fpBits) { - Assert.check(FPSet.isValid(fpBits), EC.GENERAL); - this.fpBits = fpBits; - } - public double getRatio() { return ratio; } diff --git a/tlatools/src/tlc2/tool/fp/FPSetFactory.java b/tlatools/src/tlc2/tool/fp/FPSetFactory.java index b1e64c2aa384fb0a86baba0eeab5bf099b3fed26..6dd235f42e29487d6776f818819bd79093c12131 100644 --- a/tlatools/src/tlc2/tool/fp/FPSetFactory.java +++ b/tlatools/src/tlc2/tool/fp/FPSetFactory.java @@ -6,9 +6,16 @@ import java.lang.reflect.InvocationTargetException; import java.rmi.RemoteException; import java.util.ArrayList; import java.util.List; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import tlc2.TLCGlobals; import tlc2.output.EC; import tlc2.output.MP; +import util.TLCRuntime; +import util.TLCRuntime.ARCH; public abstract class FPSetFactory { @@ -22,9 +29,10 @@ public abstract class FPSetFactory { return !OffHeapDiskFPSet.class.isAssignableFrom(clazz); } - static boolean allocatesOnHeap(final String clazz) { + public static boolean allocatesOnHeap(final String clazz) { try { final ClassLoader classLoader = FPSet.class.getClassLoader(); + @SuppressWarnings("unchecked") Class<? extends FPSet> cls = (Class<? extends FPSet>) classLoader .loadClass(clazz); return allocatesOnHeap(cls); @@ -33,6 +41,42 @@ public abstract class FPSetFactory { } } + private static boolean supportsArchitecture(String clazz) { + try { + final ClassLoader classLoader = FPSet.class.getClassLoader(); + @SuppressWarnings("unchecked") + Class<? extends FPSet> cls = (Class<? extends FPSet>) classLoader + .loadClass(clazz); + return supports32Bits(cls); + } catch (ClassNotFoundException e) { + return false; + } + } + + private static boolean supports32Bits(final Class<? extends FPSet> clazz) { + if (TLCRuntime.getInstance().getArchitecture() == TLCRuntime.ARCH.x86 + && OffHeapDiskFPSet.class.isAssignableFrom(clazz)) { + return false; + } + return true; + } + + private static boolean isDiskFPSet(Class<? extends FPSet> cls) { + return DiskFPSet.class.isAssignableFrom(cls); + } + + static boolean isDiskFPSet(final String clazz) { + try { + final ClassLoader classLoader = FPSet.class.getClassLoader(); + @SuppressWarnings("unchecked") + Class<? extends FPSet> cls = (Class<? extends FPSet>) classLoader + .loadClass(clazz); + return isDiskFPSet(cls); + } catch (ClassNotFoundException e) { + return false; + } + } + /** * @see getFPSet * @return @@ -55,28 +99,49 @@ public abstract class FPSetFactory { // fpBits > 0 indicates that the consumer requires a MultiFPSet if (fpSetConfig.allowsNesting()) { - // A MultiFPSet comes in two flavors: - // a) For FPSets that sort on LSB - // b) For FPSets that use the MSB to sort the internal hash map - if (msbBasedFPSet(implClassname)) { - // Pass physical memory instead of logical FP count to adhere to - // the general FPSet ctor contract. - // @see http://bugzilla.tlaplus.net/show_bug.cgi?id=290 - return new MSBMultiFPSet(fpSetConfig); - } else { - // Pass physical memory instead of logical FP count to adhere to - // the general FPSet ctor contract. - // @see http://bugzilla.tlaplus.net/show_bug.cgi?id=290 - return new MultiFPSet(fpSetConfig); - } + // Pass physical memory instead of logical FP count to adhere to + // the general FPSet ctor contract. + // @see http://bugzilla.tlaplus.net/show_bug.cgi?id=290 + return new MultiFPSet(fpSetConfig); } else { if (implClassname != null) { - return loadImplementation(implClassname, fpSetConfig); - } else { - return new MSBDiskFPSet(fpSetConfig); + if (supportsArchitecture(implClassname)) { + return loadImplementation(implClassname, fpSetConfig); + } else { + final ARCH architecture = TLCRuntime.getInstance().getArchitecture(); + final String msg = String.format( + "Selected fingerprint set (set of visited states) %s does not support current architecture %s. " + + "Reverting to default fingerprint set. " + + "Off-heap memory allocated via -XX:MaxDirectMemorySize flag cannot be used by default " + + "fingerprint set and is therefore wasted.", + implClassname, architecture); + MP.printWarning(EC.TLC_FEATURE_UNSUPPORTED, msg); + } } + return new MSBDiskFPSet(fpSetConfig); } } + + + /** + * Create and *initialize* the set. + */ + public static Future<FPSet> getFPSetInitialized(final FPSetConfiguration fpSetConfiguration, final String metadir, + final String mainFile) { + final ExecutorService es = Executors.newSingleThreadExecutor(); + return es.submit(new Callable<FPSet>() { + @Override + public FPSet call() throws Exception { + try { + final FPSet fpSet = FPSetFactory.getFPSet(fpSetConfiguration); + fpSet.init(TLCGlobals.getNumWorkers(), metadir, mainFile); + return fpSet; + } finally { + es.shutdown(); + } + } + }); + } /** * @return A list of classes implementing {@link FPSet}. Eventually this @@ -163,18 +228,4 @@ public abstract class FPSetFactory { MP.printWarning(EC.GENERAL, "unsuccessfully trying to load custom FPSet class: " + clazz, exp); return null; } - - /** - * @param userFpsetImplClassname - * @return true iff the given class uses the MSB to pre-sort its fingerprints - */ - private static boolean msbBasedFPSet(String userFpsetImplClassname) { - if (!allocatesOnHeap(userFpsetImplClassname)) { - return true; - } - if (userFpsetImplClassname.equals(MSBDiskFPSet.class.getName())) { - return true; - } - return false; - } } diff --git a/tlatools/src/tlc2/tool/fp/FPSetStatistic.java b/tlatools/src/tlc2/tool/fp/FPSetStatistic.java index bec95090adc5a9348f2f646c9900bec161cdd8e9..bbc287ea385116a5d1954a14221c8f4e838dffb0 100644 --- a/tlatools/src/tlc2/tool/fp/FPSetStatistic.java +++ b/tlatools/src/tlc2/tool/fp/FPSetStatistic.java @@ -97,19 +97,14 @@ public interface FPSetStatistic { public long getFlushTime(); /** - * @see DiskFPSet#getReaderWriterCnt() + * @return DiskFPSet#getLockCnt() */ - public int getReaderWriterCnt(); + public int getLockCnt(); /** - * @return DiskFPSet#getCollisionBucketCnt() - */ - public long getCollisionBucketCnt(); - - /** - * @return DiskFPSet#getCollisionRatio() + * @see DiskFPSet#getReaderWriterCnt() */ - public double getCollisionRatio(); + public int getReaderWriterCnt(); /** * @return DiskFPSet#getLoadFactor(); diff --git a/tlatools/src/tlc2/tool/fp/FPSetsIdxCalculationBits.jpg b/tlatools/src/tlc2/tool/fp/FPSetsIdxCalculationBits.jpg new file mode 100644 index 0000000000000000000000000000000000000000..75cb4cdd306cbfc59de294824ffc45ef611dfe36 Binary files /dev/null and b/tlatools/src/tlc2/tool/fp/FPSetsIdxCalculationBits.jpg differ diff --git a/tlatools/src/tlc2/tool/fp/HeapBasedDiskFPSet.java b/tlatools/src/tlc2/tool/fp/HeapBasedDiskFPSet.java index 45e8a195c85ccdf8085fec90c8fc04bd26e4ed12..9a0d165478c60db6d3f562cf341cc1648e1a9e4d 100644 --- a/tlatools/src/tlc2/tool/fp/HeapBasedDiskFPSet.java +++ b/tlatools/src/tlc2/tool/fp/HeapBasedDiskFPSet.java @@ -1,12 +1,37 @@ // Copyright (c) 2012 Microsoft Corporation. All rights reserved. package tlc2.tool.fp; +import java.io.IOException; import java.rmi.RemoteException; +import java.util.concurrent.locks.Lock; +import java.util.logging.Level; +import tlc2.TLCGlobals; +import tlc2.tool.fp.management.DiskFPSetMXWrapper; +import tlc2.util.Striped; import util.Assert; @SuppressWarnings("serial") public abstract class HeapBasedDiskFPSet extends DiskFPSet { + + /** + * Number of locks in the striped lock (#StripeLocks = 2^LogLockCnt).<br> + * Theoretically best performance should be seen with on lock per bucket in + * the primary hash table. An some point though (not yet measured), this + * performance benefit is probably eaten up by the memory consumption of the + * striped lock {@link DiskFPSet#rwLock} itself, which reduces the memory + * available to the hash set. + */ + protected static final int LogLockCnt = Integer.getInteger(DiskFPSet.class.getName() + ".logLockCnt", (31 - Integer.numberOfLeadingZeros(TLCGlobals.getNumWorkers()) + 8)); + /** + * protects n memory buckets + */ + protected final Striped rwLock; + /** + * Is (1 << LogLockCnt) and exposed here for subclasses + */ + protected final int lockCnt; + protected final int lockMask; /** * in-memory buffer of new entries @@ -19,7 +44,7 @@ public abstract class HeapBasedDiskFPSet extends DiskFPSet { protected long mask; /** - * The calculated capacity of tbl + * The calculated capacity of tbl. Will always be a power of two. */ protected final int capacity; @@ -38,7 +63,19 @@ public abstract class HeapBasedDiskFPSet extends DiskFPSet { protected HeapBasedDiskFPSet(final FPSetConfiguration fpSetConfig) throws RemoteException { super(fpSetConfig); - + + // Ideally we use one lock per bucket because even with relatively high + // lock counts, we fall victim to the birthday paradox. However, locks + // ain't cheap, which is why we have to find the sweet spot. We + // determined the constant 8 empirically on Intel Xeon CPU E5-2670 v2 @ + // 2.50GHz. This is based on the number of cores only. We ignore their + // speed. MultiThreadedMSBDiskFPSet is the most suitable performance + // test available for the job. It just inserts long values into the + // set. int is obviously going to be too small once 2^23 cores become + // commonplace. + this.lockCnt = 1 << LogLockCnt; + this.rwLock = Striped.readWriteLock(lockCnt); + // Reserve a portion of the memory for the auxiliary storage long maxMemCnt = (long) (fpSetConfig.getMemoryInFingerprintCnt() / getAuxiliaryStorageRequirement()); @@ -53,24 +90,29 @@ public abstract class HeapBasedDiskFPSet extends DiskFPSet { // guard against underflow // LL modified error message on 7 April 2012 Assert.check(logMaxMemCnt - LogMaxLoad >= 0, "Underflow when computing HeapBasedDiskFPSet"); - this.capacity = 1 << (logMaxMemCnt - LogMaxLoad); - // instead of changing maxTblCnd to long and pay an extra price when - // comparing int and long every time put(long) is called, we set it to - // Integer.MAX_VALUE instead. capacity can never grow bigger - // (unless java starts supporting 64bit array sizes) - // + // Guard against a capacity overflow with large amounts (e.g. ~1TB) of + // dedicated memory. If cap overflows, the VMs maximum allowed array + // size is used. + final int cap = 1 << (logMaxMemCnt - LogMaxLoad); + if (cap < 0) { + // You wonder why 8 and not 42? Ask the VM gods! + this.capacity = Integer.MAX_VALUE - 8; + } else { + this.capacity = cap; + } + // maxTblCnt mathematically has to be an upper limit for the in-memory storage // so that a disk flush occurs before an _evenly_ distributed fp distribution fills up // the collision buckets to a size that exceeds the VM limit (unevenly distributed // fp distributions can still cause a OutOfMemoryError which this guard). - this.maxTblCnt = (logMaxMemCnt >= 31) ? Integer.MAX_VALUE : (1 << logMaxMemCnt); // maxTblCnt := 2^logMaxMemCnt + this.maxTblCnt = (1L << logMaxMemCnt); // maxTblCnt := 2^logMaxMemCnt Assert.check(maxTblCnt <= fpSetConfig.getMemoryInFingerprintCnt(), "Exceeded upper memory storage limit"); // guard against negative maxTblCnt // LL modified error message on 7 April 2012 - Assert.check(maxTblCnt > capacity && capacity > tblCnt.get(), + Assert.check(maxTblCnt > capacity && capacity > getTblCnt(), "negative maxTblCnt"); this.mask = capacity - 1; @@ -98,6 +140,13 @@ public abstract class HeapBasedDiskFPSet extends DiskFPSet { rwLock.releaseAllLocks(); return size; } + + /* (non-Javadoc) + * @see tlc2.tool.fp.DiskFPSet#getLockCnt() + */ + public int getLockCnt() { + return this.rwLock.size(); + } /* (non-Javadoc) * @see tlc2.tool.fp.DiskFPSet#getTblCapacity() @@ -126,6 +175,36 @@ public abstract class HeapBasedDiskFPSet extends DiskFPSet { return fp & aMask; } + /* (non-Javadoc) + * @see tlc2.tool.fp.FPSet#contains(long) + * + * 0 and {@link Long#MIN_VALUE} always return false + */ + public final boolean contains(long fp) throws IOException { + fp = checkValid(fp); + // zeros the msb + long fp0 = fp & 0x7FFFFFFFFFFFFFFFL; + final Lock readLock = this.rwLock.getAt(getLockIndex(fp0)).readLock(); + readLock.lock(); + // First, look in in-memory buffer + if (this.memLookup(fp0)) { + readLock.unlock(); + this.memHitCnt.increment(); + return true; + } + + // block if disk is being re-written + // next, look on disk + boolean diskHit = this.diskLookup(fp0); + // increment while still locked + if(diskHit) { + diskHitCnt.increment(); + } + + // end read; add to memory buffer if necessary + readLock.unlock(); + return diskHit; + } /* (non-Javadoc) * @see tlc2.tool.fp.DiskFPSet#memLookup(long) @@ -146,6 +225,107 @@ public abstract class HeapBasedDiskFPSet extends DiskFPSet { return false; } + /* (non-Javadoc) + * @see tlc2.tool.fp.FPSet#put(long) + * + * 0 and {@link Long#MIN_VALUE} always return false + * + * Locking is as follows: + * + * Acquire mem read lock + * Acquire disk read lock + * Release mem read lock + * + * Acquire mem read/write lock + * Release disk read lock // interleaved + * insert into mem + * Acquire disk write lock (might cause potential writer to wait() which releases mem read lock (monitor)) + * flushToDisk + * Release disk write lock + * Release mem read lock + * + * asserts: + * - Exclusive access to disk and memory for a writer + * + */ + public final boolean put(long fp) throws IOException { + fp = checkValid(fp); + // zeros the msb + long fp0 = fp & 0x7FFFFFFFFFFFFFFFL; + + final Lock readLock = rwLock.getAt(getLockIndex(fp0)).readLock(); + readLock.lock(); + // First, look in in-memory buffer + if (this.memLookup(fp0)) { + readLock.unlock(); + this.memHitCnt.increment(); + return true; + } + + // blocks => wait() if disk is being re-written + // (means the current thread returns rwLock monitor) + // Why not return monitor first and then acquire read lock? + // => prevent deadlock by acquiring threads in same order? + + // next, look on disk + boolean diskHit = this.diskLookup(fp0); + + // In event of disk hit, return + if (diskHit) { + readLock.unlock(); + this.diskHitCnt.increment(); + return true; + } + + readLock.unlock(); + + // Another writer could write the same fingerprint here if it gets + // interleaved. This is no problem though, because memInsert again + // checks existence for fp to be inserted + + final Lock w = rwLock.getAt(getLockIndex(fp0)).writeLock(); + w.lock(); + + // if disk lookup failed, add to memory buffer + if (this.memInsert(fp0)) { + w.unlock(); + this.memHitCnt.increment(); + return true; + } + + // test if buffer is full && block until there are no more readers + if (needsDiskFlush() && this.flusherChosen.compareAndSet(false, true)) { + + // statistics + growDiskMark++; + final long timestamp = System.currentTimeMillis(); + final long insertions = getTblCnt(); + + // acquire _all_ write locks + rwLock.acquireAllLocks(); + + // flush memory entries to disk + flusher.flushTable(); + + // release _all_ write locks + rwLock.releaseAllLocks(); + + // reset forceFlush to false + forceFlush = false; + + // finish writing + this.flusherChosen.set(false); + + long l = System.currentTimeMillis() - timestamp; + flushTime += l; + + LOGGER.log(Level.FINE, "Flushed disk {0} {1}. time, in {2} sec after {3} insertions.", new Object[] { + ((DiskFPSetMXWrapper) diskFPSetMXWrapper).getObjectName(), getGrowDiskMark(), l, insertions}); + } + w.unlock(); + return false; + } + /* (non-Javadoc) * @see tlc2.tool.fp.DiskFPSet#memInsert(long) */ @@ -161,7 +341,7 @@ public abstract class HeapBasedDiskFPSet extends DiskFPSet { bucket[0] = fp; this.tbl[index] = bucket; this.bucketsCapacity += InitialBucketCapacity; - this.tblLoad++; + this.tblLoad.increment(); } else { // search for entry in existing bucket int bucketLen = bucket.length; @@ -199,7 +379,21 @@ public abstract class HeapBasedDiskFPSet extends DiskFPSet { bucket[i] = fp; } } - this.tblCnt.getAndIncrement(); + this.tblCnt.increment(); return false; } + + /* (non-Javadoc) + * @see tlc2.tool.fp.DiskFPSet#acquireTblWriteLock() + */ + void acquireTblWriteLock() { + rwLock.acquireAllLocks(); + } + + /* (non-Javadoc) + * @see tlc2.tool.fp.DiskFPSet#releaseTblWriteLock() + */ + void releaseTblWriteLock() { + rwLock.releaseAllLocks(); + } } diff --git a/tlatools/src/tlc2/tool/fp/LSBDiskFPSet.java b/tlatools/src/tlc2/tool/fp/LSBDiskFPSet.java index cc84b78fa149cdeee2dfbcd3bc27a3288526aabb..aa126bb88e1d6f150fd9699d0c752648d03ad968 100644 --- a/tlatools/src/tlc2/tool/fp/LSBDiskFPSet.java +++ b/tlatools/src/tlc2/tool/fp/LSBDiskFPSet.java @@ -9,6 +9,7 @@ import java.rmi.RemoteException; import java.util.Arrays; import tlc2.output.EC; +import tlc2.util.BufferedRandomAccessFile; import util.Assert; @SuppressWarnings("serial") @@ -36,7 +37,7 @@ public class LSBDiskFPSet extends HeapBasedDiskFPSet { @Override protected void prepareTable() { // Verify tblCnt is still within positive Integer.MAX_VALUE bounds - int cnt = (int) tblCnt.get(); + int cnt = (int) getTblCnt(); Assert.check(cnt > 0, EC.GENERAL); // Why not sort this.tbl in-place rather than doubling memory @@ -76,7 +77,7 @@ public class LSBDiskFPSet extends HeapBasedDiskFPSet { * @see tlc2.tool.fp.DiskFPSet.Flusher#mergeNewEntries(java.io.RandomAccessFile, java.io.RandomAccessFile) */ @Override - protected void mergeNewEntries(RandomAccessFile inRAF, RandomAccessFile outRAF) throws IOException { + protected void mergeNewEntries(BufferedRandomAccessFile[] inRAFs, RandomAccessFile outRAF) throws IOException { final int buffLen = buff.length; // Precompute the maximum value of the new file @@ -97,7 +98,7 @@ public class LSBDiskFPSet extends HeapBasedDiskFPSet { boolean eof = false; if (fileCnt > 0) { try { - value = inRAF.readLong(); + value = inRAFs[0].readLong(); } catch (EOFException e) { eof = true; } @@ -110,7 +111,7 @@ public class LSBDiskFPSet extends HeapBasedDiskFPSet { if (value < buff[i]) { writeFP(outRAF, value); try { - value = inRAF.readLong(); + value = inRAFs[0].readLong(); } catch (EOFException e) { eof = true; } @@ -133,7 +134,7 @@ public class LSBDiskFPSet extends HeapBasedDiskFPSet { do { writeFP(outRAF, value); try { - value = inRAF.readLong(); + value = inRAFs[0].readLong(); } catch (EOFException e) { eof = true; } diff --git a/tlatools/src/tlc2/tool/fp/LongArray.java b/tlatools/src/tlc2/tool/fp/LongArray.java new file mode 100644 index 0000000000000000000000000000000000000000..82afa7971064cadd96f791558478ada45899c1d3 --- /dev/null +++ b/tlatools/src/tlc2/tool/fp/LongArray.java @@ -0,0 +1,312 @@ +// Copyright (c) 2016 Markus Alexander Kuppe. All rights reserved. +package tlc2.tool.fp; + +import java.io.IOException; +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; + +/* + * On Java 11 (probably starting with 9) sun.misc.Unsafe is implemented on top + * of jdk.internal.misc.Unsafe which provides a similar though not quiet + * identical API. While sun.misc.Unsafe remains available to client code such + * as LongArray in Java 11, parts of its API have already been dissolved + * (see e.g. https://bugs.openjdk.java.net/browse/JDK-8202999). This does not + * affect LongArray yet, but will probably affect LongArray in the future. + * Refactoring LongArray to replace sun.misc.Unsafe with jdk.internal.misc.Unsafe + * requires the following changes: + * - Replace sun.misc.Unsafe with jdk.internal.misc.Unsafe + * - Change Unsafe#compareAndSwapLong with Unsafe#compareAndSetLong in LongArray#trySet + * - Run LongArray on Java 11 with "--add-opens=java.base/jdk.internal.misc=ALL-UNNAMED" + * to configure Java's module system (Jigsaw) to expose jdk.internal.misc.Unsafe + * to LongArray. + * The last requirement is visible to TLA+ users who run tla2tools.jar on the + * command-line. As we do not want to expose JVM parameters to them, we keep + * sun.misc.Unsafe for now. + */ +import sun.misc.Unsafe; // jdk.internal.misc.Unsafe; +import tlc2.output.EC; +import util.Assert; +import util.TLCRuntime; + +/** + * This implementation uses sun.misc.Unsafe instead of a wrapping + * java.nio.ByteBuffer due to the fact that the former's allocateMemory + * takes a long argument, while the latter is restricted to + * Integer.MAX_VALUE as its capacity.<br> + * In 2012 this poses a too hard limit on the usable memory, hence we trade + * generality for performance. + */ +@SuppressWarnings("restriction") +public final class LongArray { + + private final Unsafe unsafe; + + /** + * The base address of this direct memory allocated with Unsafe. + */ + private final long baseAddress; + + /** + * Maximum number of elements that can be contained in this array. + */ + private final long length; + + /** + * CHOOSE logAddressSize \in 1..(Long.SIZE / 8): 2^logAddressSize = (Long.SIZE / 8) + */ + private static final int logAddressSize = 3; + + LongArray(final long positions) { + this.length = positions; + this.unsafe = getUnsafe(); + + // LongArray is only implemented for 64bit architectures. A 32bit + // implementation might be possible. However, implementing CAS (see + // trySet) seems impossible when values have to be split in two + // parts (low/hi) on a 32 bit architecture. + // addressSize(): Report the size in bytes of a native pointer, as + // stored via #putAddress . This value will be either 4 or 8. We + // expect 8 (Long.SIZE / 8) which is the size of a TLC fingerprint + // (see FP64). + Assert.check(this.unsafe.addressSize() == (Long.SIZE / 8), EC.GENERAL); + baseAddress = this.unsafe.allocateMemory(positions << logAddressSize); + } + + LongArray(final Collection<Long> from) { + this(from.size()); + + final Iterator<Long> itr = from.iterator(); + long i = 0L; + while(itr.hasNext()) { + Long next = itr.next(); + set(i++, next); + } + } + + /** + * @return true iff LongArray can be used on the current JVM. It cannot be used + * if the architecture is not 64 bit and the sun.misc.Unsafe class + * cannot be loaded (on some JVM implementations, this isn't possible). + */ + public static boolean isSupported() { + if (TLCRuntime.ARCH.x86_64 != TLCRuntime.getInstance().getArchitecture()) { + return false; + } + try { + if (getUnsafe() != null) { + return true; + } + return false; + } catch (RuntimeException e) { + return false; + } + } + + /** + * @return An Unsafe object or a {@link RuntimeException} wrapping any {@link Exception}. + */ + private static sun.misc.Unsafe getUnsafe() { + // More Details can be found at: http://www.mydailyjava.blogspot.no/2013/12/sunmiscunsafe.html + try { + // Use reflection API to unhide Unsafe + final Field f = sun.misc.Unsafe.class.getDeclaredField("theUnsafe"); + f.setAccessible(true); + return (sun.misc.Unsafe) f.get(null); + } catch (Exception e) { + throw new RuntimeException( + "Trying to use Sun VM specific sun.misc.Unsafe implementation but no Sun based VM detected.", + e); + } + } + + /** + * Initializes the memory by overriding each byte with zero starting at + * <code>baseAddress</code> and ending when all positions have been written. + * + * @throws IOException + */ + public final void zeroMemory() + throws IOException { + this.unsafe.setMemory(baseAddress, length * 8L, (byte) 0); // times 8L because it only writes a single byte. + } + + /** + * Initializes the memory by overriding each byte with zero starting at + * <code>baseAddress</code> and ending when all positions have been written. + * <p> + * To speed up the initialization, <code>numThreads</code> allows to set a + * thread count with which zeroing is done in parallel. + * + * @param numThreads + * Number of threads used to zero memory + * @throws IOException + */ + public final void zeroMemory(final int numThreads) + throws IOException { + + final long segmentSize = (long) Math.floor(length / numThreads); + + final ExecutorService es = Executors.newFixedThreadPool(numThreads); + try { + final Collection<Callable<Boolean>> tasks = new ArrayList<Callable<Boolean>>(numThreads); + for (int i = 0; i < numThreads; i++) { + final int offset = i; + tasks.add(new Callable<Boolean>() { + + public Boolean call() throws Exception { + // Null memory (done in parallel on segments). + // This is essential as allocateMemory returns + // uninitialized memory and + // memInsert/memLockup utilize 0L as a mark for an + // unused fingerprint position. + // Otherwise memory garbage wouldn't be distinguishable + // from a true fp. + final long lowerBound = segmentSize * offset; + // The last threads zeros up to the end. + final long upperBound = offset == numThreads - 1 ? length : (1 + offset) * segmentSize; + for (long pos = lowerBound; pos < upperBound; pos++) { + set(pos, 0L); + } + return true; + } + }); + } + final List<Future<Boolean>> invokeAll = es.invokeAll(tasks); + Assert.check(!invokeAll.isEmpty(), EC.GENERAL); + } catch (InterruptedException e) { + // not expected to happen + e.printStackTrace(); + } finally { + es.shutdown(); + } + } + + /** + * Converts from logical positions to + * physical memory addresses. + * + * @param logical position (zero indexed) + * @return The physical address of the fp slot + */ + private final long log2phy(long logicalAddress) { + return baseAddress + (logicalAddress << logAddressSize); + } + + private final void rangeCheck(final long position) { + assert position >= 0 && position < this.length; + } + + /** + * CAS (compare and swap) variant of {@link LongArray#set(long, long)}. + * + * @param position + * @param expected + * @param value + * @return true iff successful + * @throws IndexOutOfBoundsException + */ + public final boolean trySet(final long position, final long expected, final long value) { + rangeCheck(position); + return this.unsafe.compareAndSwapLong(null, log2phy(position), expected, value); + } + + /** + * Inserts the specified element at the specified position in this + * array. Overwrites any previous occupant of the specified position. + * + * @param position position at which the specified element is to be inserted + * @param value element to be inserted + * @throws IndexOutOfBoundsException + */ + public final void set(final long position, final long value) { + rangeCheck(position); + this.unsafe.putAddress(log2phy(position), value); + } + + /** + * Returns the element at the specified position in this array. + * + * @param position position of the element to return + * @return the element at the specified position in this array + * @throws IndexOutOfBoundsException + */ + public final long get(final long position) { + rangeCheck(position); + return this.unsafe.getAddress(log2phy(position)); + } + + /** + * Swaps elements at pos1 and pos2. This is not atomic. The element at pos1 + * will for a moment not be an element of {@link LongArray}. + */ + public final void swap(final long position1, final long position2) { + rangeCheck(position1); + rangeCheck(position2); + final long tmp = get(position1); + set(position1, get(position2)); + set(position2, tmp); + } + + /* + * Variant of swap that uses copyMemory. This implementation - suprisingly - is + * *not* faster compared to swap above (see LongArrayBenchmark). + */ + void swapCopy(final long position1, final long position2) { + final long tmp = unsafe.getAddress(log2phy(position1)); + unsafe.copyMemory(log2phy(position2), log2phy(position1), 8L); + unsafe.putAddress(log2phy(position2), tmp); + } + + + /** + * Returns the number of elements in this array. + * + * @return the number of elements in this array + */ + public final long size() { + return length; + } + + /* (non-Javadoc) + * @see java.lang.Object#toString() + */ + public String toString() { + return toString(0L, length - 1L); + } + + public String toString(long start, long end) { + long iMax = end; + if (iMax == -1L) { + return "[]"; + } + + final StringBuilder b = new StringBuilder(); + b.append('['); + for (long i = start; ; i++) { + final long lng = get(i); + if (lng == 0L) { + b.append("e"); + } else { + b.append(lng); + } + if (i == iMax) { + return b.append(']').toString(); + } + b.append(", "); + } + } + + public static void main(final String[] args) throws IOException { + final long elements = 1L << Integer.valueOf(args[0]); + System.out.format("Allocating LongArray with %,d elements.\n", elements); + final LongArray longArray = new LongArray(elements); + longArray.zeroMemory(); + } +} diff --git a/tlatools/src/tlc2/tool/fp/LongArrays.java b/tlatools/src/tlc2/tool/fp/LongArrays.java new file mode 100644 index 0000000000000000000000000000000000000000..179e1a464fdcb007475e913da18fd7d00a64e053 --- /dev/null +++ b/tlatools/src/tlc2/tool/fp/LongArrays.java @@ -0,0 +1,57 @@ +// Copyright (c) 2016 Markus Alexander Kuppe. All rights reserved. +package tlc2.tool.fp; + +public class LongArrays { + + /** + * Sorts the specified range [left,right] of the array. In other words, + * sorts the elements from the array index <code>left</code> to index + * <code>right</code>. + */ + public static void sort(final LongArray a, final long left, final long right, final LongComparator cmp) { + final long size = a.size(); + if (size == 0) { + return; + } + + //+++++++ Insertions sort +++++++// + for (long i = left, j = i; i < right; j = ++i) { + final long lo = (i + 1) % size; + final long ai = a.get(lo); + while (cmp.compare(ai, lo, a.get(j % size), j % size) <= -1) { + a.set((j + 1) % size, a.get(j % size)); + if (j-- == left) { + break; + } + } + a.set((j + 1) % size, ai); + } + } + + public static class LongComparator { + public int compare(long lo, long loPos, long hi, long hiPos) { + // The default comparator ignores the positions. + return Long.compare(lo, hi); + } + } + + public static void sort(final LongArray a, final long left, final long right) { + sort(a, left, right, new LongComparator()); + } + + public static void sort(final LongArray a) { + sort(a, 0, a.size() - 1L, new LongComparator()); + } + + /** + * DOES NOT HANDLE LONG ARRAYS LARGER THAN INTEGER.MAX_VALUE + */ + public static long[] toArray(final LongArray array) { + final long[] res = new long[(int) array.size()]; + for (int i = 0; i < array.size(); i++) { + long l = array.get(i); + res[i] = l; + } + return res; + } +} diff --git a/tlatools/src/tlc2/tool/fp/MSBDiskFPSet.java b/tlatools/src/tlc2/tool/fp/MSBDiskFPSet.java index d5ceafdb440926b49fa45658913781f08fecb4af..d0afaf9b6e713c4d7ded23fb2207a8e82c7b028c 100644 --- a/tlatools/src/tlc2/tool/fp/MSBDiskFPSet.java +++ b/tlatools/src/tlc2/tool/fp/MSBDiskFPSet.java @@ -10,6 +10,7 @@ import java.util.Arrays; import java.util.NoSuchElementException; import tlc2.output.EC; +import tlc2.util.BufferedRandomAccessFile; import util.Assert; @SuppressWarnings("serial") @@ -37,7 +38,8 @@ public class MSBDiskFPSet extends HeapBasedDiskFPSet { // index. However, we cannot use the 32st bit, because it is used to // indicate if a fp has been flushed to disk. Hence we use the first n // bits starting from the second most significant bit. - this.moveBy = (31 - fpSetConfig.getPrefixBits()) - (logMaxMemCnt - LogMaxLoad); + Assert.check(fpSetConfig.getFpBits() > 0, EC.GENERAL); + this.moveBy = (32 - fpSetConfig.getFpBits()) - (logMaxMemCnt - LogMaxLoad); this.mask = (capacity - 1) << moveBy; this.flusher = new MSBFlusher(); @@ -61,6 +63,9 @@ public class MSBDiskFPSet extends HeapBasedDiskFPSet { protected long index(long fp, long aMask) { // calculate hash value (just n most significant bits of fp) which is // used as an index address + // 1) Right shift by 32 bits and cast to int (upper 32 bits are zeroed) + // 2) Zero the lower n bits according to mask + // 3) Right shift by moveBy (the bits which have previously been zeroed because of mask. return ((int) (fp >>> 32) & aMask) >> moveBy; } @@ -96,8 +101,8 @@ public class MSBDiskFPSet extends HeapBasedDiskFPSet { /* (non-Javadoc) * @see tlc2.tool.fp.DiskFPSet#mergeNewEntries(long[], int, java.io.RandomAccessFile, java.io.RandomAccessFile) */ - protected void mergeNewEntries(RandomAccessFile inRAF, RandomAccessFile outRAF) throws IOException { - final long buffLen = tblCnt.get(); + protected void mergeNewEntries(BufferedRandomAccessFile[] inRAFs, RandomAccessFile outRAF) throws IOException { + final long buffLen = getTblCnt(); final TLCIterator itr = new TLCIterator(tbl); // Precompute the maximum value of the new file. @@ -105,13 +110,9 @@ public class MSBDiskFPSet extends HeapBasedDiskFPSet { // the last element of the disk file. In that case // the new maxVal is the larger element of the two // in-memory and on-disk elements. - // The largest on-disk element is passed to the - // iterator as a lower bound. - long maxVal; + long maxVal = itr.getLast(); if (index != null) { - maxVal = itr.getLast(index[index.length - 1]); - } else { - maxVal = itr.getLast(); + maxVal = Math.max(maxVal, index[index.length - 1]); } int indexLen = calculateIndexLen(buffLen); @@ -125,7 +126,7 @@ public class MSBDiskFPSet extends HeapBasedDiskFPSet { boolean eof = false; if (fileCnt > 0) { try { - value = inRAF.readLong(); + value = inRAFs[0].readLong(); } catch (EOFException e) { eof = true; } @@ -140,7 +141,7 @@ public class MSBDiskFPSet extends HeapBasedDiskFPSet { if ((value < fp || eol) && !eof) { writeFP(outRAF, value); try { - value = inRAF.readLong(); + value = inRAFs[0].readLong(); } catch (EOFException e) { eof = true; } @@ -176,7 +177,10 @@ public class MSBDiskFPSet extends HeapBasedDiskFPSet { } /** - * + * A TLCIterator wraps the buff given to it from its outer MSBDiskFPSet and + * provides convenience methods to traverse the elements in buff, namely: + * Check if there is a next element and get it as well as obtaining the + * largest element. */ public static class TLCIterator { /** @@ -286,75 +290,15 @@ public class MSBDiskFPSet extends HeapBasedDiskFPSet { } /** - * @param lowBound - * Stop searching for the last element if no element is - * larger than lowBound - * @return The last element in the iteration that is larger than lowBound + * @return The last (largest) element in the iteration (aka the + * underlying buff of buckets). * @exception NoSuchElementException * if iteration is empty. - * <p> + * * Pre-condition: Each bucket is sorted in ascending - * order. on-disk fingerprints don't adhere to the - * ascending order though! - */ - public long getLast(final long lowBound) { - int len = buff.length; - - // Walk from the end of buff to the beginning. Each bucket that is - // found and non-null (null if no fingerprint for such an index has - // been added to the DiskFPSet) is checked for a valid fingerprint. - // A fp is valid iff it is larger zero. A zero fingerprint slot - // indicates an unoccupied slot, while a negative one corresponds to - // a fp that has already been flushed to disk. - while (len > 0) { - long[] bucket = buff[--len]; - - // Find last element > 0 in bucket (negative elements have already - // been flushed to disk, zero indicates an unoccupied slot). - if (bucket != null) { - for (int i = bucket.length - 1; i >= 0; i--) { - final long fp = bucket[i]; - // unused slot - if (fp == 0) { - continue; - } - // in-memory fingerprint - if (fp > 0) { - if (fp < lowBound) { - // smaller lowBound - return lowBound; - } else { - // larger or equal lowBound - return fp; - } - } - // Cannot take on-disk fingerprints (negative ones) into - // account. They don't adhere to the precondition that - // the bucket is sorted. The bucket is logically sorted - // only for in-memory fingerprints. - } - } - } - // At this point we have scanned the complete buff, but haven't - // found a single fingerprint that hasn't been flushed to disk - // already. - throw new NoSuchElementException(); - } - - /** - * @return The last element in the iteration. - * @exception NoSuchElementException if iteration is empty. - * - * Pre-condition: Each bucket is sorted in ascending order! + * order! */ public long getLast() { - /* - * Tempting to delegate to getLast(Long.MAX_VALUE). However, - * getLast() could not throw a NoSuchElementException when the - * value returned is Long.MAX_VALUE. It could be a valid value, but - * it might as well be the cutOff. - */ - int len = buff.length; // Walk from the end of buff to the beginning. Each bucket that is diff --git a/tlatools/src/tlc2/tool/fp/MSBMultiFPSet.java b/tlatools/src/tlc2/tool/fp/MSBMultiFPSet.java deleted file mode 100644 index f95177f2ca8f1210aab75f7b78fc1a10276ee558..0000000000000000000000000000000000000000 --- a/tlatools/src/tlc2/tool/fp/MSBMultiFPSet.java +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright (c) 2012 Markus Alexander Kuppe. All rights reserved. -package tlc2.tool.fp; - -import java.rmi.RemoteException; - -@SuppressWarnings("serial") -public class MSBMultiFPSet extends MultiFPSet { - - private final int moveBy; - - public MSBMultiFPSet(final FPSetConfiguration fpSetConfiguration) throws RemoteException { - super(fpSetConfiguration); - - this.moveBy = 63 - fpSetConfiguration.getFpBits(); - } - - /* (non-Javadoc) - * @see tlc2.tool.fp.MultiFPSet#getNestedFPSets(int, long, int) - */ - protected FPSet[] getNestedFPSets(final FPSetConfiguration fpSetConfiguration) throws RemoteException { - int len = fpSetConfiguration.getMultiFPSetCnt(); - final FPSet[] s = new FPSet[len]; - for (int i = 0; i < len; i++) { - s[i] = FPSetFactory.getFPSet(new MultiFPSetConfiguration(fpSetConfiguration)); - } - return s; - } - - /* (non-Javadoc) - * @see tlc2.tool.fp.MultiFPSet#getFPSet(long) - */ - protected FPSet getFPSet(long fp) { - long fp0 = fp & 0x7FFFFFFFFFFFFFFFL; // really zero msb? - // determine corresponding fpset based on the msb bits - final int idx = (int) (fp0 >> moveBy); - return this.sets[idx]; - } -} diff --git a/tlatools/src/tlc2/tool/fp/MemFPSet.java b/tlatools/src/tlc2/tool/fp/MemFPSet.java index 4bae4b362032a16646cbf9ff50216488e440cf97..366e2ae5f3db79e77566ac4cd78352a686ebf5cb 100644 --- a/tlatools/src/tlc2/tool/fp/MemFPSet.java +++ b/tlatools/src/tlc2/tool/fp/MemFPSet.java @@ -10,6 +10,7 @@ import java.rmi.RemoteException; import tlc2.output.EC; import tlc2.output.MP; +import tlc2.tool.TLCTrace; import util.Assert; import util.BufferedDataInputStream; import util.BufferedDataOutputStream; @@ -63,9 +64,10 @@ public class MemFPSet extends FPSet { this.mask = initialCapacity - 1; } - public final void init(int numThreads, String metadir, String filename) { + public final FPSet init(int numThreads, String metadir, String filename) { this.metadir = metadir; this.filename = filename; + return this; } /** @@ -202,6 +204,7 @@ public class MemFPSet extends FPSet { } public final void exit(boolean cleanup) throws IOException { + super.exit(cleanup); if (cleanup) { // Delete the metadata directory: File file = new File(this.metadir); @@ -212,7 +215,7 @@ public class MemFPSet extends FPSet { System.exit(0); } - public final double checkFPs() { + public final long checkFPs() { long dis = Long.MAX_VALUE; for (int i = 0; i < this.table.length; i++) { long[] bucket = this.table[i]; @@ -237,7 +240,7 @@ public class MemFPSet extends FPSet { } } } - return (1.0/dis); + return dis; } // Checkpoint. @@ -286,18 +289,14 @@ public class MemFPSet extends FPSet { this.commitChkpt(this.filename); } - final public void recover() throws IOException { + final public void recover(TLCTrace trace) throws IOException { this.recover(this.filename); } - public final void prepareRecovery() throws IOException { /*SKIP*/ } - public final void recoverFP(long fp) throws IOException { Assert.check(!this.put(fp), EC.TLC_FP_NOT_IN_SET); } - public final void completeRecovery() throws IOException { /*SKIP*/ } - final private String chkptName(String fname, String ext) { return this.metadir + FileUtil.separator + fname + ".fp." + ext; } diff --git a/tlatools/src/tlc2/tool/fp/MemFPSet1.java b/tlatools/src/tlc2/tool/fp/MemFPSet1.java index 858a142be6fa90335e6f4a1a3528057be1567cc0..dd6c5386aba1e5d0637c62f6f4966a63e4a384f3 100644 --- a/tlatools/src/tlc2/tool/fp/MemFPSet1.java +++ b/tlatools/src/tlc2/tool/fp/MemFPSet1.java @@ -14,6 +14,7 @@ import java.rmi.RemoteException; import tlc2.output.EC; import tlc2.output.MP; +import tlc2.tool.TLCTrace; import tlc2.util.SetOfLong; import util.Assert; import util.FileUtil; @@ -33,9 +34,10 @@ public final class MemFPSet1 extends FPSet { this.set = new SetOfLong(10001, 0.75f); } - public final void init(int numThreads, String metadir, String filename) { + public final FPSet init(int numThreads, String metadir, String filename) { this.metadir = metadir; this.filename = filename; + return this; } public final long size() { return this.set.size(); } @@ -51,6 +53,7 @@ public final class MemFPSet1 extends FPSet { } public final void exit(boolean cleanup) throws IOException { + super.exit(cleanup); if (cleanup) { // Delete the metadata directory: File file = new File(this.metadir); @@ -61,7 +64,7 @@ public final class MemFPSet1 extends FPSet { System.exit(0); } - public final double checkFPs() { return this.set.checkFPs(); } + public final long checkFPs() { return this.set.checkFPs(); } /* Checkpoint. */ public final void beginChkpt(String fname) throws IOException { @@ -95,18 +98,14 @@ public final class MemFPSet1 extends FPSet { this.commitChkpt(this.filename); } - public final void recover() throws IOException { + public final void recover(TLCTrace trace) throws IOException { this.recover(this.filename); } - public final void prepareRecovery() throws IOException { /*SKIP*/ } - public final void recoverFP(long fp) throws IOException { Assert.check(!this.set.put(fp), EC.TLC_FP_NOT_IN_SET); } - public final void completeRecovery() throws IOException { /*SKIP*/ } - private final String chkptName(String fname, String ext) { return this.metadir + FileUtil.separator + fname + ".fp." + ext; } diff --git a/tlatools/src/tlc2/tool/fp/MemFPSet2.java b/tlatools/src/tlc2/tool/fp/MemFPSet2.java index 4e97b12e8890a171ac2c1c89e13c0fe632a81e09..413bd337c69593eb1318d1ec133cab255b6fb11a 100644 --- a/tlatools/src/tlc2/tool/fp/MemFPSet2.java +++ b/tlatools/src/tlc2/tool/fp/MemFPSet2.java @@ -10,6 +10,7 @@ import java.rmi.RemoteException; import tlc2.output.EC; import tlc2.output.MP; +import tlc2.tool.TLCTrace; import util.Assert; import util.BufferedDataInputStream; import util.BufferedDataOutputStream; @@ -72,9 +73,10 @@ public final class MemFPSet2 extends FPSet { this.mask = spineSize - 1; } - public void init(int numThreads, String metadir, String fname) { + public FPSet init(int numThreads, String metadir, String fname) { this.metadir = metadir; this.filename = metadir + FileUtil.separator + fname; + return this; } public synchronized final long size() { return this.count; } @@ -130,6 +132,7 @@ public final class MemFPSet2 extends FPSet { } public final void exit(boolean cleanup) throws IOException { + super.exit(cleanup); if (cleanup) { // Delete the metadata directory: FileUtil.deleteDir(this.metadir, true); @@ -139,7 +142,7 @@ public final class MemFPSet2 extends FPSet { System.exit(0); } - public final double checkFPs() { + public final long checkFPs() { long dis = Long.MAX_VALUE; for (int i = 0; i < this.table.length; i++) { long low = i & 0xffffffL; @@ -188,7 +191,7 @@ public final class MemFPSet2 extends FPSet { } } } - return (1.0/dis); + return dis; } public final void beginChkpt(String fname) throws IOException { @@ -246,18 +249,14 @@ public final class MemFPSet2 extends FPSet { this.commitChkpt(this.filename); } - public final void recover() throws IOException { + public final void recover(TLCTrace trace) throws IOException { this.recover(this.filename); } - public final void prepareRecovery() throws IOException { /*SKIP*/ } - public final void recoverFP(long fp) throws IOException { Assert.check(!this.put(fp), EC.TLC_FP_NOT_IN_SET); } - public final void completeRecovery() throws IOException { /*SKIP*/ } - final private String chkptName(String fname, String ext) { return this.metadir + FileUtil.separator + fname + ".fp." + ext; } diff --git a/tlatools/src/tlc2/tool/fp/MultiFPSet.java b/tlatools/src/tlc2/tool/fp/MultiFPSet.java index d3e12673796e773d4741b13215861211808cbda3..61a379c43011e1702051cf554bda1dc8ea41bf16 100644 --- a/tlatools/src/tlc2/tool/fp/MultiFPSet.java +++ b/tlatools/src/tlc2/tool/fp/MultiFPSet.java @@ -8,10 +8,13 @@ import java.io.IOException; import java.rmi.NoSuchObjectException; import java.rmi.RemoteException; import java.rmi.server.UnicastRemoteObject; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.IntStream; import tlc2.output.EC; import tlc2.tool.TLCTrace; -import tlc2.util.BufferedRandomAccessFile; +import tlc2.tool.TLCTrace.Enumerator; import util.Assert; /** @@ -31,7 +34,7 @@ public class MultiFPSet extends FPSet { /** * Contains all nested {@link FPSet}s */ - protected FPSet[] sets; + protected final List<FPSet> sets; /** * Amount of leftmost bits used to determine nested {@link FPSet} @@ -59,11 +62,10 @@ public class MultiFPSet extends FPSet { this.fpbits = 64 - bits; } - protected FPSet[] getNestedFPSets(final FPSetConfiguration fpSetConfiguration) throws RemoteException { - int len = fpSetConfiguration.getMultiFPSetCnt(); - final FPSet[] s = new FPSet[len]; - for (int i = 0; i < len; i++) { - s[i] = FPSetFactory.getFPSet(new MultiFPSetConfiguration(fpSetConfiguration)); + protected List<FPSet> getNestedFPSets(final FPSetConfiguration fpSetConfiguration) throws RemoteException { + final List<FPSet> s = new ArrayList<>(fpSetConfiguration.getMultiFPSetCnt()); + for (int i = 0; i < fpSetConfiguration.getMultiFPSetCnt(); i++) { + s.add(FPSetFactory.getFPSet(new MultiFPSetConfiguration(fpSetConfiguration))); } return s; } @@ -71,10 +73,21 @@ public class MultiFPSet extends FPSet { /* (non-Javadoc) * @see tlc2.tool.fp.FPSet#init(int, java.lang.String, java.lang.String) */ - public final void init(int numThreads, String metadir, String filename) throws IOException { - for (int i = 0; i < this.sets.length; i++) { - this.sets[i].init(numThreads, metadir, filename + "_" + i); - } + public final FPSet init(final int numThreads, final String metadir, final String filename) throws IOException { + IntStream.range(0, this.sets.size()).parallel().forEach(i -> { + try { + sets.get(i).init(numThreads, metadir, filename + "_" + i); + } catch (IOException e) { + throw new RuntimeException(e); + } + }); + return this; + } + + + @Override + public void incWorkers(final int num) { + sets.stream().forEach(s -> s.incWorkers(num) ); } /* (non-Javadoc) @@ -82,11 +95,7 @@ public class MultiFPSet extends FPSet { */ public final long size() { /* Returns the number of fingerprints in this set. */ - long sum = 0; - for (int i = 0; i < this.sets.length; i++) { - sum += this.sets[i].size(); - } - return sum; + return sets.parallelStream().mapToLong(FPSet::size).sum(); } /** @@ -98,7 +107,7 @@ public class MultiFPSet extends FPSet { // shifts a zero into the leftmost (msb) position of the first operand for right operand times // and cast it to int loosing the leftmost 32 bit final int idx = (int) (fp >>> this.fpbits); - return this.sets[idx]; + return this.sets.get(idx); } /** @@ -126,8 +135,8 @@ public class MultiFPSet extends FPSet { * @see tlc2.tool.fp.FPSet#close() */ public final void close() { - for (int i = 0; i < this.sets.length; i++) { - this.sets[i].close(); + for (FPSet fpSet : sets) { + fpSet.close(); } } @@ -135,8 +144,8 @@ public class MultiFPSet extends FPSet { * @see tlc2.tool.fp.FPSet#unexportObject(boolean) */ public void unexportObject(boolean force) throws NoSuchObjectException { - for (int i = 0; i < this.sets.length; i++) { - this.sets[i].unexportObject(force); + for (FPSet fpSet : sets) { + fpSet.unexportObject(force); } UnicastRemoteObject.unexportObject(this, force); } @@ -144,21 +153,36 @@ public class MultiFPSet extends FPSet { /* (non-Javadoc) * @see tlc2.tool.fp.FPSet#checkFPs() */ - public final double checkFPs() throws IOException { - /* This is not quite correct. */ - double res = Double.NEGATIVE_INFINITY; - for (int i = 0; i < this.sets.length; i++) { - res = Math.max(res, this.sets[i].checkFPs()); - } - return res; + public final long checkFPs() throws IOException { + return sets.parallelStream().mapToLong(s -> { + try { + return s.checkFPs(); + } catch (IOException e) { + throw new RuntimeException(e); + } + }).min().orElse(Long.MAX_VALUE); + } + + /* (non-Javadoc) + * @see tlc2.tool.fp.FPSet#checkInvariant() + */ + public boolean checkInvariant() throws IOException { + return sets.parallelStream().allMatch(s -> { + try { + return s.checkInvariant(); + } catch (IOException e) { + throw new RuntimeException(e); + } + }); } /* (non-Javadoc) * @see tlc2.tool.fp.FPSet#exit(boolean) */ public final void exit(boolean cleanup) throws IOException { - for (int i = 0; i < this.sets.length; i++) { - this.sets[i].exit(cleanup); + super.exit(cleanup); + for (FPSet fpSet : sets) { + fpSet.exit(cleanup); } } @@ -166,8 +190,8 @@ public class MultiFPSet extends FPSet { * @see tlc2.tool.fp.FPSet#beginChkpt() */ public final void beginChkpt() throws IOException { - for (int i = 0; i < this.sets.length; i++) { - this.sets[i].beginChkpt(); + for (FPSet fpSet : sets) { + fpSet.beginChkpt(); } } @@ -175,40 +199,21 @@ public class MultiFPSet extends FPSet { * @see tlc2.tool.fp.FPSet#commitChkpt() */ public final void commitChkpt() throws IOException { - for (int i = 0; i < this.sets.length; i++) { - this.sets[i].commitChkpt(); + for (FPSet fpSet : sets) { + fpSet.commitChkpt(); } } /* (non-Javadoc) * @see tlc2.tool.fp.FPSet#recover() */ - public final void recover() throws IOException { - for (int i = 0; i < this.sets.length; i++) { - this.sets[i].prepareRecovery(); - } - - long recoverPtr = TLCTrace.getRecoverPtr(); - BufferedRandomAccessFile braf = new BufferedRandomAccessFile(TLCTrace.getFilename(), "r"); - while (braf.getFilePointer() < recoverPtr) { - braf.readLongNat(); /* drop */ - long fp = braf.readLong(); + public final void recover(TLCTrace trace) throws IOException { + final Enumerator elements = trace.elements(); + while (elements.nextPos() != -1) { + long fp = elements.nextFP(); getFPSet(fp).recoverFP(fp); } - braf.close(); - - for (int i = 0; i < this.sets.length; i++) { - this.sets[i].completeRecovery(); - } - } - - /* (non-Javadoc) - * - * NOOP! - * - * @see tlc2.tool.fp.FPSet#prepareRecovery() - */ - public final void prepareRecovery() throws IOException { /* SKIP */ + elements.close(); } /* (non-Javadoc) @@ -218,43 +223,46 @@ public class MultiFPSet extends FPSet { Assert.check(!this.put(fp), EC.TLC_FP_NOT_IN_SET); } - /* (non-Javadoc) - * - * NOOP! - * - * @see tlc2.tool.fp.FPSet#completeRecovery() - */ - public final void completeRecovery() throws IOException { /* SKIP */ - } - /* (non-Javadoc) * @see tlc2.tool.fp.FPSet#beginChkpt(java.lang.String) */ public final void beginChkpt(String filename) throws IOException { - for (int i = 0; i < this.sets.length; i++) { - this.sets[i].beginChkpt(filename + "_" + i); - } + IntStream.range(0, this.sets.size()).parallel().forEach(i -> { + try { + sets.get(i).beginChkpt(filename + "_" + i); + } catch (IOException e) { + throw new RuntimeException(e); + } + }); } /* (non-Javadoc) * @see tlc2.tool.fp.FPSet#commitChkpt(java.lang.String) */ public final void commitChkpt(String filename) throws IOException { - for (int i = 0; i < this.sets.length; i++) { - this.sets[i].commitChkpt(filename + "_" + i); - } + IntStream.range(0, this.sets.size()).parallel().forEach(i -> { + try { + sets.get(i).commitChkpt(filename + "_" + i); + } catch (IOException e) { + throw new RuntimeException(e); + } + }); } /* (non-Javadoc) * @see tlc2.tool.fp.FPSet#recover(java.lang.String) */ public final void recover(String filename) throws IOException { - for (int i = 0; i < this.sets.length; i++) { - this.sets[i].recover(filename + "_" + i); - } + IntStream.range(0, this.sets.size()).parallel().forEach(i -> { + try { + sets.get(i).recover(filename + "_" + i); + } catch (IOException e) { + throw new RuntimeException(e); + } + }); } public FPSet[] getFPSets() { - return sets; + return sets.toArray(new FPSet[sets.size()]); } } diff --git a/tlatools/src/tlc2/tool/fp/MultiFPSetConfiguration.java b/tlatools/src/tlc2/tool/fp/MultiFPSetConfiguration.java index 8d26cd5c1d4ec2552855f9389739b47cedc702a5..7bd34763870cbf8e558f15d145b7bb136b2b8bd7 100644 --- a/tlatools/src/tlc2/tool/fp/MultiFPSetConfiguration.java +++ b/tlatools/src/tlc2/tool/fp/MultiFPSetConfiguration.java @@ -11,7 +11,7 @@ class MultiFPSetConfiguration extends FPSetConfiguration { // Set wrapper to values of config to be wrapped this.fpBits = fpSetConfig.getFpBits(); this.ratio = fpSetConfig.getRatio(); - + this.implementation = fpSetConfig.getImplementation(); // Sanity check configuration right away if (getMemoryInFingerprintCnt() <= 0) { throw new IllegalArgumentException( @@ -37,6 +37,10 @@ class MultiFPSetConfiguration extends FPSetConfiguration { * @see tlc2.tool.fp.FPSetConfiguration#getMemoryInFingerprintCnt() */ public long getMemoryInFingerprintCnt() { - return super.getMemoryInFingerprintCnt() / getMultiFPSetCnt(); + // No need to divide by getMultiFPSetCnt because calls to + // super.getMemoryInFingerprintCnt eventually calls getMemoryInBytes above. + // Dividing by getMultiFPSetCnt here again will waste half the + // available memory. + return super.getMemoryInFingerprintCnt(); } } \ No newline at end of file diff --git a/tlatools/src/tlc2/tool/fp/NonCheckpointableDiskFPSet.java b/tlatools/src/tlc2/tool/fp/NonCheckpointableDiskFPSet.java new file mode 100644 index 0000000000000000000000000000000000000000..5e8e7680af32e2d63f499c447e20efde415a8cdb --- /dev/null +++ b/tlatools/src/tlc2/tool/fp/NonCheckpointableDiskFPSet.java @@ -0,0 +1,52 @@ +/******************************************************************************* + * Copyright (c) 2016 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.fp; + +import java.io.IOException; +import java.rmi.RemoteException; + +import tlc2.output.EC; +import tlc2.output.MP; + +@SuppressWarnings("serial") +public abstract class NonCheckpointableDiskFPSet extends DiskFPSet { + + protected NonCheckpointableDiskFPSet(FPSetConfiguration fpSetConfig) throws RemoteException { + super(fpSetConfig); + } + + public void beginChkpt(String fname) throws IOException { + MP.printWarning(EC.GENERAL, "Checkpointing is not implemented for " + getClass().getCanonicalName()); + } + + public void commitChkpt(String fname) throws IOException { + MP.printWarning(EC.GENERAL, "Checkpointing is not implemented for " + getClass().getCanonicalName()); + } + + public void recover(String fname) throws IOException { + MP.printWarning(EC.GENERAL, "Checkpointing is not implemented for " + getClass().getCanonicalName()); + } +} diff --git a/tlatools/src/tlc2/tool/fp/NoopFPSet.java b/tlatools/src/tlc2/tool/fp/NoopFPSet.java new file mode 100644 index 0000000000000000000000000000000000000000..d35e1e00005c0726f273ee20ccd48d285eea44cc --- /dev/null +++ b/tlatools/src/tlc2/tool/fp/NoopFPSet.java @@ -0,0 +1,131 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.fp; + +import java.io.IOException; +import java.rmi.RemoteException; + +import tlc2.tool.TLCTrace; + +/** + * An FPSet whose methods are all no-ops. + */ +@SuppressWarnings("serial") +public class NoopFPSet extends FPSet { + + protected NoopFPSet(FPSetConfiguration fpSetConfig) throws RemoteException { + super(fpSetConfig); + } + + /* (non-Javadoc) + * @see tlc2.tool.fp.FPSet#init(int, java.lang.String, java.lang.String) + */ + @Override + public FPSet init(int numThreads, String metadir, String filename) throws IOException { + return this; + } + + /* (non-Javadoc) + * @see tlc2.tool.fp.FPSet#size() + */ + @Override + public long size() { + return 0; + } + + /* (non-Javadoc) + * @see tlc2.tool.fp.FPSet#put(long) + */ + @Override + public boolean put(long fp) throws IOException { + return false; + } + + /* (non-Javadoc) + * @see tlc2.tool.fp.FPSet#contains(long) + */ + @Override + public boolean contains(long fp) throws IOException { + return false; + } + + /* (non-Javadoc) + * @see tlc2.tool.fp.FPSet#checkFPs() + */ + @Override + public long checkFPs() throws IOException { + return 0; + } + + /* (non-Javadoc) + * @see tlc2.tool.fp.FPSet#beginChkpt() + */ + @Override + public void beginChkpt() throws IOException { + } + + /* (non-Javadoc) + * @see tlc2.tool.fp.FPSet#commitChkpt() + */ + @Override + public void commitChkpt() throws IOException { + } + + /* (non-Javadoc) + * @see tlc2.tool.fp.FPSet#recover() + */ + @Override + public void recover(TLCTrace trace) throws IOException { + } + + /* (non-Javadoc) + * @see tlc2.tool.fp.FPSet#recoverFP(long) + */ + @Override + public void recoverFP(long fp) throws IOException { + } + + /* (non-Javadoc) + * @see tlc2.tool.fp.FPSet#beginChkpt(java.lang.String) + */ + @Override + public void beginChkpt(String filename) throws IOException { + } + + /* (non-Javadoc) + * @see tlc2.tool.fp.FPSet#commitChkpt(java.lang.String) + */ + @Override + public void commitChkpt(String filename) throws IOException { + } + + /* (non-Javadoc) + * @see tlc2.tool.fp.FPSet#recover(java.lang.String) + */ + @Override + public void recover(String filename) throws IOException { + } +} diff --git a/tlatools/src/tlc2/tool/fp/OffHeapDiskFPSet.java b/tlatools/src/tlc2/tool/fp/OffHeapDiskFPSet.java index d3322f5332e07e3ed97c8098f76d0e77e0906f40..954b7f72a6d2b1831f1235bafbfc92420e8deba1 100644 --- a/tlatools/src/tlc2/tool/fp/OffHeapDiskFPSet.java +++ b/tlatools/src/tlc2/tool/fp/OffHeapDiskFPSet.java @@ -1,154 +1,237 @@ // Copyright (c) 2012 Markus Alexander Kuppe. All rights reserved. package tlc2.tool.fp; -import java.io.EOFException; +import java.io.File; import java.io.IOException; import java.io.RandomAccessFile; -import java.nio.LongBuffer; import java.rmi.RemoteException; -import java.util.Arrays; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Comparator; +import java.util.HashSet; +import java.util.List; import java.util.NoSuchElementException; -import java.util.TreeSet; -import java.util.concurrent.locks.ReadWriteLock; -import java.util.concurrent.locks.ReentrantReadWriteLock; +import java.util.Set; +import java.util.concurrent.Callable; +import java.util.concurrent.CyclicBarrier; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.Phaser; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.function.ToLongFunction; +import java.util.logging.Level; -import sun.misc.Unsafe; +import tlc2.TLCGlobals; import tlc2.output.EC; import tlc2.output.MP; +import tlc2.tool.fp.LongArrays.LongComparator; +import tlc2.tool.fp.management.DiskFPSetMXWrapper; +import tlc2.util.BufferedRandomAccessFile; import util.Assert; -@SuppressWarnings({ "serial", "restriction" }) -public class OffHeapDiskFPSet extends DiskFPSet implements FPSetStatistic { +/** + * see OpenAddressing.tla + */ +@SuppressWarnings({ "serial" }) +public final class OffHeapDiskFPSet extends NonCheckpointableDiskFPSet implements FPSetStatistic { - protected static final double COLLISION_BUCKET_RATIO = .025d; - - protected final int bucketCapacity; + private static class OffHeapSynchronizer { + + private final Set<OffHeapDiskFPSet> sets = new HashSet<OffHeapDiskFPSet>(); + + private final AtomicBoolean flusherChosen = new AtomicBoolean(); + + // This barrier gets run after one thread signals the need to suspend + // put and contains operations to evict to secondary. Signaling is done + // via the flusherChoosen AtomicBoolean. All threads (numThreads) will + // then await on the barrier and the Runnable be executed when the + // last of numThreads arrives. + // Compared to an AtomicBoolean, the barrier operation use locks and + // are thus comparably expensive. + private final Phaser phaser = new Phaser(1) { - /** - * This implementation uses sun.misc.Unsafe instead of a wrapping - * java.nio.ByteBuffer due to the fact that the former's allocateMemory - * takes a long argument, while the latter is restricted to - * Integer.MAX_VALUE as its capacity.<br> - * In 2012 this poses a too hard limit on the usable memory, hence we trade - * generality for performance. - */ - private final Unsafe u; + @Override + protected boolean onAdvance(int phase, int registeredParties) { + // Atomically evict and reset flusherChosen to make sure no + // thread re-read flusherChosen=true after an eviction and + // waits again. + for (OffHeapDiskFPSet set : sets) { + set.evict(); + } + + // Release exclusive access. It has to be done by the runnable + // before workers waiting on the barrier wake up again. + Assert.check(flusherChosen.compareAndSet(true, false), EC.GENERAL); + + return super.onAdvance(phase, registeredParties); + } + }; + + private OffHeapSynchronizer() { + // Don't want any copies. + } + + public final void add(final OffHeapDiskFPSet aSet) { + this.sets.add(aSet); + } + + public final void incWorkers(final int numWorkers) { + final int parties = phaser.getRegisteredParties(); + if (parties < numWorkers) { + phaser.bulkRegister(numWorkers - parties); + } + } + + public final boolean evictPending() { + return flusherChosen.get(); + } + + public final void evict() { + flusherChosen.compareAndSet(false, true); + } + + public final void awaitEviction() { + phaser.arriveAndAwaitAdvance(); + } + + public AtomicBoolean getFlusherChosen() { + return flusherChosen; + } + } + + // We require a singleton here, because if TLC is run with multiple instances + // of FPSets - the default - workers will call evict and awaitEvict on + // all FPSet instances. Thus, an individual synchronization internal to each + // FPSet would never see the complete set of waiting workers. TLC would thus + // deadlock. This is why SYNC is a singleton and shared by all FPSet instances. + private static final OffHeapSynchronizer SYNC = new OffHeapSynchronizer(); - /** - * The base address allocated for fingerprints - */ - private final long baseAddress; + private static final int PROBE_LIMIT = Integer.getInteger(OffHeapDiskFPSet.class.getName() + ".probeLimit", 1024); + static final long EMPTY = 0L; - /** - * Address size (either 4 or 8 bytes) depending on current architecture - */ - private final int logAddressSize; /** - * A bucket containing collision elements which is used as a fall-back if a - * bucket is fully used up. Buckets cannot grow as the whole in-memory - * data-structure is static and not designed to be resized. - * - * <p> - * Open addressing - contrary to separate chaining - is not an option for an - * {@link OffHeapDiskFPSet}, because it does not support the invariant of - * monotonic increasing buckets required by the {@link Indexer}. Adhering to - * this invariant has the benefit, that only the elements in a bucket have - * to be sorted, but they don't change buckets during sort. Thus, a - * temporary sort array as in {@link LSBDiskFPSet.LSBFlusher#prepareTable()} is - * obsolete halving the memory footprint. - * </p> + * @see LongArray#isSupported() */ - protected CollisionBucket collisionBucket; + public static boolean isSupported() { + return LongArray.isSupported(); + } + + private final transient LongArray array; /** * The indexer maps a fingerprint to a in-memory bucket and the associated lock */ - private final Indexer indexer; - - private final ReadWriteLock csRWLock = new ReentrantReadWriteLock(); + private final transient Indexer indexer; + + private int numThreads; protected OffHeapDiskFPSet(final FPSetConfiguration fpSetConfig) throws RemoteException { super(fpSetConfig); - final long memoryInFingerprintCnt = fpSetConfig.getMemoryInFingerprintCnt(); + final long positions = fpSetConfig.getMemoryInFingerprintCnt(); // Determine base address which varies depending on machine architecture. - u = OffHeapDiskFPSetHelper.getUnsafe(); - int addressSize = u.addressSize(); - int cnt = -1; - while (addressSize > 0) { - cnt++; - addressSize = addressSize >>> 1; // == (n/2) - } - logAddressSize = cnt; - - // Allocate non-heap memory for maxInMemoryCapacity fingerprints - long bytes = memoryInFingerprintCnt << logAddressSize; - - baseAddress = u.allocateMemory(bytes); - - final int csCapacity = (int) (maxTblCnt * COLLISION_BUCKET_RATIO); - this.collisionBucket = new TreeSetCollisionBucket(csCapacity); - - this.flusher = new OffHeapMSBFlusher(); - - // Move n as many times to the right to calculate moveBy. moveBy is the - // number of bits the (fp & mask) has to be right shifted to make the - // logical bucket index. - long n = (Long.MAX_VALUE >>> fpSetConfig.getPrefixBits()) - (memoryInFingerprintCnt - 1); - int moveBy = 0; - while (n >= memoryInFingerprintCnt) { - moveBy++; - n = n >>> 1; // == (n/2) - } - - // Calculate Hamming weight of maxTblCnt - final int bitCount = Long.bitCount(memoryInFingerprintCnt); + this.array = new LongArray(positions); // If Hamming weight is 1, the logical index address can be calculated // significantly faster by bit-shifting. However, with large memory // sizes, only supporting increments of 2^n sizes would waste memory // (e.g. either 32GiB or 64Gib). Hence, we check if the bitCount allows // us to use bit-shifting. If not, we fall back to less efficient - // calculations. Additionally we increase the bucket capacity to make - // use of extra memory. The down side is, that larger buckets mean - // increased linear search. But linear search on maximally 31 elements - // still outperforms disk I/0. - if (bitCount == 1) { - bucketCapacity = InitialBucketCapacity; - this.indexer = new BitshiftingIndexer(moveBy, fpSetConfig.getPrefixBits()); + // calculations. + if (Long.bitCount(positions) == 1) { + this.indexer = new BitshiftingIndexer(positions, fpSetConfig.getFpBits()); } else { - // Round maxInMemoryCapacity to next lower 2^n power - cnt = -1; - while (bytes > 0) { - cnt++; - bytes = bytes >>> 1; - } - - // Extra memory that cannot be addressed by BitshiftingIndexer - final long extraMem = (memoryInFingerprintCnt * LongSize) - (long) Math.pow(2, cnt); - - // Divide extra memory across addressable buckets - int x = (int) (extraMem / ((n + 1) / InitialBucketCapacity)); - bucketCapacity = InitialBucketCapacity + (x / LongSize) ; - // Twice InitialBucketCapacity would mean we could have used one - // more bit for addressing. - Assert.check(bucketCapacity < (2 * InitialBucketCapacity), EC.GENERAL); - // non 2^n buckets cannot use a bit shifting indexer - this.indexer = new Indexer(moveBy, fpSetConfig.getPrefixBits()); + this.indexer = new Indexer(positions, fpSetConfig.getFpBits()); } + + // Use the non-concurrent flusher as the default. Will be replaced by + // the CyclicBarrier-Runnable later. Just set to prevent NPEs when + // eviction/flush is called before init. + this.flusher = new OffHeapMSBFlusher(array); + + this.flusherChosen = SYNC.getFlusherChosen(); + SYNC.add(this); } /* (non-Javadoc) * @see tlc2.tool.fp.DiskFPSet#init(int, java.lang.String, java.lang.String) */ - public void init(int numThreads, String aMetadir, String filename) + @Override + public FPSet init(final int numThreads, String aMetadir, String filename) throws IOException { super.init(numThreads, aMetadir, filename); + this.numThreads = numThreads; + + array.zeroMemory(numThreads); + return this; + } + + /* (non-Javadoc) + * @see tlc2.tool.fp.FPSet#incWorkers(int) + */ + public void incWorkers(int numWorkers) { + assert numWorkers == this.numThreads; + SYNC.incWorkers(numWorkers); + } + + public void evict() { + // statistics + growDiskMark++; + final long timestamp = System.currentTimeMillis(); + final long insertions = tblCnt.longValue(); + final double lf = tblCnt.doubleValue() / (double) maxTblCnt; - OffHeapDiskFPSetHelper.zeroMemory(u, baseAddress, numThreads, fpSetConfig.getMemoryInFingerprintCnt()); + LOGGER.log(Level.FINE, + "Started eviction of disk {0} the {1}. time at {2} after {3} insertions, load factor {4} and reprobe of {5}.", + new Object[] { ((DiskFPSetMXWrapper) diskFPSetMXWrapper).getObjectName(), getGrowDiskMark(), + timestamp, insertions, lf, PROBE_LIMIT }); + + // Check that the table adheres to our invariant. Otherwise, we + // can't hope to successfully evict it. + assert checkInput(array, indexer, PROBE_LIMIT) : "Table violates invariants prior to eviction: " + + array.toString(); + + // Only pay the price of creating threads when array is + // sufficiently large and the array size is large enough to + // partition it for multiple threads. + flusher = getFlusher(numThreads, insertions); + + try { + flusher.flushTable(); // Evict() + } catch (IOException e) { + // wrap in unchecked. + throw new OffHeapRuntimeException(e); + } + + // statistics and logging again. + final long l = System.currentTimeMillis() - timestamp; + flushTime += l; + LOGGER.log(Level.FINE, + "Finished eviction of disk {0} the {1}. time at {2}, in {3} sec after {4} insertions, load factor {5} and reprobe of {6}.", + new Object[] { ((DiskFPSetMXWrapper) diskFPSetMXWrapper).getObjectName(), getGrowDiskMark(), l, + System.currentTimeMillis(), insertions, lf, PROBE_LIMIT }); + } + + private Flusher getFlusher(final int numThreads, final long insertions) { + if (array.size() >= 8192 && Math.floor(array.size() / (double) numThreads) > 2 * PROBE_LIMIT) { + return new ConcurrentOffHeapMSBFlusher(array, PROBE_LIMIT, numThreads, insertions); + } else { + return this.flusher; + } + } + + private boolean checkEvictPending() { + if (SYNC.evictPending()) { + SYNC.awaitEviction(); + return true; + } + return false; } /* (non-Javadoc) @@ -156,21 +239,17 @@ public class OffHeapDiskFPSet extends DiskFPSet implements FPSetStatistic { */ public long sizeof() { long size = 44; // approx size of this DiskFPSet object - size += maxTblCnt * (long) LongSize; + size += maxTblCnt * LongSize; size += getIndexCapacity() * 4; - size += getCollisionBucketCnt() * (long) LongSize; // ignoring the internal TreeSet overhead here return size; } /* (non-Javadoc) * @see tlc2.tool.fp.DiskFPSet#needsDiskFlush() */ - protected boolean needsDiskFlush() { - // Only flush due to collision ratio when primary hash table is at least - // 25% full. Otherwise a second flush potentially immediately follows a - // first one, when both values for tblCnt and collision size can be small. - return (collisionRatioExceeds(COLLISION_BUCKET_RATIO) && loadFactorExceeds(.25d)) - || loadFactorExceeds(1d) || forceFlush; + @Override + protected final boolean needsDiskFlush() { + return loadFactorExceeds(1d) || forceFlush; } /** @@ -182,148 +261,163 @@ public class OffHeapDiskFPSet extends DiskFPSet implements FPSetStatistic { * from growing past it. * @return true iff the current hash table load exceeds the given limit */ - private boolean loadFactorExceeds(final double limit) { - // Base this one the primary hash table only and exclude the - // collision bucket - final double d = (this.tblCnt.doubleValue() - collisionBucket.size()) / (double) this.maxTblCnt; + private final boolean loadFactorExceeds(final double limit) { + final double d = (this.tblCnt.doubleValue()) / (double) this.maxTblCnt; return d >= limit; } - - /** - * @param limit A limit the collsionBucket is not allowed to exceed - * @return The proportional size of the collision bucket compared to the - * size of the set. - */ - private boolean collisionRatioExceeds(final double limit) { - // Do not use the thread safe getCollisionRatio here to avoid - // unnecessary locking. put() calls us holding a memory write locking - // which also blocks writers to collisionBucket. - final long size = collisionBucket.size(); - // Subtract size from overall tblCnt as it includes the cs size - // @see put(long) - final double d = (double) size / (tblCnt.doubleValue() - size); - return d >= limit; - } - + + private static final int FOUND = -1; + /* (non-Javadoc) - * @see tlc2.tool.fp.DiskFPSet#getLockIndex(long) + * @see tlc2.tool.fp.DiskFPSet#memLookup(long) */ - @Override - protected int getLockIndex(long fp) { - return this.indexer.getLockIndex(fp); + final boolean memLookup(final long fp0) { + return memLookup0(fp0) == FOUND; + } + + final int memLookup0(final long fp0) { + int free = PROBE_LIMIT; + for (int i = 0; i <= PROBE_LIMIT; i++) { + final long position = indexer.getIdx(fp0, i); + final long l = array.get(position); + if (fp0 == (l & FLUSHED_MASK)) { + // zero the long msb (which is 1 if fp has been flushed to disk) + return FOUND; + } else if (l == EMPTY) { + return Math.min(i, free); + } else if (l < EMPTY && free == PROBE_LIMIT) { + free = i; + } + } + + return free; } /* (non-Javadoc) - * @see tlc2.tool.fp.DiskFPSet#memLookup(long) + * @see tlc2.tool.fp.DiskFPSet#memInsert(long) */ - boolean memLookup(long fp) { - final long position = indexer.getLogicalPosition(fp); - - // Linearly search the logical bucket; 0L is an invalid fp and marks the - // end of the allocated bucket - long l = -1L; - for (int i = 0; i < bucketCapacity && l != 0L; i++) { - l = u.getAddress(log2phy(position, i)); - // zero the long msb (which is 1 if fp has been flushed to disk) - if (fp == (l & 0x7FFFFFFFFFFFFFFFL)) { + final boolean memInsert(final long fp0) throws IOException { + return memInsert0(fp0, 0); + } + + final boolean memInsert0(final long fp0, final int start) throws IOException { + // See OffHeapDiskFPSetJPFTest for a (verbatim) version that has + // additionally been verified with JPF. + for (int i = start; i < PROBE_LIMIT; i++) { + final long position = indexer.getIdx(fp0, i); + final long expected = array.get(position); + if (expected == EMPTY || (expected < 0 && fp0 != (expected & FLUSHED_MASK))) { + // Try to CAS the new fingerprint. + if (array.trySet(position, expected, fp0)) { + this.tblCnt.increment(); + return false; + } else { + // Retry at current position because another thread wrote a + // value concurrently (possibly the same one this thread is + // trying to write). + i = i - 1; + continue; + } + } + + // Expected is the fingerprint to be inserted. + if ((expected & FLUSHED_MASK) == fp0) { return true; } } + - return csLookup(fp); + // We failed to insert into primary. Consequently, lets try and make + // some room by signaling all threads to wait for eviction. + forceFlush(); + // We've signaled for eviction to start or failed because some other + // thread beat us to it. Actual eviction and setting flusherChosen back + // to false is done by the Barrier's Runnable. We cannot set + // flusherChosen back to false after barrier.awaits returns because it + // leaves a window during which other threads read the old true value of + // flusherChosen a second time and immediately wait again. + + return put(fp0); } - - /** - * Probes {@link OffHeapDiskFPSet#collisionBucket} for the given fingerprint. - * @param fp - * @return true iff fp is in the collision bucket + + /* (non-Javadoc) + * @see tlc2.tool.fp.FPSet#put(long) */ - protected boolean csLookup(long fp) { - try { - csRWLock.readLock().lock(); - return collisionBucket.contains(fp); - } finally { - csRWLock.readLock().unlock(); + public final boolean put(final long fp) throws IOException { + if (checkEvictPending()) { + return put(fp); } + + // zeros the msb + final long fp0 = fp & FLUSHED_MASK; + + // Only check primary and disk iff there exists a disk file. index is + // created when we wait and thus cannot race. + int start = 0; + if (index != null) { + // Lookup primary memory + if ((start = memLookup0(fp0)) == FOUND) { + this.memHitCnt.increment(); + return true; + } + + // Lookup on disk + if (this.diskLookup(fp0)) { + this.diskHitCnt.increment(); + return true; + } + } + + // Lastly, try to insert into memory. + return memInsert0(fp0, start); } /* (non-Javadoc) - * @see tlc2.tool.fp.DiskFPSet#memInsert(long) + * @see tlc2.tool.fp.FPSet#contains(long) */ - boolean memInsert(long fp) { - final long position = indexer.getLogicalPosition(fp); - - long l = -1; - long freePosition = -1L; - for (int i = 0; i < bucketCapacity && l != 0L; i++) { - l = u.getAddress(log2phy(position, i)); - // zero the long msb (which is 1 if fp has been flushed to disk) - if (fp == (l & 0x7FFFFFFFFFFFFFFFL)) { - return true; - } else if (l == 0L && freePosition == -1) { - if (i == 0) { - tblLoad++; - } - // empty or disk written slot found, simply insert at _current_ position - u.putAddress(log2phy(position, i), fp); - this.tblCnt.getAndIncrement(); - return false; - } else if (l < 0L && freePosition == -1) { - // record free (disk written fp) slot - freePosition = log2phy(position, i); - } + public final boolean contains(final long fp) throws IOException { + // maintains happen-before with regards to successful put + + if (checkEvictPending()) { + return contains(fp); } - // index slot overflow, thus add to collisionBucket of write to free - // position. - if (freePosition > -1 && !csLookup(fp)) { - u.putAddress(freePosition, fp); - this.tblCnt.getAndIncrement(); - return false; - } else { - boolean success = csInsert(fp); - if (success) { - this.tblCnt.getAndIncrement(); - } - return !success; + // zeros the msb + final long fp0 = fp & FLUSHED_MASK; + + // Lookup in primary + if (memLookup(fp0)) { + return true; + } + + // Lookup on secondary/disk + if (this.diskLookup(fp0)) { + diskHitCnt.increment(); + return true; } + + return false; } - /** - * Inserts the given fingerprint into the {@link OffHeapDiskFPSet#collisionBucket}. - * @param fp - * @return true iff fp has been added to the collision bucket + /* (non-Javadoc) + * @see tlc2.tool.fp.DiskFPSet#forceFlush() */ - protected boolean csInsert(long fp) { - try { - csRWLock.writeLock().lock(); - return collisionBucket.add(fp); - } finally { - csRWLock.writeLock().unlock(); - } + public void forceFlush() { + SYNC.evict(); } - /** - * Converts from logical bucket index numbers and in-bucket position to a - * physical memory address. - * - * @param bucketNumber - * @param inBucketPosition - * @return The physical address of the fp slot + /* (non-Javadoc) + * @see tlc2.tool.fp.DiskFPSet#acquireTblWriteLock() */ - private long log2phy(long bucketNumber, long inBucketPosition) { - return log2phy(bucketNumber + inBucketPosition); + void acquireTblWriteLock() { + // no-op for now } - - /** - * Converts from logical addresses to - * physical memory addresses. - * - * @param logicalAddress - * @return The physical address of the fp slot + + /* (non-Javadoc) + * @see tlc2.tool.fp.DiskFPSet#releaseTblWriteLock() */ - private long log2phy(long logicalAddress) { - return baseAddress + (logicalAddress << logAddressSize); + void releaseTblWriteLock() { + // no-op for now } /* (non-Javadoc) @@ -332,560 +426,1003 @@ public class OffHeapDiskFPSet extends DiskFPSet implements FPSetStatistic { public long getTblCapacity() { return maxTblCnt; } - + /* (non-Javadoc) - * @see tlc2.tool.fp.DiskFPSet#getCollisionBucketCnt() + * @see tlc2.tool.fp.DiskFPSet#getTblLoad() */ - public long getCollisionBucketCnt() { - try { - this.csRWLock.readLock().lock(); - return collisionBucket.size(); - } finally { - this.csRWLock.readLock().unlock(); - } + public long getTblLoad() { + return getTblCnt(); } - + /* (non-Javadoc) - * @see tlc2.tool.fp.DiskFPSet#getCollisionRatio() + * @see tlc2.tool.fp.DiskFPSet#getOverallCapacity() */ - public double getCollisionRatio() { - return (double) getCollisionBucketCnt() / tblCnt.doubleValue(); + public long getOverallCapacity() { + return array.size(); } - - public class Indexer { - protected final long prefixMask; - /** - * Number of bits to right shift bits during index calculation - * @see MSBDiskFPSet#moveBy - */ - protected final int moveBy; - /** - * Number of bits to right shift bits during index calculation of - * striped lock. - */ - protected final int lockMoveBy; - - public Indexer(final int moveBy, int prefixBits) { - // same for lockCnt - this.prefixMask = 0x7FFFFFFFFFFFFFFFL >>> prefixBits; - this.moveBy = moveBy; - this.lockMoveBy = 63 - prefixBits - LogLockCnt; + + /* (non-Javadoc) + * @see tlc2.tool.fp.DiskFPSet#getBucketCapacity() + */ + public long getBucketCapacity() { + // A misnomer, but bucketCapacity is obviously not applicable with open + // addressing. + return PROBE_LIMIT; + } + + /* (non-Javadoc) + * @see tlc2.tool.fp.DiskFPSet#checkFPs() + */ + @Override + public long checkFPs() throws IOException { + if (getTblCnt() <= 0) { + return Long.MAX_VALUE; } + // The superclass implementation provides insufficient performance and + // scalability. These problems get more pronounced with large arrays. + // In order to provide a faster solution to the closest pair problem in + // one-dimensional space, we cheat and determine the minimum distance + // between fingerprints only on a subset of fingerprints. The subset are + // those fingerprints which happen to be in main memory and have not yet + // been evicted to disk. We believe this to be a sufficient approximation. + // The algorithm to find the closest pair in one-dimensional space is: + // 1) Sort all fingerprints. + // 2) Perform a linear scan/search + // => O(n log n) + // Scalability is achieved by partitioning the array for 1) and 2), + // which is possible due to the bounded disorder in array. - /* (non-Javadoc) - * @see tlc2.tool.fp.DiskFPSet#getLockIndex(long) - */ - protected int getLockIndex(long fp) { - // calculate hash value (just n most significant bits of fp) which is - // used as an index address - final long idx = (fp & prefixMask) >> lockMoveBy; - Assert.check(0 <= idx && idx < lockCnt, EC.GENERAL); - return (int) idx; + // One can think of two shortcuts to approximate of the closest pair: + // a) Track the minimum distance during the 1) step in prepareTable. A possible + // implementation can pass something similar to a BinaryOpeartor to the + // LongComparator. Due to the bounded disorder, this generally works except for + // the corner case where the closest pair are two fingerprints in two + // non-adjacent partitions, i.e. [,,,fp23],[],...,[],[,,fp42,,,]. This is + // frequently the case for very sparsely populated arrays. + // b) For low load factors (thus low collision rates) simply skip step 1) and do + // not sort the array at all. A zero collision rate implies that the array + // is sorted (except for first PROBE_LIMIT slots) if fingerprints wrapped around. + // c) Select just 1..N partitions out of the set of all array partitions + // for which the closest pair gets calculated. This is a more extreme variant + // of the current approximation which skips fingerprints on disk. + + // 1) Sort all fingerprints, either with a sequential or concurrent + // flusher depending on the size of the array. + final int numThreads = TLCGlobals.getNumWorkers(); + this.flusher = getFlusher(numThreads, getTblCnt()); + this.flusher.prepareTable(); + + // 2) A task finds the pair with the minimum distance in an ordered + // subrange of array. Each task is assigned an id. This id determines + // which subrange of array (partition) it searches. + final Collection<Callable<Long>> tasks = new ArrayList<Callable<Long>>(numThreads); + final long length = (long) Math.floor(array.size() / (double) numThreads); + for (int i = 0; i < numThreads; i++) { + final int id = i; + tasks.add(new Callable<Long>() { + @Override + public Long call() throws Exception { + final boolean isLast = id == numThreads - 1; + final long start = id * length; + // Partitions overlap in case the two fingerprints with + // minimum distance are in two adjacent partitions. + final long end = (isLast ? array.size() - 1L : start + length) + 1L; + + long distance = Long.MAX_VALUE; + + // Reuse Iterator implementation which already handles the + // various cases related to the cyclic table layout. Pass it + // getTblCnt to make sure next does not keep going forever + // if the array is empty. Instead, check the itr's position. + final Iterator itr = new Iterator(array, getTblCnt(), start, indexer, + isLast && id != 0 ? Iterator.WRAP.FORBIDDEN : Iterator.WRAP.ALLOWED); + try { + // If the partition is empty, next throws a NSEE. + long x = itr.next(); + long y = 0; + while ((y = itr.next(end)) != EMPTY) { + long d = y - x; + // d < 0 indicates that the array is not sorted. + // d == 0 indicates two identical elements in the + // fingerprint set. Iff this is the last partition, + // itr.next might have returned the first element of the + // first partition. + assert d > 0 ^ (d < 0 && itr.getPos() > end && isLast); + distance = Math.min(distance, d); + x = y; + } + return distance; + } catch (NoSuchElementException e) { + return distance; + } + } + }); + } + + // Start as many tasks as we have workers. Afterwards, collect the + // minimum distance out of the set of results provided by the workers. + final ExecutorService executorService = Executors.newFixedThreadPool(numThreads); + try { + final long distance = executorService.invokeAll(tasks).stream().min(new Comparator<Future<Long>>() { + public int compare(Future<Long> o1, Future<Long> o2) { + try { + return Long.compare(o1.get(), o2.get()); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new OffHeapRuntimeException(e); + } catch (ExecutionException e) { + throw new OffHeapRuntimeException(e); + } + } + }).get().get(); + return distance; + } catch (InterruptedException ie) { + Thread.currentThread().interrupt(); + throw new OffHeapRuntimeException(ie); + } catch (ExecutionException e) { + throw new OffHeapRuntimeException(e); + } finally { + executorService.shutdown(); } + + + // Dynamic variant to maintain closest pair: + // The dynamic (during insertions and not on static data) variant of the closest + // pair problem also provides an approximation of the actual closest pair. It + // can be maintained with little overhead in runtime and space and returned in + // constant time O(1). Contrary to the current implementation, it does not skip + // fingerprints on disk but observes all fingerprints. + // For that, we observe the minimum distance OD of the fingerprint to be inserted + // and all fingerprints it collides with in the method memInsert0. Before + // returning from memInsert0, the global minimum distance GD gets updated to the + // observed distance OD iff OD < GD (for scalability reasons we want to use a + // java.util.concurrent.atomic.LongAccumulator). + // This provides a sufficient approximation iff the collision rate is + // sufficiently high. Under a low collision rate, the number of comparisons in + // memInsert0 will be low. But even under a high collision rate, the + // approximation GD does not necessarily converge with the real minimum distance: + // Let f1 differs from f2 in just the lowest significant bit (LSB) and let f1 < + // f2. Depending on the size of the array, it is possible that f1 and f2 are + // indexed to two adjacent positions. If both positions for f1 and f2 are empty + // by the time f1 and f2 get inserted, no distance for f1 and f2 will be + // calculated. + // For low hash table/array collision rates (small load factors), we could + // simply report "insufficient data". Users will still have the optimistic FP64 + // collision probability, which will also be very small. + } - /** - * @param fp - * @return The logical bucket position in the table for the given fingerprint. - */ - protected long getLogicalPosition(final long fp) { - // push MSBs for moveBy positions to the right and align with a bucket address - long position = (fp & prefixMask) >> moveBy; - position = floorToBucket(position); - Assert.check(0 <= position && position < maxTblCnt, EC.GENERAL); - return position; + //**************************** Indexer ****************************// + + public static class Indexer { + + private static final long minFingerprint = 1L; //Minimum possible fingerprint (0L marks an empty position) + + private final double tblScalingFactor; + private final long maxFingerprint; + protected final long positions; + + public Indexer(final long positions, final int fpBits) { + this(positions, fpBits, 0xFFFFFFFFFFFFFFFFL >>> fpBits); + assert fpBits > 0; } - public long getNextBucketBasePosition(long logicalPosition) { - return floorToBucket(logicalPosition + bucketCapacity); + public Indexer(final long positions, final int fpBits, final long maxValue) { + this.positions = positions; + this.maxFingerprint = maxValue; + // (position-1L) because array is zero indexed. + this.tblScalingFactor = (positions - 1L) / ((maxFingerprint - minFingerprint) * 1f); } - /** - * Returns the largest position that - * is less than or equal to the argument and is equal to bucket base address. - * - * @param logicalPosition - * @return - */ - private long floorToBucket(long logicalPosition) { - long d = (long) Math.floor(logicalPosition / bucketCapacity); - return bucketCapacity * d; + protected long getIdx(final long fp) { + return getIdx(fp, 0); } - - /** - * @param logicalPosition - * @return true iff logicalPosition is a multiple of bucketCapacity - */ - public boolean isBucketBasePosition(long logicalPosition) { - return logicalPosition % bucketCapacity == 0; + + protected long getIdx(final long fp, final int probe) { + long idx = Math.round(tblScalingFactor * (fp - minFingerprint)) + probe; + return idx % positions; } } - /** - * A {@link BitshiftingIndexer} uses the more efficient AND operation - * compared to MODULO and DIV used by {@link Indexer}. Since indexing is - * executed on every {@link FPSet#put(long)} or {@link FPSet#contains(long)} - * , it is worthwhile to minimize is execution overhead. - */ - public class BitshiftingIndexer extends Indexer { - - /** - * Mask used to round of to a bucket address which is a power of 2. - */ - protected final long bucketBaseIdx; + public static class BitshiftingIndexer extends Indexer { + private final long prefixMask; + private final int rShift; - public BitshiftingIndexer(final int moveBy, final int prefixBits) throws RemoteException { - super(moveBy, prefixBits); - this.bucketBaseIdx = 0x7FFFFFFFFFFFFFFFL - (bucketCapacity - 1); + public BitshiftingIndexer(final long positions, final int fpBits) throws RemoteException { + super(positions, fpBits); + + this.prefixMask = 0xFFFFFFFFFFFFFFFFL >>> fpBits; + + long n = (0xFFFFFFFFFFFFFFFFL >>> fpBits) - (positions - 1); + int moveBy = 0; + while (n >= positions) { + moveBy++; + n = n >>> 1; // == (n/2) + } + this.rShift = moveBy; } - /* (non-Javadoc) - * @see tlc2.tool.fp.OffHeapDiskFPSet.Indexer#getLogicalPosition(long) - */ @Override - protected long getLogicalPosition(final long fp) { - // push MSBs for moveBy positions to the right and align with a bucket address - long position = ((fp & prefixMask) >> moveBy) & bucketBaseIdx; - //Assert.check(0 <= position && position < maxTblCnt, EC.GENERAL); - return position; + protected long getIdx(final long fp) { + return (fp & prefixMask) >>> rShift; } - - /* (non-Javadoc) - * @see tlc2.tool.fp.OffHeapDiskFPSet.Indexer#getNextBucketPosition(long) - */ + @Override - public long getNextBucketBasePosition(long logicalPosition) { - return (logicalPosition + bucketCapacity) & bucketBaseIdx; + protected long getIdx(final long fp, int probe) { + // Have to mod positions because probe might cause us to overshoot. + return (((fp & prefixMask) >>> rShift) + probe) % positions; } + } - /* (non-Javadoc) - * @see tlc2.tool.fp.OffHeapDiskFPSet.Indexer#isBucketBase(long) - */ - @Override - public boolean isBucketBasePosition(long logicalPosition) { - return (logicalPosition & (InitialBucketCapacity - 1)) == 0; + //**************************** Flusher ****************************// + + private LongComparator getLongComparator() { + return new LongComparator() { + @Override + public int compare(long fpA, long posA, long fpB, long posB) { + + // Elements not in Nat \ {0} remain at their current + // position. + if (fpA <= EMPTY || fpB <= EMPTY) { + return 0; + } + + final boolean wrappedA = indexer.getIdx(fpA) > posA; + final boolean wrappedB = indexer.getIdx(fpB) > posB; + + if (wrappedA == wrappedB && posA > posB) { + return fpA < fpB ? -1 : 1; + } else if (wrappedA ^ wrappedB) { + if (posA < posB && fpA < fpB) { + return -1; + } + if (posA > posB && fpA > fpB) { + return -1; + } + } + return 0; + } + }; + } + + /** + * Returns the number of fingerprints stored in table/array in the range + * [start,limit]. + */ + private long getTableOffset(final LongArray a, final long reprobe, final Indexer indexer, final long start, + final long limit) { + long occupied = 0L; + for (long pos = start; pos < limit; pos++) { + final long fp = a.get(pos % a.size()); + if (fp <= EMPTY) { + continue; + } + final long idx = indexer.getIdx(fp); + if (idx > pos) { + // Ignore the elements that wrapped around the + // end when scanning the first partition. + continue; + } + if (idx + reprobe < pos) { + // Ignore the elements of the first partition + // when wrapping around for the last partition. + continue; + } + occupied = occupied + 1L; } + return occupied; } - public class OffHeapMSBFlusher extends Flusher { - - /* (non-Javadoc) - * @see tlc2.tool.fp.DiskFPSet.Flusher#flushTable() - */ - @Override - void flushTable() throws IOException { - super.flushTable(); - - // garbage old values in collision bucket - collisionBucket.clear(); + private long getNextLower(long idx) { + // Reverse to the next non-evicted/empty fp that belongs to this partition. + long fp = array.get(idx); + while (fp <= EMPTY || indexer.getIdx(fp) > idx) { + fp = array.get(--idx); + } + return fp; + } + + /** + * The number of fingerprints stored on disk smaller than fp. + */ + private long getDiskOffset(final int id, final long fp) throws IOException { + if (this.index == null) { + return 0L; } - /* (non-Javadoc) - * @see tlc2.tool.fp.MSBDiskFPSet#mergeNewEntries(java.io.RandomAccessFile, java.io.RandomAccessFile) - */ - @Override - protected void mergeNewEntries(RandomAccessFile inRAF, RandomAccessFile outRAF) throws IOException { - final long buffLen = tblCnt.get(); - ByteBufferIterator itr = new ByteBufferIterator(u, baseAddress, collisionBucket, buffLen); + final int indexLength = this.index.length; + int loPage = 0; + int hiPage = indexLength - 1; + long loVal = this.index[loPage]; + long hiVal = this.index[hiPage]; - // Precompute the maximum value of the new file - long maxVal = itr.getLast(); - if (index != null) { - maxVal = Math.max(maxVal, index[index.length - 1]); + if (fp <= loVal) { + return 0L; + } + if (fp >= hiVal) { + return this.braf[id].length() / FPSet.LongSize; + } + // See DiskFPSet#diskLookup for comments. + + // Lookup the corresponding disk page in index. + final double dfp = (double) fp; + while (loPage < hiPage - 1) { + final double dhi = (double) hiPage; + final double dlo = (double) loPage; + final double dhiVal = (double) hiVal; + final double dloVal = (double) loVal; + int midPage = (loPage + 1) + (int) ((dhi - dlo - 1.0) * (dfp - dloVal) / (dhiVal - dloVal)); + if (midPage == hiPage) { + midPage--; } - - int indexLen = calculateIndexLen(buffLen); - index = new long[indexLen]; - index[indexLen - 1] = maxVal; - currIndex = 0; - counter = 0; - - // initialize positions in "buff" and "inRAF" - long value = 0L; // initialize only to make compiler happy - boolean eof = false; - if (fileCnt > 0) { - try { - value = inRAF.readLong(); - } catch (EOFException e) { - eof = true; - } + final long v = this.index[midPage]; + if (fp < v) { + hiPage = midPage; + hiVal = v; + } else if (fp > v) { + loPage = midPage; + loVal = v; } else { - eof = true; + return (midPage * 1L) * (NumEntriesPerPage * 1L); } + } + // no page is in between loPage and hiPage at this point + Assert.check(hiPage == loPage + 1, EC.SYSTEM_INDEX_ERROR); + assert this.index[loPage] < fp && fp < this.index[hiPage]; + + // Read the disk page and try to find the given fingerprint or the next + // smaller one. Calculate its offset in file. + long midEntry = -1L; + long loEntry = ((long) loPage) * NumEntriesPerPage; + long hiEntry = ((loPage == indexLength - 2) ? this.fileCnt - 1 : ((long) hiPage) * NumEntriesPerPage); + final BufferedRandomAccessFile raf = this.braf[id]; + while (loEntry < hiEntry) { + midEntry = calculateMidEntry(loVal, hiVal, dfp, loEntry, hiEntry); + raf.seek(midEntry * LongSize); + final long v = raf.readLong(); - // merge while both lists still have elements remaining - boolean eol = false; - long fp = itr.next(); - while (!eof || !eol) { - if ((value < fp || eol) && !eof) { - writeFP(outRAF, value); - try { - value = inRAF.readLong(); - } catch (EOFException e) { - eof = true; - } - } else { - // prevent converting every long to String when assertion holds (this is expensive) - if (value == fp) { - //MAK: Commented cause a duplicate does not pose a risk for correctness. - // It merely indicates a bug somewhere. - //Assert.check(false, EC.TLC_FP_VALUE_ALREADY_ON_DISK, - // String.valueOf(value)); - MP.printWarning(EC.TLC_FP_VALUE_ALREADY_ON_DISK, String.valueOf(value)); - } - writeFP(outRAF, fp); - // we used one fp up, thus move to next one - try { - fp = itr.next(); - } catch (NoSuchElementException e) { - // has read all elements? - Assert.check(!itr.hasNext(), EC.GENERAL); - eol = true; - } - } + if (fp < v) { + hiEntry = midEntry; + hiVal = v; + } else if (fp > v) { + loEntry = midEntry + 1; + loVal = v; + midEntry = loEntry; + } else { + break; } - - // both sets used up completely - Assert.check(eof && eol, EC.GENERAL); - - // currIndex is amount of disk writes - Assert.check(currIndex == indexLen - 1, EC.SYSTEM_INDEX_ERROR); + } + + assert isHigher(midEntry, fp, raf); + return midEntry; + } - // maintain object invariants - fileCnt += buffLen; + public boolean isHigher(long midEntry, long fp, BufferedRandomAccessFile raf) throws IOException { + raf.seek((midEntry - 1L) * LongSize); + final long low = raf.readLong(); + final long high = raf.readLong(); + if (low < fp && fp < high) { + return true; } + return false; } - - /** - * A non-thread safe Iterator - */ - public class ByteBufferIterator { - private final CollisionBucket cs; - /** - * Number of elements in the buffer - */ - private long bufferElements; - /** - * Total amount of elements in both the buffer as well as the collisionBucket. - */ - private final long totalElements; - /** - * The logical position is the position inside the {@link LongBuffer} and - * thus reflects a fingerprints - */ - private long logicalPosition = 0; + public class ConcurrentOffHeapMSBFlusher extends OffHeapMSBFlusher { + + private final int numThreads; + private final ExecutorService executorService; + private final int r; + private final long insertions; /** - * Used to verify that the elements we hand out are strictly monotonic - * increasing. + * The length of a single partition. */ - private long previous = -1l; - /** - * Number of elements read with next() + private final long length; + private List<Result> offsets; + + public ConcurrentOffHeapMSBFlusher(final LongArray array, final int r, final int numThreads, + final long insertions) { + super(array); + this.r = r; + this.numThreads = numThreads; + this.insertions = insertions; + + this.length = (long) Math.floor(a.size() / (double) numThreads); + this.executorService = Executors.newFixedThreadPool(numThreads); + } + + /* (non-Javadoc) + * @see tlc2.tool.fp.DiskFPSet.Flusher#prepareTable() */ - private long readElements = 0L; + @Override + protected void prepareTable() { + final long now = System.currentTimeMillis(); + final CyclicBarrier phase = new CyclicBarrier(this.numThreads); + final Collection<Callable<Result>> tasks = new ArrayList<Callable<Result>>(numThreads); + for (int i = 0; i < numThreads; i++) { + final int id = i; + tasks.add(new Callable<Result>() { + @Override + public Result call() throws Exception { + final boolean isFirst = id == 0; + final boolean isLast = id == numThreads - 1; + final long start = id * length; + final long end = isLast ? a.size() - 1L : start + length; + + // Sort partition p_n. We have exclusive access. + LongArrays.sort(a, isFirst ? 0L : start + 1L, end, getLongComparator()); + assert checkSorted(a, indexer, r, isFirst ? 0L : start + 1L, end) == -1L : String.format( + "Array %s not fully sorted at index %s and reprobe %s in range [%s,%s].", a.toString(), + checkSorted(array, indexer, r, start, end), r, start, end); + + // Wait for the other threads sorting p_n+1 to be done + // before we stitch together p_n and p_n+1. + phase.await(); - private long cache = -1L; - private final Unsafe unsafe; + // Sort the range between partition p_n and + // p_n+1 bounded by reprobe. + LongArrays.sort(a, end - r+1L, end + r+1L, getLongComparator()); + + // Wait for table to be fully sorted before we calculate + // the offsets. Offsets can only be correctly calculated + // on a sorted table. + phase.await(); + + // Count the occupied positions for this + // partition. Occupied positions are those which + // get evicted (written to disk). + // This could be done as part of (insertion) sort + // above at the price of higher complexity. Thus, + // it's done here until it becomes a bottleneck. + final long limit = isLast ? a.size() + r : end; + final long occupied = getTableOffset(a, r, indexer, start, limit); + assert occupied <= limit - start; + + if (index == null) { + // No index, no need to calculate a disk offset. + return new Result(occupied, 0L); + } + + // Determine number of elements in the old/current file. + if (isFirst && isLast) { + return new Result(occupied, fileCnt); + } else if (isFirst) { + return new Result(occupied, getDiskOffset(id, getNextLower(end))); + } else if (isLast) { + return new Result(occupied, fileCnt - getDiskOffset(id, getNextLower(start))); + } else { + return new Result(occupied, + getDiskOffset(id, getNextLower(end)) - getDiskOffset(id, getNextLower(start))); + } + } + }); + } + try { + offsets = futuresToResults(executorService.invokeAll(tasks)); + } catch (InterruptedException ie) { + Thread.currentThread().interrupt(); + throw new OffHeapRuntimeException(ie); + } catch (ExecutionException notExpectedToHappen) { + throw new OffHeapRuntimeException(notExpectedToHappen); + } - public ByteBufferIterator(Unsafe u, long baseAddress, CollisionBucket collisionBucket, long expectedElements) { - this.unsafe = u; - this.logicalPosition = 0L; - this.totalElements = expectedElements; - - // Do calculation before prepareForFlush() potentially empties the cs causing size() to return 0 - this.bufferElements = expectedElements - collisionBucket.size(); + assert checkSorted(a, indexer, r) == -1L : String.format( + "Array %s not fully sorted at index %s and reprobe %s.", a.toString(), + checkSorted(array, indexer, r), r); - this.cs = collisionBucket; - this.cs.prepareForFlush(); + LOGGER.log(Level.FINE, "Sorted in-memory table with {0} workers and reprobe {1} in {2} ms.", + new Object[] { numThreads, r, System.currentTimeMillis() - now }); + } + + private List<Result> futuresToResults(List<Future<Result>> futures) throws InterruptedException, ExecutionException { + final List<Result> res = new ArrayList<Result>(futures.size()); + for (Future<Result> future : futures) { + res.add(future.get()); + } + return res; } - /** - * Returns the next element in the iteration. - * - * @return the next element in the iteration. - * @exception NoSuchElementException iteration has no more elements. - */ - public long next() { - long result = -1l; + @Override + protected void mergeNewEntries(final BufferedRandomAccessFile[] inRAFs, final RandomAccessFile outRAF, final Iterator ignored) throws IOException { + final long now = System.currentTimeMillis(); + assert offsets.stream().mapToLong(new ToLongFunction<Result>() { + public long applyAsLong(Result result) { + return result.getTable(); + } + }).sum() == insertions : "Missing inserted elements during eviction."; + assert offsets.stream().mapToLong(new ToLongFunction<Result>() { + public long applyAsLong(Result result) { + return result.getDisk(); + } + }).sum() == fileCnt : "Missing disk elements during eviction."; - if (cache < 0L && bufferElements > 0) { - result = getNextFromBuffer(); - bufferElements--; - } else { - result = cache; - cache = -1L; + // Calculate offsets for in and the out file, i.e. sum up the + // combined number of elements in lower partitions. + for (int id = 1; id < numThreads; id++) { + final Result prev = offsets.get(id - 1); + final Result result = offsets.get(id); + result.setInOffset(prev.getInOffset() + prev.getDisk()); + result.setOutOffSet(prev.getOutOffset() + prev.getTotal()); } - if (!cs.isEmpty()) { - long first = cs.first(); - if (result > first || result == -1L) { - cs.remove(first); - cache = result; - result = first; - } + final long outLength = outRAF.length(); + final Collection<Callable<Void>> tasks = new ArrayList<Callable<Void>>(numThreads); + final BufferedRandomAccessFile[] tmpRAFs = new BufferedRandomAccessFile[numThreads]; + for (int i = 0; i < numThreads; i++) { + final int id = i; + + // Create a new RAF instance. The outRAF instance is + // otherwise shared by multiple writers leading to race + // conditions and inconsistent fingerprint set files. + tmpRAFs[id] = new BufferedRandomAccessFile(new File(tmpFilename), "rw"); + tmpRAFs[id].setLength(outLength); + + // Set offsets into the out (tmp) file. + final Result result = offsets.get(id); + tmpRAFs[id].seekAndMark(result.getOutOffset() * FPSet.LongSize); + + // Set offset and the number of elements the + // iterator is supposed to return. + final Iterator itr = new Iterator(a, result.getTable(), id * length, indexer, + id == 0 ? Iterator.WRAP.ALLOWED : Iterator.WRAP.FORBIDDEN); + + final BufferedRandomAccessFile inRAF = inRAFs[id]; + assert (result.getInOffset() + result.getDisk()) * FPSet.LongSize <= inRAF.length(); + inRAF.seekAndMark(result.getInOffset() * FPSet.LongSize); + + // Stop reading after diskReads elements (after + // which the next thread continues) except for the + // last thread which reads until EOF. Pass 0 when + // nothing can be read from disk. + final long diskReads = id == numThreads - 1 ? fileCnt - result.getInOffset() : result.getDisk(); + + tasks.add(new Callable<Void>() { + public Void call() throws Exception { + ConcurrentOffHeapMSBFlusher.super.mergeNewEntries(inRAF, tmpRAFs[id], itr, diskReads); + assert tmpRAFs[id].getFilePointer() == (result.getOutOffset() + result.getTotal()) * FPSet.LongSize : id + + " writer did not write expected amount of fingerprints to disk."; + return null; + } + }); } - - // adhere to the general Iterator contract to fail fast and not hand out - // meaningless values - if (result == -1L) { - throw new NoSuchElementException(); + // Combine the callable results. + try { + executorService.invokeAll(tasks); + executorService.shutdown(); + executorService.awaitTermination(Long.MAX_VALUE, TimeUnit.MILLISECONDS); + assert checkRAFs(tmpRAFs); + } catch (InterruptedException ie) { + Thread.currentThread().interrupt(); + throw new OffHeapRuntimeException(ie); + } finally { + executorService.shutdown(); } - // hand out strictly monotonic increasing elements - Assert.check(previous < result, EC.GENERAL); - previous = result; - - // maintain read statistics - readElements++; + // Finally close the out rafs after all tasks have finished. On + // Linux, closing an instance of the tmpRAFs appears to be racy when + // other tasks still execute. + for (int i = 0; i < tmpRAFs.length; i++) { + tmpRAFs[i].close(); + } + + assert checkRAFs(inRAFs); + assert checkTable(a) : "Missed element during eviction."; - return result; + LOGGER.log(Level.FINE, "Wrote table to disk with {0} workers in {1} ms.", + new Object[] { numThreads, (System.currentTimeMillis() - now) }); } - private long getNextFromBuffer() { - sortNextBucket(); + private class Result { + private final long occupiedTable; + private final long occupiedDisk; + private long outOffset; + private long inOffset; - long l = unsafe.getAddress(log2phy(logicalPosition)); - if (l > 0L) { - unsafe.putAddress(log2phy(logicalPosition++), l | 0x8000000000000000L); - return l; + public Result(long occupiedTable, long occupiedDisk) { + this.occupiedTable = occupiedTable; + this.occupiedDisk = occupiedDisk; } - - while ((l = unsafe.getAddress(log2phy(logicalPosition))) <= 0L && logicalPosition < maxTblCnt) { - // increment position to next bucket - logicalPosition = indexer.getNextBucketBasePosition(logicalPosition); - sortNextBucket(); + public long getDisk() { + return occupiedDisk; } - - if (l > 0L) { - unsafe.putAddress(log2phy(logicalPosition++), l | 0x8000000000000000L); - return l; + public long getTable() { + return occupiedTable; + } + public long getTotal() { + return occupiedDisk + occupiedTable; + } + public void setOutOffSet(long offset) { + this.outOffset = offset; + } + public void setInOffset(long offset) { + this.inOffset = offset; + } + public long getInOffset() { + return this.inOffset; + } + public long getOutOffset() { + return this.outOffset; } - throw new NoSuchElementException(); } + } + + public class OffHeapMSBFlusher extends Flusher { + + protected final LongArray a; - // sort the current logical bucket if we reach the first slot of the - // bucket - private void sortNextBucket() { - if (indexer.isBucketBasePosition(logicalPosition)) { - long[] longBuffer = new long[bucketCapacity]; - int i = 0; - for (; i < bucketCapacity; i++) { - long l = unsafe.getAddress(log2phy(logicalPosition + i)); - if (l <= 0L) { - break; - } else { - longBuffer[i] = l; - } - } - if (i > 0) { - Arrays.sort(longBuffer, 0, i); - for (int j = 0; j < i; j++) { - unsafe.putAddress(log2phy(logicalPosition, j), - longBuffer[j]); - } - } - } + public OffHeapMSBFlusher(LongArray array) { + a = array; } - /** - * Returns <tt>true</tt> if the iteration has more elements. (In other - * words, returns <tt>true</tt> if <tt>next</tt> would return an element - * rather than throwing an exception.) - * - * @return <tt>true</tt> if the iterator has more elements. - */ - public boolean hasNext() { - // hasNext does not move the indices at all! - return readElements < totalElements; + /* (non-Javadoc) + * @see tlc2.tool.fp.DiskFPSet.Flusher#prepareTable() + */ + @Override + protected void prepareTable() { + super.prepareTable(); + final int r = PROBE_LIMIT; + + assert checkInput(array, indexer, r) : "Table violates invariants prior to eviction"; + + // Sort with a single thread. + LongArrays.sort(a, 0, a.size() - 1L + r, getLongComparator()); + + assert checkSorted(a, indexer, r) == -1L : String.format( + "Array %s not fully sorted at index %s and reprobe %s.", a.toString(), + checkSorted(array, indexer, r), r); } - - /** - * @return The last element in the iteration. - * @exception NoSuchElementException if iteration is empty. + + /* (non-Javadoc) + * @see tlc2.tool.fp.MSBDiskFPSet#mergeNewEntries(java.io.RandomAccessFile, java.io.RandomAccessFile) */ - public long getLast() { - // Remember current position - final long tmpLogicalPosition = logicalPosition; + @Override + protected void mergeNewEntries(BufferedRandomAccessFile[] inRAFs, RandomAccessFile outRAF) throws IOException { + final long buffLen = tblCnt.sum(); + final Iterator itr = new Iterator(array, buffLen, indexer); - // Calculate last bucket position and have it sorted - logicalPosition = maxTblCnt - bucketCapacity; - sortNextBucket(); + final int indexLen = calculateIndexLen(buffLen); + index = new long[indexLen]; + mergeNewEntries(inRAFs, outRAF, itr); - // Reverse the current bucket to obtain last element (More elegantly - // this could be achieved recursively, but this can cause a - // stack overflow). - long l = 1L; - while ((l = unsafe.getAddress(log2phy(logicalPosition-- + bucketCapacity - 1))) <= 0L) { - sortNextBucket(); - } + final long length = (outRAF.length() / LongSize) - 1L; + writeIndex(index, outRAF, length); + assert checkIndex(index) : "Broken disk index."; + assert checkIndex(index, outRAF, length) : "Misaligned disk index."; - // Done searching in-memory storage backwards, reset position to - // original value. - logicalPosition = tmpLogicalPosition; + // maintain object invariants + fileCnt += buffLen; + } + + protected void mergeNewEntries(BufferedRandomAccessFile[] inRAFs, RandomAccessFile outRAF, Iterator itr) + throws IOException { + inRAFs[0].seek(0); + mergeNewEntries(inRAFs[0], outRAF, itr, inRAFs[0].length() / FPSet.LongSize); + } + + /* + * See PlusCal spec OpenAddressing.ConcurrentFlusher.tla which has been checked for Nat == 0..6. + */ + protected void mergeNewEntries(BufferedRandomAccessFile inRAF, RandomAccessFile outRAF, final Iterator itr, + long diskReads) throws IOException { - // Compare max element found in main in-memory buffer to man - // element in collisionBucket. Return max of the two. - if (!cs.isEmpty()) { - l = Math.max(cs.last(), l); + // Disk might be empty. + long value = 0L; + if (diskReads > 0) { + value = inRAF.readLong(); + } else { + assert fileCnt == 0L; } - // Either return the maximum element or fail fast. - if (l > 0L) { - return l; - } - throw new NoSuchElementException(); + long tableReads = itr.elements; + long fp = itr.markNext(); + + do { + if (value == fp) { + MP.printWarning(EC.TLC_FP_VALUE_ALREADY_ON_DISK, String.valueOf(value)); + } + assert fp > EMPTY : "Wrote an invalid fingerprint to disk."; + + // From memory/table + if (tableReads > 0 && (fp < value || diskReads == 0)) { + outRAF.writeLong(fp); + tableReads--; + diskWriteCnt.increment(); + // Read next value if any. + if (tableReads > 0) { + final long nextFP = itr.markNext(); + assert nextFP > fp : nextFP + " > " + fp + " from table at pos " + itr.pos + " " + + a.toString(itr.pos - 10L, itr.pos + 10L); + fp = nextFP; + } + } + + // From file/disk + if (diskReads > 0 && (value < fp || tableReads == 0)) { + outRAF.writeLong(value); + diskReads--; + diskWriteCnt.increment(); + // Read next value if any. + if (diskReads > 0) { + final long nextValue = inRAF.readLong(); + assert value < nextValue; + value = nextValue; + } + } + + } while (diskReads > 0 || tableReads > 0); + + // both sets used up completely + Assert.check(diskReads == 0L && tableReads == 0L, EC.GENERAL); + assert !itr.hasNext(); } - - // prevent synthetic methods - private long log2phy(long logicalAddress) { - return OffHeapDiskFPSet.this.log2phy(logicalAddress); + } + + /* (non-Javadoc) + * @see tlc2.tool.fp.DiskFPSet#calculateIndexLen(long) + */ + @Override + protected int calculateIndexLen(final long tblcnt) { + int indexLen = super.calculateIndexLen(tblcnt); + if ((tblcnt + fileCnt - 1L) % NumEntriesPerPage == 0L) { + // This is the special case where the largest fingerprint + // happened is going to end up on the last entry of the previous + // page. Thus, we won't need the last extra index cell. + indexLen--; } - private long log2phy(long bucketAddress, long inBucketAddress) { - return OffHeapDiskFPSet.this.log2phy(bucketAddress, inBucketAddress); + return indexLen; + } + + protected void writeIndex(long[] index, final RandomAccessFile raf, long length) throws IOException { + for (int i = 0; i < index.length; i++) { + long pos = Math.min(((long) i) * NumEntriesPerPage, length); + raf.seek(pos * LongSize); + final long value = raf.readLong(); + index[i] = value; } } - public interface CollisionBucket { - void clear(); + /** + * A non-thread safe Iterator whose next method returns the next largest + * element. + */ + public static class Iterator { - void prepareForFlush(); + private enum WRAP { + ALLOWED, FORBIDDEN; + } - void remove(long first); + private final long elements; + private final LongArray array; + private final Indexer indexer; + private final WRAP canWrap; - long first(); + private long pos = 0; + private long elementsRead = 0L; - long last(); - - boolean isEmpty(); - - /** - * @param fp - * @return {@code true} if this set did not already contain the specified - * fingerprint - */ - boolean add(long fp); - - boolean contains(long fp); + public Iterator(final LongArray array, final long elements, final Indexer indexer) { + this(array, elements, 0L, indexer, WRAP.ALLOWED); + } - long size(); - } - - public class TreeSetCollisionBucket implements CollisionBucket { - private final TreeSet<Long> set; + public Iterator(final LongArray array, final long elements, final long start, final Indexer indexer) { + this(array, elements, start, indexer, WRAP.FORBIDDEN); + } - public TreeSetCollisionBucket(int initialCapacity) { - this.set = new TreeSet<Long>(); + public Iterator(final LongArray array, final long elements, final long start, final Indexer indexer, + final WRAP canWrap) { + this.array = array; + this.elements = elements; + this.indexer = indexer; + this.pos = start; + this.canWrap = canWrap; } - /* (non-Javadoc) - * @see tlc2.tool.fp.OffHeapDiskFPSet.CollisionBucket#clear() - */ - public void clear() { - set.clear(); + public long getPos() { + return pos; } - /* (non-Javadoc) - * @see tlc2.tool.fp.OffHeapDiskFPSet.CollisionBucket#prepareForFlush() + /** + * Returns the next element in the iteration that is not EMPTY nor + * marked evicted. + * + * @return the next element in the iteration that is not EMPTY nor + * marked evicted. + * @exception NoSuchElementException + * iteration has no more elements. */ - public void prepareForFlush() { - // no-op + public long next() { + return next0(false, Long.MAX_VALUE); } - - /* (non-Javadoc) - * @see tlc2.tool.fp.OffHeapDiskFPSet.CollisionBucket#remove(long) + + long next(long maxPos) { + if (pos >= maxPos) { + return EMPTY; + } + return next0(false, maxPos); + } + + /** + * Returns the next element in the iteration that is not EMPTY nor + * marked evicted and marks it to be evicted. + * <p> + * THIS IS NOT SIDEEFFECT FREE. AFTERWARDS, THE ELEMENT WILL BE MARKED + * EVICTED. + * + * @return the next element in the iteration that is not EMPTY nor + * marked evicted. + * @exception NoSuchElementException + * iteration has no more elements. */ - public void remove(long first) { - set.remove(first); + public long markNext() { + return next0(true, Long.MAX_VALUE); } - /* (non-Javadoc) - * @see tlc2.tool.fp.OffHeapDiskFPSet.CollisionBucket#first() - */ - public long first() { - return set.first(); + private long next0(final boolean mark, final long maxPos) { + long elem; + do { + final long position = pos % array.size(); + elem = array.get(position); + if (elem == EMPTY) { + pos = pos + 1L; + continue; + } + if (elem < EMPTY) { + pos = pos + 1L; + continue; + } + final long baseIdx = indexer.getIdx(elem); + if (baseIdx > pos) { + // This branch should only be active for thread with id 0. + assert canWrap == WRAP.ALLOWED; + pos = pos + 1L; + continue; + } + pos = pos + 1L; + // mark elem in array as being evicted. + if (mark) {array.set(position, elem | MARK_FLUSHED);} + elementsRead = elementsRead + 1L; + return elem; + } while (hasNext() && pos < maxPos); + if (pos >= maxPos) { + return EMPTY; + } + throw new NoSuchElementException(); } - /* (non-Javadoc) - * @see tlc2.tool.fp.OffHeapDiskFPSet.CollisionBucket#last() - */ - public long last() { - return set.last(); + /** + * Returns <tt>true</tt> if the iteration has more elements. (In other + * words, returns <tt>true</tt> if <tt>next</tt> would return an element + * rather than throwing an exception.) + * + * @return <tt>true</tt> if the iterator has more elements. + */ + public boolean hasNext() { + return elementsRead < elements; } + } - /* (non-Javadoc) - * @see tlc2.tool.fp.OffHeapDiskFPSet.CollisionBucket#isEmpty() - */ - public boolean isEmpty() { - return set.isEmpty(); + public static class OffHeapRuntimeException extends RuntimeException { + + public OffHeapRuntimeException(Exception ie) { + super(ie); } + } + + // ***************** assertion helpers *******************// - /* (non-Javadoc) - * @see tlc2.tool.fp.OffHeapDiskFPSet.CollisionBucket#add(long) - * - * If this set already contains the element, the call leaves the set - * unchanged and returns false. - */ - public boolean add(long fp) { - return set.add(fp); + private static boolean checkInput(final LongArray array, final Indexer indexer, final int reprobe) { + for (long pos = 0; pos <= array.size() + reprobe - 1L; pos++) { + long tmp = array.get(pos % array.size()); + if (tmp == EMPTY) { + continue; + } + if (tmp < EMPTY) { + // Convert evicted fps back. + tmp = tmp & FLUSHED_MASK; + } + final long idx = indexer.getIdx(tmp); + // Accept wrapped elements. + if (pos < reprobe && idx > (array.size() - 1L - pos - reprobe)) { + continue; + } + // Accept non-wrapped elements when pos > array.size + if (pos > array.size() - 1L && idx + reprobe < pos) { + continue; + } + // Accept any other valid elements where pos is within the range + // given by [idx,idx + reprobe]. + if (idx <= pos && pos <= idx + reprobe) { + continue; + } + System.err.println(String.format("%s with idx %s at pos %s (reprobe: %s).", tmp, idx, pos, reprobe)); + return false; } + return true; + } - /* (non-Javadoc) - * @see tlc2.tool.fp.OffHeapDiskFPSet.CollisionBucket#contains(long) - */ - public boolean contains(long fp) { - return set.contains(fp); + /** + * @return -1L iff array is sorted, index/position of the element that violates otherwise in the range [start, end]. + */ + private static long checkSorted(final LongArray array, final Indexer indexer, int reprobe, long start, long end) { + if (reprobe >= array.size()) { + // When the array is smaller than the number of positions/slots, the + // loop below will run past its bounds. If this happens, it compares + // elements the element at the start with the element at the end. + reprobe = (int) (array.size() - 1); + } + long e = 0L; + for (long pos = start; pos <= end; pos++) { + final long tmp = array.get(pos % array.size()); + if (tmp <= EMPTY) { + continue; + } + final long idx = indexer.getIdx(tmp); + if (idx > pos) { + continue; + } + if (idx + reprobe < pos) { + continue; + } + if (e == 0L) { + // Initialize e with the first element that is not <=EMPTY + // or has wrapped. + e = tmp; + continue; + } + if (e >= tmp) { + System.err.println(String.format("%s >= %s at pos %s.", e, tmp, pos)); + return pos; + } + e = tmp; } + return -1L; + } - /* (non-Javadoc) - * @see tlc2.tool.fp.OffHeapDiskFPSet.CollisionBucket#size() - */ - public long size() { - return set.size(); + /** + * @return -1L iff array is sorted, index/position of the element that violates otherwise. + */ + private static long checkSorted(final LongArray array, final Indexer indexer, final int reprobe) { + return checkSorted(array, indexer, reprobe, 0, array.size() - 1L + reprobe); + } + + private static boolean checkRAFs(final BufferedRandomAccessFile[] rafs) throws IOException { + for (int i = 0; i < rafs.length - 1; i++) { + final long end = rafs[i].getFilePointer(); + final long start = rafs[i + 1].getMark(); + if (end != start) { + return false; + } } + return true; } - public class PrettyPrinter { - /** - * Print the current in-memory hash table to System.out with increments - */ - public void printDistribution(final int increments) { - final int mask = increments - 1; - int cnt = 0; - int min = Integer.MAX_VALUE; - int max = 0; - for (long i = maxTblCnt - 1; i >= 0; i--) { - if ((i & mask) == 0) { - if (cnt > max) { - max = cnt; - } - if (cnt < min) { - min = cnt; - } - System.out.println(i + " " + cnt); - cnt = 0; - } - if (u.getAddress(log2phy(i)) > 0L) { - cnt++; - } + private static boolean checkTable(LongArray array) { + for (long i = 0L; i < array.size(); i++) { + long elem = array.get(i); + if (elem > EMPTY) { + System.err.println(String.format("%s elem at pos %s.", elem, i)); + return false; } - System.out.println("max: " + max + " min: " + min + " avg:" + (tblLoad / tblCnt.doubleValue())); } - - public void printBuckets() { - printBuckets(0, maxTblCnt); + return true; + } + + private static boolean checkIndex(final long[] idx) { + for (int i = 1; i < idx.length; i++) { + if (idx[i - 1] >= idx[i]) { + return false; + } } - - /** - * @param from inclusive lower bound - * @param to exclusive upper bound - */ - public void printBuckets(int from, long to) { - for (long i = from; i < maxTblCnt && i < to; i++) { - if (i % bucketCapacity == 0) { - System.out.println("Bucket idx: " + i); - } - System.out.println(u.getAddress(log2phy(i))); + return true; + } + + private static boolean checkIndex(final long[] idx, final RandomAccessFile raf, final long length) throws IOException { + for (long i = 0L; i < idx.length; i++) { + final long pos = Math.min(i * NumEntriesPerPage, length); + raf.seek(pos * LongSize); + final long value = raf.readLong(); + final long index = idx[(int) i]; + if (value != index) { + return false; } } + return true; } } diff --git a/tlatools/src/tlc2/tool/fp/OffHeapDiskFPSetHelper.java b/tlatools/src/tlc2/tool/fp/OffHeapDiskFPSetHelper.java deleted file mode 100644 index 9a71228dac904480f07b887080d785082d1ec8e1..0000000000000000000000000000000000000000 --- a/tlatools/src/tlc2/tool/fp/OffHeapDiskFPSetHelper.java +++ /dev/null @@ -1,97 +0,0 @@ -// Copyright (c) 2012 Markus Alexander Kuppe. All rights reserved. -package tlc2.tool.fp; - -import java.io.IOException; -import java.lang.reflect.Field; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; -import java.util.concurrent.Callable; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.Future; - -import tlc2.output.EC; -import util.Assert; - -import sun.misc.Unsafe; - -@SuppressWarnings("restriction") -public final class OffHeapDiskFPSetHelper { - - private OffHeapDiskFPSetHelper() { - // no instantiation! - } - - /** - * @return An Unsafe object or a {@link RuntimeException} wrapping any {@link Exception}. - */ - public static sun.misc.Unsafe getUnsafe() { - // More Details can be found at: http://www.mydailyjava.blogspot.no/2013/12/sunmiscunsafe.html - try { - // Use reflection API to unhide Unsafe - final Field f = sun.misc.Unsafe.class.getDeclaredField("theUnsafe"); - f.setAccessible(true); - return (sun.misc.Unsafe) f.get(null); - } catch (Exception e) { - throw new RuntimeException( - "Trying to use Sun VM specific sun.misc.Unsafe implementation but no Sun based VM detected.", - e); - } - } - - /** - * Initializes the memory by overriding each byte with zero starting at - * <code>baseAddress</code> and ending when <code>fingerprintCount</code> - * bytes have been written. - * <p> - * To speed up the initialization, <code>numThreads</code> allows to set a - * thread count with which zeroing is then done in parallel. - * - * @param u - * @param baseAddress Base address of the (previously) allocated memory - * @param numThreads Number of threads used to zero memory - * @param fingerprintCount Number of fingerprint for which the memory should be initialized - * @throws IOException - */ - public static void zeroMemory(final Unsafe u, final long baseAddress, int numThreads, final long fingerprintCount) - throws IOException { - - final int addressSize = u.addressSize(); - final long segmentSize = fingerprintCount / numThreads; - - final ExecutorService es = Executors.newFixedThreadPool(numThreads); - try { - final Collection<Callable<Boolean>> tasks = new ArrayList<Callable<Boolean>>(numThreads); - for (int i = 0; i < numThreads; i++) { - final int offset = i; - tasks.add(new Callable<Boolean>() { - - public Boolean call() throws Exception { - // Null memory (done in parallel on segments). - // This is essential as allocateMemory returns - // uninitialized memory and - // memInsert/memLockup utilize 0L as a mark for an - // unused fingerprint slot. - // Otherwise memory garbage wouldn't be distinguishable - // from a true fp. - final long lowerBound = segmentSize * offset; - final long upperBound = (1 + offset) * segmentSize; - for (long i = lowerBound; i < upperBound; i++) { - final long address = baseAddress + (i * addressSize); - u.putAddress(address, 0L); - } - return true; - } - }); - } - final List<Future<Boolean>> invokeAll = es.invokeAll(tasks); - Assert.check(!invokeAll.isEmpty(), EC.GENERAL); - } catch (InterruptedException e) { - // not expected to happen - e.printStackTrace(); - } finally { - es.shutdown(); - } - } -} diff --git a/tlatools/src/tlc2/tool/fp/OpenAddressing.ConcurrentFlusher.tla b/tlatools/src/tlc2/tool/fp/OpenAddressing.ConcurrentFlusher.tla new file mode 100644 index 0000000000000000000000000000000000000000..53eafd5599f16d9f73070695b96a256869a38a82 --- /dev/null +++ b/tlatools/src/tlc2/tool/fp/OpenAddressing.ConcurrentFlusher.tla @@ -0,0 +1,225 @@ +------------------------------- MODULE Merge ------------------------------- +EXTENDS Integers, TLC, Sequences, FiniteSets + +(**********************************) +(* The larger of the two elements *) +(**********************************) +Max(a,b) == IF a > b THEN a ELSE b + +(***************************************************************************) +(* The image of the function F. *) +(***************************************************************************) +Image(F) == { F[x] : x \in DOMAIN F } + +(***************************************************************************) +(* Convertes the given Sequence seq into a Set of all the *) +(* Sequence's elements. In other words, the image of the function *) +(* that seq is. *) +(***************************************************************************) +SeqToSet(seq) == {seq[i] : i \in 1..Len(seq)} + +(***************************************************************************) +(* Returns a Set of all possible permutations with distinct elemenents *) +(* created out of the elements of Set set. All elements of set occur in *) +(* in the sequence. *) +(***************************************************************************) +IsOrdered(seq) == IF seq = <<>> \/ Len(seq) = 1 THEN TRUE ELSE + \A i \in 1..(Len(seq) - 1): seq[i] < seq[i+1] +OrderedSequences(set) == UNION {{perm \in [1..Cardinality(set) -> set]: + IsOrdered(perm)}} + +(*** +--fair algorithm Merge { + + variables history = {}, + a = 0, + aLength = 0, + A = <<>>, + b = 0, + bLength = 0, + B = <<>>, + O = <<>>; + + define { + Inv == /\ Image(O) \subseteq history + /\ IsOrdered(O) + } + + macro Consume(var, seq) { + var := Head(seq); + seq := Tail(seq); + } + { + + (* Initialize a and b with all possible combination of sorted sequences. *) + init: + with (r \in SUBSET Nat \ {{}}) { + with (s \in ((SUBSET (Nat \ r)) \ {{}})) { + with (t \in OrderedSequences(s) \cup {<<>>}) { + B := t; + bLength := Len(B); + with (u \in OrderedSequences(r) \cup {<<>>}) { + A := u; + aLength := Len(A); + } + }; + }; + }; + + (* Keep history. *) + hstry: + history := SeqToSet(A) \cup SeqToSet(B); + if (Cardinality(history) = 0) { + goto "Done"; + }; + + (* Merge sorted A and B to obtain sorted O. *) + mrg1: + if (aLength > 0) { + Consume(a, A); + }; + if (bLength > 0) { + Consume(b, B); + }; + + mrg2: + if (aLength > 0 /\ (a < b \/ bLength = 0)) { + O := Append(O, a); + aLength := aLength - 1; + if (aLength > 0) { + Consume(a, A); + }; + }; + mrg2b: + if (bLength > 0 /\ (b < a \/ aLength = 0)) { + O := Append(O, b); + bLength := bLength - 1; + if (bLength > 0) { + Consume(b, B); + }; + }; + mrg3: + if (bLength > 0 \/ aLength > 0) { + goto mrg2; + }; + mrg4: + assert bLength = 0 /\ aLength = 0; + assert Len(O) = Cardinality(history); + assert Image(O) = history; + } +} + +*** this ends the comment containg the pluscal code **********) +\* BEGIN TRANSLATION +VARIABLES history, a, aLength, A, b, bLength, B, O, pc + +(* define statement *) +Inv == /\ Image(O) \subseteq history + /\ IsOrdered(O) + + +vars == << history, a, aLength, A, b, bLength, B, O, pc >> + +Init == (* Global variables *) + /\ history = {} + /\ a = 0 + /\ aLength = 0 + /\ A = <<>> + /\ b = 0 + /\ bLength = 0 + /\ B = <<>> + /\ O = <<>> + /\ pc = "init" + +init == /\ pc = "init" + /\ \E r \in SUBSET Nat \ {{}}: + \E s \in ((SUBSET (Nat \ r)) \ {{}}): + \E t \in OrderedSequences(s) \cup {<<>>}: + /\ B' = t + /\ bLength' = Len(B') + /\ \E u \in OrderedSequences(r) \cup {<<>>}: + /\ A' = u + /\ aLength' = Len(A') + /\ pc' = "hstry" + /\ UNCHANGED << history, a, b, O >> + +hstry == /\ pc = "hstry" + /\ history' = (SeqToSet(A) \cup SeqToSet(B)) + /\ IF Cardinality(history') = 0 + THEN /\ pc' = "Done" + ELSE /\ pc' = "mrg1" + /\ UNCHANGED << a, aLength, A, b, bLength, B, O >> + +mrg1 == /\ pc = "mrg1" + /\ IF aLength > 0 + THEN /\ a' = Head(A) + /\ A' = Tail(A) + ELSE /\ TRUE + /\ UNCHANGED << a, A >> + /\ IF bLength > 0 + THEN /\ b' = Head(B) + /\ B' = Tail(B) + ELSE /\ TRUE + /\ UNCHANGED << b, B >> + /\ pc' = "mrg2" + /\ UNCHANGED << history, aLength, bLength, O >> + +mrg2 == /\ pc = "mrg2" + /\ IF aLength > 0 /\ (a < b \/ bLength = 0) + THEN /\ O' = Append(O, a) + /\ aLength' = aLength - 1 + /\ IF aLength' > 0 + THEN /\ a' = Head(A) + /\ A' = Tail(A) + ELSE /\ TRUE + /\ UNCHANGED << a, A >> + ELSE /\ TRUE + /\ UNCHANGED << a, aLength, A, O >> + /\ pc' = "mrg2b" + /\ UNCHANGED << history, b, bLength, B >> + +mrg2b == /\ pc = "mrg2b" + /\ IF bLength > 0 /\ (b < a \/ aLength = 0) + THEN /\ O' = Append(O, b) + /\ bLength' = bLength - 1 + /\ IF bLength' > 0 + THEN /\ b' = Head(B) + /\ B' = Tail(B) + ELSE /\ TRUE + /\ UNCHANGED << b, B >> + ELSE /\ TRUE + /\ UNCHANGED << b, bLength, B, O >> + /\ pc' = "mrg3" + /\ UNCHANGED << history, a, aLength, A >> + +mrg3 == /\ pc = "mrg3" + /\ IF bLength > 0 \/ aLength > 0 + THEN /\ pc' = "mrg2" + ELSE /\ pc' = "mrg4" + /\ UNCHANGED << history, a, aLength, A, b, bLength, B, O >> + +mrg4 == /\ pc = "mrg4" + /\ Assert(bLength = 0 /\ aLength = 0, + "Failure of assertion at line 106, column 8.") + /\ Assert(Len(O) = Cardinality(history), + "Failure of assertion at line 107, column 8.") + /\ Assert(Image(O) = history, + "Failure of assertion at line 108, column 8.") + /\ pc' = "Done" + /\ UNCHANGED << history, a, aLength, A, b, bLength, B, O >> + +Next == init \/ hstry \/ mrg1 \/ mrg2 \/ mrg2b \/ mrg3 \/ mrg4 + \/ (* Disjunct to prevent deadlock on termination *) + (pc = "Done" /\ UNCHANGED vars) + +Spec == /\ Init /\ [][Next]_vars + /\ WF_vars(Next) + +Termination == <>(pc = "Done") + +\* END TRANSLATION + +============================================================================= +\* Modification History +\* Last modified Tue Jun 06 20:39:04 CEST 2017 by markus +\* Created Mon Jun 05 23:14:05 CEST 2017 by markus diff --git a/tlatools/src/tlc2/tool/fp/OpenAddressing.tla b/tlatools/src/tlc2/tool/fp/OpenAddressing.tla new file mode 100644 index 0000000000000000000000000000000000000000..9505db8a30cc7e216caa18fc2e17e737534e4f14 --- /dev/null +++ b/tlatools/src/tlc2/tool/fp/OpenAddressing.tla @@ -0,0 +1,866 @@ +\begin{ppcal} +-------------------------- MODULE OpenAddressing -------------------------- +EXTENDS Sequences, FiniteSets, Integers, TLC + +(***************************************************************************) +(* K: The overall number of fingerprints that fit into the table. *) +(* fps: The set of fingerprints to be inserted into the hash table. *) +(* empty: An empty (model) value. Used to mark an unoccupied table element.*) +(* Writer: The set of processes/threads which insert fingerprints. Reader: *) +(* The set of processes which check fingerprints with contains. L: The *) +(* probing limit. *) +(***************************************************************************) +CONSTANT K, fps, empty, Writer, Reader, L + +(***************************************************************************) +(* K is a positive natural. emtpy is disjunct to all elements in fps. *) +(* fingerprints are natural numbers and can be well-ordered. *) +(***************************************************************************) +ASSUME /\ K \in (Nat \ {0}) + /\ \A fp \in fps: fp \in (Nat \ {0}) + /\ empty \notin fps + /\ (2*L) <= K + +---------------------------------------------------------------------------- + +(***************************************************************************) +(* The element of position Len(seq) of a sequence seq. *) +(***************************************************************************) +last(seq) == seq[Len(seq)] + +(***************************************************************************) +(* The largest element in the sequence, assuming sequence to be sorted in *) +(* ascending order. *) +(***************************************************************************) +largestElem(sortedSeq) == IF sortedSeq = <<>> THEN 0 ELSE last(sortedSeq) + +(***************************************************************************) +(* All elements of seq1 smaller than elem and the largest element in seq2. *) +(***************************************************************************) +subSetSmaller(seq1, seq2, elem) == SelectSeq(seq1, LAMBDA p: + p < elem /\ p > largestElem(seq2)) + +(***************************************************************************) +(* All elements of seq1 larger than the largest element in seq2. *) +(***************************************************************************) +subSetLarger(seq1, seq2) == IF seq2 = <<>> + THEN seq1 + ELSE SelectSeq(seq1, LAMBDA p: + p > largestElem(seq2)) + +(***************************************************************************) +(* TRUE iff the sequence seq contains the element elem. *) +(***************************************************************************) +containsElem(seq, elem) == \E i \in 1..Len(seq): seq[i] = elem + +(***************************************************************************) +(* The minimum and maximum element in set s. *) +(***************************************************************************) +min(S) == CHOOSE s \in S: \A a \in S: s <= a +max(S) == CHOOSE s \in S: \A a \in S: s >= a + +(***************************************************************************) +(* The smaller of the two values. *) +(***************************************************************************) +minimum(a, b) == IF a < b THEN a ELSE b + +(***************************************************************************) +(* The given index i modulo the sequences' length. *) +(***************************************************************************) +mod(i,len) == IF i % len = 0 THEN len ELSE (i % len) + +(***************************************************************************) +(* Logical bit-shifting to the right (shifts in zeros from the left/MSB). *) +(* TLC's standard division does not round towards zero, thus this is *) +(* specified recursively, manually taking care of rounding. *) +(***************************************************************************) +RECURSIVE shiftR(_,_) +shiftR(n,pos) == IF pos = 0 THEN n + ELSE LET odd(z) == z % 2 = 1 + m == IF odd(n) THEN (n-1) \div 2 ELSE n \div 2 + IN shiftR(m, pos - 1) + +(***************************************************************************) +(* Bitshifting (faster for any real implementation). *) +(***************************************************************************) +bitshift(fp, p) == LET k == CHOOSE k \in 1..K: 2^k = K + IN mod(shiftR(fp, k - 1) + 1 + p, K) + +(***************************************************************************) +(* Re-scale. *) +(***************************************************************************) +rescale(k,maxF,minF,fp,p) == LET f == (k - 1) \div (maxF - minF) + IN mod((f * (fp - minF + 1)) + p, k) + +(***************************************************************************) +(* Calculates an fp's index where fp \in fps. p is an alternative address, *) +(* such that: p \in 0..P. Uses bitshifting iff K is power of two. *) +(***************************************************************************) +idx(fp, p) == rescale(K, max(fps), min(fps), fp, p) + +(***************************************************************************) +(* TRUE iff the fingerprint at table position index is equal to fp or its *) +(* corresponding negative fp value (marked as to be copied to secondary). *) +(***************************************************************************) +isMatch(fp, index, table) == \/ table[index] = fp + \/ table[index] = (-1*fp) + +(***************************************************************************) +(* TRUE iff the table at position index is empty. *) +(***************************************************************************) +isEmpty(index, table) == table[index] = empty + +(***************************************************************************) +(* TRUE iff the table at position index is marked evicted. *) +(***************************************************************************) +isMarked(index, table) == table[index] < 0 + +(***************************************************************************) +(* t is sorted iff its empty-filtered sub-sequence is sorted. An empty *) +(* sequence is defined to be sorted. *) +(***************************************************************************) +isSorted(seq) == LET sub == SelectSeq(seq, LAMBDA e: e # empty) + IN IF sub = <<>> THEN TRUE + ELSE \A i \in 1..(Len(sub) - 1): + sub[mod(i, Len(sub))] < sub[mod(i+1, Len(sub))] + +(***************************************************************************) +(* TRUE iff fingerprint fp is either found within table according to *) +(* isMatch or in seq. *) +(***************************************************************************) +contains(f,t,seq,Q) == \/ \E i \in 0..Q: isMatch(f,idx(f,i),t) + \/ \E i \in 1..Len(seq): seq[i] = f + +---------------------------------------------------------------------------- + +(***************************************************************************) +(* A fp wrapped around if its alternate indices are beyond K and its *) +(* actual index is lower than its primary idx. Another mental picture for *) +(* table is a circular list and wrapped means that a fingerprint crossed *) +(* the logically first position of table. *) +(***************************************************************************) +wrapped(fp, pos) == idx(fp, 0) > mod(pos, K) + +(***************************************************************************) +(* TRUE iff the given two fingerprints have to be swapped to satisfy the *) +(* expected order. *) +(***************************************************************************) +compare(fp1,i1,fp2,i2) == IF fp1 # empty /\ fp2 # empty + THEN IF wrapped(fp1, i1) = wrapped(fp2, i2) /\ i1 > i2 + THEN IF fp1 < fp2 THEN -1 ELSE 1 + ELSE IF ~(wrapped(fp1, i1) <=> wrapped(fp2, i2)) + THEN IF i1 < i2 /\ fp1 < fp2 + THEN -1 + ELSE IF i1 > i2 /\ fp1 > fp2 + THEN -1 + ELSE 0 + ELSE 0 + ELSE 0 + +---------------------------------------------------------------------------- + +(*** this is a comment containing the PlusCal code * +--fair algorithm OpenAddressing +(***************************************************************************) +(* table: The actual hash table specified as a TLA+ sequence. history: An *) +(* auxiliary (history) variable unrelated to the actual hash table *) +(* specification. It just records the inserted fingerprints to be verified *) +(* by Inv. An implementation won't need history. secondary: The secondary *) +(* storage where fingerprints are eventually evicted to. outer/inner: *) +(* Index variables local to the sort action. P: The number of times an *) +(* alterante index is to be tried. *) +(***************************************************************************) +{ variable table = [i \in 1..K |-> empty], + secondary = <<>>, + newsecondary = <<>>, + P = 0, \* LongAccumulator in Java + evict = FALSE, \* AtomicBoolean in Java + waitCnt = 0, \* CyclicBarrier in Java + history = {}; + + (************************************************************************) + (* Compare-and-swap is linearizable (appears atomic from the POV of *) + (* observers such as other threads) and disjoint-access-parallel (two *) + (* CAS operations on disjoint memory locations do not synchronize). *) + (* *) + (* Variants of CAS (only basic CAS supported by Java. No HW support *) + (* for DCAS, CASN): *) + (* CAS: Basic compare and swap of a 64bit memory location. *) + (* DWCAS: CAS of two adjacent/contiguous 64bit memory locations. *) + (* (CMPXCHG16) (to swap to adjacent positions in e.g table) *) + (* DCAS: CAS of two arbitrary 64bit memory locations (swap of arbitrary *) + (* locations). *) + (* CASN: CAS N arbitrary 64bit memory locations. *) + (* *) + (* http://liblfds.org/mediawiki/index.php?title=Article:CAS_and_LL/ *) + (* SC_Implementation_Details_by_Processor_family *) + (************************************************************************) + + (* Atomically compare and swap. *) + macro CAS(result, pos, expected, new) { + if (table[pos] = expected) { + table[pos] := new; + result := TRUE + } else { + result := FALSE + } + } + + procedure Evict() + variables ei = 1, ej = 1, lo = 0; { + (* Insertion sort. *) + strIns: while (ei <= K+P) { + lo := table[mod(ei + 1, K)]; + nestedIns: while (compare(lo, mod(ei + 1, K), + table[mod(ej, K)], mod(ej, K)) <= -1) { + table[mod(ej + 1, K)] := table[mod(ej, K)]; + if (ej = 0) { + ej := ej - 1; + goto set; + } else { + ej := ej - 1; + }; + }; + set: table[mod(ej + 1, K)] := lo; + ej := ei + 1; + ei := ei + 1; + }; + ei := 1; + + (* Write to external storage. *) + flush: while (ei <= K+P) { + lo := table[mod(ei, K)]; + if (lo # empty /\ + lo > largestElem(newsecondary) /\ + ((ei <= K /\ ~wrapped(lo,ei)) \/ + (ei > K /\ wrapped(lo,ei)))) { + (* Copy all smaller fps than lo from *) + (* secondary to newsecondary. *) + newsecondary := Append(newsecondary \o + subSetSmaller(secondary, newsecondary, lo), lo); + (* Mark table[mod(cpy,table)] as being *) + (* written to secondary. *) + table[mod(ei, K)] := lo * (-1); + }; + ei := ei + 1; + }; + (* Append remainder of secondary to newsecondary and *) + (* assign newsecondary to secondary. *) + secondary := newsecondary \o + subSetLarger(secondary, newsecondary); + newsecondary := <<>>; + (* Setting P to 0 means a larger portion of cntns queries *) + (* go to secondary. Leaving P unchanged results in a longer *) + (* lookup chain. However neither violates any invariants. *) + (* An implementation is likely to set P to the *) + (* mean/median/some other fancy statistics or treats P as *) + (* as an optimization to speedup insertions prior to the *) + (* first eviction. *) + with (p \in 0..P) { + P := p; + }; + rtrn: return; + } + + process (q \in Reader) + variables rfp = 0, rindex = 0, checked = {}; { + rwait: await history # {}; + rpick: while (TRUE) { + if (checked = history /\ history = fps) { + goto Done; + } else { + with (f \in history) { + rfp := f; + checked := checked \cup {f}; + }; + }; + + rcntns: rindex := 0; + if (evict) { + waitCnt := waitCnt + 1; + rwaitEv: await evict = FALSE; + rendWEv: waitCnt := waitCnt - 1; + goto rcntns + }; + + ronPrm: while (rindex <= P) { + if (isMatch(rfp, idx(rfp, rindex), table)) { + goto rpick + } else { + if (isEmpty(idx(rfp, rindex), table) + \/ isMarked(idx(rfp, rindex), table)) { + goto ronSnc; + } else { + rindex := rindex + 1 + } + } + }; + ronSnc: if (containsElem(secondary,rfp)) { + goto rpick + } else { + (* Since we picked a fp from history, it always *) + (* either has to be in table or secondary. *) + assert(FALSE); + }; + } + } + + (* A weak fair process. *) + fair process (p \in Writer) + variables fp = 0, index = 0, result = FALSE, expected = -1; { + pick: while (TRUE) { + (* No deadlock once all fingerprints have been inserted. *) + if ((fps \ history) = {}) { + goto Done; + } else { + (* Select some fp to be inserted *) + with (f \in (fps \ history)) { fp := f; }; + }; + + put: index := 0; + result := FALSE; + (* Set expected to infinity. expected is reused when *) + (* the algorithm runs a primary lookup and finds a *) + (* position which is either EMPTY or isMarked(...). *) + (* expected stores the (open) position for later use *) + (* where the fp is inserted. Maximally, a position *) + (* can be K + L, thus expected is set to K + L + 1; *) + (* as an approximation of infinity. *) + expected := K + L + 1; + (* Wait for eviction thread to do its work. *) + if (evict) { + waitCnt := waitCnt + 1; + waitEv: await evict = FALSE; + endWEv: waitCnt := waitCnt - 1; + goto put + }; + + (* Check secondary unless empty. First though, we do *) + (* a primary lookup in case the fp in question has not *) + (* been evicted to secondary yet. *) + chkSnc: if (secondary # <<>>) { + (* Primary lookup. *) + cntns: while (index < P) { + if (isMatch(fp, idx(fp, index), table)) { + goto pick + } else { + if (isEmpty(idx(fp, index), table)) { + (* Found an EMPTY position which proves *) + (* that fp cannot be found at higher *) + (* positions. Thus, no need to continue.*) + expected := minimum(expected, index); + goto onSnc; + } else { + if (isMarked(idx(fp, index), table)) { + (* None of the lower positions has *) + (* fp, thus keep the lowest position *) + (* for the second loop as the start *) + (* index. No point in checking known *) + (* lower positions in the loop again. *) + expected := minimum(expected, index); + index := index + 1; + } else { + index := index + 1 + } + } + } + }; + + + (* Secondary lookup. *) + onSnc: if (containsElem(secondary,fp)) { + goto pick + } else { + (* Have next loop start at expected determined *) + (* by previous loop. *) + index := expected; + (* Re-init expected to be used for its alternate purpose. *) + expected := -1; + }; + }; + + (* Put inserts the given fp into the hash table by sequentially *) + (* trying the primary to the P's alternate position. *) + insrt: while (index < L) { + expected := table[idx(fp,index)]; + if (expected = empty \/ + (expected < 0 /\ expected # (-1) * fp)) { + incP: if (index > P) {P := index}; + cas: CAS(result, idx(fp,index), expected, fp); + if (result) { + history := history \cup {fp}; + goto pick + } else { + (* Has been occupied in the meantime, *) + (* try to find another position. *) + goto insrt + } + }; + + (* Has fp been inserted by another process? Check isMatch *) + (* AFTER empty and on-secondary because of two reasons: *) + (* a) Thread A finds table[pos] to be empty but fails *) + (* to CAS fpX. Thread B concurrently also finds *) + (* table[pos] to be empty and succeeds to CAS fpX. *) + (* b) Iff table[pos] is empty or -1, higher positions *) + (* cannot be a match. *) + isMth: if (isMatch(fp,idx(fp,index),table)) { + goto pick + } else { + index := index + 1; + }; + }; \* end of while/insrt + + + (* We failed to insert fp into a full table, thus try *) + (* to become the thread that evicts to secondary. *) + tryEv: if (evict = FALSE) { + evict := TRUE; + (* Wait for all other insertion threads and *) + (* the one reader to park. *) + waitIns: await waitCnt = Cardinality(Writer) - 1 + Cardinality(Reader); + call Evict(); + endEv: evict := FALSE; + goto put; + } else { + goto put + } + } + } \* end while/pick + } +} +*** this ends the comment containg the pluscal code **********) +---------------------------------------------------------------------------- +\* BEGIN TRANSLATION +VARIABLES table, secondary, newsecondary, P, evict, waitCnt, history, pc, + stack, ei, ej, lo, rfp, rindex, checked, fp, index, result, + expected + +vars == << table, secondary, newsecondary, P, evict, waitCnt, history, pc, + stack, ei, ej, lo, rfp, rindex, checked, fp, index, result, + expected >> + +ProcSet == (Reader) \cup (Writer) + +Init == (* Global variables *) + /\ table = [i \in 1..K |-> empty] + /\ secondary = <<>> + /\ newsecondary = <<>> + /\ P = 0 + /\ evict = FALSE + /\ waitCnt = 0 + /\ history = {} + (* Procedure Evict *) + /\ ei = [ self \in ProcSet |-> 1] + /\ ej = [ self \in ProcSet |-> 1] + /\ lo = [ self \in ProcSet |-> 0] + (* Process q *) + /\ rfp = [self \in Reader |-> 0] + /\ rindex = [self \in Reader |-> 0] + /\ checked = [self \in Reader |-> {}] + (* Process p *) + /\ fp = [self \in Writer |-> 0] + /\ index = [self \in Writer |-> 0] + /\ result = [self \in Writer |-> FALSE] + /\ expected = [self \in Writer |-> -1] + /\ stack = [self \in ProcSet |-> << >>] + /\ pc = [self \in ProcSet |-> CASE self \in Reader -> "rwait" + [] self \in Writer -> "pick"] + +strEv(self) == /\ pc[self] = "strEv" + /\ IF ei[self] <= K+P + THEN /\ lo' = [lo EXCEPT ![self] = table[mod(ei[self] + 1, K)]] + /\ pc' = [pc EXCEPT ![self] = "fdsaf"] + /\ ei' = ei + ELSE /\ ei' = [ei EXCEPT ![self] = 1] + /\ pc' = [pc EXCEPT ![self] = "flush"] + /\ lo' = lo + /\ UNCHANGED << table, secondary, newsecondary, P, evict, + waitCnt, history, stack, ej, rfp, rindex, + checked, fp, index, result, expected >> + +fdsaf(self) == /\ pc[self] = "fdsaf" + /\ IF compare(lo[self], mod(ei[self] + 1, K), table[mod(ej[self], K)], mod(ej[self], K)) <= -1 + THEN /\ table' = [table EXCEPT ![mod(ej[self] + 1, K)] = table[mod(ej[self], K)]] + /\ IF ej[self] = 0 + THEN /\ ej' = [ej EXCEPT ![self] = ej[self] - 1] + /\ pc' = [pc EXCEPT ![self] = "set"] + ELSE /\ ej' = [ej EXCEPT ![self] = ej[self] - 1] + /\ pc' = [pc EXCEPT ![self] = "fdsaf"] + ELSE /\ pc' = [pc EXCEPT ![self] = "set"] + /\ UNCHANGED << table, ej >> + /\ UNCHANGED << secondary, newsecondary, P, evict, waitCnt, + history, stack, ei, lo, rfp, rindex, checked, + fp, index, result, expected >> + +set(self) == /\ pc[self] = "set" + /\ table' = [table EXCEPT ![mod(ej[self] + 1, K)] = lo[self]] + /\ ej' = [ej EXCEPT ![self] = ei[self] + 1] + /\ ei' = [ei EXCEPT ![self] = ei[self] + 1] + /\ pc' = [pc EXCEPT ![self] = "strEv"] + /\ UNCHANGED << secondary, newsecondary, P, evict, waitCnt, + history, stack, lo, rfp, rindex, checked, fp, + index, result, expected >> + +flush(self) == /\ pc[self] = "flush" + /\ IF ei[self] <= K+P + THEN /\ lo' = [lo EXCEPT ![self] = table[mod(ei[self], K)]] + /\ IF lo'[self] # empty /\ + lo'[self] > largestElem(newsecondary) /\ + ((ei[self] <= K /\ ~wrapped(lo'[self],ei[self])) \/ + (ei[self] > K /\ wrapped(lo'[self],ei[self]))) + THEN /\ newsecondary' = Append(newsecondary \o subSetSmaller(secondary, newsecondary, lo'[self]), lo'[self]) + /\ table' = [table EXCEPT ![mod(ei[self], K)] = lo'[self] * (-1)] + ELSE /\ TRUE + /\ UNCHANGED << table, newsecondary >> + /\ ei' = [ei EXCEPT ![self] = ei[self] + 1] + /\ pc' = [pc EXCEPT ![self] = "flush"] + /\ UNCHANGED << secondary, P >> + ELSE /\ secondary' = newsecondary \o subSetLarger(secondary, newsecondary) + /\ newsecondary' = <<>> + /\ P' = 0 + /\ pc' = [pc EXCEPT ![self] = "rtrn"] + /\ UNCHANGED << table, ei, lo >> + /\ UNCHANGED << evict, waitCnt, history, stack, ej, rfp, rindex, + checked, fp, index, result, expected >> + +rtrn(self) == /\ pc[self] = "rtrn" + /\ pc' = [pc EXCEPT ![self] = Head(stack[self]).pc] + /\ ei' = [ei EXCEPT ![self] = Head(stack[self]).ei] + /\ ej' = [ej EXCEPT ![self] = Head(stack[self]).ej] + /\ lo' = [lo EXCEPT ![self] = Head(stack[self]).lo] + /\ stack' = [stack EXCEPT ![self] = Tail(stack[self])] + /\ UNCHANGED << table, secondary, newsecondary, P, evict, + waitCnt, history, rfp, rindex, checked, fp, + index, result, expected >> + +Evict(self) == strEv(self) \/ fdsaf(self) \/ set(self) \/ flush(self) + \/ rtrn(self) + +rwait(self) == /\ pc[self] = "rwait" + /\ history # {} + /\ pc' = [pc EXCEPT ![self] = "rpick"] + /\ UNCHANGED << table, secondary, newsecondary, P, evict, + waitCnt, history, stack, ei, ej, lo, rfp, + rindex, checked, fp, index, result, expected >> + +rpick(self) == /\ pc[self] = "rpick" + /\ IF checked[self] = history /\ history = fps + THEN /\ pc' = [pc EXCEPT ![self] = "Done"] + /\ UNCHANGED << rfp, checked >> + ELSE /\ \E f \in history: + /\ rfp' = [rfp EXCEPT ![self] = f] + /\ checked' = [checked EXCEPT ![self] = checked[self] \cup {f}] + /\ pc' = [pc EXCEPT ![self] = "rcntns"] + /\ UNCHANGED << table, secondary, newsecondary, P, evict, + waitCnt, history, stack, ei, ej, lo, rindex, fp, + index, result, expected >> + +rcntns(self) == /\ pc[self] = "rcntns" + /\ rindex' = [rindex EXCEPT ![self] = 0] + /\ IF evict + THEN /\ waitCnt' = waitCnt + 1 + /\ pc' = [pc EXCEPT ![self] = "rwaitEv"] + ELSE /\ pc' = [pc EXCEPT ![self] = "ronPrm"] + /\ UNCHANGED waitCnt + /\ UNCHANGED << table, secondary, newsecondary, P, evict, + history, stack, ei, ej, lo, rfp, checked, fp, + index, result, expected >> + +rwaitEv(self) == /\ pc[self] = "rwaitEv" + /\ evict = FALSE + /\ pc' = [pc EXCEPT ![self] = "rendWEv"] + /\ UNCHANGED << table, secondary, newsecondary, P, evict, + waitCnt, history, stack, ei, ej, lo, rfp, + rindex, checked, fp, index, result, expected >> + +rendWEv(self) == /\ pc[self] = "rendWEv" + /\ waitCnt' = waitCnt - 1 + /\ pc' = [pc EXCEPT ![self] = "rcntns"] + /\ UNCHANGED << table, secondary, newsecondary, P, evict, + history, stack, ei, ej, lo, rfp, rindex, + checked, fp, index, result, expected >> + +ronPrm(self) == /\ pc[self] = "ronPrm" + /\ IF rindex[self] <= P + THEN /\ IF isMatch(rfp[self], idx(rfp[self], rindex[self]), table) + THEN /\ pc' = [pc EXCEPT ![self] = "rpick"] + /\ UNCHANGED rindex + ELSE /\ IF isEmpty(idx(rfp[self], rindex[self]), table) + \/ isMarked(idx(rfp[self], rindex[self]), table) + THEN /\ pc' = [pc EXCEPT ![self] = "ronSnc"] + /\ UNCHANGED rindex + ELSE /\ rindex' = [rindex EXCEPT ![self] = rindex[self] + 1] + /\ pc' = [pc EXCEPT ![self] = "ronPrm"] + ELSE /\ pc' = [pc EXCEPT ![self] = "ronSnc"] + /\ UNCHANGED rindex + /\ UNCHANGED << table, secondary, newsecondary, P, evict, + waitCnt, history, stack, ei, ej, lo, rfp, + checked, fp, index, result, expected >> + +ronSnc(self) == /\ pc[self] = "ronSnc" + /\ IF containsElem(secondary,rfp[self]) + THEN /\ pc' = [pc EXCEPT ![self] = "rpick"] + ELSE /\ Assert((FALSE), + "Failure of assertion at line 291, column 23.") + /\ pc' = [pc EXCEPT ![self] = "rpick"] + /\ UNCHANGED << table, secondary, newsecondary, P, evict, + waitCnt, history, stack, ei, ej, lo, rfp, + rindex, checked, fp, index, result, expected >> + +q(self) == rwait(self) \/ rpick(self) \/ rcntns(self) \/ rwaitEv(self) + \/ rendWEv(self) \/ ronPrm(self) \/ ronSnc(self) + +pick(self) == /\ pc[self] = "pick" + /\ IF (fps \ history) = {} + THEN /\ pc' = [pc EXCEPT ![self] = "Done"] + /\ fp' = fp + ELSE /\ \E f \in (fps \ history): + fp' = [fp EXCEPT ![self] = f] + /\ pc' = [pc EXCEPT ![self] = "put"] + /\ UNCHANGED << table, secondary, newsecondary, P, evict, + waitCnt, history, stack, ei, ej, lo, rfp, rindex, + checked, index, result, expected >> + +put(self) == /\ pc[self] = "put" + /\ index' = [index EXCEPT ![self] = 0] + /\ result' = [result EXCEPT ![self] = FALSE] + /\ expected' = [expected EXCEPT ![self] = -1] + /\ IF evict + THEN /\ waitCnt' = waitCnt + 1 + /\ pc' = [pc EXCEPT ![self] = "waitEv"] + ELSE /\ pc' = [pc EXCEPT ![self] = "chkSnc"] + /\ UNCHANGED waitCnt + /\ UNCHANGED << table, secondary, newsecondary, P, evict, history, + stack, ei, ej, lo, rfp, rindex, checked, fp >> + +waitEv(self) == /\ pc[self] = "waitEv" + /\ evict = FALSE + /\ pc' = [pc EXCEPT ![self] = "endWEv"] + /\ UNCHANGED << table, secondary, newsecondary, P, evict, + waitCnt, history, stack, ei, ej, lo, rfp, + rindex, checked, fp, index, result, expected >> + +endWEv(self) == /\ pc[self] = "endWEv" + /\ waitCnt' = waitCnt - 1 + /\ pc' = [pc EXCEPT ![self] = "put"] + /\ UNCHANGED << table, secondary, newsecondary, P, evict, + history, stack, ei, ej, lo, rfp, rindex, + checked, fp, index, result, expected >> + +chkSnc(self) == /\ pc[self] = "chkSnc" + /\ IF secondary # <<>> + THEN /\ pc' = [pc EXCEPT ![self] = "cntns"] + ELSE /\ pc' = [pc EXCEPT ![self] = "insrt"] + /\ UNCHANGED << table, secondary, newsecondary, P, evict, + waitCnt, history, stack, ei, ej, lo, rfp, + rindex, checked, fp, index, result, expected >> + +cntns(self) == /\ pc[self] = "cntns" + /\ IF index[self] < P + THEN /\ IF isMatch(fp[self], idx(fp[self], index[self]), table) + THEN /\ pc' = [pc EXCEPT ![self] = "pick"] + /\ index' = index + ELSE /\ IF isEmpty(idx(fp[self], index[self]), table) + \/ isMarked(idx(fp[self], index[self]), table) + THEN /\ pc' = [pc EXCEPT ![self] = "onSnc"] + /\ index' = index + ELSE /\ index' = [index EXCEPT ![self] = index[self] + 1] + /\ pc' = [pc EXCEPT ![self] = "cntns"] + ELSE /\ pc' = [pc EXCEPT ![self] = "onSnc"] + /\ index' = index + /\ UNCHANGED << table, secondary, newsecondary, P, evict, + waitCnt, history, stack, ei, ej, lo, rfp, + rindex, checked, fp, result, expected >> + +onSnc(self) == /\ pc[self] = "onSnc" + /\ IF containsElem(secondary,fp[self]) + THEN /\ pc' = [pc EXCEPT ![self] = "pick"] + /\ index' = index + ELSE /\ index' = [index EXCEPT ![self] = 0] + /\ pc' = [pc EXCEPT ![self] = "insrt"] + /\ UNCHANGED << table, secondary, newsecondary, P, evict, + waitCnt, history, stack, ei, ej, lo, rfp, + rindex, checked, fp, result, expected >> + +insrt(self) == /\ pc[self] = "insrt" + /\ IF index[self] < L + THEN /\ expected' = [expected EXCEPT ![self] = table[idx(fp[self],index[self])]] + /\ IF expected'[self] = empty \/ (expected'[self] < 0 /\ expected'[self] # (-1) * fp[self]) + THEN /\ pc' = [pc EXCEPT ![self] = "incP"] + ELSE /\ pc' = [pc EXCEPT ![self] = "isMth"] + ELSE /\ pc' = [pc EXCEPT ![self] = "tryEv"] + /\ UNCHANGED expected + /\ UNCHANGED << table, secondary, newsecondary, P, evict, + waitCnt, history, stack, ei, ej, lo, rfp, + rindex, checked, fp, index, result >> + +isMth(self) == /\ pc[self] = "isMth" + /\ IF isMatch(fp[self],idx(fp[self],index[self]),table) + THEN /\ pc' = [pc EXCEPT ![self] = "pick"] + /\ index' = index + ELSE /\ index' = [index EXCEPT ![self] = index[self] + 1] + /\ pc' = [pc EXCEPT ![self] = "insrt"] + /\ UNCHANGED << table, secondary, newsecondary, P, evict, + waitCnt, history, stack, ei, ej, lo, rfp, + rindex, checked, fp, result, expected >> + +incP(self) == /\ pc[self] = "incP" + /\ IF index[self] > P + THEN /\ P' = index[self] + ELSE /\ TRUE + /\ P' = P + /\ pc' = [pc EXCEPT ![self] = "cas"] + /\ UNCHANGED << table, secondary, newsecondary, evict, waitCnt, + history, stack, ei, ej, lo, rfp, rindex, checked, + fp, index, result, expected >> + +cas(self) == /\ pc[self] = "cas" + /\ IF table[(idx(fp[self],index[self]))] = expected[self] + THEN /\ table' = [table EXCEPT ![(idx(fp[self],index[self]))] = fp[self]] + /\ result' = [result EXCEPT ![self] = TRUE] + ELSE /\ result' = [result EXCEPT ![self] = FALSE] + /\ table' = table + /\ IF result'[self] + THEN /\ history' = (history \cup {fp[self]}) + /\ pc' = [pc EXCEPT ![self] = "pick"] + ELSE /\ pc' = [pc EXCEPT ![self] = "insrt"] + /\ UNCHANGED history + /\ UNCHANGED << secondary, newsecondary, P, evict, waitCnt, stack, + ei, ej, lo, rfp, rindex, checked, fp, index, + expected >> + +tryEv(self) == /\ pc[self] = "tryEv" + /\ IF evict = FALSE + THEN /\ evict' = TRUE + /\ pc' = [pc EXCEPT ![self] = "waitIns"] + ELSE /\ pc' = [pc EXCEPT ![self] = "put"] + /\ evict' = evict + /\ UNCHANGED << table, secondary, newsecondary, P, waitCnt, + history, stack, ei, ej, lo, rfp, rindex, + checked, fp, index, result, expected >> + +waitIns(self) == /\ pc[self] = "waitIns" + /\ waitCnt = Cardinality(Writer) - 1 + Cardinality(Reader) + /\ stack' = [stack EXCEPT ![self] = << [ procedure |-> "Evict", + pc |-> "endEv", + ei |-> ei[self], + ej |-> ej[self], + lo |-> lo[self] ] >> + \o stack[self]] + /\ ei' = [ei EXCEPT ![self] = 1] + /\ ej' = [ej EXCEPT ![self] = 1] + /\ lo' = [lo EXCEPT ![self] = 0] + /\ pc' = [pc EXCEPT ![self] = "strEv"] + /\ UNCHANGED << table, secondary, newsecondary, P, evict, + waitCnt, history, rfp, rindex, checked, fp, + index, result, expected >> + +endEv(self) == /\ pc[self] = "endEv" + /\ evict' = FALSE + /\ pc' = [pc EXCEPT ![self] = "put"] + /\ UNCHANGED << table, secondary, newsecondary, P, waitCnt, + history, stack, ei, ej, lo, rfp, rindex, + checked, fp, index, result, expected >> + +p(self) == pick(self) \/ put(self) \/ waitEv(self) \/ endWEv(self) + \/ chkSnc(self) \/ cntns(self) \/ onSnc(self) \/ insrt(self) + \/ isMth(self) \/ incP(self) \/ cas(self) \/ tryEv(self) + \/ waitIns(self) \/ endEv(self) + +Next == (\E self \in ProcSet: Evict(self)) + \/ (\E self \in Reader: q(self)) + \/ (\E self \in Writer: p(self)) + \/ (* Disjunct to prevent deadlock on termination *) + ((\A self \in ProcSet: pc[self] = "Done") /\ UNCHANGED vars) + +Spec == /\ Init /\ [][Next]_vars + /\ WF_vars(Next) + /\ \A self \in Writer : WF_vars(p(self)) /\ WF_vars(Evict(self)) + +Termination == <>(\A self \in ProcSet: pc[self] = "Done") + +\* END TRANSLATION + +(***************************************************************************) +(* A type correctness invariant for outer, inner and P. *) +(***************************************************************************) +TypeOK == /\ P \in Nat + /\ P <= L + +(***************************************************************************) +(* All fingerprint in history are (always) hash table members, *) +(* all fps \ history never are. *) +(***************************************************************************) +Inv == evict = FALSE => /\ \A seen \in history: + contains(seen,table,secondary,P+1) + /\ \A unseen \in (fps \ history): + ~contains(unseen,table,secondary,P+1) + +(***************************************************************************) +(* FALSE iff table contains duplicate elements (excluding empty). *) +(***************************************************************************) +Duplicates == evict = FALSE => LET sub == SelectSeq(table, LAMBDA e: e # empty) + IN IF Len(sub) < 2 THEN TRUE + ELSE \A i \in 1..(Len(sub) - 1): + /\ sub[i] # sub[i+1] + /\ sub[i] # (-1) * sub[i+1] + +(***************************************************************************) +(* Secondary storage is always sorted in ascending order. *) +(***************************************************************************) +Sorted == isSorted(secondary) /\ isSorted(newsecondary) + +(***************************************************************************) +(* TRUE iff f is found in table within idx(f,0)..id(f,r). *) +(***************************************************************************) +containedInTable(f) == \E r \in 0..P+1: IF f < 0 + THEN table[idx((-1)*f, r)] = f + ELSE table[idx(f, r)] = f + +(***************************************************************************) +(* TRUE iff f is found in secondary. *) +(***************************************************************************) +containedInSecondary(f) == containsElem(secondary,f) + +(***************************************************************************) +(* TRUE iff f is found in newsecondary. *) +(***************************************************************************) +containedInNewSecondary(f) == containsElem(newsecondary,f) + +(***************************************************************************) +(* TRUE iff fingerprints correctly transition from table to newsecondary *) +(* to secondary. *) +(***************************************************************************) +Consistent == evict = FALSE => \A seen \in history: + /\ (containedInTable(seen) => ~containedInSecondary(seen)) + /\ (containedInTable(seen) => ~containedInNewSecondary(seen)) + /\ (~containedInTable(seen) => (containedInSecondary(seen)) \/ + containedInNewSecondary(seen)) + /\ (containedInTable(seen * (-1)) => (containedInSecondary(seen)) \/ + containedInNewSecondary(seen)) +\* Universal quantification and leads-to not supported by TLC +\* /\ (containedInTable((-1)*seen) ~> containedInSecondary(seen)) + +(***************************************************************************) +(* TRUE iff the maximum displacement of all elements (without empty) is *) +(* equal to P. In other words, P is always minimal. *) +(***************************************************************************) +Minimal == \A i \in 1..Len(table): IF table[i] = empty + THEN TRUE + ELSE LET f == IF table[i] < 0 + THEN -1 * table[i] + ELSE table[i] + IN (i - idx(f, 0)) <= P + +(***************************************************************************) +(* P has to reach the limit L in some behaviors. *) +(***************************************************************************) +Maxed == <>(P = L) + +(***************************************************************************) +(* Under all behaviors, the algorithm makes progress. *) +(***************************************************************************) +Prop == <>[](history = fps) +============================================================================= +\end{ppcal} diff --git a/tlatools/src/tlc2/tool/fp/dfid/FPIntSet.java b/tlatools/src/tlc2/tool/fp/dfid/FPIntSet.java index d7e2663497f429db2332d720d3c27b4f988382c3..3a4edd2305a0b0919ec1804656d758c07281081d 100644 --- a/tlatools/src/tlc2/tool/fp/dfid/FPIntSet.java +++ b/tlatools/src/tlc2/tool/fp/dfid/FPIntSet.java @@ -115,7 +115,7 @@ public abstract class FPIntSet public abstract void exit(boolean cleanup) throws IOException; - public abstract double checkFPs() throws IOException; + public abstract long checkFPs() throws IOException; public abstract void beginChkpt() throws IOException; public abstract void commitChkpt() throws IOException; diff --git a/tlatools/src/tlc2/tool/fp/dfid/MemFPIntSet.java b/tlatools/src/tlc2/tool/fp/dfid/MemFPIntSet.java index 20e07455f5d212827f5bb80756e308084e53c2d2..b54df811813cb3e6742274df171f502833c1a3e3 100644 --- a/tlatools/src/tlc2/tool/fp/dfid/MemFPIntSet.java +++ b/tlatools/src/tlc2/tool/fp/dfid/MemFPIntSet.java @@ -262,7 +262,7 @@ public class MemFPIntSet extends FPIntSet { System.exit(0); } - public final double checkFPs() { + public final long checkFPs() { long dis = Long.MAX_VALUE; for (int i = 0; i < this.table.length; i++) { int[] bucket = this.table[i]; @@ -289,7 +289,7 @@ public class MemFPIntSet extends FPIntSet { } } } - return (1.0/dis); + return dis; } // Checkpoint. diff --git a/tlatools/src/tlc2/tool/fp/dfid/MultiFPIntSet.java b/tlatools/src/tlc2/tool/fp/dfid/MultiFPIntSet.java index 32c5dfc47eeb3b0838d39b1949dd91dff58d16e0..533c3d545fd67557f8a6557dac238211d9ef295f 100644 --- a/tlatools/src/tlc2/tool/fp/dfid/MultiFPIntSet.java +++ b/tlatools/src/tlc2/tool/fp/dfid/MultiFPIntSet.java @@ -73,8 +73,8 @@ public class MultiFPIntSet extends FPIntSet { } /* This is not quite correct. */ - public final double checkFPs() throws IOException { - double res = Double.NEGATIVE_INFINITY; + public final long checkFPs() throws IOException { + long res = Long.MIN_VALUE; for (int i = 0; i < this.sets.length; i++) { res = Math.max(res, this.sets[i].checkFPs()); } diff --git a/tlatools/src/tlc2/tool/fp/management/DiskFPSetMXBean.java b/tlatools/src/tlc2/tool/fp/management/DiskFPSetMXBean.java index 6ca77dfc1cfce40e641db52692cd7bb50893ccd9..573c2674c74a782205844bf14f8e3ea132e68540 100644 --- a/tlatools/src/tlc2/tool/fp/management/DiskFPSetMXBean.java +++ b/tlatools/src/tlc2/tool/fp/management/DiskFPSetMXBean.java @@ -8,6 +8,16 @@ import tlc2.tool.fp.FPSet; public interface DiskFPSetMXBean { + /** + * @return The version of TLC. + */ + String getVersion(); + + /** + * @return The code revision corresponding to this version of TLC. + */ + String getRevision(); + /** * @see DiskFPSet#getDiskHitCnt() */ @@ -90,19 +100,14 @@ public interface DiskFPSetMXBean { long getFlushTime(); /** - * @see DiskFPSet#getReaderWriterCnt() + * @see DiskFPSet#getLockCnt() */ - int getReaderWriterCnt(); - - /** - * @see DiskFPSet#getCollisionBucketCnt() - */ - long getCollisionBucketCnt(); + int getLockCnt(); /** - * @see DiskFPSet#getCollisionRatio() + * @see DiskFPSet#getReaderWriterCnt() */ - double getCollisionRatio(); + int getReaderWriterCnt(); /** * @see DiskFPSet#getLoadFactor() diff --git a/tlatools/src/tlc2/tool/fp/management/DiskFPSetMXWrapper.java b/tlatools/src/tlc2/tool/fp/management/DiskFPSetMXWrapper.java index 74d097f8b843f17229672f6ef8cd2bf250457dcc..6da1f5e7c868ba587576bdb0a5df74c19690d217 100644 --- a/tlatools/src/tlc2/tool/fp/management/DiskFPSetMXWrapper.java +++ b/tlatools/src/tlc2/tool/fp/management/DiskFPSetMXWrapper.java @@ -21,8 +21,11 @@ public class DiskFPSetMXWrapper extends TLCStandardMBean implements DiskFPSetMXB super(DiskFPSetMXBean.class); fpset = diskFPSet; + // Append ",name=COUNT" suffix to objectname to expose all DiskFPSet instances + // as children of type DiskFPSet. However, jfr2jmx does not support it, nor does + // jmx2munin used by cloud based distributed TLC. objectName = "DiskFPSet" + COUNT++; - registerMBean("tlc2.tool.fp:type=" + objectName); + registerMBean("tlc2.tool.fp:type=" + objectName/* + ",name=" + COUNT++*/); } public String getObjectName() { @@ -164,20 +167,6 @@ public class DiskFPSetMXWrapper extends TLCStandardMBean implements DiskFPSetMXB return fpset.getReaderWriterCnt(); } - /* (non-Javadoc) - * @see tlc2.tool.fp.management.DiskFPSetMXBean#getCollisionBucketCnt() - */ - public long getCollisionBucketCnt() { - return fpset.getCollisionBucketCnt(); - } - - /* (non-Javadoc) - * @see tlc2.tool.fp.management.DiskFPSetMXBean#getCollisionRatio() - */ - public double getCollisionRatio() { - return fpset.getCollisionRatio(); - } - /* (non-Javadoc) * @see tlc2.tool.fp.management.DiskFPSetMXBean#getLoadFactor() */ @@ -198,4 +187,11 @@ public class DiskFPSetMXWrapper extends TLCStandardMBean implements DiskFPSetMXB public boolean checkInvariant() throws IOException { return fpset.checkInvariant(); } + + /* (non-Javadoc) + * @see tlc2.tool.fp.management.DiskFPSetMXBean#getLockCnt() + */ + public int getLockCnt() { + return fpset.getLockCnt(); + } } diff --git a/tlatools/src/tlc2/tool/ActionItemList.java b/tlatools/src/tlc2/tool/impl/ActionItemList.java similarity index 58% rename from tlatools/src/tlc2/tool/ActionItemList.java rename to tlatools/src/tlc2/tool/impl/ActionItemList.java index 2a1cfe8f53ecca727ea14ca73deac08e4b39ce78..f4a9df648c812e95d054cdb8ada0f7f6234835cd 100644 --- a/tlatools/src/tlc2/tool/ActionItemList.java +++ b/tlatools/src/tlc2/tool/impl/ActionItemList.java @@ -1,48 +1,61 @@ // Copyright (c) 2003 Compaq Corporation. All rights reserved. // Portions Copyright (c) 2003 Microsoft Corporation. All rights reserved. -package tlc2.tool; +package tlc2.tool.impl; import tla2sany.semantic.SemanticNode; +import tlc2.TLCGlobals; +import tlc2.tool.Action; +import tlc2.tool.IActionItemList; +import tlc2.tool.coverage.CostModel; import tlc2.util.Context; -public class ActionItemList { - /** +class ActionItemList implements IActionItemList { + private static final boolean coverage = TLCGlobals.isCoverageEnabled(); + /** * We assume that this.pred is null iff the list is empty. - * The meaning of this.kind is given as follows: - * kind > 0: pred of a conjunction - * kind = -1: pred - * kind = -2: UNCHANGED pred - * kind = -3: pred' # pred */ public final SemanticNode pred; // Expression of the action public final Context con; // Context of the action - public final int kind; + private final int kind; public final ActionItemList next; + public final CostModel cm; public final static ActionItemList - Empty = new ActionItemList(null, null, 0, null); + Empty = new ActionItemList(null, null, 0, null, null); /* Constructors */ private ActionItemList(SemanticNode pred, Context con, - int kind, ActionItemList next) { + int kind, ActionItemList next, CostModel cm) { this.pred = pred; this.con = con; this.kind = kind; this.next = next; + this.cm = cm; } public final SemanticNode carPred() { return this.pred; } public final Context carContext() { return this.con; } + /** + * The meaning of this.kind is given as follows: + * kind > 0: pred of a conjunction + * kind = -1: pred + * kind = -2: UNCHANGED pred + * kind = -3: pred' # pred + */ public final int carKind() { return this.kind; } public final ActionItemList cdr() { return this.next; } - public final ActionItemList cons(SemanticNode pred, - Context con, int kind) { - return new ActionItemList(pred, con, kind, this); + public final IActionItemList cons(SemanticNode pred, + Context con, CostModel cm, int kind) { + return new ActionItemList(pred, con, kind, this, coverage ? cm.get(pred) : cm); + } + + public ActionItemList cons(final Action act, final int kind) { + return new ActionItemList(act.pred, act.con, kind, this, coverage ? act.cm.get(pred) : act.cm); } public final boolean isEmpty() { return this == Empty; } diff --git a/tlatools/src/tlc2/tool/impl/CallStackTool.java b/tlatools/src/tlc2/tool/impl/CallStackTool.java new file mode 100644 index 0000000000000000000000000000000000000000..14ad1102dec57d93a2e857617cddab8cf638ec0f --- /dev/null +++ b/tlatools/src/tlc2/tool/impl/CallStackTool.java @@ -0,0 +1,242 @@ +/******************************************************************************* + * Copyright (c) 2019 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.impl; + +import tla2sany.semantic.OpApplNode; +import tla2sany.semantic.SemanticNode; +import tlc2.tool.Action; +import tlc2.tool.CallStack; +import tlc2.tool.EvalException; +import tlc2.tool.IActionItemList; +import tlc2.tool.INextStateFunctor; +import tlc2.tool.IStateFunctor; +import tlc2.tool.ITool; +import tlc2.tool.TLCState; +import tlc2.tool.coverage.CostModel; +import tlc2.util.Context; +import tlc2.value.impl.FcnLambdaValue; +import tlc2.value.impl.OpLambdaValue; +import tlc2.value.impl.SetPredValue; +import tlc2.value.impl.Value; +import util.Assert.TLCRuntimeException; + +public final class CallStackTool extends Tool { + + private final CallStack callStack = new CallStack(); + + public CallStackTool(ITool other) { + super((Tool) other); + } + + @Override + public final String toString() { + return this.callStack.toString(); + } + + @Override + protected final void getInitStates(final SemanticNode init, final ActionItemList acts, final Context c, + final TLCState ps, final IStateFunctor states, final CostModel cm) { + this.callStack.push(init); + try { + super.getInitStates(init, acts, c, ps, states, cm); + } catch (TLCRuntimeException | EvalException e) { + // Freeze the callStack to ignore subsequent pop operations. This is + // necessary to ignore the callStack#pop calls in the finally blocks when the + // Java call stack gets unwounded. + this.callStack.freeze(); + throw e; + } finally { + this.callStack.pop(); + } + } + + protected void getInitStatesAppl(final OpApplNode init, final ActionItemList acts, final Context c, + final TLCState ps, final IStateFunctor states, final CostModel cm) { + this.callStack.push(init); + try { + super.getInitStatesAppl(init, acts, c, ps, states, cm); + } catch (TLCRuntimeException | EvalException e) { + // see tlc2.tool.Tool.getInitStates(SemanticNode, ActionItemList, Context, + // TLCState, IStateFunctor) + this.callStack.freeze(); + throw e; + } finally { + this.callStack.pop(); + } + } + + @Override + protected final TLCState getNextStates(final Action action, final SemanticNode pred, final ActionItemList acts, + final Context c, final TLCState s0, final TLCState s1, final INextStateFunctor nss, final CostModel cm) { + this.callStack.push(pred); + try { + return getNextStatesImpl(action, pred, acts, c, s0, s1, nss, cm); + } catch (TLCRuntimeException | EvalException e) { + // see tlc2.tool.Tool.getInitStates(SemanticNode, ActionItemList, Context, + // TLCState, IStateFunctor) + this.callStack.freeze(); + throw e; + } finally { + this.callStack.pop(); + } + } + + @Override + protected final TLCState getNextStatesAppl(final Action action, final OpApplNode pred, final ActionItemList acts, + final Context c, final TLCState s0, final TLCState s1, final INextStateFunctor nss, final CostModel cm) { + this.callStack.push(pred); + try { + return getNextStatesApplImpl(action, pred, acts, c, s0, s1, nss, cm); + } catch (TLCRuntimeException | EvalException e) { + // see tlc2.tool.Tool.getInitStates(SemanticNode, ActionItemList, Context, + // TLCState, IStateFunctor) + this.callStack.freeze(); + throw e; + } finally { + this.callStack.pop(); + } + } + + @Override + public final Value eval(final SemanticNode expr, final Context c, final TLCState s0, final TLCState s1, + final int control, final CostModel cm) { + this.callStack.push(expr); + try { + // Replace stale ITool instances with this instance. + final Value value = evalImpl(expr, c, s0, s1, control, cm); + if (value instanceof SetPredValue) { + return new SetPredValue((SetPredValue) value, this); + } else if (value instanceof FcnLambdaValue) { + return new FcnLambdaValue((FcnLambdaValue) value, this); + } else if (value instanceof OpLambdaValue) { + return new OpLambdaValue((OpLambdaValue) value, this); + } + return value; + } catch (TLCRuntimeException | EvalException e) { + // see tlc2.tool.Tool.getInitStates(SemanticNode, ActionItemList, Context, + // TLCState, IStateFunctor) + this.callStack.freeze(); + throw e; + } finally { + this.callStack.pop(); + } + } + + @Override + protected final Value evalAppl(final OpApplNode expr, final Context c, final TLCState s0, final TLCState s1, + final int control, final CostModel cm) { + this.callStack.push(expr); + try { + // Replace stale ITool instances with this instance. + final Value value = evalApplImpl(expr, c, s0, s1, control, cm); + if (value instanceof SetPredValue) { + return new SetPredValue((SetPredValue) value, this); + } else if (value instanceof FcnLambdaValue) { + return new FcnLambdaValue((FcnLambdaValue) value, this); + } else if (value instanceof OpLambdaValue) { + return new OpLambdaValue((OpLambdaValue) value, this); + } + return value; + } catch (TLCRuntimeException | EvalException e) { + // see tlc2.tool.Tool.getInitStates(SemanticNode, ActionItemList, Context, + // TLCState, IStateFunctor) + this.callStack.freeze(); + throw e; + } finally { + this.callStack.pop(); + } + } + + @Override + public final TLCState enabled(final SemanticNode pred, final IActionItemList acts, final Context c, + final TLCState s0, final TLCState s1, final CostModel cm) { + this.callStack.push(pred); + try { + return enabledImpl(pred, (ActionItemList) acts, c, s0, s1, cm); + } catch (TLCRuntimeException | EvalException e) { + // see tlc2.tool.Tool.getInitStates(SemanticNode, ActionItemList, Context, + // TLCState, IStateFunctor) + this.callStack.freeze(); + throw e; + } finally { + this.callStack.pop(); + } + } + + @Override + protected final TLCState enabledAppl(final OpApplNode pred, final ActionItemList acts, final Context c, + final TLCState s0, final TLCState s1, CostModel cm) { + this.callStack.push(pred); + try { + return enabledApplImpl(pred, acts, c, s0, s1, cm); + } catch (TLCRuntimeException | EvalException e) { + // see tlc2.tool.Tool.getInitStates(SemanticNode, ActionItemList, Context, + // TLCState, IStateFunctor) + this.callStack.freeze(); + throw e; + } finally { + this.callStack.pop(); + } + } + + @Override + protected final TLCState processUnchanged(final Action action, final SemanticNode expr, final ActionItemList acts, + final Context c, final TLCState s0, final TLCState s1, final INextStateFunctor nss, CostModel cm) { + this.callStack.push(expr); + try { + return processUnchangedImpl(action, expr, acts, c, s0, s1, nss, cm); + } catch (TLCRuntimeException | EvalException e) { + // see tlc2.tool.Tool.getInitStates(SemanticNode, ActionItemList, Context, + // TLCState, IStateFunctor) + this.callStack.freeze(); + throw e; + } finally { + this.callStack.pop(); + } + } + + @Override + protected final TLCState enabledUnchanged(final SemanticNode expr, final ActionItemList acts, final Context c, + final TLCState s0, final TLCState s1, final CostModel cm) { + this.callStack.push(expr); + try { + return enabledUnchangedImpl(expr, acts, c, s0, s1, cm); + } catch (TLCRuntimeException | EvalException e) { + // see tlc2.tool.Tool.getInitStates(SemanticNode, ActionItemList, Context, + // TLCState, IStateFunctor) + this.callStack.freeze(); + throw e; + } finally { + this.callStack.pop(); + } + } + + @Override + protected final Value setSource(final SemanticNode expr, final Value value) { + value.setSource(expr); + return value; + } +} diff --git a/tlatools/src/tlc2/tool/ContextEnumerator.java b/tlatools/src/tlc2/tool/impl/ContextEnumerator.java similarity index 90% rename from tlatools/src/tlc2/tool/ContextEnumerator.java rename to tlatools/src/tlc2/tool/impl/ContextEnumerator.java index 2ed8032915a7840b39006f6d7d36d7b93be4ef22..516282c030f4ec4f1aefb776f182c5d1b9b6192f 100644 --- a/tlatools/src/tlc2/tool/ContextEnumerator.java +++ b/tlatools/src/tlc2/tool/impl/ContextEnumerator.java @@ -3,17 +3,18 @@ // Last modified on Mon 30 Apr 2007 at 15:29:55 PST by lamport // modified on Tue Nov 9 11:06:41 PST 1999 by yuanyu -package tlc2.tool; +package tlc2.tool.impl; import tla2sany.semantic.SymbolNode; import tlc2.output.EC; +import tlc2.tool.IContextEnumerator; import tlc2.util.Context; -import tlc2.value.TupleValue; -import tlc2.value.Value; -import tlc2.value.ValueEnumeration; +import tlc2.value.impl.TupleValue; +import tlc2.value.impl.Value; +import tlc2.value.impl.ValueEnumeration; import util.Assert; -public final class ContextEnumerator { +public final class ContextEnumerator implements IContextEnumerator { private Context con; private Object[] vars; private ValueEnumeration[] enums; @@ -35,6 +36,7 @@ public final class ContextEnumerator { } } + @Override public final Context nextElement() { Context con1 = this.con; if (this.isDone) return null; diff --git a/tlatools/src/tlc2/tool/impl/FastTool.java b/tlatools/src/tlc2/tool/impl/FastTool.java new file mode 100644 index 0000000000000000000000000000000000000000..89b71cf2b584b808e2d0e393b0ea0a5d9fc4c5f7 --- /dev/null +++ b/tlatools/src/tlc2/tool/impl/FastTool.java @@ -0,0 +1,119 @@ +/******************************************************************************* + * Copyright (c) 2019 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.impl; + +import tla2sany.semantic.OpApplNode; +import tla2sany.semantic.SemanticNode; +import tlc2.tool.Action; +import tlc2.tool.IActionItemList; +import tlc2.tool.INextStateFunctor; +import tlc2.tool.TLCState; +import tlc2.tool.coverage.CostModel; +import tlc2.util.Context; +import tlc2.util.ExpectInlined; +import tlc2.value.impl.Value; +import util.FilenameToStream; + +public final class FastTool extends Tool { + + public FastTool(String mainFile, String configFile) { + super(mainFile, configFile); + } + + public FastTool(String mainFile, String configFile, FilenameToStream resolver) { + super(mainFile, configFile, resolver); + } + + public FastTool(String specDir, String specFile, String configFile, FilenameToStream fts) { + super(specDir, specFile, configFile, fts); + } + + // The methods below are supposed to be inlined during execution for performance + // reasons, collapsing this class effectively into Tool. Later and in case of a + // violation, the FastTool instance will be exchanged for the CallStackTool + // instance that properly records error for the purpose of error reporting. + @ExpectInlined + @Override + protected final TLCState getNextStates(final Action action, final SemanticNode pred, final ActionItemList acts, + final Context c, final TLCState s0, final TLCState s1, final INextStateFunctor nss, final CostModel cm) { + return getNextStatesImpl(action, pred, acts, c, s0, s1, nss, cm); + } + + @ExpectInlined + @Override + protected final TLCState getNextStatesAppl(final Action action, final OpApplNode pred, final ActionItemList acts, + final Context c, final TLCState s0, final TLCState s1, final INextStateFunctor nss, final CostModel cm) { + return getNextStatesApplImpl(action, pred, acts, c, s0, s1, nss, cm); + } + + @ExpectInlined + @Override + protected final TLCState processUnchanged(final Action action, final SemanticNode expr, final ActionItemList acts, + final Context c, final TLCState s0, final TLCState s1, final INextStateFunctor nss, final CostModel cm) { + return processUnchangedImpl(action, expr, acts, c, s0, s1, nss, cm); + } + + @ExpectInlined + @Override + public final Value eval(final SemanticNode expr, final Context c, final TLCState s0, final TLCState s1, + final int control, final CostModel cm) { + return evalImpl(expr, c, s0, s1, control, cm); + } + + @ExpectInlined + @Override + protected final Value evalAppl(final OpApplNode expr, final Context c, final TLCState s0, final TLCState s1, + final int control, final CostModel cm) { + return evalApplImpl(expr, c, s0, s1, control, cm); + } + + @ExpectInlined + @Override + protected final Value setSource(final SemanticNode expr, final Value value) { + return value; + } + + @ExpectInlined + @Override + public final TLCState enabled(final SemanticNode pred, final IActionItemList acts, final Context c, + final TLCState s0, final TLCState s1, final CostModel cm) { + return enabledImpl(pred, (ActionItemList) acts, c, s0, s1, cm); // TODO This cast sucks performance-wise. + } + + @ExpectInlined + @Override + protected final TLCState enabledAppl(final OpApplNode pred, final ActionItemList acts, final Context c, + final TLCState s0, final TLCState s1, final CostModel cm) { + return enabledApplImpl(pred, acts, c, s0, s1, cm); + } + + @ExpectInlined + @Override + protected final TLCState enabledUnchanged(final SemanticNode expr, final ActionItemList acts, final Context c, + final TLCState s0, final TLCState s1, final CostModel cm) { + return enabledUnchangedImpl(expr, acts, c, s0, s1, cm); + } +} diff --git a/tlatools/src/tlc2/tool/ModelConfig.java b/tlatools/src/tlc2/tool/impl/ModelConfig.java similarity index 82% rename from tlatools/src/tlc2/tool/ModelConfig.java rename to tlatools/src/tlc2/tool/impl/ModelConfig.java index a106dea9ef888c9c385898cd04724f032ffe8f47..7f8af9803409c7a2b49c601dea874a17f53d41a6 100644 --- a/tlatools/src/tlc2/tool/ModelConfig.java +++ b/tlatools/src/tlc2/tool/impl/ModelConfig.java @@ -3,7 +3,7 @@ // Last modified on Mon 30 Apr 2007 at 15:29:56 PST by lamport // modified on Thu Aug 23 17:46:39 PDT 2001 by yuanyu -package tlc2.tool; +package tlc2.tool.impl; import java.io.FileInputStream; import java.io.IOException; @@ -16,53 +16,67 @@ import tla2sany.parser.TLAplusParserTokenManager; import tla2sany.parser.Token; import tla2sany.parser.TokenMgrError; import tlc2.output.EC; +import tlc2.tool.ConfigFileException; import tlc2.util.Vect; -import tlc2.value.IntValue; -import tlc2.value.ModelValue; -import tlc2.value.SetEnumValue; -import tlc2.value.StringValue; -import tlc2.value.Value; +import tlc2.value.IValue; import tlc2.value.ValueConstants; -import tlc2.value.ValueVec; +import tlc2.value.impl.BoolValue; +import tlc2.value.impl.IntValue; +import tlc2.value.impl.ModelValue; +import tlc2.value.impl.SetEnumValue; +import tlc2.value.impl.StringValue; +import tlc2.value.impl.Value; +import tlc2.value.impl.ValueVec; import util.FileUtil; import util.FilenameToStream; import util.SimpleFilenameToStream; +import util.TLAConstants; /** * Stores information from user's model configuration file. + * + * TODO we should move from Hashtable to HashMap (we should probably also stop using our own collection implmentations + * like {@link Vect}.) + * TODO we're storing a heterogeneous mishmash in the values of configTbl - sometimes a Vect, sometimes a String, sometime + * that Vect has only String instances, sometimes is has a String instance and Value subclasses, ... it would + * be nice were the design cleaner. + * * @author Yuan Yu, Leslie Lamport - * @version $Id$ */ -public class ModelConfig implements ValueConstants, Serializable -{ - // keywords of the configuration file - private static final String Constant = "CONSTANT"; - private static final String Constants = "CONSTANTS"; +public class ModelConfig implements ValueConstants, Serializable { + // keywords of the configuration file. + // CAREFUL: HAVE TO BE IN CONFIGTBL FOR PARSING TO WORK! + private static final String Constant = TLAConstants.KeyWords.CONSTANT; + private static final String Constants = TLAConstants.KeyWords.CONSTANTS; private static final String Constraint = "CONSTRAINT"; private static final String Constraints = "CONSTRAINTS"; - private static final String ActionConstraint = "ACTION_CONSTRAINT"; - private static final String ActionConstraints = "ACTION_CONSTRAINTS"; - private static final String Invariant = "INVARIANT"; - private static final String Invariants = "INVARIANTS"; - private static final String Init = "INIT"; - private static final String Next = "NEXT"; + private static final String ActionConstraint = TLAConstants.KeyWords.ACTION_CONSTRAINT; + private static final String ActionConstraints = ActionConstraint + 'S'; + private static final String Invariant = TLAConstants.KeyWords.INVARIANT; + private static final String Invariants = Invariant + 'S'; + private static final String Init = TLAConstants.KeyWords.INIT; + private static final String Next = TLAConstants.KeyWords.NEXT; private static final String View = "VIEW"; - private static final String Symmetry = "SYMMETRY"; - private static final String Spec = "SPECIFICATION"; - private static final String Prop = "PROPERTY"; + private static final String Symmetry = TLAConstants.KeyWords.SYMMETRY; + private static final String Spec = TLAConstants.KeyWords.SPECIFICATION; + private static final String Prop = TLAConstants.KeyWords.PROPERTY; private static final String Props = "PROPERTIES"; private static final String Type = "TYPE"; private static final String TypeConstraint = "TYPE_CONSTRAINT"; + private static final String CheckDeadlock = "CHECK_DEADLOCK"; + + private static final long serialVersionUID = 1L; /** * All keywords used in the configuration file */ public final static String[] ALL_KEYWORDS = { Constant, Constants, Constraint, Constraints, ActionConstraint, ActionConstraints, Invariant, Invariants, Init, Next, View, Symmetry, Spec, Prop, Props, Type, - TypeConstraint }; + TypeConstraint, CheckDeadlock }; private Hashtable configTbl; - private Hashtable overrides; + private Hashtable<String, String> overrides; + private Hashtable<String, String> overridesReverseMap; private Hashtable modConstants; private Hashtable modOverrides; private String configFileName; @@ -89,17 +103,17 @@ public class ModelConfig implements ValueConstants, Serializable ModelValue.init(); this.configFileName = configFileName; - this.configTbl = new Hashtable(); - Vect temp = new Vect(); + this.configTbl = new Hashtable<>(); + Vect temp = new Vect<>(); this.configTbl.put(Constant, temp); this.configTbl.put(Constants, temp); - temp = new Vect(); + temp = new Vect<>(); this.configTbl.put(Constraint, temp); this.configTbl.put(Constraints, temp); - temp = new Vect(); + temp = new Vect<>(); this.configTbl.put(ActionConstraint, temp); this.configTbl.put(ActionConstraints, temp); - temp = new Vect(); + temp = new Vect<>(); this.configTbl.put(Invariant, temp); this.configTbl.put(Invariants, temp); this.configTbl.put(Init, ""); @@ -107,15 +121,17 @@ public class ModelConfig implements ValueConstants, Serializable this.configTbl.put(View, ""); this.configTbl.put(Symmetry, ""); this.configTbl.put(Spec, ""); - temp = new Vect(); + temp = new Vect<>(); this.configTbl.put(Prop, temp); this.configTbl.put(Props, temp); this.configTbl.put(Type, ""); this.configTbl.put(TypeConstraint, ""); - - this.modConstants = new Hashtable(); - this.modOverrides = new Hashtable(); - this.overrides = new Hashtable(); + this.configTbl.put(CheckDeadlock, "undef"); + + this.modConstants = new Hashtable<>(); + this.modOverrides = new Hashtable<>(); + this.overrides = new Hashtable<>(); + this.overridesReverseMap = new Hashtable<>(); } /** @@ -155,7 +171,7 @@ public class ModelConfig implements ValueConstants, Serializable String old = (String) this.configTbl.put(Init, tt.image); if (old.length() != 0) { - throw new ConfigFileException(EC.CFG_TWICE_KEYWORD, new String[] { String.valueOf(loc), Spec }); + throw new ConfigFileException(EC.CFG_TWICE_KEYWORD, new String[] { String.valueOf(loc), Init }); } tt = getNextToken(tmgr); } else if (tval.equals(Next)) @@ -306,7 +322,9 @@ public class ModelConfig implements ValueConstants, Serializable throw new ConfigFileException(EC.CFG_EXPECT_ID, new String[] { String.valueOf(scs.getBeginLine()), "<-" }); } - this.overrides.put(line.elementAt(0), tt.image); + final String string = (String)line.elementAt(0); + this.overrides.put(string, tt.image); + this.overridesReverseMap.put(tt.image, string); } } else { @@ -315,7 +333,7 @@ public class ModelConfig implements ValueConstants, Serializable while (true) { tt = getNextToken(tmgr); - Value arg = this.parseValue(tt, scs, tmgr); + IValue arg = this.parseValue(tt, scs, tmgr); line.addElement(arg); tt = getNextToken(tmgr); if (!tt.image.equals(",")) @@ -398,6 +416,26 @@ public class ModelConfig implements ValueConstants, Serializable break; actionConstraints.addElement(tt.image); } + } else if (tval.equals(CheckDeadlock)) { + tt = getNextToken(tmgr); + if (tt.kind == TLAplusParserConstants.EOF) + { + throw new ConfigFileException(EC.CFG_MISSING_ID, new String[] { String.valueOf(loc), CheckDeadlock }); + } + Object previous; + if (tt.image.equals("TRUE")) { + previous = this.configTbl.put(CheckDeadlock, true); + } else if (tt.image.equals("FALSE")) { + previous = this.configTbl.put(CheckDeadlock, false); + } else { + throw new ConfigFileException(EC.CFG_EXPECTED_SYMBOL, new String[] { + String.valueOf(scs.getBeginLine()), "TRUE or FALSE" }); + } + if (previous != "undef") + { + throw new ConfigFileException(EC.CFG_TWICE_KEYWORD, new String[] { String.valueOf(loc), CheckDeadlock }); + } + tt = getNextToken(tmgr); } else { throw new ConfigFileException(EC.CFG_EXPECTED_SYMBOL, new String[] { @@ -426,10 +464,10 @@ public class ModelConfig implements ValueConstants, Serializable return new StringValue(tval.substring(1, tval.length() - 1)); } else if (tt.image.equals("TRUE")) { - return ValTrue; + return BoolValue.ValTrue; } else if (tt.image.equals("FALSE")) { - return ValFalse; + return BoolValue.ValFalse; } else if (tt.image.equals("{")) { ValueVec elems = new ValueVec(); @@ -438,7 +476,7 @@ public class ModelConfig implements ValueConstants, Serializable { while (true) { - Value elem = this.parseValue(tt, scs, tmgr); + Value elem = this.parseValue(tt, scs, tmgr); elems.addElement(elem); tt = getNextToken(tmgr); if (!tt.image.equals(",")) @@ -488,10 +526,14 @@ public class ModelConfig implements ValueConstants, Serializable return this.modConstants; } - public synchronized final Hashtable getOverrides() + public synchronized final Hashtable<String, String> getOverrides() { return this.overrides; } + + public synchronized final String getOverridenSpecNameForConfigName(final String configName) { + return this.overridesReverseMap.get(configName); + } public synchronized final Hashtable getModOverrides() { @@ -522,6 +564,12 @@ public class ModelConfig implements ValueConstants, Serializable { return (String) this.configTbl.get(View); } + + public synchronized final boolean configDefinesSpecification() { + final String spec = getSpec(); + + return ((spec != null) && (spec.trim().length() > 0)); + } public synchronized final String getSymmetry() { @@ -553,6 +601,15 @@ public class ModelConfig implements ValueConstants, Serializable return (String) this.configTbl.get(TypeConstraint); } + public synchronized final boolean getCheckDeadlock() + { + Object object = this.configTbl.get(CheckDeadlock); + if (object instanceof Boolean) { + return (boolean) object; + } + return true; + } + /** * Testing method of the parser * @param args diff --git a/tlatools/src/tlc2/tool/impl/OpDefEvaluator.java b/tlatools/src/tlc2/tool/impl/OpDefEvaluator.java new file mode 100644 index 0000000000000000000000000000000000000000..38d56f884fe143ca604322d6f3ff849528e84009 --- /dev/null +++ b/tlatools/src/tlc2/tool/impl/OpDefEvaluator.java @@ -0,0 +1,11 @@ +package tlc2.tool.impl; + +import tla2sany.semantic.SemanticNode; +import tlc2.tool.TLCState; +import tlc2.tool.coverage.CostModel; +import tlc2.util.Context; +import tlc2.value.IValue; + +public interface OpDefEvaluator { + IValue eval(SemanticNode body, Context empty, TLCState empty2, CostModel doNotRecord); +} diff --git a/tlatools/src/tlc2/tool/impl/Spec.java b/tlatools/src/tlc2/tool/impl/Spec.java new file mode 100644 index 0000000000000000000000000000000000000000..69558c2a53c220631d5ca78a51c39e9860f11373 --- /dev/null +++ b/tlatools/src/tlc2/tool/impl/Spec.java @@ -0,0 +1,644 @@ +// Copyright (c) 2003 Compaq Corporation. All rights reserved. +// Portions Copyright (c) 2003 Microsoft Corporation. All rights reserved. +// Last modified on Mon 19 May 2008 at 1:13:48 PST by lamport +// modified on Fri Aug 24 14:43:24 PDT 2001 by yuanyu + +package tlc2.tool.impl; + +import java.io.File; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import tla2sany.modanalyzer.ParseUnit; +import tla2sany.semantic.APSubstInNode; +import tla2sany.semantic.ExprNode; +import tla2sany.semantic.ExprOrOpArgNode; +import tla2sany.semantic.FormalParamNode; +import tla2sany.semantic.FrontEnd; +import tla2sany.semantic.LabelNode; +import tla2sany.semantic.LetInNode; +import tla2sany.semantic.OpApplNode; +import tla2sany.semantic.OpDefNode; +import tla2sany.semantic.SemanticNode; +import tla2sany.semantic.Subst; +import tla2sany.semantic.SubstInNode; +import tla2sany.semantic.SymbolNode; +import tla2sany.semantic.ThmOrAssumpDefNode; +import tlc2.TLCGlobals; +import tlc2.output.EC; +import tlc2.tool.Action; +import tlc2.tool.BuiltInOPs; +import tlc2.tool.Defns; +import tlc2.tool.TLCState; +import tlc2.tool.ToolGlobals; +import tlc2.util.Context; +import tlc2.util.ObjLongTable; +import tlc2.util.Vect; +import tlc2.value.ValueConstants; +import tlc2.value.impl.LazyValue; +import tlc2.value.impl.ModelValue; +import util.Assert; +import util.FilenameToStream; +import util.TLAConstants; +import util.UniqueString; + +// Note that we use all of the {@code default} defined functionality in our +// implemented interface {@link SymbolNodeValueLookupProvider} (and our +// lack of implementation of {@link OpDefEvaluator} is why this class +// is marked {@code abstract}.) +abstract class Spec + implements ValueConstants, ToolGlobals, Serializable, OpDefEvaluator, SymbolNodeValueLookupProvider { + private static final long serialVersionUID = 1L; + + /** + * @see See note on performance in CostModelCreator. + */ + protected static final boolean coverage = TLCGlobals.isCoverageEnabled(); + + protected static final int toolId = FrontEnd.getToolId(); + + protected final String specDir; // The spec directory. + protected final String rootFile; // The root file of this spec. + protected final String configFile; // The model config file. + protected final Set<OpDefNode> processedDefs ; + // The set of OpDefNodes on which processSpec has been called. + // Added by LL & YY on 25 June 2014 to eliminate infinite + // loop when a recursively defined operator is used as an + // operator argument in its own definition. + protected final Defns defns; // Global definitions reachable from root + protected final Defns unprocessedDefns; + protected final TLAClass tlaClass; // TLA built-in classes. + private final FilenameToStream resolver; // takes care of path to stream resolution + + protected final ModelConfig config; // The model configuration. + private final SpecProcessor specProcessor; + + // SZ Feb 20, 2009: added support to name resolver, to be able to run outside of the tool + public Spec(final String specDir, final String specFile, final String configFile, final FilenameToStream resolver) { + this.specDir = specDir; + this.rootFile = specFile; + this.defns = new Defns(); + this.tlaClass = new TLAClass("tlc2.module", resolver); + this.processedDefs = new HashSet<OpDefNode>(); + this.resolver = resolver; + + // SZ Mar 9, 2009: added initialization of the modelValue class + ModelValue.init(); + this.configFile = configFile; + this.config = new ModelConfig(configFile + TLAConstants.Files.CONFIG_EXTENSION, resolver); + this.config.parse(); + ModelValue.setValues(); // called after seeing all model values + + specProcessor = new SpecProcessor(getRootName(), resolver, toolId, defns, config, this, this, tlaClass); + + this.unprocessedDefns = specProcessor.getUnprocessedDefns(); + } + + protected Spec(final Spec other) { + this.specDir = other.specDir; + this.rootFile = other.rootFile; + this.configFile = other.configFile; + this.processedDefs = other.processedDefs; + this.defns = other.defns; + this.tlaClass = other.tlaClass; + this.resolver = other.resolver; + this.unprocessedDefns = other.unprocessedDefns; + this.config = other.config; + this.specProcessor = other.specProcessor; + } + + public ModelConfig getModelConfig() { + return config; + } + + public SpecProcessor getSpecProcessor() { + return specProcessor; + } + + /* Return the variable if expr is a primed state variable. Otherwise, null. */ + public final SymbolNode getPrimedVar(SemanticNode expr, Context c, boolean cutoff) + { + if (expr instanceof OpApplNode) + { + OpApplNode expr1 = (OpApplNode) expr; + SymbolNode opNode = expr1.getOperator(); + + if (BuiltInOPs.getOpCode(opNode.getName()) == OPCODE_prime) + { + return this.getVar(expr1.getArgs()[0], c, cutoff, toolId); + } + + if (opNode.getArity() == 0) + { + boolean isVarDecl = (opNode.getKind() == VariableDeclKind); + Object val = this.lookup(opNode, c, cutoff && isVarDecl, toolId); + + if (val instanceof LazyValue) + { + LazyValue lval = (LazyValue) val; + return this.getPrimedVar(lval.expr, lval.con, cutoff); + } + if (val instanceof OpDefNode) + { + return this.getPrimedVar(((OpDefNode) val).getBody(), c, cutoff); + } + } + } + return null; + } + + /** + * Get model constraints. + */ + public final ExprNode[] getModelConstraints() { + return specProcessor.getModelConstraints(); + } + + /** + * Get action constraints. + */ + public final ExprNode[] getActionConstraints() { + return specProcessor.getActionConstraints(); + } + + /* Get the initial state predicate of the specification. */ + public final Vect<Action> getInitStateSpec() { + return specProcessor.getInitPred(); + } + + /* Get the action (next state) predicate of the specification. */ + public final Action getNextStateSpec() { + return specProcessor.getNextPred(); + } + + /** + * Get the view mapping for the specification. + */ + public final SemanticNode getViewSpec() + { + String name = this.config.getView(); + if (name.length() == 0) + return null; + + Object view = this.defns.get(name); + if (view == null) + { + Assert.fail(EC.TLC_CONFIG_SPECIFIED_NOT_DEFINED, new String[] { "view function", name }); + } + if (!(view instanceof OpDefNode)) + { + Assert.fail(EC.TLC_CONFIG_ID_MUST_NOT_BE_CONSTANT, new String[] { "view function", name }); + } + OpDefNode def = (OpDefNode) view; + if (def.getArity() != 0) + { + Assert.fail(EC.TLC_CONFIG_ID_REQUIRES_NO_ARG, new String[] { "view function", name }); + } + return def.getBody(); + } + + /* Get the type declaration for the state variables. */ + public final SemanticNode getTypeSpec() + { + String name = this.config.getType(); + if (name.length() == 0) + { + Assert.fail(EC.TLC_CONFIG_NO_STATE_TYPE); + } + + Object type = this.defns.get(name); + if (type == null) + { + Assert.fail(EC.TLC_CONFIG_SPECIFIED_NOT_DEFINED, new String[] { "type", name }); + } + if (!(type instanceof OpDefNode)) + { + Assert.fail(EC.TLC_CONFIG_ID_MUST_NOT_BE_CONSTANT, new String[] { "type", name }); + } + OpDefNode def = (OpDefNode) type; + if (def.getArity() != 0) + { + Assert.fail(EC.TLC_CONFIG_ID_REQUIRES_NO_ARG, new String[] { "type", name }); + } + return def.getBody(); + } + + /* Get the type declaration for the state variables. */ + public final SemanticNode getTypeConstraintSpec() + { + String name = this.config.getTypeConstraint(); + if (name.length() == 0) + { + return null; + } + + Object type = this.defns.get(name); + if (type == null) + { + Assert.fail(EC.TLC_CONFIG_SPECIFIED_NOT_DEFINED, new String[] { "type constraint", name }); + } + if (!(type instanceof OpDefNode)) + { + Assert.fail(EC.TLC_CONFIG_ID_MUST_NOT_BE_CONSTANT, new String[] { "type constraint", name }); + } + OpDefNode def = (OpDefNode) type; + if (def.getArity() != 0) + { + Assert.fail(EC.TLC_CONFIG_ID_REQUIRES_NO_ARG, new String[] { "type constraint", name }); + + } + return def.getBody(); + } + + public final boolean livenessIsTrue() { + return getImpliedTemporals().length == 0; + } + + /* Get the fairness condition of the specification. */ + public final Action[] getTemporals() + { + return specProcessor.getTemporal(); + } + + public final String[] getTemporalNames() + { + return specProcessor.getTemporalNames(); + } + + /* Get the liveness checks of the specification. */ + public final Action[] getImpliedTemporals() { + return specProcessor.getImpliedTemporals(); + } + + public final String[] getImpliedTemporalNames() { + return specProcessor.getImpliedTemporalNames(); + } + + /* Get the invariants of the specification. */ + public final Action[] getInvariants() { + return specProcessor.getInvariants(); + } + + public final String[] getInvNames() { + return specProcessor.getInvariantsNames(); + } + + /* Get the implied-inits of the specification. */ + public final Action[] getImpliedInits() { + return specProcessor.getImpliedInits(); + } + + public final String[] getImpliedInitNames() { + return specProcessor.getImpliedInitNames(); + } + + /* Get the implied-actions of the specification. */ + public final Action[] getImpliedActions() { + return specProcessor.getImpliedActions(); + } + + public final String[] getImpliedActNames() { + return specProcessor.getImpliedActionNames(); + } + + /* Get the assumptions of the specification. */ + public final ExprNode[] getAssumptions() { + return specProcessor.getAssumptions(); + } + + /* Get the assumptionIsAxiom field */ + public final boolean[] getAssumptionIsAxiom() { + return specProcessor.getAssumptionIsAxiom(); + } + + /** + * This method gets the value of a symbol from the environment. We + * look up in the context c, its tool object, and the state s. + * + * It and the lookup method that follows it were modified by LL + * on 10 April 2011 to fix the following bug. When a constant definition + * Foo == ... + * is overridden to substitute Bar for Foo, the TLC tool object for + * the body of Foo's OpDef node is set to the OpDefNode for Bar. + * When evaluating a use of Foo, the lookup method is apparently + * supposed to return the OpDefNode for Bar. (I don't understand + * how the callers make use of the returned value.) That's what it + * does for uses of Foo in the module in which Foo is defined. + * However, if Foo is imported by instantiation with renaming as + * X!Foo, then it appears that looking up X!Foo should also return + * the OpDefNode for Bar. If the instantiated module had no + * parameters, then that's what happened because the body of the + * OpDefNode for X!Foo is the same (contains a pointer to the + * same object) as the body of Foo's OpDefNode. However, that + * wasn't the case if the instantiated module had parameters, + * because then X!Foo's OpDefNode consists of a sequence of + * nested SubstInNode objects, the last of which points to + * the body of Foo's OpDefNode. So, LL modified the lookup + * methods so they follow the sequence of SubstInNode bodies + * down to the body of Foo's OpDefNode when looking up the result. + * (If a SubstInNode has a non-null TLC tool object for a + * SubstInNode, then it returns that object. I don't think this + * should ever be the case, and if it is, I have no idea what the + * lookup method should do.) + * + */ + public final Object lookup(SymbolNode opNode, Context c, TLCState s, boolean cutoff) + { + Object result = lookup(opNode, c, cutoff, toolId); + if (result != opNode) { + return result; + } + + // CalvinL/LL/MAK 02/2021: Added conditional as part of Github issue #362 Name + // clash between variable in refined spec and operator in instantiated spec. See + // releated test in Github362.java. + if (opNode.getKind() != UserDefinedOpKind) { + result = s.lookup(opNode.getName()); + if (result != null) { + return result; + } + } + + return opNode; + } + + public final Object lookup(final SymbolNode opNode) + { + return lookup(opNode, Context.Empty, false, toolId); + } + + /** + * The following added by LL on 23 October 2012 to fix bug in evaluation of names of theorems and + * assumptions imported by parameterized instantiation. + * + * @param opDef + * @param args + * @param c + * @param cachable + * @return + */ + public final Context getOpContext(ThmOrAssumpDefNode opDef, ExprOrOpArgNode[] args, Context c, boolean cachable) + { + FormalParamNode[] formals = opDef.getParams(); + int alen = args.length; + Context c1 = c; + for (int i = 0; i < alen; i++) + { + Object aval = this.getVal(args[i], c, cachable, toolId); + c1 = c1.cons(formals[i], aval); + } + return c1; + } + + /** + * Return a table containing the locations of subexpression in the + * spec of forms x' = e and x' \in e. Warning: Current implementation + * may not be able to find all such locations. + */ + public final ObjLongTable<SemanticNode> getPrimedLocs() + { + final ObjLongTable<SemanticNode> tbl = new ObjLongTable<SemanticNode>(10); + final Action act = this.getNextStateSpec(); + if (act == null) { + // MAK 10/17/2018: If spec defines no next-state action (see e.g. + // tlc2.tool.ASTest) and this method is called before ModelChecker checks + // actions (search for tlc2.output.EC.TLC_STATES_AND_NO_NEXT_ACTION) this will + // NPE. + return tbl; + } + this.collectPrimedLocs(act.pred, act.con, tbl); + return tbl; + } + + public final void collectPrimedLocs(SemanticNode pred, Context c, ObjLongTable<SemanticNode> tbl) + { + switch (pred.getKind()) { + case OpApplKind: { + OpApplNode pred1 = (OpApplNode) pred; + this.collectPrimedLocsAppl(pred1, c, tbl); + return; + } + case LetInKind: { + LetInNode pred1 = (LetInNode) pred; + this.collectPrimedLocs(pred1.getBody(), c, tbl); + return; + } + case SubstInKind: { + SubstInNode pred1 = (SubstInNode) pred; + Subst[] subs = pred1.getSubsts(); + Context c1 = c; + for (int i = 0; i < subs.length; i++) + { + Subst sub = subs[i]; + c1 = c1.cons(sub.getOp(), this.getVal(sub.getExpr(), c, true, toolId)); + } + this.collectPrimedLocs(pred1.getBody(), c, tbl); + return; + } + + // Added by LL on 13 Nov 2009 to handle theorem and assumption names. + case APSubstInKind: { + APSubstInNode pred1 = (APSubstInNode) pred; + Subst[] subs = pred1.getSubsts(); + Context c1 = c; + for (int i = 0; i < subs.length; i++) + { + Subst sub = subs[i]; + c1 = c1.cons(sub.getOp(), this.getVal(sub.getExpr(), c, true, toolId)); + } + this.collectPrimedLocs(pred1.getBody(), c, tbl); + return; + } + + + /*********************************************************************** + * LabelKind case added by LL on 13 Jun 2007. * + ***********************************************************************/ + case LabelKind: { + LabelNode pred1 = (LabelNode) pred; + this.collectPrimedLocs(pred1.getBody(), c, tbl); + return; + } + } + } + + private final void collectPrimedLocsAppl(OpApplNode pred, Context c, ObjLongTable<SemanticNode> tbl) + { + ExprOrOpArgNode[] args = pred.getArgs(); + SymbolNode opNode = pred.getOperator(); + int opcode = BuiltInOPs.getOpCode(opNode.getName()); + + switch (opcode) { + case OPCODE_fa: // FcnApply + { + this.collectPrimedLocs(args[0], c, tbl); + break; + } + case OPCODE_ite: // IfThenElse + { + this.collectPrimedLocs(args[1], c, tbl); + this.collectPrimedLocs(args[2], c, tbl); + break; + } + case OPCODE_case: // Case + { + for (int i = 0; i < args.length; i++) + { + OpApplNode pair = (OpApplNode) args[i]; + this.collectPrimedLocs(pair.getArgs()[1], c, tbl); + } + break; + } + case OPCODE_eq: // x' = 42 + case OPCODE_in: { // x' \in S (eq case "falls through") + SymbolNode var = this.getPrimedVar(args[0], c, false); + if (var != null && var.getName().getVarLoc() != -1) + { + tbl.put(pred, 0); + } + break; + } + case OPCODE_cl: // ConjList + case OPCODE_dl: // DisjList + case OPCODE_be: // BoundedExists + case OPCODE_bf: // BoundedForall + case OPCODE_land: + case OPCODE_lor: + case OPCODE_implies: + case OPCODE_nop: // This case added 13 Nov 2009 by LL to handle subexpression names. + { + for (int i = 0; i < args.length; i++) + { + this.collectPrimedLocs(args[i], c, tbl); + } + break; + } + case OPCODE_unchanged: { + this.collectUnchangedLocs(args[0], c, tbl); + break; + } + case OPCODE_aa: // AngleAct <A>_e + { + this.collectPrimedLocs(args[0], c, tbl); + break; + } + case OPCODE_sa: // [A]_e + { + this.collectPrimedLocs(args[0], c, tbl); + tbl.put(args[1], 0); + break; + } + default: { + if (opcode == 0) + { + Object val = this.lookup(opNode, c, false, toolId); + + if (val instanceof OpDefNode) + { + OpDefNode opDef = (OpDefNode) val; + // Following added by LL on 10 Apr 2010 to avoid infinite + // recursion for recursive operator definitions + if (opDef.getInRecursive()) { + return ; + } + Context c1 = this.getOpContext(opDef, args, c, true, toolId); + this.collectPrimedLocs(opDef.getBody(), c1, tbl); + } else if (val instanceof LazyValue) + { + LazyValue lv = (LazyValue) val; + this.collectPrimedLocs(lv.expr, lv.con, tbl); + } + } + } + } + } + + private final void collectUnchangedLocs(final SemanticNode expr, final Context c, + final ObjLongTable<SemanticNode> tbl) { + if (expr instanceof OpApplNode) + { + OpApplNode expr1 = (OpApplNode) expr; + SymbolNode opNode = expr1.getOperator(); + UniqueString opName = opNode.getName(); + int opcode = BuiltInOPs.getOpCode(opName); + + if (opName.getVarLoc() >= 0) + { + // a state variable: + tbl.put(expr, 0); + return; + } + + ExprOrOpArgNode[] args = expr1.getArgs(); + if (opcode == OPCODE_tup) + { + // a tuple, might be: + // UNCHANGED <<x,y,z>> + // or: + // vars == <<x,y,z>> + // ... + // UNCHANGED vars + // For the latter, we don't want vars == <<x,y,z>> to show up, but the vars in + // UNCHANGED vars (see CoverageStatisticsTest). + for (int i = 0; i < args.length; i++) + { + this.collectUnchangedLocs(args[i], c, tbl); + } + return; + } + + if (opcode == 0 && args.length == 0) + { + // a 0-arity operator: + Object val = this.lookup(opNode, c, false, toolId); + if (val instanceof OpDefNode) + { + this.collectUnchangedLocs(((OpDefNode) val).getBody(), c, tbl); + return; + } + } + } + return; + } + + public FilenameToStream getResolver() + { + return resolver; + } + + public String getRootName() { + return new File(this.rootFile).getName(); + } + + public String getRootFile() { + return this.rootFile; + } + + public String getConfigFile() { + return this.configFile; + } + + public String getSpecDir() { + return this.specDir; + } + + public int getId() { + return toolId; + } + + public List<File> getModuleFiles(final FilenameToStream resolver) { + final List<File> result = new ArrayList<File>(); + + final Enumeration<ParseUnit> parseUnitContext = specProcessor.getSpecObj().parseUnitContext.elements(); + while (parseUnitContext.hasMoreElements()) { + ParseUnit pu = (ParseUnit) parseUnitContext.nextElement(); + File resolve = resolver.resolve(pu.getFileName(), false); + result.add(resolve); + } + return result; + } +} diff --git a/tlatools/src/tlc2/tool/Spec.java b/tlatools/src/tlc2/tool/impl/SpecProcessor.java similarity index 53% rename from tlatools/src/tlc2/tool/Spec.java rename to tlatools/src/tlc2/tool/impl/SpecProcessor.java index 1094f2586c269d9947ab6c304b36071706ae412d..172150f14d0aec289b870a10d5dfc69ec3a1d1ec 100644 --- a/tlatools/src/tlc2/tool/Spec.java +++ b/tlatools/src/tlc2/tool/impl/SpecProcessor.java @@ -1,30 +1,53 @@ -// Copyright (c) 2003 Compaq Corporation. All rights reserved. -// Portions Copyright (c) 2003 Microsoft Corporation. All rights reserved. -// Last modified on Mon 19 May 2008 at 1:13:48 PST by lamport -// modified on Fri Aug 24 14:43:24 PDT 2001 by yuanyu - -package tlc2.tool; +/******************************************************************************* + * Copyright (c) 2019 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.impl; import java.io.Serializable; import java.lang.reflect.Method; import java.lang.reflect.Modifier; +import java.net.URL; +import java.util.Arrays; import java.util.Enumeration; +import java.util.HashMap; import java.util.HashSet; import java.util.Hashtable; -import java.util.Iterator; +import java.util.Map; +import java.util.Set; import tla2sany.drivers.FrontEndException; import tla2sany.drivers.SANY; import tla2sany.modanalyzer.SpecObj; +import tla2sany.semantic.APSubstInNode; import tla2sany.semantic.AssumeNode; import tla2sany.semantic.DecimalNode; import tla2sany.semantic.ExprNode; import tla2sany.semantic.ExprOrOpArgNode; import tla2sany.semantic.ExternalModuleTable; -import tla2sany.semantic.FormalParamNode; import tla2sany.semantic.LabelNode; import tla2sany.semantic.LetInNode; -import tla2sany.semantic.LevelNode; import tla2sany.semantic.ModuleNode; import tla2sany.semantic.NumeralNode; import tla2sany.semantic.OpApplNode; @@ -35,168 +58,310 @@ import tla2sany.semantic.SemanticNode; import tla2sany.semantic.StringNode; import tla2sany.semantic.Subst; import tla2sany.semantic.SubstInNode; -import tla2sany.semantic.APSubstInNode; import tla2sany.semantic.SymbolNode; import tla2sany.semantic.TheoremNode; -import tla2sany.semantic.ThmOrAssumpDefNode; import tlc2.TLCGlobals; +import tlc2.module.BuiltInModuleHelper; import tlc2.output.EC; import tlc2.output.MP; +import tlc2.overrides.Evaluation; +import tlc2.overrides.ITLCOverrides; +import tlc2.overrides.TLAPlusCallable; +import tlc2.overrides.TLAPlusOperator; +import tlc2.tool.Action; +import tlc2.tool.BuiltInOPs; +import tlc2.tool.Defns; +import tlc2.tool.EvalException; +import tlc2.tool.Specs; +import tlc2.tool.TLAPlusExecutorState; +import tlc2.tool.TLCStateMut; +import tlc2.tool.ToolGlobals; import tlc2.util.Context; import tlc2.util.List; -import tlc2.util.ObjLongTable; import tlc2.util.Vect; -import tlc2.value.BoolValue; -import tlc2.value.IntValue; -import tlc2.value.LazyValue; -import tlc2.value.MethodValue; -import tlc2.value.ModelValue; -import tlc2.value.OpRcdValue; -import tlc2.value.SetEnumValue; -import tlc2.value.StringValue; -import tlc2.value.Value; +import tlc2.value.IBoolValue; +import tlc2.value.IValue; import tlc2.value.ValueConstants; +import tlc2.value.impl.BoolValue; +import tlc2.value.impl.CallableValue; +import tlc2.value.impl.EvaluatingValue; +import tlc2.value.impl.IntValue; +import tlc2.value.impl.LazyValue; +import tlc2.value.impl.MethodValue; +import tlc2.value.impl.OpRcdValue; +import tlc2.value.impl.SetEnumValue; +import tlc2.value.impl.StringValue; +import tlc2.value.impl.Value; import util.Assert; import util.FilenameToStream; import util.ToolIO; import util.UniqueString; -public class Spec implements ValueConstants, ToolGlobals, Serializable -{ - - public String specDir; // The spec directory. - public String rootFile; // The root file of this spec. - protected String configFile; // The model config file. - protected ModelConfig config; // The model configuration. - protected ExternalModuleTable moduleTbl; // The external modules reachable from root - protected ModuleNode rootModule; // The root module. - protected HashSet processedDefs ; - // The set of OpDefNodes on which processSpec has been called. - // Added by LL & YY on 25 June 2014 to eliminate infinite - // loop when a recursively defined operator is used as an - // operator argument in its own definition. - protected Defns defns; // Global definitions reachable from root - // SZ 10.04.2009: changed the name of the variable to reflect its nature - public OpDeclNode[] variablesNodes; // The state variables. - protected TLAClass tlaClass; // TLA built-in classes. - protected Vect initPredVec; // The initial state predicate. - protected Action nextPred; // The next state predicate. - protected Action[] temporals; // Fairness specifications... - protected String[] temporalNames; // ... and their names - protected Action[] impliedTemporals; // Liveness conds to check... - protected String[] impliedTemporalNames; // ... and their names - protected Action[] invariants; // Invariants to be checked... - protected String[] invNames; // ... and their names - protected Action[] impliedInits; // Implied-inits to be checked... - protected String[] impliedInitNames; // ... and their names - protected Action[] impliedActions; // Implied-actions to be checked... - protected String[] impliedActNames; // ... and their names - protected ExprNode[] modelConstraints; // Model constraints - protected ExprNode[] actionConstraints; // Action constraints - protected ExprNode[] assumptions; // Assumptions - protected boolean[] assumptionIsAxiom; // assumptionIsAxiom[i] is true iff assumptions[i] +public class SpecProcessor implements ValueConstants, ToolGlobals { + + private final String rootFile; // The root file of this spec. + private final FilenameToStream resolver; // takes care of path to stream resolution + private final int toolId; + private final Defns defns; // Global definitions reachable from root + private final ModelConfig config; // The model configuration. + private final OpDefEvaluator opDefEvaluator; + private final SymbolNodeValueLookupProvider symbolNodeValueLookupProvider; + private final TLAClass tlaClass; + + private OpDeclNode[] variablesNodes; // The state variables. + private ExternalModuleTable moduleTbl; // The external modules reachable from root + private ModuleNode rootModule; // The root module. + private Set<OpDefNode> processedDefs; + private SpecObj specObj; + private Defns snapshot; + + private Vect<Action> initPredVec; // The initial state predicate. + private Action nextPred; // The next state predicate. + private Action[] temporals; // Fairness specifications... + private String[] temporalNames; // ... and their names + private Action[] impliedTemporals; // Liveness conds to check... + private String[] impliedTemporalNames; // ... and their names + private Action[] invariants; // Invariants to be checked... + private String[] invNames; // ... and their names + private Action[] impliedInits; // Implied-inits to be checked... + private String[] impliedInitNames; // ... and their names + private Action[] impliedActions; // Implied-actions to be checked... + private String[] impliedActNames; // ... and their names + private ExprNode[] modelConstraints; // Model constraints + private ExprNode[] actionConstraints; // Action constraints + private ExprNode[] assumptions; // Assumpt ions + private boolean[] assumptionIsAxiom; // assumptionIsAxiom[i] is true iff assumptions[i] // is an AXIOM. Added 26 May 2010 by LL - private FilenameToStream resolver; // takes car of path to stream resoltion + + private Vect<Action> invVec = new Vect<>(); + private Vect<String> invNameVec = new Vect<>(); + private Vect<Action> impliedInitVec = new Vect<>(); + private Vect<String> impliedInitNameVec = new Vect<>(); + private Vect<Action> impliedActionVec = new Vect<>(); + private Vect<String> impliedActNameVec = new Vect<>(); + private Vect<Action> temporalVec = new Vect<>(); + private Vect<String> temporalNameVec = new Vect<>(); + private Vect<Action> impliedTemporalVec = new Vect<>(); + private Vect<String> impliedTemporalNameVec = new Vect<>(); + + public SpecProcessor(final String rootFile, final FilenameToStream resolver, final int toolId, final Defns defns, + final ModelConfig config, final SymbolNodeValueLookupProvider snvlp, final OpDefEvaluator ode, + final TLAClass tlaClass) { + super(); + this.rootFile = rootFile; + this.resolver = resolver; + this.toolId = toolId; + this.defns = defns; + this.config = config; + this.tlaClass = tlaClass; + this.processedDefs = new HashSet<OpDefNode>(); + this.initPredVec = new Vect<>(5); + + opDefEvaluator = ode; + symbolNodeValueLookupProvider = snvlp; - public Spec(String specDir, String file, FilenameToStream resolver) - { - this.processedDefs = new HashSet(); - this.specDir = specDir; - this.rootFile = file; - this.rootModule = null; - this.config = null; - this.moduleTbl = null; - this.variablesNodes = null; - this.defns = new Defns(); - this.tlaClass = new TLAClass("tlc2.module"); - this.initPredVec = new Vect(5); - this.nextPred = null; - this.temporals = null; - this.temporalNames = null; - this.impliedTemporals = null; - this.impliedTemporalNames = null; - this.invariants = null; - this.invNames = null; - this.impliedInits = null; - this.impliedInitNames = null; - this.impliedActions = null; - this.impliedActNames = null; - this.modelConstraints = null; - this.actionConstraints = null; - this.assumptions = null; - this.assumptionIsAxiom = null; // added 26 May 2010 by LL - this.resolver = resolver; + // Parse and process this spec. + // It takes care of all overrides. + processSpec(); + + snapshot = defns.snapshot(); + + if (opDefEvaluator != null) { + // Pre-evaluate all the definitions in the spec that are constants. + processConstantDefns(); + } + + // Finally, process the config file. + processConfig(); + } + + /** + * This method converts every definition that is constant into TLC + * value. By doing this, TLC avoids evaluating the same expression + * multiple times. + * + * The method runs for every module in the module tables. + * + * Modified by LL on 23 July 2013 so it is not run for modules that are + * instantiated and have parameters (CONSTANT or VARIABLE declarations) + */ + private void processConstantDefns() { + ModuleNode[] mods = this.moduleTbl.getModuleNodes(); + for (int i = 0; i < mods.length; i++) { + if ( (! mods[i].isInstantiated()) + || ( (mods[i].getConstantDecls().length == 0) + && (mods[i].getVariableDecls().length == 0) ) ) { + this.processConstantDefns(mods[i]); + } + } } - // SZ Feb 20, 2009: added support to name resolver, to be able to run outside of the tool - public Spec(String specDir, String specFile, String configFile, FilenameToStream resolver) - { - this(specDir, specFile, resolver); - // SZ Mar 9, 2009: added initialization of the modelValue class - ModelValue.init(); - this.configFile = configFile; - this.config = new ModelConfig(configFile + ".cfg", resolver); - this.config.parse(); - ModelValue.setValues(); // called after seeing all model values + /** + * Converts the constant definitions in the corresponding value for the + * module -- that is, it "converts" (which seems to mean calling deepNormalize) + * the values substituted for the declared constants. On 17 Mar 2012 it was + * modified by LL to evaluate the OpDefNode when a defined operator is substituted + * for an ordinary declared constant (not a declared operator constant). Without this + * evaluation, the definition gets re-evaluated every time TLC evaluates the declared + * constant. LL also added a check that an operator substituted for the declared + * constant also has the correct arity. + * + * @param mod the module to run on + */ + private void processConstantDefns(ModuleNode mod) { + + // run for constant definitions + OpDeclNode[] consts = mod.getConstantDecls(); + for (int i = 0; i < consts.length; i++) { + Object val = consts[i].getToolObject(toolId); + if (val != null && val instanceof IValue) { + // We do not wrap this value in a WorkerValue, because we assume that explicit + // initialization does not pose a problem here. This is based on the observation, + // that val is either an atom (IValue#isAtom) or a set (of sets) of atoms (primarily + // ModelValues). + ((IValue)val).initialize(); + // System.err.println(consts[i].getName() + ": " + val); + } // The following else clause was added by LL on 17 March 2012. + else if (val != null && val instanceof OpDefNode) { + OpDefNode opDef = (OpDefNode) val; + // The following check logically belongs in Spec.processSpec, but it's not there. + // So, LL just added it here. This error cannot occur when running TLC from + // the Toolbox. + Assert.check(opDef.getArity() == consts[i].getArity(), + EC.TLC_CONFIG_WRONG_SUBSTITUTION_NUMBER_OF_ARGS, + new String[] {consts[i].getName().toString(), opDef.getName().toString()}); + + if (opDef.getArity() == 0) { + try { + Object defVal = WorkerValue.demux(opDefEvaluator, consts[i], opDef); + opDef.setToolObject(toolId, defVal); + } catch (Assert.TLCRuntimeException | EvalException e) { + final String addendum = (e instanceof EvalException) ? "" : (" - specifically: " + e.getMessage()); + Assert.fail(EC.TLC_CONFIG_SUBSTITUTION_NON_CONSTANT, + new String[] { consts[i].getName().toString(), opDef.getName().toString(), addendum }); + } + } + } + } + + // run for constant operator definitions + OpDefNode[] opDefs = mod.getOpDefs(); + DEFS: for (int i = 0; i < opDefs.length; i++) { + OpDefNode opDef = opDefs[i]; + + // The following variable evaluate and its value added by LL on 24 July 2013 + // to prevent pre-evaluation of a definition from an EXTENDS of a module that + // is also instantiated. + ModuleNode moduleNode = opDef.getOriginallyDefinedInModuleNode() ; + boolean evaluate = (moduleNode == null) + || (! moduleNode.isInstantiated()) + || ( (moduleNode.getConstantDecls().length == 0) + && (moduleNode.getVariableDecls().length == 0) ) ; + + if (evaluate && opDef.getArity() == 0) { + Object realDef = symbolNodeValueLookupProvider.lookup(opDef, Context.Empty, false, toolId); + if (realDef instanceof OpDefNode) { + opDef = (OpDefNode)realDef; + if (symbolNodeValueLookupProvider.getLevelBound(opDef.getBody(), Context.Empty, toolId) == 0) { + try { + UniqueString opName = opDef.getName(); + if (isVetoed(opName)) { + continue DEFS; + } + // System.err.println(opName); + final Object val = WorkerValue.demux(opDefEvaluator, opDef); + // System.err.println(opName + ": " + val); + opDef.setToolObject(toolId, val); + Object def = this.defns.get(opName); + if (def == opDef) { + this.defns.put(opName, val); + } + } + catch (Throwable swallow) { + // We get here when Op fails to evaluate. e is swallowed because Op might e.g. be + // Reals!Infinity from the standard module that has to be redefined iff it appears + // in the actual spec. Another example is TLC!TLCGet(42) that the code above + // attempts to evaluate that fails with an EvalException. By definition, TLCGet + // is not constant. + } + } + } + } + } + + // run for all inner modules + ModuleNode[] imods = mod.getInnerModules(); + for (int i = 0; i < imods.length; i++) { + this.processConstantDefns(imods[i]); + } } + public static final String LAZY_CONSTANT_OPERATORS = SpecProcessor.class.getName() + ".vetoed"; + + private static final Set<String> vetos = new HashSet<String>( + Arrays.asList(System.getProperty(LAZY_CONSTANT_OPERATORS, ""))); + + private boolean isVetoed(final UniqueString us) { + return vetos.contains(us.toString()); + } + /** * Processes the specification and collects information to be used * by tools. The processing tries to use any customized module (Java * class) to override the corresponding TLA+ module. */ // SZ Feb 20, 2009: added support for existing specObj - protected final SpecObj processSpec(SpecObj spec) + private final void processSpec() { - if (spec == null) - { - // construct new specification object, if the - // passed one was null - spec = new SpecObj(this.rootFile, resolver); - - // We first call the SANY front-end to parse and semantic-analyze - // the complete TLA+ spec starting with the main module rootFile. - if (TLCGlobals.tool) - { - MP.printMessage(EC.TLC_SANY_START); - } - try - { - // SZ Feb 20, 2009: - // call SANY to parse the module - // this method will not throw any exceptions on - // checked errors (init, parse, semantic). - // Only if something unexpected happens the - // exception is thrown - SANY.frontEndMain(spec, this.rootFile, ToolIO.out); - } catch (FrontEndException e) - { - Assert.fail(EC.TLC_PARSING_FAILED2, e); - } + // construct new specification object, if the + // passed one was null + specObj = new SpecObj(this.rootFile, resolver); - if (TLCGlobals.tool) - { - MP.printMessage(EC.TLC_SANY_END); - } - // The following statement moved here by LL on 11 March 2011 - MP.printMessage(EC.TLC_STARTING); + // We first call the SANY front-end to parse and semantic-analyze + // the complete TLA+ spec starting with the main module rootFile. + if (TLCGlobals.tool) + { + MP.printMessage(EC.TLC_SANY_START); + } + try + { + // SZ Feb 20, 2009: + // call SANY to parse the module + // this method will not throw any exceptions on + // checked errors (init, parse, semantic). + // Only if something unexpected happens the + // exception is thrown + SANY.frontEndMain(specObj, this.rootFile, ToolIO.out); + } catch (FrontEndException e) + { + Assert.fail(EC.TLC_PARSING_FAILED2, e); + } + if (TLCGlobals.tool) + { + MP.printMessage(EC.TLC_SANY_END); } + // The following statement moved here by LL on 11 March 2011 + MP.printMessage(EC.TLC_STARTING); // SZ Feb 20, 2009: // since failed parsing is not marked by an exception, // check the status of the spec // check if the specification has been successfully created - if (!spec.initErrors.isSuccess() || !spec.parseErrors.isSuccess() || !spec.semanticErrors.isSuccess()) + if (!specObj.initErrors.isSuccess() || !specObj.parseErrors.isSuccess() || !specObj.semanticErrors.isSuccess()) { Assert.fail(EC.TLC_PARSING_FAILED); } // Set the rootModule: - this.moduleTbl = spec.getExternalModuleTable(); + this.moduleTbl = specObj.getExternalModuleTable(); UniqueString rootName = UniqueString.uniqueStringOf(this.rootFile); this.rootModule = this.moduleTbl.getModuleNode(rootName); + + Assert.check(this.rootModule != null, EC.TLC_PARSING_FAILED2, + String.format(" Module-Table lookup failure for module name %s derived from %s file name.", + rootName.toString(), this.rootFile)); // Get all the state variables in the spec: OpDeclNode[] varDecls = this.rootModule.getVariableDecls(); @@ -211,9 +376,6 @@ public class Spec implements ValueConstants, ToolGlobals, Serializable varNames[i].setLoc(i); } - // set variables to the static filed in the state - TLCState.setVariables(this.variablesNodes); - // SZ 11.04.2009: set the number of variables UniqueString.setVariableCount(varDecls.length); @@ -226,14 +388,14 @@ public class Spec implements ValueConstants, ToolGlobals, Serializable this.defns.setDefnCount(varDecls.length); // Add predefined (Boolean and String) in defns. - this.defns.put("TRUE", ValTrue); - this.defns.put("FALSE", ValFalse); + this.defns.put("TRUE", BoolValue.ValTrue); + this.defns.put("FALSE", BoolValue.ValFalse); Value[] elems = new Value[2]; - elems[0] = ValFalse; - elems[1] = ValTrue; + elems[0] = BoolValue.ValFalse; + elems[1] = BoolValue.ValTrue; this.defns.put("BOOLEAN", new SetEnumValue(elems, true)); - Class stringModule = this.tlaClass.loadClass("Strings"); + Class<?> stringModule = this.tlaClass.loadClass("Strings"); if (stringModule == null) { @@ -253,9 +415,7 @@ public class Spec implements ValueConstants, ToolGlobals, Serializable // should not be accessed by application code. Thus, exclude // synthetic members from being processed. if (!ms[i].isSynthetic()) { - MethodValue mv = new MethodValue(ms[i]); - Value val = (acnt == 0) ? mv.apply(EmptyArgs, EvalControl.Clear) : mv; - this.defns.put(name, val); + this.defns.put(name, MethodValue.get(ms[i])); } } } @@ -264,11 +424,11 @@ public class Spec implements ValueConstants, ToolGlobals, Serializable // here since we use defns. Things added into defns later will make it // wrong to use it in the method processConstants. ModuleNode[] mods = this.moduleTbl.getModuleNodes(); - HashSet modSet = new HashSet(); + final Map<String, ModuleNode> modSet = new HashMap<String, ModuleNode>(); for (int i = 0; i < mods.length; i++) { this.processConstants(mods[i]); - modSet.add(mods[i].getName().toString()); + modSet.put(mods[i].getName().toString(), mods[i]); } // Collect all the assumptions. @@ -296,7 +456,7 @@ public class Spec implements ValueConstants, ToolGlobals, Serializable { Assert.fail(EC.TLC_CONFIG_VALUE_NOT_ASSIGNED_TO_CONSTANT_PARAM, name.toString()); } - rootConsts[i].setToolObject(TLCGlobals.ToolId, val); + rootConsts[i].setToolObject(toolId, val); this.defns.put(name, val); } @@ -311,18 +471,18 @@ public class Spec implements ValueConstants, ToolGlobals, Serializable this.defns.put(name, rootOpDefs[i]); } else { - rootOpDefs[i].setToolObject(TLCGlobals.ToolId, val); + rootOpDefs[i].setToolObject(toolId, val); this.defns.put(name, val); } } // Apply config file module specific constants to operator defns. // We do not allow this kind of replacement for constant decls. - Hashtable modConstants = this.initializeModConstants(); + Hashtable<String, Hashtable> modConstants = this.initializeModConstants(); for (int i = 0; i < mods.length; i++) { UniqueString modName = mods[i].getName(); - Hashtable mConsts = (Hashtable) modConstants.get(modName.toString()); + Hashtable mConsts = modConstants.get(modName.toString()); if (mConsts != null) { OpDefNode[] opDefs = mods[i].getOpDefs(); @@ -332,7 +492,7 @@ public class Spec implements ValueConstants, ToolGlobals, Serializable Object val = mConsts.get(name.toString()); if (val != null) { - opDefs[j].getBody().setToolObject(TLCGlobals.ToolId, val); + opDefs[j].getBody().setToolObject(toolId, val); } } } @@ -341,26 +501,55 @@ public class Spec implements ValueConstants, ToolGlobals, Serializable // Apply module overrides: for (int i = 0; i < mods.length; i++) { - UniqueString modName = mods[i].getName(); - Class userModule = this.tlaClass.loadClass(modName.toString()); + + final UniqueString modName = mods[i].getName(); + final Class<?> userModule = this.tlaClass.loadClass(modName.toString()); if (userModule != null) { + final Map<UniqueString, Integer> opname2arity = new HashMap<>(); + if (!BuiltInModuleHelper.isBuiltInModule(userModule)) { + // Remember arity for non built-in overrides to later match with java override + // when loading. + for (OpDefNode opDefNode : rootOpDefs) { + if (opDefNode.getOriginallyDefinedInModuleNode().getName().equals(modName)) { + opname2arity.put(opDefNode.getName(), opDefNode.getArity()); + } + } + } // Override with a user defined Java class for the TLA+ module. // Collects new definitions: - Hashtable javaDefs = new Hashtable(); - Method[] mds = userModule.getDeclaredMethods(); + final Hashtable<UniqueString, IValue> javaDefs = new Hashtable<UniqueString, IValue>(); + final Method[] mds = userModule.getDeclaredMethods(); for (int j = 0; j < mds.length; j++) { - int mdf = mds[j].getModifiers(); + final Method method = mds[j]; + int mdf = method.getModifiers(); if (Modifier.isPublic(mdf) && Modifier.isStatic(mdf)) { - String name = TLARegistry.mapName(mds[j].getName()); + String name = TLARegistry.mapName(method.getName()); UniqueString uname = UniqueString.uniqueStringOf(name); - int acnt = mds[j].getParameterTypes().length; - MethodValue mv = new MethodValue(mds[j]); - boolean isConstant = (acnt == 0) && Modifier.isFinal(mdf); - Value val = isConstant ? mv.apply(EmptyArgs, EvalControl.Clear) : mv; - javaDefs.put(uname, val); + if (method.getAnnotation(TLAPlusOperator.class) != null) { + // Skip, handled below with annotation based mechanism. + continue; + } + final int acnt = method.getParameterCount(); + final Value val = MethodValue.get(method); + + if (!BuiltInModuleHelper.isBuiltInModule(userModule)) { + final URL resource = userModule.getResource(userModule.getSimpleName() + ".class"); + // Print success or failure of loading the module override (arity mismatch). + final Integer arity = opname2arity.get(uname); + if (arity == null || arity != acnt) { + MP.printWarning(EC.TLC_MODULE_VALUE_JAVA_METHOD_OVERRIDE_MISMATCH, uname.toString(), + resource.toExternalForm(), val.toString()); + } else { + javaDefs.put(uname, val); + MP.printMessage(EC.TLC_MODULE_VALUE_JAVA_METHOD_OVERRIDE_LOADED, uname.toString(), + resource.toExternalForm(), val.toString()); + } + } else { + javaDefs.put(uname, val); + } } } // Adds/overrides new definitions: @@ -371,14 +560,133 @@ public class Spec implements ValueConstants, ToolGlobals, Serializable Object val = javaDefs.get(uname); if (val != null) { - opDefs[j].getBody().setToolObject(TLCGlobals.ToolId, val); + opDefs[j].getBody().setToolObject(toolId, val); this.defns.put(uname, val); } } } } + // Load override definitions through user-provided index class. In other words, + // a user creates a class that implements the interface ITLCOverrides. + // ITLCOverride defines a single method that returns an array of classes which + // define Java overrides (this approach is simpler and faster than scanning + // the complete classpath). The convention is to name the index class + // "tlc2.overrides.TLCOverrides": + //TODO: Support multiple loader classes (MyTLCOverrides, AnotherTLCOverrides, ...). + boolean hasCallableValue = false; + final Class<?> idx = this.tlaClass.loadClass("tlc2.overrides.TLCOverrides"); + if (idx != null && ITLCOverrides.class.isAssignableFrom(idx)) { + try { + final ITLCOverrides index = (ITLCOverrides) idx.newInstance(); + final Class<?>[] candidateClasses = index.get(); + for (Class<?> c : candidateClasses) { + final Method[] candidateMethods = c.getDeclaredMethods(); + LOOP: for (Method m : candidateMethods) { + + + final Evaluation evaluation = m.getAnnotation(Evaluation.class); + if (evaluation != null) { + final Value val = new EvaluatingValue(m, evaluation.minLevel()); + + final ModuleNode moduleNode = modSet.get(evaluation.module()); + if (moduleNode == null) { + if (evaluation.warn()) MP.printMessage(EC.TLC_MODULE_VALUE_JAVA_METHOD_OVERRIDE_MODULE_MISMATCH, + evaluation.module() + "!" + evaluation.definition(), + c.getResource(c.getSimpleName() + ".class").toExternalForm(), val.toString()); + continue LOOP; + } + final OpDefNode opDef = moduleNode.getOpDef(evaluation.definition()); + if (opDef == null) { + if (evaluation.warn()) MP.printMessage(EC.TLC_MODULE_VALUE_JAVA_METHOD_OVERRIDE_IDENTIFIER_MISMATCH, + evaluation.module() + "!" + evaluation.definition(), + c.getResource(c.getSimpleName() + ".class").toExternalForm(), val.toString()); + continue LOOP; + } + + opDef.getBody().setToolObject(toolId, val); + this.defns.put(evaluation.definition(), val); + + // Print success of loading the module override. + MP.printMessage(EC.TLC_MODULE_VALUE_JAVA_METHOD_OVERRIDE_LOADED, + evaluation.module() + "!" + evaluation.definition(), + c.getResource(c.getSimpleName() + ".class").toExternalForm(), val.toString()); + + // continue with next method (don't try to also load Execution annotation below). + continue LOOP; + } + + final TLAPlusCallable jev = m.getAnnotation(TLAPlusCallable.class); + if (jev != null) { + final Value val = new CallableValue(m, jev.minLevel()); + + final ModuleNode moduleNode = modSet.get(jev.module()); + if (moduleNode == null) { + if (jev.warn()) MP.printMessage(EC.TLC_MODULE_VALUE_JAVA_METHOD_OVERRIDE_MODULE_MISMATCH, + jev.module() + "!" + jev.definition(), + c.getResource(c.getSimpleName() + ".class").toExternalForm(), val.toString()); + continue LOOP; + } + final OpDefNode opDef = moduleNode.getOpDef(jev.definition()); + if (opDef == null) { + if (jev.warn()) MP.printMessage(EC.TLC_MODULE_VALUE_JAVA_METHOD_OVERRIDE_IDENTIFIER_MISMATCH, + jev.module() + "!" + jev.definition(), + c.getResource(c.getSimpleName() + ".class").toExternalForm(), val.toString()); + continue LOOP; + } + + opDef.getBody().setToolObject(toolId, val); + this.defns.put(jev.definition(), val); + hasCallableValue = true; + + // Print success of loading the module override. + MP.printMessage(EC.TLC_MODULE_VALUE_JAVA_METHOD_OVERRIDE_LOADED, + jev.module() + "!" + jev.definition(), + c.getResource(c.getSimpleName() + ".class").toExternalForm(), val.toString()); + + // continue with next method (don't try to also load Execution annotation below). + continue LOOP; + } + + final TLAPlusOperator opOverrideCandidate = m.getAnnotation(TLAPlusOperator.class); + if (opOverrideCandidate != null) { + final ModuleNode moduleNode = modSet.get(opOverrideCandidate.module()); + if (moduleNode == null) { + if (opOverrideCandidate.warn()) MP.printWarning(EC.TLC_MODULE_VALUE_JAVA_METHOD_OVERRIDE_MODULE_MISMATCH, + opOverrideCandidate.identifier(), opOverrideCandidate.module(), m.toString()); + continue LOOP; + } + final OpDefNode opDef = moduleNode.getOpDef(opOverrideCandidate.identifier()); + if (opDef == null) { + if (opOverrideCandidate.warn()) MP.printWarning(EC.TLC_MODULE_VALUE_JAVA_METHOD_OVERRIDE_IDENTIFIER_MISMATCH, + opOverrideCandidate.identifier(), opOverrideCandidate.module(), m.toString()); + continue LOOP; + } + + final Value val = MethodValue.get(m, opOverrideCandidate.minLevel()); + if (opDef.getArity() != m.getParameterCount()) { + if (opOverrideCandidate.warn()) MP.printWarning(EC.TLC_MODULE_VALUE_JAVA_METHOD_OVERRIDE_MISMATCH, + opDef.getName().toString(), c.getName(), val.toString()); + continue LOOP; + } else { + if (opOverrideCandidate.warn()) MP.printMessage(EC.TLC_MODULE_VALUE_JAVA_METHOD_OVERRIDE_LOADED, + opDef.getName().toString(), c.getName(), + val instanceof MethodValue ? val.toString() : val.getClass().getName()); // toString of non-MethodValue instances can be expensive. + } + + opDef.getBody().setToolObject(toolId, val); + this.defns.put(opOverrideCandidate.identifier(), val); + } + } + } + } catch (InstantiationException | IllegalAccessException e) { + // TODO Specific error code. + Assert.fail(EC.GENERAL); + return; + } + } + - HashSet overriden = new HashSet(); + Set<String> overriden = new HashSet<String>(); // Apply config file overrides to constants: for (int i = 0; i < rootConsts.length; i++) { @@ -395,12 +703,19 @@ public class Spec implements ValueConstants, ToolGlobals, Serializable { Assert.fail(EC.TLC_CONFIG_WRONG_SUBSTITUTION, new String[] { lhs.toString(), rhs }); } - rootConsts[i].setToolObject(TLCGlobals.ToolId, myVal); + rootConsts[i].setToolObject(toolId, myVal); this.defns.put(lhs, myVal); overriden.add(lhs.toString()); } } + // set variables to the static filed in the state + if (hasCallableValue) { + TLAPlusExecutorState.setVariables(this.variablesNodes); + } else { + TLCStateMut.setVariables(this.variablesNodes); + } + // Apply config file overrides to operator definitions: for (int i = 0; i < rootOpDefs.length; i++) { @@ -422,7 +737,7 @@ public class Spec implements ValueConstants, ToolGlobals, Serializable { Assert.fail(EC.TLC_CONFIG_WRONG_SUBSTITUTION_NUMBER_OF_ARGS, new String[] { lhs.toString(), rhs }); } - rootOpDefs[i].setToolObject(TLCGlobals.ToolId, myVal); + rootOpDefs[i].setToolObject(toolId, myVal); this.defns.put(lhs, myVal); overriden.add(lhs.toString()); } @@ -440,12 +755,12 @@ public class Spec implements ValueConstants, ToolGlobals, Serializable // Apply config file module specific overrides to operator defns. // We do not allow this kind of replacement for constant decls. - Hashtable modOverrides = this.config.getModOverrides(); + Hashtable<String, Hashtable> modOverrides = this.config.getModOverrides(); for (int i = 0; i < mods.length; i++) { UniqueString modName = mods[i].getName(); - Hashtable mDefs = (Hashtable) modOverrides.get(modName.toString()); - HashSet modOverriden = new HashSet(); + Hashtable mDefs = modOverrides.get(modName.toString()); + HashSet<String> modOverriden = new HashSet<>(); if (mDefs != null) { // the operator definitions: @@ -472,7 +787,7 @@ public class Spec implements ValueConstants, ToolGlobals, Serializable Assert.fail(EC.TLC_CONFIG_WRONG_SUBSTITUTION_NUMBER_OF_ARGS, new String[] { lhs.toString(), rhs }); } - opDefs[j].getBody().setToolObject(TLCGlobals.ToolId, myVal); + opDefs[j].getBody().setToolObject(toolId, myVal); modOverriden.add(lhs.toString()); } } @@ -494,367 +809,120 @@ public class Spec implements ValueConstants, ToolGlobals, Serializable while (modKeys.hasMoreElements()) { Object modName = modKeys.nextElement(); - if (!modSet.contains(modName)) + if (!modSet.keySet().contains(modName)) { Assert.fail(EC.TLC_NO_MODULES, modName.toString()); } } - - return spec; } - /************************************************************************* - * The following method goes through all the nodes to set their * - * tool-specific fields. It was modified on 1 May 2007 so it would find * - * the nodes in the body of a Lambda expression. Obviously, if new * - * semantic node types are added, this method will have to be modified. * - * Less obviously, if a tool wants to call TLC on a specification that * - * was not all created inside a module, then this method may need to be * - * modified so TLC finds those nodes not part of the module. * - * * - * Yuan claims that this is the only method in TLC that has to find all * - * the nodes in such a way. * - *************************************************************************/ - private final void processConstants(SemanticNode expr) + /** + * Process the configuration file. + */ + private final void processConfig() { - switch (expr.getKind()) { - case ModuleKind: { - ModuleNode expr1 = (ModuleNode) expr; - // Process operator definitions: - OpDefNode[] opDefs = expr1.getOpDefs(); - for (int i = 0; i < opDefs.length; i++) + // Process the invariants: + this.processConfigInvariants(); + + // Process specification: + String specName = this.config.getSpec(); + if (specName.length() == 0) + { + this.processConfigInitAndNext(); + } else + { + if (this.config.getInit().length() != 0 || this.config.getNext().length() != 0) { - Object def = opDefs[i].getToolObject(TLCGlobals.ToolId); - if (def instanceof OpDefNode) + Assert.fail(EC.TLC_CONFIG_NOT_BOTH_SPEC_AND_INIT); + } + Object spec = this.defns.get(specName); + if (spec instanceof OpDefNode) + { + OpDefNode opDef = (OpDefNode) spec; + if (opDef.getArity() != 0) { - this.processedDefs.add(def); - this.processConstants(((OpDefNode) def).getBody()); + Assert.fail(EC.TLC_CONFIG_ID_REQUIRES_NO_ARG, new String[] { specName }); } - this.processConstants(opDefs[i].getBody()); - } - // Process all the inner modules: - ModuleNode[] imods = expr1.getInnerModules(); - for (int i = 0; i < imods.length; i++) + this.processConfigSpec(opDef.getBody(), Context.Empty, List.Empty); + } else if (spec == null) { - this.processConstants(imods[i]); - } - // Process all the assumptions: - AssumeNode[] assumps = expr1.getAssumptions(); - for (int i = 0; i < assumps.length; i++) + Assert.fail(EC.TLC_CONFIG_SPECIFIED_NOT_DEFINED, new String[] { "name", specName }); + } else { - this.processConstants(assumps[i]); - } - // On 13 Nov 2009, Yuan Yu added the following - // processing of all TheoremNodes, which was needed to - // allow Theorem and Assumption names to be used as expressions. - // - // Process all the theorems: - TheoremNode[] thms = expr1.getTheorems(); - for (int i = 0; i < thms.length; i++) { - this.processConstants(thms[i]); + Assert.fail(EC.TLC_CONFIG_ID_HAS_VALUE, new String[] { "value", specName, spec.toString() }); } - - return; } - case OpApplKind: { - OpApplNode expr1 = (OpApplNode) expr; - SymbolNode opNode = expr1.getOperator(); - Object val = this.defns.get(opNode.getName()); - if (val != null) - { - opNode.setToolObject(TLCGlobals.ToolId, val); - } else + + // Process the properties: + Vect<String> propNames = this.config.getProperties(); + for (int i = 0; i < propNames.size(); i++) + { + String propName = (String) propNames.elementAt(i); + Object prop = this.defns.get(propName); + if (prop instanceof OpDefNode) { - SemanticNode[] args = expr1.getArgs(); - for (int i = 0; i < args.length; i++) - { - if (args[i] != null) - { - this.processConstants(args[i]); - } - } - ExprNode[] bnds = expr1.getBdedQuantBounds(); - for (int i = 0; i < bnds.length; i++) + OpDefNode opDef = (OpDefNode) prop; + if (opDef.getArity() != 0) { - this.processConstants(bnds[i]); + Assert.fail(EC.TLC_CONFIG_ID_REQUIRES_NO_ARG, new String[] { propName }); } - } - return; - } - case LetInKind: { - LetInNode expr1 = (LetInNode) expr; - OpDefNode[] letDefs = expr1.getLets(); - for (int i = 0; i < letDefs.length; i++) + this.processConfigProps(propName, opDef.getBody(), Context.Empty, List.Empty); + } else if (prop == null) { - this.processConstants(letDefs[i].getBody()); - } - this.processConstants(expr1.getBody()); - return; - } - case SubstInKind: { - SubstInNode expr1 = (SubstInNode) expr; - Subst[] subs = expr1.getSubsts(); - for (int i = 0; i < subs.length; i++) + Assert.fail(EC.TLC_CONFIG_SPECIFIED_NOT_DEFINED, new String[] { "property", propName }); + } else if (!(prop instanceof IBoolValue) || !(((BoolValue) prop).val)) { - this.processConstants(subs[i].getExpr()); + Assert.fail(EC.TLC_CONFIG_ID_HAS_VALUE, new String[] { "property", propName, prop.toString() }); } - this.processConstants(expr1.getBody()); - return; } - // Added by LL on 13 Nov 2009 to handle theorem and assumption names. - case APSubstInKind: { - APSubstInNode expr1 = (APSubstInNode) expr; - Subst[] subs = expr1.getSubsts(); - for (int i = 0; i < subs.length; i++) - { - this.processConstants(subs[i].getExpr()); - } - this.processConstants(expr1.getBody()); - return; + // Postprocess: + this.invariants = new Action[this.invVec.size()]; + this.invNames = new String[this.invVec.size()]; + for (int i = 0; i < this.invariants.length; i++) + { + this.invariants[i] = (Action) this.invVec.elementAt(i); + this.invNames[i] = (String) this.invNameVec.elementAt(i); } + this.invVec = null; + this.invNameVec = null; + this.impliedInits = new Action[this.impliedInitVec.size()]; + this.impliedInitNames = new String[this.impliedInitVec.size()]; + for (int i = 0; i < this.impliedInits.length; i++) + { + this.impliedInits[i] = (Action) this.impliedInitVec.elementAt(i); + this.impliedInitNames[i] = (String) this.impliedInitNameVec.elementAt(i); + } + this.impliedInitVec = null; + this.impliedInitNameVec = null; - case NumeralKind: { - NumeralNode expr1 = (NumeralNode) expr; - IntValue val = IntValue.gen(expr1.val()); - // LL added this test on 20 Jul 2011; otherwise - // TLC treats a number bigger than MAX_VALUE - // (2^31-1 or 2,147,483,647) as if it equals 0. - if (expr1.bigVal() != null) { - Assert.fail(EC.TLC_INTEGER_TOO_BIG, expr1.toString()); - return; - } - expr1.setToolObject(TLCGlobals.ToolId, val); - return; + this.impliedActions = new Action[this.impliedActionVec.size()]; + this.impliedActNames = new String[this.impliedActionVec.size()]; + for (int i = 0; i < this.impliedActions.length; i++) + { + this.impliedActions[i] = (Action) this.impliedActionVec.elementAt(i); + this.impliedActNames[i] = (String) this.impliedActNameVec.elementAt(i); } - case DecimalKind: { - DecimalNode expr1 = (DecimalNode) expr; // SZ: using typed variable - Assert.fail(EC.TLC_CANT_HANDLE_REAL_NUMBERS, expr1.toString()); - return; + this.impliedActionVec = null; + this.impliedActNameVec = null; + + this.temporals = new Action[this.temporalVec.size()]; + this.temporalNames = new String[this.temporalNameVec.size()]; + for (int i = 0; i < this.temporals.length; i++) + { + this.temporals[i] = (Action) this.temporalVec.elementAt(i); + this.temporalNames[i] = (String) this.temporalNameVec.elementAt(i); } - case StringKind: { - StringNode expr1 = (StringNode) expr; - StringValue val = new StringValue(expr1.getRep()); - expr1.setToolObject(TLCGlobals.ToolId, val); - return; - } - case AssumeKind: { - AssumeNode expr1 = (AssumeNode) expr; - this.processConstants(expr1.getAssume()); - return; - } - // On 13 Nov 2009, Yuan Yu added the following case, which was - // needed to allow Theorem and Assumption names to be used as - // expressions. - // - case TheoremKind: - { - TheoremNode expr1 = (TheoremNode)expr; - this.processConstants(expr1.getTheorem()); - return; - } - case OpArgKind: { - SymbolNode opArgNode = ((OpArgNode) expr).getOp(); - if (opArgNode.getKind() == UserDefinedOpKind) - { OpDefNode opdef = (OpDefNode) opArgNode ; - if (! processedDefs.contains(opdef)) { - processedDefs.add(opdef) ; - this.processConstants(opdef.getBody()); - } - } - return; - } - /*********************************************************************** - * LabelKind case added by LL on 13 Jun 2007. * - ***********************************************************************/ - case LabelKind: { - LabelNode expr1 = (LabelNode) expr; - this.processConstants(expr1.getBody()); - } - } - } - - /* Return the variable if expr is a state variable. Otherwise, null. */ - public final SymbolNode getVar(SemanticNode expr, Context c, boolean cutoff) - { - if (expr instanceof OpApplNode) - { - SymbolNode opNode = ((OpApplNode) expr).getOperator(); - - if (opNode.getArity() == 0) - { - boolean isVarDecl = (opNode.getKind() == VariableDeclKind); - Object val = this.lookup(opNode, c, cutoff && isVarDecl); - - if (val instanceof LazyValue) - { - LazyValue lval = (LazyValue) val; - return this.getVar(lval.expr, lval.con, cutoff); - } - if (val instanceof OpDefNode) - { - return this.getVar(((OpDefNode) val).getBody(), c, cutoff); - } - if (isVarDecl) - { - return opNode; - } - } - } - return null; - } - - /* Return the variable if expr is a primed state variable. Otherwise, null. */ - public final SymbolNode getPrimedVar(SemanticNode expr, Context c, boolean cutoff) - { - if (expr instanceof OpApplNode) - { - OpApplNode expr1 = (OpApplNode) expr; - SymbolNode opNode = expr1.getOperator(); - - if (BuiltInOPs.getOpCode(opNode.getName()) == OPCODE_prime) - { - return this.getVar(expr1.getArgs()[0], c, cutoff); - } - - if (opNode.getArity() == 0) - { - boolean isVarDecl = (opNode.getKind() == VariableDeclKind); - Object val = this.lookup(opNode, c, cutoff && isVarDecl); - - if (val instanceof LazyValue) - { - LazyValue lval = (LazyValue) val; - return this.getPrimedVar(lval.expr, lval.con, cutoff); - } - if (val instanceof OpDefNode) - { - return this.getPrimedVar(((OpDefNode) val).getBody(), c, cutoff); - } - } - } - return null; - } - - private Vect invVec = new Vect(); - private Vect invNameVec = new Vect(); - private Vect impliedInitVec = new Vect(); - private Vect impliedInitNameVec = new Vect(); - private Vect impliedActionVec = new Vect(); - private Vect impliedActNameVec = new Vect(); - private Vect temporalVec = new Vect(); - private Vect temporalNameVec = new Vect(); - private Vect impliedTemporalVec = new Vect(); - private Vect impliedTemporalNameVec = new Vect(); - - /** - * Process the configuration file. - */ - public final void processConfig() - { - // Process the invariants: - this.processConfigInvariants(); - - // Process specification: - String specName = this.config.getSpec(); - if (specName.length() == 0) - { - this.processConfigInitAndNext(); - } else - { - if (this.config.getInit().length() != 0 || this.config.getNext().length() != 0) - { - Assert.fail(EC.TLC_CONFIG_NOT_BOTH_SPEC_AND_INIT); - } - Object spec = this.defns.get(specName); - if (spec instanceof OpDefNode) - { - OpDefNode opDef = (OpDefNode) spec; - if (opDef.getArity() != 0) - { - Assert.fail(EC.TLC_CONFIG_ID_REQUIRES_NO_ARG, new String[] { specName }); - } - this.processConfigSpec(opDef.getBody(), Context.Empty, List.Empty); - } else if (spec == null) - { - Assert.fail(EC.TLC_CONFIG_SPECIFIED_NOT_DEFINED, new String[] { "name", specName }); - } else - { - Assert.fail(EC.TLC_CONFIG_ID_HAS_VALUE, new String[] { "value", specName, spec.toString() }); - } - } - - // Process the properties: - Vect propNames = this.config.getProperties(); - for (int i = 0; i < propNames.size(); i++) - { - String propName = (String) propNames.elementAt(i); - Object prop = this.defns.get(propName); - if (prop instanceof OpDefNode) - { - OpDefNode opDef = (OpDefNode) prop; - if (opDef.getArity() != 0) - { - Assert.fail(EC.TLC_CONFIG_ID_REQUIRES_NO_ARG, new String[] { propName }); - } - this.processConfigProps(propName, opDef.getBody(), Context.Empty, List.Empty); - } else if (prop == null) - { - Assert.fail(EC.TLC_CONFIG_SPECIFIED_NOT_DEFINED, new String[] { "property", propName }); - } else if (!(prop instanceof BoolValue) || !(((BoolValue) prop).val)) - { - Assert.fail(EC.TLC_CONFIG_ID_HAS_VALUE, new String[] { "property", propName, prop.toString() }); - } - } - - // Postprocess: - this.invariants = new Action[this.invVec.size()]; - this.invNames = new String[this.invVec.size()]; - for (int i = 0; i < this.invariants.length; i++) - { - this.invariants[i] = (Action) this.invVec.elementAt(i); - this.invNames[i] = (String) this.invNameVec.elementAt(i); - } - this.invVec = null; - this.invNameVec = null; - - this.impliedInits = new Action[this.impliedInitVec.size()]; - this.impliedInitNames = new String[this.impliedInitVec.size()]; - for (int i = 0; i < this.impliedInits.length; i++) - { - this.impliedInits[i] = (Action) this.impliedInitVec.elementAt(i); - this.impliedInitNames[i] = (String) this.impliedInitNameVec.elementAt(i); - } - this.impliedInitVec = null; - this.impliedInitNameVec = null; - - this.impliedActions = new Action[this.impliedActionVec.size()]; - this.impliedActNames = new String[this.impliedActionVec.size()]; - for (int i = 0; i < this.impliedActions.length; i++) - { - this.impliedActions[i] = (Action) this.impliedActionVec.elementAt(i); - this.impliedActNames[i] = (String) this.impliedActNameVec.elementAt(i); - } - this.impliedActionVec = null; - this.impliedActNameVec = null; - - this.temporals = new Action[this.temporalVec.size()]; - this.temporalNames = new String[this.temporalNameVec.size()]; - for (int i = 0; i < this.temporals.length; i++) - { - this.temporals[i] = (Action) this.temporalVec.elementAt(i); - this.temporalNames[i] = (String) this.temporalNameVec.elementAt(i); - } - this.temporalVec = null; - this.temporalNameVec = null; - - this.impliedTemporals = new Action[this.impliedTemporalVec.size()]; - this.impliedTemporalNames = new String[this.impliedTemporalNameVec.size()]; - for (int i = 0; i < this.impliedTemporals.length; i++) - { - this.impliedTemporals[i] = (Action) this.impliedTemporalVec.elementAt(i); - this.impliedTemporalNames[i] = (String) this.impliedTemporalNameVec.elementAt(i); + this.temporalVec = null; + this.temporalNameVec = null; + + this.impliedTemporals = new Action[this.impliedTemporalVec.size()]; + this.impliedTemporalNames = new String[this.impliedTemporalNameVec.size()]; + for (int i = 0; i < this.impliedTemporals.length; i++) + { + this.impliedTemporals[i] = (Action) this.impliedTemporalVec.elementAt(i); + this.impliedTemporalNames[i] = (String) this.impliedTemporalNameVec.elementAt(i); } this.impliedTemporalVec = null; this.impliedTemporalNameVec = null; @@ -870,6 +938,15 @@ public class Spec implements ValueConstants, ToolGlobals, Serializable { Assert.fail(EC.TLC_CONFIG_MISSING_NEXT); } + + // Process the model constraints in the config. It's done after all + // other config processing because it used to be processed lazy upon the + // first invocation of getModelConstraints. However, this caused a NPE + // in distributed mode due to concurrency issues and there was no + // apparent reason to process the model constraints lazily. + processModelConstraints(); + + processActionConstraints(); } /** @@ -894,7 +971,7 @@ public class Spec implements ValueConstants, ToolGlobals, Serializable { Assert.fail(EC.TLC_CONFIG_ID_REQUIRES_NO_ARG, new String[] { "initial predicate", name }); } - this.initPredVec.addElement(new Action(def.getBody(), Context.Empty)); + this.initPredVec.addElement(new Action(def.getBody(), Context.Empty, def)); } name = this.config.getNext(); @@ -914,7 +991,7 @@ public class Spec implements ValueConstants, ToolGlobals, Serializable { Assert.fail(EC.TLC_CONFIG_ID_REQUIRES_NO_ARG, new String[] { "next state action", name }); } - this.nextPred = new Action(def.getBody(), Context.Empty); + this.nextPred = new Action(def.getBody(), Context.Empty, def); } } @@ -934,13 +1011,27 @@ public class Spec implements ValueConstants, ToolGlobals, Serializable if (def.getArity() != 0) { Assert.fail(EC.TLC_CONFIG_ID_REQUIRES_NO_ARG, new String[] { "invariant", name }); + } + // MK 07/25/2017: Check if the invariant is a valid state predicate and produce + // a meaningful warning otherwise. With this enhancement, a rare bug in TLC's + // level-checking surfaced for which we don't have a fix right now. Fortunately, + // the bug is rather unlikely which is why TLC simply produces a warning for now + // if it "thinks" a user might be affected by the bug. + // see LevelNode.java line 590ff, Test52, TestInvalidInvariant, and related files + // for more context. + if (def.getLevel() >= 2) + { + if (!def.getBody().levelParams.isEmpty()) { + Assert.fail(EC.TLC_INVARIANT_VIOLATED_LEVEL, new String[] { def.getName().toString(), "includeWarning" }); + } + Assert.fail(EC.TLC_INVARIANT_VIOLATED_LEVEL, def.getName().toString()); } this.invNameVec.addElement(name); - this.invVec.addElement(new Action(def.getBody(), Context.Empty)); + this.invVec.addElement(new Action(def.getBody(), Context.Empty, def)); } else if (inv == null) { Assert.fail(EC.TLC_CONFIG_SPECIFIED_NOT_DEFINED, new String[] { "invariant", name }); - } else if (!(inv instanceof BoolValue) || !(((BoolValue) inv).val)) + } else if (!(inv instanceof IBoolValue) || !(((BoolValue) inv).val)) { Assert.fail(EC.TLC_CONFIG_ID_HAS_VALUE, new String[] { "invariant", name, inv.toString() }); } @@ -965,7 +1056,7 @@ public class Spec implements ValueConstants, ToolGlobals, Serializable if (args.length == 0) { SymbolNode opNode = pred1.getOperator(); - Object val = this.lookup(opNode, c, false); + Object val = symbolNodeValueLookupProvider.lookup(opNode, c, false, toolId); if (val instanceof OpDefNode) { if (((OpDefNode) val).getArity() != 0) @@ -973,9 +1064,9 @@ public class Spec implements ValueConstants, ToolGlobals, Serializable Assert.fail(EC.TLC_CONFIG_OP_NO_ARGS, new String[] { opNode.getName().toString() }); } ExprNode body = ((OpDefNode) val).getBody(); - if (this.getLevelBound(body, c) == 1) + if (symbolNodeValueLookupProvider.getLevelBound(body, c, toolId) == 1) { - this.initPredVec.addElement(new Action(Spec.addSubsts(body, subs), c)); + this.initPredVec.addElement(new Action(Specs.addSubsts(body, subs), c, ((OpDefNode) val))); } else { this.processConfigSpec(body, c, subs); @@ -983,7 +1074,7 @@ public class Spec implements ValueConstants, ToolGlobals, Serializable } else if (val == null) { Assert.fail(EC.TLC_CONFIG_OP_NOT_IN_SPEC, new String[] { opNode.getName().toString() }); - } else if (val instanceof BoolValue) + } else if (val instanceof IBoolValue) { if (!((BoolValue) val).val) { @@ -999,6 +1090,10 @@ public class Spec implements ValueConstants, ToolGlobals, Serializable } int opcode = BuiltInOPs.getOpCode(pred1.getOperator().getName()); + if ((opcode == OPCODE_te) || (opcode == OPCODE_tf)) + { + Assert.fail(EC.TLC_SPECIFICATION_FEATURES_TEMPORAL_QUANTIFIER); + } if (opcode == OPCODE_cl || opcode == OPCODE_land) { for (int i = 0; i < args.length; i++) @@ -1032,17 +1127,17 @@ public class Spec implements ValueConstants, ToolGlobals, Serializable * of this specification object -- otherwise TLC's * analysis is incorrect. **/ - Vect components; + Vect<SymbolNode> components; SubscriptCollector() { - this.components = new Vect(); + this.components = new Vect<>(); } void enter(ExprNode subscript, Context c) { // if it's a variable, add it to the vector and return - SymbolNode var = getVar(subscript, c, false); + SymbolNode var = symbolNodeValueLookupProvider.getVar(subscript, c, false, toolId); if (var != null) { components.addElement(var); @@ -1070,11 +1165,11 @@ public class Spec implements ValueConstants, ToolGlobals, Serializable return; } // user-defined operator: look up its definition - Object opDef = lookup(opNode, c, false); + Object opDef = symbolNodeValueLookupProvider.lookup(opNode, c, false, toolId); if (opDef instanceof OpDefNode) { OpDefNode opDef1 = (OpDefNode) opDef; - this.enter(opDef1.getBody(), getOpContext(opDef1, args, c, false)); + this.enter(opDef1.getBody(), symbolNodeValueLookupProvider.getOpContext(opDef1, args, c, false, toolId)); return; } if (opDef instanceof LazyValue) @@ -1092,7 +1187,7 @@ public class Spec implements ValueConstants, ToolGlobals, Serializable Context c1 = c; for (int i = 0; i < subs.length; i++) { - c1 = c1.cons(subs[i].getOp(), getVal(subs[i].getExpr(), c, false)); + c1 = c1.cons(subs[i].getOp(), symbolNodeValueLookupProvider.getVal(subs[i].getExpr(), c, false, toolId)); } this.enter(subscript1.getBody(), c1); return; @@ -1122,7 +1217,7 @@ public class Spec implements ValueConstants, ToolGlobals, Serializable } } - Vect getComponents() + Vect<SymbolNode> getComponents() { return components; } @@ -1137,7 +1232,7 @@ public class Spec implements ValueConstants, ToolGlobals, Serializable Subst[] snsubs = sn.getSubsts(); for (int i = 0; i < snsubs.length; i++) { - c1 = c1.cons(snsubs[i].getOp(), getVal(snsubs[i].getExpr(), c, false)); + c1 = c1.cons(snsubs[i].getOp(), symbolNodeValueLookupProvider.getVal(snsubs[i].getExpr(), c, false, toolId)); } subs1 = subs1.cdr(); } @@ -1165,7 +1260,7 @@ public class Spec implements ValueConstants, ToolGlobals, Serializable } if (this.nextPred == null) { - this.nextPred = new Action(Spec.addSubsts(arg, subs), c); + this.nextPred = new Action(Specs.addSubsts(arg, subs), c); } else { Assert.fail(EC.TLC_CANT_HANDLE_TOO_MANY_NEXT_STATE_RELS); @@ -1173,7 +1268,7 @@ public class Spec implements ValueConstants, ToolGlobals, Serializable // ---sm 09/06/04 >>> } else { - this.temporalVec.addElement(new Action(Spec.addSubsts(pred, subs), c)); + this.temporalVec.addElement(new Action(Specs.addSubsts(pred, subs), c)); this.temporalNameVec.addElement(pred.toString()); } return; @@ -1186,13 +1281,13 @@ public class Spec implements ValueConstants, ToolGlobals, Serializable } } - int level = this.getLevelBound(pred, c); + int level = symbolNodeValueLookupProvider.getLevelBound(pred, c, toolId); if (level <= 1) { - this.initPredVec.addElement(new Action(Spec.addSubsts(pred, subs), c)); + this.initPredVec.addElement(new Action(Specs.addSubsts(pred, subs), c)); } else if (level == 3) { - this.temporalVec.addElement(new Action(Spec.addSubsts(pred, subs), c)); + this.temporalVec.addElement(new Action(Specs.addSubsts(pred, subs), c)); this.temporalNameVec.addElement(pred.toString()); } else { @@ -1216,7 +1311,7 @@ public class Spec implements ValueConstants, ToolGlobals, Serializable if (args.length == 0) { SymbolNode opNode = pred1.getOperator(); - Object val = this.lookup(opNode, c, false); + Object val = symbolNodeValueLookupProvider.lookup(opNode, c, false, toolId); if (val instanceof OpDefNode) { if (((OpDefNode) val).getArity() != 0) @@ -1227,7 +1322,7 @@ public class Spec implements ValueConstants, ToolGlobals, Serializable } else if (val == null) { Assert.fail(EC.TLC_CONFIG_OP_NOT_IN_SPEC, opNode.getName().toString()); - } else if (val instanceof BoolValue) + } else if (val instanceof IBoolValue) { if (!((BoolValue) val).val) { @@ -1263,10 +1358,10 @@ public class Spec implements ValueConstants, ToolGlobals, Serializable name = boxArg1.getOperator().getName().toString(); } this.impliedActNameVec.addElement(name); - this.impliedActionVec.addElement(new Action(Spec.addSubsts(boxArg, subs), c)); - } else if (this.getLevelBound(boxArg, c) < 2) + this.impliedActionVec.addElement(new Action(Specs.addSubsts(boxArg, subs), c)); + } else if (symbolNodeValueLookupProvider.getLevelBound(boxArg, c, toolId) < 2) { - this.invVec.addElement(new Action(Spec.addSubsts(boxArg, subs), c)); + this.invVec.addElement(new Action(Specs.addSubsts(boxArg, subs), c)); if ((boxArg instanceof OpApplNode) && (((OpApplNode) boxArg).getArgs().length == 0)) { name = ((OpApplNode) boxArg).getOperator().getName().toString(); @@ -1274,7 +1369,7 @@ public class Spec implements ValueConstants, ToolGlobals, Serializable this.invNameVec.addElement(name); } else { - this.impliedTemporalVec.addElement(new Action(Spec.addSubsts(pred, subs), c)); + this.impliedTemporalVec.addElement(new Action(Specs.addSubsts(pred, subs), c)); this.impliedTemporalNameVec.addElement(name); } return; @@ -1286,874 +1381,414 @@ public class Spec implements ValueConstants, ToolGlobals, Serializable return; } } - int level = this.getLevelBound(pred, c); + int level = symbolNodeValueLookupProvider.getLevelBound(pred, c, toolId); if (level <= 1) { - this.impliedInitVec.addElement(new Action(Spec.addSubsts(pred, subs), c)); + this.impliedInitVec.addElement(new Action(Specs.addSubsts(pred, subs), c)); this.impliedInitNameVec.addElement(name); } else if (level == 3) { - this.impliedTemporalVec.addElement(new Action(Spec.addSubsts(pred, subs), c)); + this.impliedTemporalVec.addElement(new Action(Specs.addSubsts(pred, subs), c)); this.impliedTemporalNameVec.addElement(name); } else { Assert.fail(EC.TLC_CONFIG_PROPERTY_NOT_CORRECTLY_DEFINED, name); } } - - private final Hashtable makeConstantTable(Vect consts) + + private void processActionConstraints() { + Vect names = this.config.getActionConstraints(); + this.actionConstraints = new ExprNode[names.size()]; + int idx = 0; + for (int i = 0; i < names.size(); i++) + { + String name = (String) names.elementAt(i); + Object constr = this.defns.get(name); + if (constr instanceof OpDefNode) + { + OpDefNode def = (OpDefNode) constr; + if (def.getArity() != 0) + { + Assert.fail(EC.TLC_CONFIG_ID_REQUIRES_NO_ARG, new String[] { "action constraint", name }); + } + this.actionConstraints[idx++] = def.getBody(); + } else if (constr != null) + { + if (!(constr instanceof IBoolValue) || !((BoolValue) constr).val) + { + Assert.fail(EC.TLC_CONFIG_ID_HAS_VALUE, + new String[] { "action constraint", name, constr.toString() }); + } + } else + { + Assert.fail(EC.TLC_CONFIG_SPECIFIED_NOT_DEFINED, new String[] { "action constraint", name }); + } + } + // Shrink in case array has been overallocated + if (idx < this.actionConstraints.length) + { + ExprNode[] constrs = new ExprNode[idx]; + for (int i = 0; i < idx; i++) + { + constrs[i] = this.actionConstraints[i]; + } + this.actionConstraints = constrs; + } + } + + private final void processModelConstraints() { + Vect names = this.config.getConstraints(); + this.modelConstraints = new ExprNode[names.size()]; + int idx = 0; + for (int i = 0; i < names.size(); i++) + { + String name = (String) names.elementAt(i); + Object constr = this.defns.get(name); + if (constr instanceof OpDefNode) + { + OpDefNode def = (OpDefNode) constr; + if (def.getArity() != 0) + { + Assert.fail(EC.TLC_CONFIG_ID_REQUIRES_NO_ARG, new String[] { "constraint", name }); + } + this.modelConstraints[idx++] = def.getBody(); + } else if (constr != null) + { + if (!(constr instanceof IBoolValue) || !((BoolValue) constr).val) + { + Assert.fail(EC.TLC_CONFIG_ID_HAS_VALUE, new String[] { "constraint", name, constr.toString() }); + } + } else + { + Assert.fail(EC.TLC_CONFIG_SPECIFIED_NOT_DEFINED, new String[] { "constraint", name }); + } + } + // Shrink modelContraints in case we allocated a too large array. See + // nested if block above for why some constraints don't get instantiated. + if (idx < this.modelConstraints.length) + { + ExprNode[] constrs = new ExprNode[idx]; + for (int i = 0; i < idx; i++) + { + constrs[i] = this.modelConstraints[i]; + } + this.modelConstraints = constrs; + } + } + + /************************************************************************* + * The following method goes through all the nodes to set their * + * tool-specific fields. It was modified on 1 May 2007 so it would find * + * the nodes in the body of a Lambda expression. Obviously, if new * + * semantic node types are added, this method will have to be modified. * + * Less obviously, if a tool wants to call TLC on a specification that * + * was not all created inside a module, then this method may need to be * + * modified so TLC finds those nodes not part of the module. * + * * + * Yuan claims that this is the only method in TLC that has to find all * + * the nodes in such a way. * + *************************************************************************/ + private final void processConstants(SemanticNode expr) { - Hashtable constTbl = new Hashtable(); - for (int i = 0; i < consts.size(); i++) - { - Vect line = (Vect) consts.elementAt(i); - int len = line.size(); - String name = (String) line.elementAt(0); - if (len <= 2) - { - constTbl.put(name, line.elementAt(1)); - } else + switch (expr.getKind()) { + case ModuleKind: { + ModuleNode expr1 = (ModuleNode) expr; + // Process operator definitions: + OpDefNode[] opDefs = expr1.getOpDefs(); + for (int i = 0; i < opDefs.length; i++) { - Object val = constTbl.get(name); - if (val == null) - { - OpRcdValue opVal = new OpRcdValue(); - opVal.addLine(line); - constTbl.put(name, opVal); - } else + Object def = opDefs[i].getToolObject(toolId); + if (def instanceof OpDefNode) { - OpRcdValue opVal = (OpRcdValue) val; - int arity = ((Value[]) opVal.domain.elementAt(0)).length; - if (len != arity + 2) - { - Assert.fail(EC.TLC_CONFIG_OP_ARITY_INCONSISTENT, name); - } - opVal.addLine(line); + this.processedDefs.add((OpDefNode) def); + this.processConstants(((OpDefNode) def).getBody()); } + this.processConstants(opDefs[i].getBody()); } - } - return constTbl; - } - - /** - * Initialize the spec constants using the config file. - */ - public final Hashtable initializeConstants() - { - Vect consts = this.config.getConstants(); - if (consts == null) - { - return new Hashtable(); - } - return this.makeConstantTable(consts); - } - - public final Hashtable initializeModConstants() - { - Hashtable modConstants = this.config.getModConstants(); - Hashtable constants = new Hashtable(); - Enumeration mods = modConstants.keys(); - while (mods.hasMoreElements()) - { - String modName = (String) mods.nextElement(); - constants.put(modName, this.makeConstantTable((Vect) modConstants.get(modName))); - } - return constants; - } - - /** - * Get model constraints. - */ - public final ExprNode[] getModelConstraints() - { - if (this.modelConstraints != null) - { - return this.modelConstraints; - } - Vect names = this.config.getConstraints(); - this.modelConstraints = new ExprNode[names.size()]; - int idx = 0; - for (int i = 0; i < names.size(); i++) - { - String name = (String) names.elementAt(i); - Object constr = this.defns.get(name); - if (constr instanceof OpDefNode) + // Process all the inner modules: + ModuleNode[] imods = expr1.getInnerModules(); + for (int i = 0; i < imods.length; i++) { - OpDefNode def = (OpDefNode) constr; - if (def.getArity() != 0) - { - Assert.fail(EC.TLC_CONFIG_ID_REQUIRES_NO_ARG, new String[] { "constraint", name }); - } - this.modelConstraints[idx++] = def.getBody(); - } else if (constr != null) - { - if (!(constr instanceof BoolValue) || !((BoolValue) constr).val) - { - Assert.fail(EC.TLC_CONFIG_ID_HAS_VALUE, new String[] { "constraint", name, constr.toString() }); - } - } else - { - Assert.fail(EC.TLC_CONFIG_SPECIFIED_NOT_DEFINED, new String[] { "constraint", name }); - } - } - if (idx < this.modelConstraints.length) - { - ExprNode[] constrs = new ExprNode[idx]; - for (int i = 0; i < idx; i++) - { - constrs[i] = this.modelConstraints[i]; - } - this.modelConstraints = constrs; - } - return this.modelConstraints; - } - - /** - * Get action constraints. - */ - public final ExprNode[] getActionConstraints() - { - if (this.actionConstraints != null) - { - return this.actionConstraints; - } - Vect names = this.config.getActionConstraints(); - this.actionConstraints = new ExprNode[names.size()]; - int idx = 0; - for (int i = 0; i < names.size(); i++) - { - String name = (String) names.elementAt(i); - Object constr = this.defns.get(name); - if (constr instanceof OpDefNode) - { - OpDefNode def = (OpDefNode) constr; - if (def.getArity() != 0) - { - Assert.fail(EC.TLC_CONFIG_ID_REQUIRES_NO_ARG, new String[] { "action constraint", name }); - } - this.actionConstraints[idx++] = def.getBody(); - } else if (constr != null) - { - if (!(constr instanceof BoolValue) || !((BoolValue) constr).val) - { - Assert.fail(EC.TLC_CONFIG_ID_HAS_VALUE, - new String[] { "action constraint", name, constr.toString() }); - } - } else - { - Assert.fail(EC.TLC_CONFIG_SPECIFIED_NOT_DEFINED, new String[] { "action constraint", name }); + this.processConstants(imods[i]); } - } - if (idx < this.actionConstraints.length) - { - ExprNode[] constrs = new ExprNode[idx]; - for (int i = 0; i < idx; i++) + // Process all the assumptions: + AssumeNode[] assumps = expr1.getAssumptions(); + for (int i = 0; i < assumps.length; i++) { - constrs[i] = this.actionConstraints[i]; - } - this.actionConstraints = constrs; - } - return this.actionConstraints; - } - - /* Get the initial state predicate of the specification. */ - public final Vect getInitStateSpec() - { - return this.initPredVec; - } - - /* Get the action (next state) predicate of the specification. */ - public final Action getNextStateSpec() - { - return this.nextPred; - } - - /** - * Get the view mapping for the specification. - */ - public final SemanticNode getViewSpec() - { - String name = this.config.getView(); - if (name.length() == 0) - return null; - - Object view = this.defns.get(name); - if (view == null) - { - Assert.fail(EC.TLC_CONFIG_SPECIFIED_NOT_DEFINED, new String[] { "view function", name }); - } - if (!(view instanceof OpDefNode)) - { - Assert.fail(EC.TLC_CONFIG_ID_MUST_NOT_BE_CONSTANT, new String[] { "view function", name }); - } - OpDefNode def = (OpDefNode) view; - if (def.getArity() != 0) - { - Assert.fail(EC.TLC_CONFIG_ID_REQUIRES_NO_ARG, new String[] { "view function", name }); - } - return def.getBody(); - } - - /* Get the type declaration for the state variables. */ - public final SemanticNode getTypeSpec() - { - String name = this.config.getType(); - if (name.length() == 0) - { - Assert.fail(EC.TLC_CONFIG_NO_STATE_TYPE); - } - - Object type = this.defns.get(name); - if (type == null) - { - Assert.fail(EC.TLC_CONFIG_SPECIFIED_NOT_DEFINED, new String[] { "type", name }); - } - if (!(type instanceof OpDefNode)) - { - Assert.fail(EC.TLC_CONFIG_ID_MUST_NOT_BE_CONSTANT, new String[] { "type", name }); - } - OpDefNode def = (OpDefNode) type; - if (def.getArity() != 0) - { - Assert.fail(EC.TLC_CONFIG_ID_REQUIRES_NO_ARG, new String[] { "type", name }); - } - return def.getBody(); - } - - /* Get the type declaration for the state variables. */ - public final SemanticNode getTypeConstraintSpec() - { - String name = this.config.getTypeConstraint(); - if (name.length() == 0) - { - return null; - } - - Object type = this.defns.get(name); - if (type == null) - { - Assert.fail(EC.TLC_CONFIG_SPECIFIED_NOT_DEFINED, new String[] { "type constraint", name }); - } - if (!(type instanceof OpDefNode)) - { - Assert.fail(EC.TLC_CONFIG_ID_MUST_NOT_BE_CONSTANT, new String[] { "type constraint", name }); - } - OpDefNode def = (OpDefNode) type; - if (def.getArity() != 0) - { - Assert.fail(EC.TLC_CONFIG_ID_REQUIRES_NO_ARG, new String[] { "type cinstraint", name }); - - } - return def.getBody(); - } - - public final boolean livenessIsTrue() - { - return this.impliedTemporals.length == 0; - } - - /* Get the fairness condition of the specification. */ - public final Action[] getTemporals() - { - return this.temporals; - } - - public final String[] getTemporalNames() - { - return this.temporalNames; - } - - /* Get the liveness checks of the specification. */ - public final Action[] getImpliedTemporals() - { - return this.impliedTemporals; - } - - public final String[] getImpliedTemporalNames() - { - return this.impliedTemporalNames; - } - - /* Get the invariants of the specification. */ - public final Action[] getInvariants() - { - return this.invariants; - } - - public final String[] getInvNames() - { - return this.invNames; - } - - /* Get the implied-inits of the specification. */ - public final Action[] getImpliedInits() - { - return this.impliedInits; - } - - public final String[] getImpliedInitNames() - { - return this.impliedInitNames; - } - - /* Get the implied-actions of the specification. */ - public final Action[] getImpliedActions() - { - return this.impliedActions; - } - - public final String[] getImpliedActNames() - { - return this.impliedActNames; - } - - /* Get the assumptions of the specification. */ - public final ExprNode[] getAssumptions() - { - return this.assumptions; - } - - /* Get the assumptionIsAxiom field */ - public final boolean[] getAssumptionIsAxiom() { - return this.assumptionIsAxiom; - } - /** - * This method gets the value of a symbol from the enviroment. We - * look up in the context c, its tool object, and the state s. - * - * It and the lookup method that follows it were modified by LL - * on 10 April 2011 to fix the following bug. When a constant definition - * Foo == ... - * is overridden to substitute Bar for Foo, the TLC tool object for - * the body of Foo's OpDef node is set to the OpDefNode for Bar. - * When evaluating a use of Foo, the lookup method is apparently - * supposed to return the OpDefNode for Bar. (I don't understand - * how the callers make use of the returned value.) That's what it - * does for uses of Foo in the module in which Foo is defined. - * However, if Foo is imported by instantiation with renaming as - * X!Foo, then it appears that looking up X!Foo should also return - * the OpDefNode for Bar. If the instantiated module had no - * parameters, then that's what happened because the body of the - * OpDefNode for X!Foo is the same (contains a pointer to the - * same object) as the body of Foo's OpDefNode. However, that - * wasn't the case if the instantiated module had parameters, - * because then X!Foo's OpDefNode consists of a sequence of - * nested SubstInNode objects, the last of which points to - * the body of Foo's OpDefNode. So, LL modified the lookup - * methods so they follow the sequence of SubstInNode bodies - * down to the body of Foo's OpDefNode when looking up the result. - * (If a SubstInNode has a non-null TLC tool object for a - * SubstInNode, then it returns that object. I don't think this - * should ever be the case, and if it is, I have no idea what the - * lookup method should do.) - * - */ - public final Object lookup(SymbolNode opNode, Context c, TLCState s, boolean cutoff) - { - boolean isVarDecl = (opNode.getKind() == VariableDeclKind); - Object result = c.lookup(opNode, cutoff && isVarDecl); - if (result != null) - return result; - - result = opNode.getToolObject(TLCGlobals.ToolId); - if (result != null) - return result; - - if (opNode.getKind() == UserDefinedOpKind) - { - // Changed by LL on 10 Apr 2011 from - // - // result = ((OpDefNode) opNode).getBody().getToolObject(TLCGlobals.ToolId); - // - // to the following - ExprNode body = ((OpDefNode) opNode).getBody(); - result = body.getToolObject(TLCGlobals.ToolId); - while ((result == null) && (body.getKind() == SubstInKind)) { - body = ((SubstInNode) body).getBody(); - result = body.getToolObject(TLCGlobals.ToolId); + this.processConstants(assumps[i]); } - // end change - - if (result != null) - return result; - } - - result = s.lookup(opNode.getName()); - if (result != null) - return result; - return opNode; - } - - public final Object lookup(SymbolNode opNode, Context c, boolean cutoff) - { - boolean isVarDecl = (opNode.getKind() == VariableDeclKind); - Object result = c.lookup(opNode, cutoff && isVarDecl); - if (result != null) - return result; - - result = opNode.getToolObject(TLCGlobals.ToolId); - if (result != null) - return result; - - if (opNode.getKind() == UserDefinedOpKind) - { - // Changed by LL on 10 Apr 2011 from - // - // result = ((OpDefNode) opNode).getBody().getToolObject(TLCGlobals.ToolId); + // On 13 Nov 2009, Yuan Yu added the following + // processing of all TheoremNodes, which was needed to + // allow Theorem and Assumption names to be used as expressions. // - // to the following - ExprNode body = ((OpDefNode) opNode).getBody(); - result = body.getToolObject(TLCGlobals.ToolId); - while ((result == null) && (body.getKind() == SubstInKind)) { - body = ((SubstInNode) body).getBody(); - result = body.getToolObject(TLCGlobals.ToolId); - } - // end change - if (result != null) - return result; - } - return opNode; - } - - public final Object getVal(ExprOrOpArgNode expr, Context c, boolean cachable) - { - if (expr instanceof ExprNode) - { - LazyValue lval = new LazyValue(expr, c); - if (!cachable) - { - lval.setUncachable(); - } - return lval; - } - SymbolNode opNode = ((OpArgNode) expr).getOp(); - return this.lookup(opNode, c, false); - } - - public final Context getOpContext(OpDefNode opDef, ExprOrOpArgNode[] args, Context c, boolean cachable) - { - FormalParamNode[] formals = opDef.getParams(); - int alen = args.length; - Context c1 = c; - for (int i = 0; i < alen; i++) - { - Object aval = this.getVal(args[i], c, cachable); - c1 = c1.cons(formals[i], aval); - } - return c1; - } - - /** - * The following added by LL on 23 October 2012 to fix bug in evaluation of names of theorems and - * assumptions imported by parameterized instantiation. - * - * @param opDef - * @param args - * @param c - * @param cachable - * @return - */ - public final Context getOpContext(ThmOrAssumpDefNode opDef, ExprOrOpArgNode[] args, Context c, boolean cachable) - { - FormalParamNode[] formals = opDef.getParams(); - int alen = args.length; - Context c1 = c; - for (int i = 0; i < alen; i++) - { - Object aval = this.getVal(args[i], c, cachable); - c1 = c1.cons(formals[i], aval); - } - return c1; - } - /** - * Return a table containing the locations of subexpression in the - * spec of forms x' = e and x' \in e. Warning: Current implementation - * may not be able to find all such locations. - */ - public final ObjLongTable getPrimedLocs() - { - ObjLongTable tbl = new ObjLongTable(10); - Action act = this.getNextStateSpec(); - this.collectPrimedLocs(act.pred, act.con, tbl); - return tbl; - } - - public final void collectPrimedLocs(SemanticNode pred, Context c, ObjLongTable tbl) - { - switch (pred.getKind()) { - case OpApplKind: { - OpApplNode pred1 = (OpApplNode) pred; - this.collectPrimedLocsAppl(pred1, c, tbl); - return; - } - case LetInKind: { - LetInNode pred1 = (LetInNode) pred; - this.collectPrimedLocs(pred1.getBody(), c, tbl); - return; - } - case SubstInKind: { - SubstInNode pred1 = (SubstInNode) pred; - Subst[] subs = pred1.getSubsts(); - Context c1 = c; - for (int i = 0; i < subs.length; i++) - { - Subst sub = subs[i]; - c1 = c1.cons(sub.getOp(), this.getVal(sub.getExpr(), c, true)); - } - this.collectPrimedLocs(pred1.getBody(), c, tbl); - return; - } - - // Added by LL on 13 Nov 2009 to handle theorem and assumption names. - case APSubstInKind: { - APSubstInNode pred1 = (APSubstInNode) pred; - Subst[] subs = pred1.getSubsts(); - Context c1 = c; - for (int i = 0; i < subs.length; i++) - { - Subst sub = subs[i]; - c1 = c1.cons(sub.getOp(), this.getVal(sub.getExpr(), c, true)); + // Process all the theorems: + TheoremNode[] thms = expr1.getTheorems(); + for (int i = 0; i < thms.length; i++) { + this.processConstants(thms[i]); } - this.collectPrimedLocs(pred1.getBody(), c, tbl); - return; - } - - /*********************************************************************** - * LabelKind case added by LL on 13 Jun 2007. * - ***********************************************************************/ - case LabelKind: { - LabelNode pred1 = (LabelNode) pred; - this.collectPrimedLocs(pred1.getBody(), c, tbl); return; } - } - } - - private final void collectPrimedLocsAppl(OpApplNode pred, Context c, ObjLongTable tbl) - { - ExprOrOpArgNode[] args = pred.getArgs(); - SymbolNode opNode = pred.getOperator(); - int opcode = BuiltInOPs.getOpCode(opNode.getName()); - - switch (opcode) { - case OPCODE_fa: // FcnApply - { - this.collectPrimedLocs(args[0], c, tbl); - break; - } - case OPCODE_ite: // IfThenElse - { - this.collectPrimedLocs(args[1], c, tbl); - this.collectPrimedLocs(args[2], c, tbl); - break; - } - case OPCODE_case: // Case - { - for (int i = 0; i < args.length; i++) - { - OpApplNode pair = (OpApplNode) args[i]; - this.collectPrimedLocs(pair.getArgs()[1], c, tbl); - } - break; - } - case OPCODE_eq: - case OPCODE_in: { - SymbolNode var = this.getPrimedVar(args[0], c, false); - if (var != null && var.getName().getVarLoc() != -1) - { - tbl.put(pred.toString(), 0); - } - break; - } - case OPCODE_cl: // ConjList - case OPCODE_dl: // DisjList - case OPCODE_be: // BoundedExists - case OPCODE_bf: // BoundedForall - case OPCODE_land: - case OPCODE_lor: - case OPCODE_implies: - case OPCODE_nop: // This case added 13 Nov 2009 by LL to handle subexpression names. - { - for (int i = 0; i < args.length; i++) - { - this.collectPrimedLocs(args[i], c, tbl); - } - break; - } - case OPCODE_unchanged: { - this.collectUnchangedLocs(args[0], c, tbl); - break; - } - case OPCODE_aa: // AngleAct <A>_e - { - this.collectPrimedLocs(args[0], c, tbl); - break; - } - case OPCODE_sa: // [A]_e - { - this.collectPrimedLocs(args[0], c, tbl); - tbl.put(args[1].toString(), 0); - break; - } - default: { - if (opcode == 0) - { - Object val = this.lookup(opNode, c, false); - - if (val instanceof OpDefNode) - { - OpDefNode opDef = (OpDefNode) val; - // Following added by LL on 10 Apr 2010 to avoid infinite - // recursion for recursive operator definitions - if (opDef.getInRecursive()) { - return ; - } - Context c1 = this.getOpContext(opDef, args, c, true); - this.collectPrimedLocs(opDef.getBody(), c1, tbl); - } else if (val instanceof LazyValue) - { - LazyValue lv = (LazyValue) val; - this.collectPrimedLocs(lv.expr, lv.con, tbl); - } - } - } - } - } - - private final void collectUnchangedLocs(SemanticNode expr, Context c, ObjLongTable tbl) - { - if (expr instanceof OpApplNode) - { + case OpApplKind: { OpApplNode expr1 = (OpApplNode) expr; SymbolNode opNode = expr1.getOperator(); - UniqueString opName = opNode.getName(); - int opcode = BuiltInOPs.getOpCode(opName); - - if (opName.getVarLoc() >= 0) + Object val = this.defns.get(opNode.getName()); + if (val != null) { - // a state variable: - tbl.put(expr.toString(), 0); - return; - } - - ExprOrOpArgNode[] args = expr1.getArgs(); - if (opcode == OPCODE_tup) + opNode.setToolObject(toolId, val); + } else { - // a tuple: + SemanticNode[] args = expr1.getArgs(); for (int i = 0; i < args.length; i++) { - this.collectUnchangedLocs(args[i], c, tbl); + if (args[i] != null) + { + this.processConstants(args[i]); + } } - return; - } - - if (opcode == 0 && args.length == 0) - { - // a 0-arity operator: - Object val = this.lookup(opNode, c, false); - if (val instanceof OpDefNode) + ExprNode[] bnds = expr1.getBdedQuantBounds(); + for (int i = 0; i < bnds.length; i++) { - this.collectUnchangedLocs(((OpDefNode) val).getBody(), c, tbl); - return; + this.processConstants(bnds[i]); } } - } - return; - } - - /** - * This method only returns an approximation of the level of the - * expression. The "real" level is at most the return value. Adding - * <name, ValOne> to the context means that there is no need to - * compute level for name. - * - * Note that this method does not work if called on a part of an - * EXCEPT expression. - */ - public final int getLevelBound(SemanticNode expr, Context c) - { - switch (expr.getKind()) { - case OpApplKind: { - OpApplNode expr1 = (OpApplNode) expr; - return this.getLevelBoundAppl(expr1, c); + return; } case LetInKind: { LetInNode expr1 = (LetInNode) expr; OpDefNode[] letDefs = expr1.getLets(); - int letLen = letDefs.length; - Context c1 = c; - int level = 0; - for (int i = 0; i < letLen; i++) + for (int i = 0; i < letDefs.length; i++) { - OpDefNode opDef = letDefs[i]; - level = Math.max(level, this.getLevelBound(opDef.getBody(), c1)); - c1 = c1.cons(opDef, ValOne); + this.processConstants(letDefs[i].getBody()); } - return Math.max(level, this.getLevelBound(expr1.getBody(), c1)); + this.processConstants(expr1.getBody()); + return; } case SubstInKind: { SubstInNode expr1 = (SubstInNode) expr; Subst[] subs = expr1.getSubsts(); - int slen = subs.length; - Context c1 = c; - for (int i = 0; i < slen; i++) + for (int i = 0; i < subs.length; i++) { - Subst sub = subs[i]; - c1 = c1.cons(sub.getOp(), this.getVal(sub.getExpr(), c, true)); + this.processConstants(subs[i].getExpr()); } - return this.getLevelBound(expr1.getBody(), c1); + this.processConstants(expr1.getBody()); + return; } // Added by LL on 13 Nov 2009 to handle theorem and assumption names. case APSubstInKind: { APSubstInNode expr1 = (APSubstInNode) expr; Subst[] subs = expr1.getSubsts(); - int slen = subs.length; - Context c1 = c; - for (int i = 0; i < slen; i++) + for (int i = 0; i < subs.length; i++) { - Subst sub = subs[i]; - c1 = c1.cons(sub.getOp(), this.getVal(sub.getExpr(), c, true)); + this.processConstants(subs[i].getExpr()); } - return this.getLevelBound(expr1.getBody(), c1); + this.processConstants(expr1.getBody()); + return; } - /*********************************************************************** - * LabelKind case added by LL on 13 Jun 2007. * - ***********************************************************************/ - case LabelKind: { - LabelNode expr1 = (LabelNode) expr; - return this.getLevelBound(expr1.getBody(), c); - } - default: { - return 0; - } + case NumeralKind: { + NumeralNode expr1 = (NumeralNode) expr; + IntValue val = IntValue.gen(expr1.val()); + // LL added this test on 20 Jul 2011; otherwise + // TLC treats a number bigger than MAX_VALUE + // (2^31-1 or 2,147,483,647) as if it equals 0. + if (expr1.bigVal() != null) { + Assert.fail(EC.TLC_INTEGER_TOO_BIG, expr1.toString()); + return; + } + expr1.setToolObject(toolId, val); + return; } - } - - private final int getLevelBoundAppl(OpApplNode expr, Context c) - { - SymbolNode opNode = expr.getOperator(); - UniqueString opName = opNode.getName(); - int opcode = BuiltInOPs.getOpCode(opName); - - if (BuiltInOPs.isTemporal(opcode)) - { - return 3; // Conservative estimate + case DecimalKind: { + DecimalNode expr1 = (DecimalNode) expr; // SZ: using typed variable + Assert.fail(EC.TLC_CANT_HANDLE_REAL_NUMBERS, expr1.toString()); + return; } - - if (BuiltInOPs.isAction(opcode)) - { - return 2; // Conservative estimate + case StringKind: { + StringNode expr1 = (StringNode) expr; + StringValue val = new StringValue(expr1.getRep()); + expr1.setToolObject(toolId, val); + return; } - - if (opcode == OPCODE_enabled) - { - return 1; // Conservative estimate + case AssumeKind: { + AssumeNode expr1 = (AssumeNode) expr; + this.processConstants(expr1.getAssume()); + return; } - - int level = 0; - ExprNode[] bnds = expr.getBdedQuantBounds(); - for (int i = 0; i < bnds.length; i++) - { - level = Math.max(level, this.getLevelBound(bnds[i], c)); + // On 13 Nov 2009, Yuan Yu added the following case, which was + // needed to allow Theorem and Assumption names to be used as + // expressions. + // + case TheoremKind: + { + TheoremNode expr1 = (TheoremNode)expr; + this.processConstants(expr1.getTheorem()); + return; + } + case OpArgKind: { + SymbolNode opArgNode = ((OpArgNode) expr).getOp(); + if (opArgNode.getKind() == UserDefinedOpKind) + { OpDefNode opdef = (OpDefNode) opArgNode ; + if (! processedDefs.contains(opdef)) { + processedDefs.add(opdef) ; + this.processConstants(opdef.getBody()); + } + } + return; } - - if (opcode == OPCODE_rfs) - { - // For recursive function, don't compute level of the function body - // again in the recursive call. - SymbolNode fname = expr.getUnbdedQuantSymbols()[0]; - c = c.cons(fname, ValOne); + /*********************************************************************** + * LabelKind case added by LL on 13 Jun 2007. * + ***********************************************************************/ + case LabelKind: { + LabelNode expr1 = (LabelNode) expr; + this.processConstants(expr1.getBody()); } - - ExprOrOpArgNode[] args = expr.getArgs(); - int alen = args.length; - for (int i = 0; i < alen; i++) - { - if (args[i] != null) - { - level = Math.max(level, this.getLevelBound(args[i], c)); - } } + } - if (opcode == 0) + private final Hashtable<String, Serializable> makeConstantTable(Vect<Vect<String>> consts) + { + Hashtable<String, Serializable> constTbl = new Hashtable<>(); + for (int i = 0; i < consts.size(); i++) { - // This operator is a user-defined operator. - if (opName.getVarLoc() >= 0) - return 1; - - Object val = this.lookup(opNode, c, false); - if (val instanceof OpDefNode) + Vect<String> line = (Vect<String>) consts.elementAt(i); + int len = line.size(); + String name = (String) line.elementAt(0); + if (len <= 2) { - OpDefNode opDef = (OpDefNode) val; - c = c.cons(opNode, ValOne); - level = Math.max(level, this.getLevelBound(opDef.getBody(), c)); - } else if (val instanceof LazyValue) + constTbl.put(name, line.elementAt(1)); + } else { - LazyValue lv = (LazyValue) val; - level = Math.max(level, this.getLevelBound(lv.expr, lv.con)); + Object val = constTbl.get(name); + if (val == null) + { + OpRcdValue opVal = new OpRcdValue(); + opVal.addLine(line); + constTbl.put(name, opVal); + } else + { + OpRcdValue opVal = (OpRcdValue) val; + int arity = ((IValue[]) opVal.domain.elementAt(0)).length; + if (len != arity + 2) + { + Assert.fail(EC.TLC_CONFIG_OP_ARITY_INCONSISTENT, name); + } + opVal.addLine(line); + } } } - return level; - } - - public FilenameToStream getResolver() - { - return resolver; + return constTbl; } /** - * The level of the expression according to level checking. - * static method, does not change instance state + * Initialize the spec constants using the config file. */ - public static int getLevel(LevelNode expr, Context c) + private final Hashtable initializeConstants() { - HashSet lpSet = expr.getLevelParams(); - if (lpSet.isEmpty()) - return expr.getLevel(); - - int level = expr.getLevel(); - Iterator iter = lpSet.iterator(); - while (iter.hasNext()) + Vect<Vect<String>> consts = this.config.getConstants(); + if (consts == null) { - SymbolNode param = (SymbolNode) iter.next(); - Object res = c.lookup(param, true); - if (res != null) - { - if (res instanceof LazyValue) - { - LazyValue lv = (LazyValue) res; - int plevel = getLevel((LevelNode) lv.expr, lv.con); - level = (plevel > level) ? plevel : level; - } else if (res instanceof OpDefNode) - { - int plevel = getLevel((LevelNode) res, c); - level = (plevel > level) ? plevel : level; - } - } + return new Hashtable<>(); } - return level; + return this.makeConstantTable(consts); } - /** - * Static method, does not change instance state - * @param expr - * @param subs - * @return - */ - private static final ExprNode addSubsts(ExprNode expr, List subs) + private final Hashtable<String, Hashtable> initializeModConstants() { - ExprNode res = expr; - - while (!subs.isEmpty()) + Hashtable<String, ?> modConstants = this.config.getModConstants(); + Hashtable<String, Hashtable> constants = new Hashtable<>(); + Enumeration<String> mods = modConstants.keys(); + while (mods.hasMoreElements()) { - SubstInNode sn = (SubstInNode) subs.car(); - res = new SubstInNode(sn.stn, sn.getSubsts(), res, sn.getInstantiatingModule(), sn.getInstantiatedModule()); - subs = subs.cdr(); + String modName = mods.nextElement(); + constants.put(modName, this.makeConstantTable((Vect<Vect<String>>) modConstants.get(modName))); } - return res; + return constants; } + + public ModuleNode getRootModule() { + return rootModule; + } + + public ExternalModuleTable getModuleTbl() { + return moduleTbl; + } + + public OpDeclNode[] getVariablesNodes() { + return variablesNodes; + } + + public Vect<Action> getInitPred() { + return initPredVec; + } + + public Action getNextPred() { + return nextPred; + } + + public Action[] getTemporal() { + return temporals; + } + + public String[] getTemporalNames() { + return temporalNames; + } + + public Action[] getImpliedTemporals() { + return impliedTemporals; + } + + public String[] getImpliedTemporalNames() { + return impliedTemporalNames; + } + + public Action[] getInvariants() { + return invariants; + } + + public String[] getInvariantsNames() { + return invNames; + } + + public Action[] getImpliedInits() { + return impliedInits; + } + + public String[] getImpliedInitNames() { + return impliedInitNames; + } + + public Action[] getImpliedActions() { + return impliedActions; + } + + public String[] getImpliedActionNames() { + return impliedActNames; + } + + public ExprNode[] getModelConstraints() { + return modelConstraints; + } + + public ExprNode[] getActionConstraints() { + return actionConstraints; + } + + public ExprNode[] getAssumptions() { + return assumptions; + } + + public boolean[] getAssumptionIsAxiom() { + return assumptionIsAxiom; + } + + public SpecObj getSpecObj() { + return specObj; + } + + public Defns getUnprocessedDefns() { + return snapshot; + } } diff --git a/tlatools/src/tlc2/tool/impl/SymbolNodeValueLookupProvider.java b/tlatools/src/tlc2/tool/impl/SymbolNodeValueLookupProvider.java new file mode 100644 index 0000000000000000000000000000000000000000..e56b9b850d2ebfab196cae52f7c0481999cbe9b0 --- /dev/null +++ b/tlatools/src/tlc2/tool/impl/SymbolNodeValueLookupProvider.java @@ -0,0 +1,256 @@ +package tlc2.tool.impl; + +import tla2sany.semantic.APSubstInNode; +import tla2sany.semantic.ASTConstants; +import tla2sany.semantic.ExprNode; +import tla2sany.semantic.ExprOrOpArgNode; +import tla2sany.semantic.FormalParamNode; +import tla2sany.semantic.LabelNode; +import tla2sany.semantic.LetInNode; +import tla2sany.semantic.OpApplNode; +import tla2sany.semantic.OpArgNode; +import tla2sany.semantic.OpDefNode; +import tla2sany.semantic.SemanticNode; +import tla2sany.semantic.Subst; +import tla2sany.semantic.SubstInNode; +import tla2sany.semantic.SymbolNode; +import tlc2.tool.BuiltInOPs; +import tlc2.tool.ToolGlobals; +import tlc2.tool.coverage.CostModel; +import tlc2.util.Context; +import tlc2.value.impl.EvaluatingValue; +import tlc2.value.impl.IntValue; +import tlc2.value.impl.LazyValue; +import tlc2.value.impl.MethodValue; +import util.UniqueString; + +public interface SymbolNodeValueLookupProvider { + /* Return the variable if expr is a state variable. Otherwise, null. */ + default SymbolNode getVar(final SemanticNode expr, final Context c, final boolean cutoff, final int forToolId) { + if (expr instanceof OpApplNode) { + SymbolNode opNode = ((OpApplNode) expr).getOperator(); + + if (opNode.getArity() == 0) { + boolean isVarDecl = (opNode.getKind() == ASTConstants.VariableDeclKind); + Object val = lookup(opNode, c, cutoff && isVarDecl, forToolId); + + if (val instanceof LazyValue) { + LazyValue lval = (LazyValue) val; + return getVar(lval.expr, lval.con, cutoff, forToolId); + } + if (val instanceof OpDefNode) { + return getVar(((OpDefNode) val).getBody(), c, cutoff, forToolId); + } + if (isVarDecl) { + return opNode; + } + } + } + return null; + } + + default Object lookup(final SymbolNode opNode, final Context c, final boolean cutoff, final int forToolId) { + final boolean isVarDecl = (opNode.getKind() == ASTConstants.VariableDeclKind); + Object result = c.lookup(opNode, cutoff && isVarDecl); + if (result != null) { + return result; + } + + result = opNode.getToolObject(forToolId); + if (result != null) { + return WorkerValue.mux(result); + } + + if (opNode.getKind() == ASTConstants.UserDefinedOpKind) { + // Changed by LL on 10 Apr 2011 from + // + // result = ((OpDefNode) opNode).getBody().getToolObject(toolId); + // + // to the following + ExprNode body = ((OpDefNode) opNode).getBody(); + result = body.getToolObject(forToolId); + while ((result == null) && (body.getKind() == ASTConstants.SubstInKind)) { + body = ((SubstInNode) body).getBody(); + result = body.getToolObject(forToolId); + } + // end change + + if (result != null) { + return result; + } + } + return opNode; + } + + default Object getVal(final ExprOrOpArgNode expr, final Context c, final boolean cachable, final int forToolId) { + return getVal(expr, c, cachable, CostModel.DO_NOT_RECORD, forToolId); + } + + default Object getVal(final ExprOrOpArgNode expr, final Context c, final boolean cachable, final CostModel cm, final int forToolId) { + if (!LazyValue.LAZYEVAL_OFF && expr instanceof OpApplNode) { + final OpApplNode oan = (OpApplNode) expr; + // Do not create a LazyValue that "points to" another LazyValue. + // Related: + // https://github.com/tlaplus/tlaplus/issues/113 + // https://github.com/tlaplus/tlaplus/issues/798 + final Object l = c.lookup(oan.getOperator()); + if (l instanceof LazyValue) { + return l; + } + return new LazyValue(expr, c, cachable, cm); + } + if (expr instanceof ExprNode) { + return new LazyValue(expr, c, cachable, cm); + } + final SymbolNode opNode = ((OpArgNode) expr).getOp(); + return lookup(opNode, c, false, forToolId); + } + + default Context getOpContext(final OpDefNode opDef, final ExprOrOpArgNode[] args, final Context c, final boolean cachable, final int forToolId) { + return getOpContext(opDef, args, c, cachable, CostModel.DO_NOT_RECORD, forToolId); + } + + default Context getOpContext(final OpDefNode opDef, final ExprOrOpArgNode[] args, final Context c, + final boolean cachable, final CostModel cm, int forToolId) { + final FormalParamNode[] formals = opDef.getParams(); + final int alen = args.length; + Context c1 = c; + for (int i = 0; i < alen; i++) { + Object aval = getVal(args[i], c, cachable, cm, forToolId); + c1 = c1.cons(formals[i], aval); + } + return c1; + } + + /** + * This method only returns an approximation of the level of the + * expression. The "real" level is at most the return value. Adding + * <name, ValOne> to the context means that there is no need to + * compute level for name. + * + * Note that this method does not work if called on a part of an + * EXCEPT expression. + */ + default int getLevelBound(final SemanticNode expr, final Context c, final int forToolId) { + switch (expr.getKind()) { + case ASTConstants.OpApplKind: { + final OpApplNode expr1 = (OpApplNode) expr; + return getLevelBoundAppl(expr1, c, forToolId); + } + case ASTConstants.LetInKind: { + final LetInNode expr1 = (LetInNode) expr; + final OpDefNode[] letDefs = expr1.getLets(); + final int letLen = letDefs.length; + Context c1 = c; + int level = 0; + for (int i = 0; i < letLen; i++) { + OpDefNode opDef = letDefs[i]; + level = Math.max(level, getLevelBound(opDef.getBody(), c1, forToolId)); + c1 = c1.cons(opDef, IntValue.ValOne); + } + return Math.max(level, getLevelBound(expr1.getBody(), c1, forToolId)); + } + case ASTConstants.SubstInKind: { + final SubstInNode expr1 = (SubstInNode) expr; + final Subst[] subs = expr1.getSubsts(); + final int slen = subs.length; + Context c1 = c; + for (int i = 0; i < slen; i++) { + final Subst sub = subs[i]; + c1 = c1.cons(sub.getOp(), getVal(sub.getExpr(), c, true, forToolId)); + } + return getLevelBound(expr1.getBody(), c1, forToolId); + } + + // Added by LL on 13 Nov 2009 to handle theorem and assumption names. + case ASTConstants.APSubstInKind: { + final APSubstInNode expr1 = (APSubstInNode) expr; + final Subst[] subs = expr1.getSubsts(); + final int slen = subs.length; + Context c1 = c; + for (int i = 0; i < slen; i++) { + final Subst sub = subs[i]; + c1 = c1.cons(sub.getOp(), getVal(sub.getExpr(), c, true, forToolId)); + } + return getLevelBound(expr1.getBody(), c1, forToolId); + } + + /*********************************************************************** + * LabelKind case added by LL on 13 Jun 2007. * + ***********************************************************************/ + case ASTConstants.LabelKind: { + final LabelNode expr1 = (LabelNode) expr; + return getLevelBound(expr1.getBody(), c, forToolId); + } + default: { + return 0; + } + } + } + + /** + * Users will likely want to call only {@link #getLevelBound(SemanticNode, Context, int)} - this + * method is called from that method in certain cases. + */ + default int getLevelBoundAppl(final OpApplNode expr, Context c, final int forToolId) { + final SymbolNode opNode = expr.getOperator(); + final UniqueString opName = opNode.getName(); + final int opcode = BuiltInOPs.getOpCode(opName); + + if (BuiltInOPs.isTemporal(opcode)) { + return 3; // Conservative estimate + } + + if (BuiltInOPs.isAction(opcode)) { + return 2; // Conservative estimate + } + + if (opcode == ToolGlobals.OPCODE_enabled) { + return 1; // Conservative estimate + } + + int level = 0; + final ExprNode[] bnds = expr.getBdedQuantBounds(); + for (int i = 0; i < bnds.length; i++) { + level = Math.max(level, getLevelBound(bnds[i], c, forToolId)); + } + + if (opcode == ToolGlobals.OPCODE_rfs) { + // For recursive function, don't compute level of the function body + // again in the recursive call. + SymbolNode fname = expr.getUnbdedQuantSymbols()[0]; + c = c.cons(fname, IntValue.ValOne); + } + + final ExprOrOpArgNode[] args = expr.getArgs(); + final int alen = args.length; + for (int i = 0; i < alen; i++) { + if (args[i] != null) { + level = Math.max(level, getLevelBound(args[i], c, forToolId)); + } + } + + if (opcode == 0) { + // This operator is a user-defined operator. + if (opName.getVarLoc() >= 0) + return 1; + + final Object val = lookup(opNode, c, false, forToolId); + if (val instanceof OpDefNode) { + final OpDefNode opDef = (OpDefNode) val; + c = c.cons(opNode, IntValue.ValOne); + level = Math.max(level, getLevelBound(opDef.getBody(), c, forToolId)); + } else if (val instanceof LazyValue) { + final LazyValue lv = (LazyValue) val; + level = Math.max(level, getLevelBound(lv.expr, lv.con, forToolId)); + } else if (val instanceof EvaluatingValue) { + final EvaluatingValue ev = (EvaluatingValue) val; + level = Math.max(level, ev.getMinLevel()); + } else if (val instanceof MethodValue) { + final MethodValue mv = (MethodValue) val; + level = Math.max(level, mv.getMinLevel()); + } + } + return level; + } +} diff --git a/tlatools/src/tlc2/tool/impl/TLAClass.java b/tlatools/src/tlc2/tool/impl/TLAClass.java new file mode 100644 index 0000000000000000000000000000000000000000..e6a0de3ab32d948284306792ab5613539afe2c39 --- /dev/null +++ b/tlatools/src/tlc2/tool/impl/TLAClass.java @@ -0,0 +1,109 @@ +// Copyright (c) 2003 Compaq Corporation. All rights reserved. +// Portions Copyright (c) 2003 Microsoft Corporation. All rights reserved. +// Last modified on Mon 30 Apr 2007 at 13:18:28 PST by lamport +// modified on Mon Jan 29 16:21:11 PST 2001 by yuanyu + +package tlc2.tool.impl; + +import java.io.File; +import java.net.URL; +import java.net.URLClassLoader; + +import tlc2.output.EC; +import util.Assert; +import util.FilenameToStream; + +/** + * + * @author Yuan Yu, Simon Zambrovski + * @version $Id$ + */ +public class TLAClass +{ + /* Load a class from a file. */ + private final String pkg; + private final FilenameToStream resolver; + + public TLAClass(String pkg, FilenameToStream resolver) + { + this.resolver = resolver; + if (pkg.length() != 0 && pkg.charAt(pkg.length() - 1) != '.') + { + this.pkg = pkg + '.'; + } else + { + this.pkg = pkg; + } + } + + /** + * This method attempts to load the java class with the given name. + * + * Loading classes is done in three steps: + * + * <ul> + * <li>1) TLC's {@link FilenameToStream} resolver is used to locate a class + * file in the resolver's lookup path. Check {@link FilenameToStream}'s + * documentation on the lookup order. If a class file with name "name.class" + * can be found, it is loaded.</li> + * <li>2) With the unqualified name, we try to lookup the class in the + * regular VM's CLASSPATH.</li> + * <li>3) As a last resort, we try to load a class fully qualified with the + * package name given in {@link TLAClass#pkg}.</li> + * </ul> + * <p> + * If no class could be loaded, <code>null</code> is returned. + **/ + public synchronized Class loadClass(String name) + { + Class cl = null; + try + { + try { + if (resolver != null) { + final File module = resolver.resolve(name + ".class", false); + if (module != null && module.getAbsoluteFile() != null) { + final URL url = module.getAbsoluteFile().getParentFile().toURI().toURL(); + cl = new URLClassLoader(new URL[] {url}).loadClass(name); + } + } + } catch (Exception ignored1) { + /*SKIP*/ + } finally { + if (cl == null) { + try + { + cl = Class.forName(name); + } catch (Exception e) + { /*SKIP*/ + } + } + } + if (cl == null) + { + try + { + cl = Class.forName(this.pkg + name); + } catch (Exception e) + { /*SKIP*/ + } + } + } catch (Throwable e) + { + Assert.fail(EC.TLC_ERROR_REPLACING_MODULES, new String[] { name, + (e.getMessage()==null)?e.toString():e.getMessage() }); + } + return cl; + } + +// +// public static void main(String argv[]) +// { +// TLAClass tc = new TLAClass("tlc2.module"); +// Class c = tc.loadClass("Strings"); // must set CLASSPATH correctly +// System.err.println("c = " + c); +// // Class c1 = tc.loadClass("Class"); +// // System.err.println("c1 = " + c1); +// } +// +} diff --git a/tlatools/src/tlc2/tool/TLARegistry.java b/tlatools/src/tlc2/tool/impl/TLARegistry.java similarity index 83% rename from tlatools/src/tlc2/tool/TLARegistry.java rename to tlatools/src/tlc2/tool/impl/TLARegistry.java index 4350e3ddf8f8da20d3b36b8021e381f4d913153c..30b9c1d3ed2e0adc649f9469a77ccefa16320ad9 100644 --- a/tlatools/src/tlc2/tool/TLARegistry.java +++ b/tlatools/src/tlc2/tool/impl/TLARegistry.java @@ -3,7 +3,7 @@ // Last modified on Mon 30 Apr 2007 at 13:18:28 PST by lamport // modified on Fri Sep 15 11:06:03 PDT 2000 by yuanyu -package tlc2.tool; +package tlc2.tool.impl; import java.util.Enumeration; import java.util.Hashtable; @@ -13,14 +13,13 @@ import java.util.Hashtable; * <br><b>Note:</b> * * @author Simon Zambrovski - * @version $Id$ */ public class TLARegistry { - private static Hashtable javaToTLA = new Hashtable(); + private static final Hashtable<String, String> javaToTLA = new Hashtable<String, String>(); public static String get(String name) { - return (String)javaToTLA.get(name); + return javaToTLA.get(name); } /** @@ -30,7 +29,7 @@ public class TLARegistry { * @return the previous value, if one */ public static String put(String tname, String jname) { - return (String)javaToTLA.put(tname, jname); + return javaToTLA.put(tname, jname); } public static String mapName(String name) { @@ -41,7 +40,7 @@ public class TLARegistry { /* Used only for debugging. */ public static String allNames() { StringBuffer sb = new StringBuffer("{"); - Enumeration eNames = javaToTLA.keys(); + Enumeration<String> eNames = javaToTLA.keys(); if (eNames.hasMoreElements()) { sb.append(eNames.nextElement()); } diff --git a/tlatools/src/tlc2/tool/impl/Tool.java b/tlatools/src/tlc2/tool/impl/Tool.java new file mode 100644 index 0000000000000000000000000000000000000000..93785eb86c511dfba5d854897526c450a5e8885c --- /dev/null +++ b/tlatools/src/tlc2/tool/impl/Tool.java @@ -0,0 +1,3524 @@ +// Copyright (c) 2003 Compaq Corporation. All rights reserved. +// Portions Copyright (c) 2003 Microsoft Corporation. All rights reserved. +// Last modified on Wed 12 Jul 2017 at 16:10:00 PST by ian morris nieves +// modified on Thu 2 Aug 2007 at 10:25:48 PST by lamport +// modified on Fri Jan 4 22:46:57 PST 2002 by yuanyu + +package tlc2.tool.impl; + +import java.io.File; +import java.util.HashSet; +import java.util.List; + +import tla2sany.parser.SyntaxTreeNode; +import tla2sany.semantic.APSubstInNode; +import tla2sany.semantic.ExprNode; +import tla2sany.semantic.ExprOrOpArgNode; +import tla2sany.semantic.FormalParamNode; +import tla2sany.semantic.LabelNode; +import tla2sany.semantic.LetInNode; +import tla2sany.semantic.LevelConstants; +import tla2sany.semantic.LevelNode; +import tla2sany.semantic.OpApplNode; +import tla2sany.semantic.OpArgNode; +import tla2sany.semantic.OpDefNode; +import tla2sany.semantic.OpDefOrDeclNode; +import tla2sany.semantic.SemanticNode; +import tla2sany.semantic.Subst; +import tla2sany.semantic.SubstInNode; +import tla2sany.semantic.SymbolNode; +import tla2sany.semantic.ThmOrAssumpDefNode; +import tlc2.output.EC; +import tlc2.output.MP; +import tlc2.tool.Action; +import tlc2.tool.BuiltInOPs; +import tlc2.tool.EvalControl; +import tlc2.tool.EvalException; +import tlc2.tool.IActionItemList; +import tlc2.tool.IContextEnumerator; +import tlc2.tool.INextStateFunctor; +import tlc2.tool.IStateFunctor; +import tlc2.tool.ITool; +import tlc2.tool.StateVec; +import tlc2.tool.TLCState; +import tlc2.tool.TLCStateFun; +import tlc2.tool.TLCStateInfo; +import tlc2.tool.TLCStateMut; +import tlc2.tool.ToolGlobals; +import tlc2.tool.coverage.CostModel; +import tlc2.util.Context; +import tlc2.util.ExpectInlined; +import tlc2.util.IdThread; +import tlc2.util.Vect; +import tlc2.value.IFcnLambdaValue; +import tlc2.value.IMVPerm; +import tlc2.value.IValue; +import tlc2.value.ValueConstants; +import tlc2.value.Values; +import tlc2.value.impl.Applicable; +import tlc2.value.impl.BoolValue; +import tlc2.value.impl.Enumerable; +import tlc2.value.impl.EvaluatingValue; +import tlc2.value.impl.FcnLambdaValue; +import tlc2.value.impl.FcnParams; +import tlc2.value.impl.FcnRcdValue; +import tlc2.value.impl.LazyValue; +import tlc2.value.impl.MVPerm; +import tlc2.value.impl.MVPerms; +import tlc2.value.impl.MethodValue; +import tlc2.value.impl.ModelValue; +import tlc2.value.impl.OpLambdaValue; +import tlc2.value.impl.OpValue; +import tlc2.value.impl.RecordValue; +import tlc2.value.impl.Reducible; +import tlc2.value.impl.SetCapValue; +import tlc2.value.impl.SetCupValue; +import tlc2.value.impl.SetDiffValue; +import tlc2.value.impl.SetEnumValue; +import tlc2.value.impl.SetOfFcnsValue; +import tlc2.value.impl.SetOfRcdsValue; +import tlc2.value.impl.SetOfTuplesValue; +import tlc2.value.impl.SetPredValue; +import tlc2.value.impl.StringValue; +import tlc2.value.impl.SubsetValue; +import tlc2.value.impl.TupleValue; +import tlc2.value.impl.UnionValue; +import tlc2.value.impl.Value; +import tlc2.value.impl.ValueEnumeration; +import tlc2.value.impl.ValueExcept; +import tlc2.value.impl.ValueVec; +import util.Assert; +import util.FilenameToStream; +import util.TLAConstants; +import util.UniqueString; + +/** + * This class provides useful methods for tools like model checker + * and simulator. + * + * It's instance serves as a spec handle + * This is one of two places in TLC, where not all messages are retrieved from the message printer, + * but constructed just here in the code. + */ +public abstract class Tool + extends Spec + implements ValueConstants, ToolGlobals, ITool +{ + + public static final Value[] EmptyArgs = new Value[0]; + + protected final Action[] actions; // the list of TLA actions. + private Vect<Action> actionVec = new Vect<>(10); + + /** + * Creates a new tool handle + */ + public Tool(String specFile, String configFile) { + this(new File(specFile), specFile, configFile, null); + } + + public Tool(String specFile, String configFile, FilenameToStream resolver) { + this(new File(specFile), specFile, configFile, resolver); + } + + private Tool(File specDir, String specFile, String configFile, FilenameToStream resolver) + { + this(specDir.isAbsolute() ? specDir.getParent() : "", specFile, configFile, resolver); + } + + public Tool(String specDir, String specFile, String configFile, FilenameToStream resolver) + { + super(specDir, specFile, configFile, resolver); + + // Initialize state. + TLCStateMut.setTool(this); + + Action next = this.getNextStateSpec(); + if (next == null) { + this.actions = new Action[0]; + } else { + this.getActions(next); + int sz = this.actionVec.size(); + this.actions = new Action[sz]; + for (int i = 0; i < sz; i++) { + this.actions[i] = (Action) this.actionVec.elementAt(i); + } + } + } + + Tool(Tool other) { + super(other); + this.actions = other.actions; + this.actionVec = other.actionVec; + } + + /** + * This method returns the set of all possible actions of the + * spec, and sets the actions field of this object. In fact, we + * could simply treat the next predicate as one "giant" action. + * But for efficiency, we preprocess the next state predicate by + * splitting it into a set of actions for the maximum prefix + * of disjunction and existential quantification. + */ + @Override + public final Action[] getActions() { + return this.actions; + } + + private final void getActions(final Action next) { + this.getActions(next.pred, next.con, next.getOpDef(), next.cm); + } + + private final void getActions(SemanticNode next, Context con, final OpDefNode opDefNode, CostModel cm) { + switch (next.getKind()) { + case OpApplKind: + { + OpApplNode next1 = (OpApplNode)next; + this.getActionsAppl(next1, con, opDefNode, cm); + return; + } + case LetInKind: + { + LetInNode next1 = (LetInNode)next; + this.getActions(next1.getBody(), con, opDefNode, cm); + return; + } + case SubstInKind: + { + SubstInNode next1 = (SubstInNode)next; + Subst[] substs = next1.getSubsts(); + if (substs.length == 0) { + this.getActions(next1.getBody(), con, opDefNode, cm); + } + else { + Action action = new Action(next1, con, opDefNode); + this.actionVec.addElement(action); + } + return; + } + + // Added by LL on 13 Nov 2009 to handle theorem and assumption names. + case APSubstInKind: + { + APSubstInNode next1 = (APSubstInNode)next; + Subst[] substs = next1.getSubsts(); + if (substs.length == 0) { + this.getActions(next1.getBody(), con, opDefNode, cm); + } + else { + Action action = new Action(next1, con, opDefNode); + this.actionVec.addElement(action); + } + return; + } + + /*********************************************************************** + * LabelKind class added by LL on 13 Jun 2007. * + ***********************************************************************/ + case LabelKind: + { + LabelNode next1 = (LabelNode)next; + this.getActions(next1.getBody(), con, opDefNode, cm); + return; + } + default: + { + Assert.fail("The next state relation is not a boolean expression.\n" + next); + } + } + } + + private final void getActionsAppl(OpApplNode next, Context con, final OpDefNode actionName, CostModel cm) { + ExprOrOpArgNode[] args = next.getArgs(); + SymbolNode opNode = next.getOperator(); + int opcode = BuiltInOPs.getOpCode(opNode.getName()); + + if (opcode == 0) { + Object val = this.lookup(opNode, con, false); + + if (val instanceof OpDefNode) { + OpDefNode opDef = (OpDefNode)val; + opcode = BuiltInOPs.getOpCode(opDef.getName()); + if (opcode == 0) { + try { + FormalParamNode[] formals = opDef.getParams(); + int alen = args.length; + int argLevel = 0; + for (int i = 0; i < alen; i++) { + argLevel = args[i].getLevel(); + if (argLevel != 0) break; + } + if (argLevel == 0) { + Context con1 = con; + for (int i = 0; i < alen; i++) { + IValue aval = this.eval(args[i], con, TLCState.Empty, cm); + con1 = con1.cons(formals[i], aval); + } + this.getActions(opDef.getBody(), con1, opDef, cm); + return; + } + } + catch (Throwable e) { /*SKIP*/ } + } + } + if (opcode == 0) { + Action action = new Action(next, con, (OpDefNode) opNode); + this.actionVec.addElement(action); + return; + } + } + + switch (opcode) { + case OPCODE_be: // BoundedExists + { + int cnt = this.actionVec.size(); + try { + ContextEnumerator Enum = + this.contexts(next, con, TLCState.Empty, TLCState.Empty, EvalControl.Clear, cm); + Context econ; + while ((econ = Enum.nextElement()) != null) { + this.getActions(args[0], econ, actionName, cm); + } + } + catch (Throwable e) { + Action action = new Action(next, con, actionName); + this.actionVec.removeAll(cnt); + this.actionVec.addElement(action); + } + return; + } + case OPCODE_dl: // DisjList + case OPCODE_lor: + { + for (int i = 0; i < args.length; i++) { + this.getActions(args[i], con, actionName, cm); + } + return; + } + default: + { + // We handle all the other builtin operators here. + Action action = new Action(next, con, actionName); + this.actionVec.addElement(action); + return; + } + } + } + + /* + * This method returns the set of possible initial states that + * satisfies the initial state predicate. Initial state predicate + * can be under-specified. Too many possible initial states will + * probably make tools like TLC useless. + */ + @Override + public final StateVec getInitStates() { + final StateVec initStates = new StateVec(0); + getInitStates(initStates); + return initStates; + } + + @Override + public final void getInitStates(IStateFunctor functor) { + Vect<Action> init = this.getInitStateSpec(); + ActionItemList acts = ActionItemList.Empty; + // MAK 09/11/2018: Tail to head iteration order cause the first elem added with + // acts.cons to be acts tail. This fixes the bug/funny behavior that the init + // predicate Init == A /\ B /\ C /\ D was evaluated in the order A, D, C, B (A + // doesn't get added to acts at all). + for (int i = (init.size() - 1); i > 0; i--) { + Action elem = (Action)init.elementAt(i); + acts = (ActionItemList) acts.cons(elem, IActionItemList.PRED); + } + if (init.size() != 0) { + Action elem = (Action)init.elementAt(0); + TLCState ps = TLCState.Empty.createEmpty(); + this.getInitStates(elem.pred, acts, elem.con, ps, functor, elem.cm); + } + } + + /* Create the state specified by pred. */ + @Override + public final TLCState makeState(SemanticNode pred) { + ActionItemList acts = ActionItemList.Empty; + TLCState ps = TLCState.Empty.createEmpty(); + StateVec states = new StateVec(0); + this.getInitStates(pred, acts, Context.Empty, ps, states, acts.cm); + if (states.size() != 1) { + Assert.fail("The predicate does not specify a unique state." + pred); + } + TLCState state = states.elementAt(0); + if (!this.isGoodState(state)) { + Assert.fail("The state specified by the predicate is not complete." + pred); + } + return state; + } + + protected void getInitStates(SemanticNode init, ActionItemList acts, + Context c, TLCState ps, IStateFunctor states, CostModel cm) { + switch (init.getKind()) { + case OpApplKind: + { + OpApplNode init1 = (OpApplNode)init; + this.getInitStatesAppl(init1, acts, c, ps, states, cm); + return; + } + case LetInKind: + { + LetInNode init1 = (LetInNode)init; + this.getInitStates(init1.getBody(), acts, c, ps, states, cm); + return; + } + case SubstInKind: + { + SubstInNode init1 = (SubstInNode)init; + Subst[] subs = init1.getSubsts(); + Context c1 = c; + for (int i = 0; i < subs.length; i++) { + Subst sub = subs[i]; + c1 = c1.cons(sub.getOp(), this.getVal(sub.getExpr(), c, false, coverage ? sub.getCM() : cm, toolId)); + } + this.getInitStates(init1.getBody(), acts, c1, ps, states, cm); + return; + } + // Added by LL on 13 Nov 2009 to handle theorem and assumption names. + case APSubstInKind: + { + APSubstInNode init1 = (APSubstInNode)init; + Subst[] subs = init1.getSubsts(); + Context c1 = c; + for (int i = 0; i < subs.length; i++) { + Subst sub = subs[i]; + c1 = c1.cons(sub.getOp(), this.getVal(sub.getExpr(), c, false, cm, toolId)); + } + this.getInitStates(init1.getBody(), acts, c1, ps, states, cm); + return; + } + // LabelKind class added by LL on 13 Jun 2007 + case LabelKind: + { + LabelNode init1 = (LabelNode)init; + this.getInitStates(init1.getBody(), acts, c, ps, states, cm); + return; + } + default: + { + Assert.fail("The init state relation is not a boolean expression.\n" + init); + } + } + } + + private final void getInitStates(ActionItemList acts, TLCState ps, IStateFunctor states, CostModel cm) { + if (acts.isEmpty()) { + if (coverage) { + cm.incInvocations(); + cm.getRoot().incInvocations(); + } + states.addElement(ps.copy()); + return; + } else if (ps.allAssigned()) { + // MAK 05/25/2018: If all values of the initial state have already been + // assigned, there is no point in further trying to assign values. Instead, all + // remaining statements (ActionItemList) can just be evaluated for their boolean + // value. + // This optimization is especially useful to check inductive invariants which + // require TLC to generate a very large set of initial states. + while (!acts.isEmpty()) { + final Value bval = this.eval(acts.carPred(), acts.carContext(), ps, TLCState.Empty, EvalControl.Init, acts.cm); + if (!(bval instanceof BoolValue)) { + //TODO Choose more fitting error message. + Assert.fail(EC.TLC_EXPECTED_EXPRESSION_IN_COMPUTING, + new String[] { "initial states", "boolean", bval.toString(), acts.pred.toString() }); + } + if (!((BoolValue) bval).val) { + if (coverage) { + // Increase "states found". + cm.getRoot().incSecondary(); + } + return; + } + // Move on to the next action in the ActionItemList. + acts = acts.cdr(); + } + if (coverage) { + cm.incInvocations(); + cm.getRoot().incInvocations(); + } + states.addElement(ps.copy()); + return; + } + // Assert.check(act.kind > 0 || act.kind == -1); + ActionItemList acts1 = acts.cdr(); + this.getInitStates(acts.carPred(), acts1, acts.carContext(), ps, states, acts.cm); + } + + protected void getInitStatesAppl(OpApplNode init, ActionItemList acts, + Context c, TLCState ps, IStateFunctor states, CostModel cm) { + if (coverage) {cm = cm.get(init);} + ExprOrOpArgNode[] args = init.getArgs(); + int alen = args.length; + SymbolNode opNode = init.getOperator(); + int opcode = BuiltInOPs.getOpCode(opNode.getName()); + + if (opcode == 0) { + // This is a user-defined operator with one exception: it may + // be substed by a builtin operator. This special case occurs + // when the lookup returns an OpDef with opcode # 0. + Object val = this.lookup(opNode, c, ps, false); + + if (val instanceof OpDefNode) { + OpDefNode opDef = (OpDefNode)val; + opcode = BuiltInOPs.getOpCode(opDef.getName()); + if (opcode == 0) { + // Context c1 = this.getOpContext(opDef, args, c, false); + Context c1 = this.getOpContext(opDef, args, c, true, cm, toolId); + this.getInitStates(opDef.getBody(), acts, c1, ps, states, cm); + return; + } + } + // Added 13 Nov 2009 by LL to fix Yuan's fix. + /********************************************************************* + * Modified on 23 October 2012 by LL to work if ThmOrAssumpDefNode * + * imported with parameterized instantiation. * + *********************************************************************/ + if (val instanceof ThmOrAssumpDefNode) { + ThmOrAssumpDefNode opDef = (ThmOrAssumpDefNode)val; + opcode = BuiltInOPs.getOpCode(opDef.getName()); + Context c1 = this.getOpContext(opDef, args, c, true); + this.getInitStates(opDef.getBody(), acts, c1, ps, states, cm); + return; + } + + if (val instanceof LazyValue) { + LazyValue lv = (LazyValue)val; + if (lv.getValue() == null || lv.isUncachable()) { + this.getInitStates(lv.expr, acts, lv.con, ps, states, cm); + return; + } + val = lv.getValue(); + } + + Object bval = val; + if (alen == 0) { + if (val instanceof MethodValue) { + bval = ((MethodValue)val).apply(EmptyArgs, EvalControl.Init); + } else if (val instanceof EvaluatingValue) { + // Allow EvaluatingValue overwrites to have zero arity. + bval = ((EvaluatingValue) val).eval(this, args, c, ps, TLCState.Empty, EvalControl.Init, cm); + } + } + else { + if (val instanceof OpValue) { + bval = ((OpValue) val).eval(this, args, c, ps, TLCState.Empty, EvalControl.Init, cm); + } + } + + if (opcode == 0) + { + if (!(bval instanceof BoolValue)) + { + Assert.fail(EC.TLC_EXPECTED_EXPRESSION_IN_COMPUTING, new String[] { "initial states", "boolean", + bval.toString(), init.toString() }); + } + if (((BoolValue) bval).val) + { + this.getInitStates(acts, ps, states, cm); + } + return; + } + } + + switch (opcode) { + case OPCODE_dl: // DisjList + case OPCODE_lor: + { + for (int i = 0; i < alen; i++) { + this.getInitStates(args[i], acts, c, ps, states, cm); + } + return; + } + case OPCODE_cl: // ConjList + case OPCODE_land: + { + for (int i = alen-1; i > 0; i--) { + acts = (ActionItemList) acts.cons(args[i], c, cm, i); + } + this.getInitStates(args[0], acts, c, ps, states, cm); + return; + } + case OPCODE_be: // BoundedExists + { + SemanticNode body = args[0]; + ContextEnumerator Enum = this.contexts(init, c, ps, TLCState.Empty, EvalControl.Init, cm); + Context c1; + while ((c1 = Enum.nextElement()) != null) { + this.getInitStates(body, acts, c1, ps, states, cm); + } + return; + } + case OPCODE_bf: // BoundedForall + { + SemanticNode body = args[0]; + ContextEnumerator Enum = this.contexts(init, c, ps, TLCState.Empty, EvalControl.Init, cm); + Context c1 = Enum.nextElement(); + if (c1 == null) { + this.getInitStates(acts, ps, states, cm); + } + else { + ActionItemList acts1 = acts; + Context c2; + while ((c2 = Enum.nextElement()) != null) { + acts1 = (ActionItemList) acts1.cons(body, c2, cm, IActionItemList.PRED); + } + this.getInitStates(body, acts1, c1, ps, states, cm); + } + return; + } + case OPCODE_ite: // IfThenElse + { + Value guard = this.eval(args[0], c, ps, TLCState.Empty, EvalControl.Init, cm); + if (!(guard instanceof BoolValue)) { + Assert.fail("In computing initial states, a non-boolean expression (" + + guard.getKindString() + ") was used as the condition " + + "of an IF.\n" + init); + } + int idx = (((BoolValue)guard).val) ? 1 : 2; + this.getInitStates(args[idx], acts, c, ps, states, cm); + return; + } + case OPCODE_case: // Case + { + SemanticNode other = null; + for (int i = 0; i < alen; i++) { + OpApplNode pair = (OpApplNode)args[i]; + ExprOrOpArgNode[] pairArgs = pair.getArgs(); + if (pairArgs[0] == null) { + other = pairArgs[1]; + } + else { + Value bval = this.eval(pairArgs[0], c, ps, TLCState.Empty, EvalControl.Init, cm); + if (!(bval instanceof BoolValue)) { + Assert.fail("In computing initial states, a non-boolean expression (" + + bval.getKindString() + ") was used as a guard condition" + + " of a CASE.\n" + pairArgs[1]); + } + if (((BoolValue)bval).val) { + this.getInitStates(pairArgs[1], acts, c, ps, states, cm); + return; + } + } + } + if (other == null) { + Assert.fail("In computing initial states, TLC encountered a CASE with no" + + " conditions true.\n" + init); + } + this.getInitStates(other, acts, c, ps, states, cm); + return; + } + case OPCODE_fa: // FcnApply + { + Value fval = this.eval(args[0], c, ps, TLCState.Empty, EvalControl.Init, cm); + if (fval instanceof FcnLambdaValue) { + FcnLambdaValue fcn = (FcnLambdaValue)fval; + if (fcn.fcnRcd == null) { + Context c1 = this.getFcnContext(fcn, args, c, ps, TLCState.Empty, EvalControl.Init, cm); + this.getInitStates(fcn.body, acts, c1, ps, states, cm); + return; + } + fval = fcn.fcnRcd; + } + else if (!(fval instanceof Applicable)) { + Assert.fail("In computing initial states, a non-function (" + + fval.getKindString() + ") was applied as a function.\n" + init); + } + Applicable fcn = (Applicable) fval; + Value argVal = this.eval(args[1], c, ps, TLCState.Empty, EvalControl.Init, cm); + Value bval = fcn.apply(argVal, EvalControl.Init); + if (!(bval instanceof BoolValue)) + { + Assert.fail(EC.TLC_EXPECTED_EXPRESSION_IN_COMPUTING2, new String[] { "initial states", "boolean", + init.toString() }); + } + if (((BoolValue)bval).val) { + this.getInitStates(acts, ps, states, cm); + } + return; + } + case OPCODE_eq: + { + SymbolNode var = this.getVar(args[0], c, false, toolId); + if (var == null || var.getName().getVarLoc() < 0) { + Value bval = this.eval(init, c, ps, TLCState.Empty, EvalControl.Init, cm); + if (!((BoolValue)bval).val) { + return; + } + } + else { + UniqueString varName = var.getName(); + IValue lval = ps.lookup(varName); + Value rval = this.eval(args[1], c, ps, TLCState.Empty, EvalControl.Init, cm); + if (lval == null) { + ps = ps.bind(varName, rval); + this.getInitStates(acts, ps, states, cm); + ps.unbind(varName); + return; + } + else { + if (!lval.equals(rval)) { + return; + } + } + } + this.getInitStates(acts, ps, states, cm); + return; + } + case OPCODE_in: + { + SymbolNode var = this.getVar(args[0], c, false, toolId); + if (var == null || var.getName().getVarLoc() < 0) { + Value bval = this.eval(init, c, ps, TLCState.Empty, EvalControl.Init, cm); + if (!((BoolValue)bval).val) { + return; + } + } + else { + UniqueString varName = var.getName(); + Value lval = (Value) ps.lookup(varName); + Value rval = this.eval(args[1], c, ps, TLCState.Empty, EvalControl.Init, cm); + if (lval == null) { + if (!(rval instanceof Enumerable)) { + Assert.fail("In computing initial states, the right side of \\IN" + + " is not enumerable.\n" + init); + } + ValueEnumeration Enum = ((Enumerable)rval).elements(); + Value elem; + while ((elem = Enum.nextElement()) != null) { + ps.bind(varName, elem); + this.getInitStates(acts, ps, states, cm); + ps.unbind(varName); + } + return; + } + else { + if (!rval.member(lval)) { + return; + } + } + } + this.getInitStates(acts, ps, states, cm); + return; + } + case OPCODE_implies: + { + Value lval = this.eval(args[0], c, ps, TLCState.Empty, EvalControl.Init, cm); + if (!(lval instanceof BoolValue)) { + Assert.fail("In computing initial states of a predicate of form" + + " P => Q, P was " + lval.getKindString() + "\n." + init); + } + if (((BoolValue)lval).val) { + this.getInitStates(args[1], acts, c, ps, states, cm); + } + else { + this.getInitStates(acts, ps, states, cm); + } + return; + } + // The following case added by LL on 13 Nov 2009 to handle subexpression names. + case OPCODE_nop: + { + this.getInitStates(args[0], acts, c, ps, states, cm); + return; + } + default: + { + // For all the other builtin operators, simply evaluate: + Value bval = this.eval(init, c, ps, TLCState.Empty, EvalControl.Init, cm); + if (!(bval instanceof BoolValue)) { + + Assert.fail("In computing initial states, TLC expected a boolean expression," + + "\nbut instead found " + bval + ".\n" + init); + } + if (((BoolValue)bval).val) { + this.getInitStates(acts, ps, states, cm); + } + return; + } + } + } + + /** + * This method returns the set of next states when taking the action + * in the given state. + */ + @Override + public final StateVec getNextStates(Action action, TLCState state) { + return getNextStates(action, action.con, state); + } + + public final StateVec getNextStates(final Action action, final Context ctx, final TLCState state) { + ActionItemList acts = ActionItemList.Empty; + TLCState s1 = TLCState.Empty.createEmpty(); + StateVec nss = new StateVec(0); + this.getNextStates(action, action.pred, acts, ctx, state, s1, nss, action.cm); + if (coverage) { action.cm.incInvocations(nss.size()); } + return nss; + } + + @Override + public final boolean getNextStates(final INextStateFunctor functor, final TLCState state) { + for (int i = 0; i < actions.length; i++) { + final Action action = actions[i]; + this.getNextStates(action, action.pred, ActionItemList.Empty, action.con, state, TLCState.Empty.createEmpty(), + functor, action.cm); + } + return false; + } + + protected abstract TLCState getNextStates(final Action action, SemanticNode pred, ActionItemList acts, Context c, + TLCState s0, TLCState s1, INextStateFunctor nss, CostModel cm); + + protected final TLCState getNextStatesImpl(final Action action, SemanticNode pred, ActionItemList acts, Context c, + TLCState s0, TLCState s1, INextStateFunctor nss, CostModel cm) { + switch (pred.getKind()) { + case OpApplKind: + { + OpApplNode pred1 = (OpApplNode)pred; + if (coverage) {cm = cm.get(pred);} + return this.getNextStatesAppl(action, pred1, acts, c, s0, s1, nss, cm); + } + case LetInKind: + { + LetInNode pred1 = (LetInNode)pred; + return this.getNextStates(action, pred1.getBody(), acts, c, s0, s1, nss, cm); + } + case SubstInKind: + { + return getNextStatesImplSubstInKind(action, (SubstInNode) pred, acts, c, s0, s1, nss, cm); + } + // Added by LL on 13 Nov 2009 to handle theorem and assumption names. + case APSubstInKind: + { + return getNextStatesImplApSubstInKind(action, (APSubstInNode) pred, acts, c, s0, s1, nss, cm); + } + // LabelKind class added by LL on 13 Jun 2007 + case LabelKind: + { + LabelNode pred1 = (LabelNode)pred; + return this.getNextStates(action, pred1.getBody(), acts, c, s0, s1, nss, cm); + } + default: + { + Assert.fail("The next state relation is not a boolean expression.\n" + pred); + } + } + return s1; + } + + @ExpectInlined + private final TLCState getNextStatesImplSubstInKind(final Action action, SubstInNode pred1, ActionItemList acts, Context c, TLCState s0, TLCState s1, INextStateFunctor nss, final CostModel cm) { + Subst[] subs = pred1.getSubsts(); + int slen = subs.length; + Context c1 = c; + for (int i = 0; i < slen; i++) { + Subst sub = subs[i]; + c1 = c1.cons(sub.getOp(), this.getVal(sub.getExpr(), c, false, coverage ? sub.getCM() : cm, toolId)); + } + return this.getNextStates(action, pred1.getBody(), acts, c1, s0, s1, nss, cm); + } + + @ExpectInlined + private final TLCState getNextStatesImplApSubstInKind(final Action action, APSubstInNode pred1, ActionItemList acts, Context c, TLCState s0, TLCState s1, INextStateFunctor nss, final CostModel cm) { + Subst[] subs = pred1.getSubsts(); + int slen = subs.length; + Context c1 = c; + for (int i = 0; i < slen; i++) { + Subst sub = subs[i]; + c1 = c1.cons(sub.getOp(), this.getVal(sub.getExpr(), c, false, cm, toolId)); + } + return this.getNextStates(action, pred1.getBody(), acts, c1, s0, s1, nss, cm); + } + + @ExpectInlined + private final TLCState getNextStates(final Action action, ActionItemList acts, final TLCState s0, final TLCState s1, + final INextStateFunctor nss, CostModel cm) { + final TLCState copy = getNextStates0(action, acts, s0, s1, nss, cm); + if (coverage && copy != s1) { + cm.incInvocations(); + } + return copy; + } + + @ExpectInlined + private final TLCState getNextStates0(final Action action, ActionItemList acts, final TLCState s0, final TLCState s1, + final INextStateFunctor nss, CostModel cm) { + if (acts.isEmpty()) { + nss.addElement(s0, action, s1); + return s1.copy(); + } else if (s1.allAssigned()) { + return getNextStatesAllAssigned(action, acts, s0, s1, nss, cm); + } + + final int kind = acts.carKind(); + SemanticNode pred = acts.carPred(); + Context c = acts.carContext(); + ActionItemList acts1 = acts.cdr(); + cm = acts.cm; + if (kind > 0) { + return this.getNextStates(action, pred, acts1, c, s0, s1, nss, cm); + } + else if (kind == -1) { + return this.getNextStates(action, pred, acts1, c, s0, s1, nss, cm); + } + else if (kind == -2) { + return this.processUnchanged(action, pred, acts1, c, s0, s1, nss, cm); + } + else { + IValue v1 = this.eval(pred, c, s0, cm); + IValue v2 = this.eval(pred, c, s1, cm); + if (!v1.equals(v2)) { + if (coverage) { + return this.getNextStates(action, acts1, s0, s1, nss, cm); + } else { + return this.getNextStates0(action, acts1, s0, s1, nss, cm); + } + } + } + return s1; + } + + private final TLCState getNextStatesAllAssigned(final Action action, ActionItemList acts, final TLCState s0, final TLCState s1, + final INextStateFunctor nss, final CostModel cm) { + int kind = acts.carKind(); + SemanticNode pred = acts.carPred(); + Context c = acts.carContext(); + CostModel cm2 = acts.cm; + while (!acts.isEmpty()) { + if (kind > 0 || kind == -1) { + final Value bval = this.eval(pred, c, s0, s1, EvalControl.Clear, cm2); + if (!(bval instanceof BoolValue)) { + // TODO Choose more fitting error message. + Assert.fail(EC.TLC_EXPECTED_EXPRESSION_IN_COMPUTING, + new String[] { "next states", "boolean", bval.toString(), acts.pred.toString() }); + } + if (!((BoolValue) bval).val) { + return s1; + } + } else if (kind == -2) { + // Identical to default handling below (line 876). Ignored during this optimization. + return this.processUnchanged(action, pred, acts.cdr(), c, s0, s1, nss, cm2); + } else { + final IValue v1 = this.eval(pred, c, s0, cm2); + final IValue v2 = this.eval(pred, c, s1, cm2); + if (v1.equals(v2)) { + return s1; + } + } + // Move on to the next action in the ActionItemList. + acts = acts.cdr(); + pred = acts.carPred(); + c = acts.carContext(); + kind = acts.carKind(); + cm2 = acts.cm; + } + nss.addElement(s0, action, s1); + return s1.copy(); + } + + /* getNextStatesAppl */ + + @ExpectInlined + protected abstract TLCState getNextStatesAppl(final Action action, OpApplNode pred, ActionItemList acts, Context c, + TLCState s0, TLCState s1, INextStateFunctor nss, final CostModel cm); + + protected final TLCState getNextStatesApplImpl(final Action action, final OpApplNode pred, final ActionItemList acts, final Context c, + final TLCState s0, final TLCState s1, final INextStateFunctor nss, final CostModel cm) { + final ExprOrOpArgNode[] args = pred.getArgs(); + final int alen = args.length; + final SymbolNode opNode = pred.getOperator(); + + int opcode = BuiltInOPs.getOpCode(opNode.getName()); + + if (opcode == 0) { + // This is a user-defined operator with one exception: it may + // be substed by a builtin operator. This special case occurs + // when the lookup returns an OpDef with opcode # 0. + Object val = this.lookup(opNode, c, s0, false); + + if (val instanceof OpDefNode) { + final OpDefNode opDef = (OpDefNode) val; + opcode = BuiltInOPs.getOpCode(opDef.getName()); + if (opcode == 0) { + return this.getNextStates(action, opDef.getBody(), acts, this.getOpContext(opDef, args, c, true, cm, toolId), s0, s1, nss, cm); + } + } + + // Added by LL 13 Nov 2009 to fix Yuan's fix + /********************************************************************* + * Modified on 23 October 2012 by LL to work if ThmOrAssumpDefNode * + * imported with parameterized instantiation. * + *********************************************************************/ + if (val instanceof ThmOrAssumpDefNode) { + final ThmOrAssumpDefNode opDef = (ThmOrAssumpDefNode)val; + return this.getNextStates(action, opDef.getBody(), acts, this.getOpContext(opDef, args, c, true), s0, s1, nss, cm); + } + + if (val instanceof LazyValue) { + final LazyValue lv = (LazyValue)val; + if (lv.getValue() == null || lv.isUncachable()) { + return this.getNextStates(action, lv.expr, acts, lv.con, s0, s1, nss, lv.cm); + } + val = lv.getValue(); + } + + //TODO If all eval/apply in getNextStatesApplEvalAppl would be side-effect free (ie. not mutate args, c, s0,...), + // this call could be moved into the if(opcode==0) branch below. However, opcode!=0 will only be the case if + // OpDefNode above has been substed with a built-in operator. In other words, a user defines an operator Op1, + // and re-defines Op1 with a TLA+ built-in one in a TLC model (not assumed to be common). => No point in trying + // to move this call into if(opcode==0) because this will be the case most of the time anyway. + final Object bval = getNextStatesApplEvalAppl(alen, args, c, s0, s1, cm, val); + + // opcode == 0 is a user-defined operator. + if (opcode == 0) + { + return getNextStatesApplUsrDefOp(action, pred, acts, s0, s1, nss, cm, bval); + } + } + + return getNextStatesApplSwitch(action, pred, acts, c, s0, s1, nss, cm, args, alen, opcode); + } + + private final Object getNextStatesApplEvalAppl(final int alen, final ExprOrOpArgNode[] args, final Context c, + final TLCState s0, final TLCState s1, final CostModel cm, final Object val) { + if (alen == 0) { + if (val instanceof MethodValue) { + return ((MethodValue)val).apply(EmptyArgs, EvalControl.Clear); + } else if (val instanceof EvaluatingValue) { + return ((EvaluatingValue)val).eval(this, args, c, s0, s1, EvalControl.Clear, cm); + } + } + else { + if (val instanceof OpValue) { // EvaluatingValue sub-class of OpValue! + return ((OpValue) val).eval(this, args, c, s0, s1, EvalControl.Clear, cm); + } + } + return val; + } + + private final TLCState getNextStatesApplUsrDefOp(final Action action, final OpApplNode pred, final ActionItemList acts, final TLCState s0, + final TLCState s1, final INextStateFunctor nss, final CostModel cm, final Object bval) { + if (!(bval instanceof BoolValue)) + { + Assert.fail(EC.TLC_EXPECTED_EXPRESSION_IN_COMPUTING, new String[] { "next states", "boolean", + bval.toString(), pred.toString() }); + } + if (((BoolValue) bval).val) + { + if (coverage) { + return this.getNextStates(action, acts, s0, s1, nss, cm); + } else { + return this.getNextStates0(action, acts, s0, s1, nss, cm); + } + } + return s1; + } + + private final TLCState getNextStatesApplSwitch(final Action action, final OpApplNode pred, final ActionItemList acts, final Context c, final TLCState s0, + final TLCState s1, final INextStateFunctor nss, final CostModel cm, final ExprOrOpArgNode[] args, final int alen, final int opcode) { + TLCState resState = s1; + switch (opcode) { + case OPCODE_cl: // ConjList + case OPCODE_land: + { + ActionItemList acts1 = acts; + for (int i = alen - 1; i > 0; i--) { + acts1 = (ActionItemList) acts1.cons(args[i], c, cm, i); + } + return this.getNextStates(action, args[0], acts1, c, s0, s1, nss, cm); + } + case OPCODE_dl: // DisjList + case OPCODE_lor: + { + for (int i = 0; i < alen; i++) { + resState = this.getNextStates(action, args[i], acts, c, s0, resState, nss, cm); + } + return resState; + } + case OPCODE_be: // BoundedExists + { + SemanticNode body = args[0]; + ContextEnumerator Enum = this.contexts(pred, c, s0, s1, EvalControl.Clear, cm); + Context c1; + while ((c1 = Enum.nextElement()) != null) { + resState = this.getNextStates(action, body, acts, c1, s0, resState, nss, cm); + } + return resState; + } + case OPCODE_bf: // BoundedForall + { + SemanticNode body = args[0]; + ContextEnumerator Enum = this.contexts(pred, c, s0, s1, EvalControl.Clear, cm); + Context c1 = Enum.nextElement(); + if (c1 == null) { + resState = this.getNextStates(action, acts, s0, s1, nss, cm); + } + else { + ActionItemList acts1 = acts; + Context c2; + while ((c2 = Enum.nextElement()) != null) { + acts1 = (ActionItemList) acts1.cons(body, c2, cm, IActionItemList.PRED); + } + resState = this.getNextStates(action, body, acts1, c1, s0, s1, nss, cm); + } + return resState; + } + case OPCODE_fa: // FcnApply + { + Value fval = this.eval(args[0], c, s0, s1, EvalControl.KeepLazy, cm); + if (fval instanceof FcnLambdaValue) { + FcnLambdaValue fcn = (FcnLambdaValue)fval; + if (fcn.fcnRcd == null) { + Context c1 = this.getFcnContext(fcn, args, c, s0, s1, EvalControl.Clear, cm); + return this.getNextStates(action, fcn.body, acts, c1, s0, s1, nss, fcn.cm); + } + fval = fcn.fcnRcd; + } + if (!(fval instanceof Applicable)) { + Assert.fail("In computing next states, a non-function (" + + fval.getKindString() + ") was applied as a function.\n" + pred); + } + Applicable fcn = (Applicable)fval; + Value argVal = this.eval(args[1], c, s0, s1, EvalControl.Clear, cm); + Value bval = fcn.apply(argVal, EvalControl.Clear); + if (!(bval instanceof BoolValue)) { + Assert.fail(EC.TLC_EXPECTED_EXPRESSION_IN_COMPUTING2, new String[] { "next states", "boolean", + pred.toString() }); + } + if (((BoolValue)bval).val) { + return this.getNextStates(action, acts, s0, s1, nss, cm); + } + return resState; + } + case OPCODE_aa: // AngleAct <A>_e + { + ActionItemList acts1 = (ActionItemList) acts.cons(args[1], c, cm, IActionItemList.CHANGED); + return this.getNextStates(action, args[0], acts1, c, s0, s1, nss, cm); + } + case OPCODE_sa: // [A]_e + { + /* The following two lines of code did not work, and were changed by + * YuanYu to mimic the way \/ works. Change made + * 11 Mar 2009, with LL sitting next to him. + */ + // this.getNextStates(action, args[0], acts, c, s0, s1, nss); + // return this.processUnchanged(args[1], acts, c, s0, s1, nss); + resState = this.getNextStates(action, args[0], acts, c, s0, resState, nss, cm); + return this.processUnchanged(action, args[1], acts, c, s0, resState, nss, cm); + } + case OPCODE_ite: // IfThenElse + { + Value guard = this.eval(args[0], c, s0, s1, EvalControl.Clear, cm); + if (!(guard instanceof BoolValue)) { + Assert.fail("In computing next states, a non-boolean expression (" + + guard.getKindString() + ") was used as the condition of" + + " an IF." + pred); + } + if (((BoolValue)guard).val) { + return this.getNextStates(action, args[1], acts, c, s0, s1, nss, cm); + } + else { + return this.getNextStates(action, args[2], acts, c, s0, s1, nss, cm); + } + } + case OPCODE_case: // Case + { + SemanticNode other = null; + for (int i = 0; i < alen; i++) { + OpApplNode pair = (OpApplNode)args[i]; + ExprOrOpArgNode[] pairArgs = pair.getArgs(); + if (pairArgs[0] == null) { + other = pairArgs[1]; + } + else { + Value bval = this.eval(pairArgs[0], c, s0, s1, EvalControl.Clear, coverage ? cm.get(args[i]) : cm); + if (!(bval instanceof BoolValue)) { + Assert.fail("In computing next states, a non-boolean expression (" + + bval.getKindString() + ") was used as a guard condition" + + " of a CASE.\n" + pairArgs[1]); + } + if (((BoolValue)bval).val) { + return this.getNextStates(action, pairArgs[1], acts, c, s0, s1, nss, coverage ? cm.get(args[i]) : cm); + } + } + } + if (other == null) { + Assert.fail("In computing next states, TLC encountered a CASE with no" + + " conditions true.\n" + pred); + } + return this.getNextStates(action, other, acts, c, s0, s1, nss, coverage ? cm.get(args[alen - 1]) : cm); + } + case OPCODE_eq: + { + SymbolNode var = this.getPrimedVar(args[0], c, false); + // Assert.check(var.getName().getVarLoc() >= 0); + if (var == null) { + Value bval = this.eval(pred, c, s0, s1, EvalControl.Clear, cm); + if (!((BoolValue)bval).val) { + return resState; + } + } + else { + UniqueString varName = var.getName(); + IValue lval = s1.lookup(varName); + Value rval = this.eval(args[1], c, s0, s1, EvalControl.Clear, cm); + if (lval == null) { + resState.bind(varName, rval); + resState = this.getNextStates(action, acts, s0, resState, nss, cm); + resState.unbind(varName); + return resState; + } + else if (!lval.equals(rval)) { + return resState; + } + } + return this.getNextStates(action, acts, s0, s1, nss, cm); + } + case OPCODE_in: + { + SymbolNode var = this.getPrimedVar(args[0], c, false); + // Assert.check(var.getName().getVarLoc() >= 0); + if (var == null) { + Value bval = this.eval(pred, c, s0, s1, EvalControl.Clear, cm); + if (!((BoolValue)bval).val) { + return resState; + } + } + else { + UniqueString varName = var.getName(); + Value lval = (Value) s1.lookup(varName); + Value rval = this.eval(args[1], c, s0, s1, EvalControl.Clear, cm); + if (lval == null) { + if (!(rval instanceof Enumerable)) { + Assert.fail("In computing next states, the right side of \\IN" + + " is not enumerable.\n" + pred); + } + ValueEnumeration Enum = ((Enumerable)rval).elements(); + Value elem; + while ((elem = Enum.nextElement()) != null) { + resState.bind(varName, elem); + resState = this.getNextStates(action, acts, s0, resState, nss, cm); + resState.unbind(varName); + } + return resState; + } + else if (!rval.member(lval)) { + return resState; + } + } + return this.getNextStates(action, acts, s0, s1, nss, cm); + } + case OPCODE_implies: + { + Value bval = this.eval(args[0], c, s0, s1, EvalControl.Clear, cm); + if (!(bval instanceof BoolValue)) { + Assert.fail("In computing next states of a predicate of the form" + + " P => Q, P was\n" + bval.getKindString() + ".\n" + pred); + } + if (((BoolValue)bval).val) { + return this.getNextStates(action, args[1], acts, c, s0, s1, nss, cm); + } + else { + return this.getNextStates(action, acts, s0, s1, nss, cm); + } + } + case OPCODE_unchanged: + { + return this.processUnchanged(action, args[0], acts, c, s0, s1, nss, cm); + } + case OPCODE_cdot: + { + Assert.fail("The current version of TLC does not support action composition."); + /*** + TLCState s01 = TLCStateFun.Empty; + StateVec iss = new StateVec(0); + this.getNextStates(action, args[0], ActionItemList.Empty, c, s0, s01, iss); + int sz = iss.size(); + for (int i = 0; i < sz; i++) { + s01 = iss.elementAt(i); + this.getNextStates(action, args[1], acts, c, s01, s1, nss); + } + ***/ + return s1; + } + // The following case added by LL on 13 Nov 2009 to handle subexpression names. + case OPCODE_nop: + { + return this.getNextStates(action, args[0], acts, c, s0, s1, nss, cm); + } + default: + { + // We handle all the other builtin operators here. + Value bval = this.eval(pred, c, s0, s1, EvalControl.Clear, cm); + if (!(bval instanceof BoolValue)) { + Assert.fail(EC.TLC_EXPECTED_EXPRESSION_IN_COMPUTING, new String[] { "next states", "boolean", + bval.toString(), pred.toString() }); + } + if (((BoolValue)bval).val) { + resState = this.getNextStates(action, acts, s0, s1, nss, cm); + } + return resState; + } + } + } + + /* processUnchanged */ + + @ExpectInlined + protected abstract TLCState processUnchanged(final Action action, SemanticNode expr, ActionItemList acts, Context c, + TLCState s0, TLCState s1, INextStateFunctor nss, CostModel cm); + + protected final TLCState processUnchangedImpl(final Action action, SemanticNode expr, ActionItemList acts, Context c, + TLCState s0, TLCState s1, INextStateFunctor nss, CostModel cm) { + if (coverage){cm = cm.get(expr);} + SymbolNode var = this.getVar(expr, c, false, toolId); + TLCState resState = s1; + if (var != null) { + return processUnchangedImplVar(action, expr, acts, s0, s1, nss, var, cm); + } + + if (expr instanceof OpApplNode) { + OpApplNode expr1 = (OpApplNode)expr; + ExprOrOpArgNode[] args = expr1.getArgs(); + int alen = args.length; + SymbolNode opNode = expr1.getOperator(); + UniqueString opName = opNode.getName(); + int opcode = BuiltInOPs.getOpCode(opName); + + if (opcode == OPCODE_tup) { + return processUnchangedImplTuple(action, acts, c, s0, s1, nss, args, alen, cm, coverage ? cm.get(expr1) : cm); + } + + if (opcode == 0 && alen == 0) { + // a 0-arity operator: + return processUnchangedImpl0Arity(action, expr, acts, c, s0, s1, nss, cm, opNode, opName); + } + } + + IValue v0 = this.eval(expr, c, s0, cm); + Value v1 = this.eval(expr, c, s1, null, EvalControl.Clear, cm); + if (v0.equals(v1)) { + resState = this.getNextStates(action, acts, s0, s1, nss, cm); + } + return resState; + } + + @ExpectInlined + private final TLCState processUnchangedImpl0Arity(final Action action, final SemanticNode expr, final ActionItemList acts, + final Context c, final TLCState s0, final TLCState s1, final INextStateFunctor nss, final CostModel cm, + final SymbolNode opNode, final UniqueString opName) { + final Object val = this.lookup(opNode, c, false); + + if (val instanceof OpDefNode) { + return this.processUnchanged(action, ((OpDefNode)val).getBody(), acts, c, s0, s1, nss, cm); + } + else if (val instanceof LazyValue) { + final LazyValue lv = (LazyValue)val; + return this.processUnchanged(action, lv.expr, acts, lv.con, s0, s1, nss, cm); + } + else { + Assert.fail("In computing next states, TLC found the identifier\n" + + opName + " undefined in an UNCHANGED expression at\n" + expr); + } + return this.getNextStates(action, acts, s0, s1, nss, cm); + } + + @Override + public final IValue eval(SemanticNode expr, Context c, TLCState s0) { + return this.eval(expr, c, s0, TLCState.Empty, EvalControl.Clear, CostModel.DO_NOT_RECORD); + } + + @ExpectInlined + private final TLCState processUnchangedImplTuple(final Action action, ActionItemList acts, Context c, TLCState s0, TLCState s1, INextStateFunctor nss, + ExprOrOpArgNode[] args, int alen, CostModel cm, CostModel cmNested) { + // a tuple: + if (alen != 0) { + ActionItemList acts1 = acts; + for (int i = alen-1; i > 0; i--) { + acts1 = (ActionItemList) acts1.cons(args[i], c, cmNested, IActionItemList.UNCHANGED); + } + return this.processUnchanged(action, args[0], acts1, c, s0, s1, nss, cmNested); + } + return this.getNextStates(action, acts, s0, s1, nss, cm); + } + + @ExpectInlined + private final TLCState processUnchangedImplVar(final Action action, SemanticNode expr, ActionItemList acts, TLCState s0, TLCState s1, INextStateFunctor nss, + SymbolNode var, final CostModel cm) { + TLCState resState = s1; + // expr is a state variable: + final UniqueString varName = var.getName(); + final IValue val0 = s0.lookup(varName); + final IValue val1 = s1.lookup(varName); + if (val1 == null) { + resState.bind(varName, val0); + if (coverage) { + resState = this.getNextStates(action, acts, s0, resState, nss, cm); + } else { + resState = this.getNextStates0(action, acts, s0, resState, nss, cm); + } + resState.unbind(varName); + } + else if (val0.equals(val1)) { + if (coverage) { + resState = this.getNextStates(action, acts, s0, s1, nss, cm); + } else { + resState = this.getNextStates0(action, acts, s0, s1, nss, cm); + } + } + else { + MP.printWarning(EC.TLC_UNCHANGED_VARIABLE_CHANGED, new String[]{varName.toString(), expr.toString()}); + } + return resState; + } + + /* eval */ + + /* Special version of eval for state expressions. */ + @Override + public final IValue eval(SemanticNode expr, Context c, TLCState s0, CostModel cm) { + return this.eval(expr, c, s0, TLCState.Empty, EvalControl.Clear, cm); + } + + @Override + public final IValue eval(SemanticNode expr, Context c, TLCState s0, + TLCState s1, final int control) { + return eval(expr, c, s0, s1, control, CostModel.DO_NOT_RECORD); + } + /* + * This method evaluates the expression expr in the given context, + * current state, and partial next state. + */ + public abstract Value eval(SemanticNode expr, Context c, TLCState s0, + TLCState s1, final int control, final CostModel cm); + + @ExpectInlined + protected final Value evalImpl(final SemanticNode expr, final Context c, final TLCState s0, + final TLCState s1, final int control, CostModel cm) { + switch (expr.getKind()) { + /*********************************************************************** + * LabelKind class added by LL on 13 Jun 2007. * + ***********************************************************************/ + case LabelKind: + { + LabelNode expr1 = (LabelNode) expr; + return this.eval(expr1.getBody(), c, s0, s1, control, cm); + } + case OpApplKind: + { + OpApplNode expr1 = (OpApplNode)expr; + if (coverage) {cm = cm.get(expr);} + return this.evalAppl(expr1, c, s0, s1, control, cm); + } + case LetInKind: + { + return evalImplLetInKind((LetInNode) expr, c, s0, s1, control, cm); + } + case SubstInKind: + { + return evalImplSubstInKind((SubstInNode) expr, c, s0, s1, control, cm); + } + // Added by LL on 13 Nov 2009 to handle theorem and assumption names. + case APSubstInKind: + { + return evalImplApSubstInKind((APSubstInNode) expr, c, s0, s1, control, cm); + } + case NumeralKind: + case DecimalKind: + case StringKind: + { + return (Value) WorkerValue.mux(expr.getToolObject(toolId)); + } + case AtNodeKind: + { + return (Value)c.lookup(EXCEPT_AT); + } + case OpArgKind: + { + return evalImplOpArgKind((OpArgNode) expr, c, s0, s1, cm); + } + default: + { + Assert.fail("Attempted to evaluate an expression that cannot be evaluated.\n" + + expr); + return null; // make compiler happy + } + } + } + + @ExpectInlined + private final Value evalImplLetInKind(LetInNode expr1, Context c, TLCState s0, TLCState s1, final int control, final CostModel cm) { + OpDefNode[] letDefs = expr1.getLets(); + int letLen = letDefs.length; + Context c1 = c; + for (int i = 0; i < letLen; i++) { + OpDefNode opDef = letDefs[i]; + if (opDef.getArity() == 0) { + Value rhs = new LazyValue(opDef.getBody(), c1, cm); + c1 = c1.cons(opDef, rhs); + } + } + return this.eval(expr1.getBody(), c1, s0, s1, control, cm); + } + + @ExpectInlined + private final Value evalImplSubstInKind(SubstInNode expr1, Context c, TLCState s0, TLCState s1, final int control, final CostModel cm) { + Subst[] subs = expr1.getSubsts(); + int slen = subs.length; + Context c1 = c; + for (int i = 0; i < slen; i++) { + Subst sub = subs[i]; + c1 = c1.cons(sub.getOp(), this.getVal(sub.getExpr(), c, true, coverage ? sub.getCM() : cm, toolId)); + } + return this.eval(expr1.getBody(), c1, s0, s1, control, cm); + } + + @ExpectInlined + private final Value evalImplApSubstInKind(APSubstInNode expr1, Context c, TLCState s0, TLCState s1, final int control, final CostModel cm) { + Subst[] subs = expr1.getSubsts(); + int slen = subs.length; + Context c1 = c; + for (int i = 0; i < slen; i++) { + Subst sub = subs[i]; + c1 = c1.cons(sub.getOp(), this.getVal(sub.getExpr(), c, true, cm, toolId)); + } + return this.eval(expr1.getBody(), c1, s0, s1, control, cm); + } + + @ExpectInlined + private final Value evalImplOpArgKind(OpArgNode expr1, Context c, TLCState s0, TLCState s1, final CostModel cm) { + SymbolNode opNode = expr1.getOp(); + Object val = this.lookup(opNode, c, false); + if (val instanceof OpDefNode) { + return setSource(expr1, new OpLambdaValue((OpDefNode)val, this, c, s0, s1, cm)); + } + return (Value)val; + } + + /* evalAppl */ + + @ExpectInlined + protected abstract Value evalAppl(final OpApplNode expr, Context c, TLCState s0, + TLCState s1, final int control, final CostModel cm); + + protected final Value evalApplImpl(final OpApplNode expr, Context c, TLCState s0, + TLCState s1, final int control, CostModel cm) { + if (coverage){ + cm = cm.getAndIncrement(expr); + } + ExprOrOpArgNode[] args = expr.getArgs(); + SymbolNode opNode = expr.getOperator(); + int opcode = BuiltInOPs.getOpCode(opNode.getName()); + + if (opcode == 0) { + // This is a user-defined operator with one exception: it may + // be substed by a builtin operator. This special case occurs + // when the lookup returns an OpDef with opcode # 0. + Object val = this.lookup(opNode, c, s0, EvalControl.isPrimed(control)); + + // First, unlazy if it is a lazy value. We cannot use the cached + // value when s1 == null or isEnabled(control). + if (val instanceof LazyValue) { + final LazyValue lv = (LazyValue) val; + if (s1 == null) { + val = this.eval(lv.expr, lv.con, s0, null, control, lv.getCostModel()); + } else if (lv.isUncachable() || EvalControl.isEnabled(control)) { + // Never use cached LazyValues in an ENABLED expression. This is why all + // this.enabled* methods pass EvalControl.Enabled (the only exception being the + // call on line line 2799 which passes EvalControl.Primed). This is why we can + // be sure that ENALBED expressions are not affected by the caching bug tracked + // in Github issue 113 (see below). + val = this.eval(lv.expr, lv.con, s0, s1, control, lv.getCostModel()); + } else { + val = lv.getValue(); + if (val == null) { + final Value res = this.eval(lv.expr, lv.con, s0, s1, control, lv.getCostModel()); + // This check has been suggested by Yuan Yu on 01/15/2018: + // + // If init-states are being generated, level has to be <= ConstantLevel for + // caching/LazyValue to be allowed. If next-states are being generated, level + // has to be <= VariableLevel. The level indicates if the expression to be + // evaluated contains only constants, constants & variables, constants & + // variables and primed variables (thus action) or is a temporal formula. + // + // This restriction is in place to fix Github issue 113 + // (https://github.com/tlaplus/tlaplus/issues/113) - + // TLC can generate invalid sets of init or next-states caused by broken + // LazyValue evaluation. The related tests are AssignmentInit* and + // AssignmentNext*. Without this fix, TLC essentially reuses a stale lv.val when + // it needs to re-evaluate res because the actual operands to eval changed. + // Below is Leslie's formal description of the bug: + // + // The possible initial values of some variable var are specified by a subformula + // + // F(..., var, ...) + // + // in the initial predicate, for some operator F such that expanding the + // definition of F results in a formula containing more than one occurrence of + // var , not all occurring in separate disjuncts of that formula. + // + // The possible next values of some variable var are specified by a subformula + // + // F(..., var', ...) + // + // in the next-state relation, for some operator F such that expanding the + // definition of F results in a formula containing more than one occurrence of + // var' , not all occurring in separate disjuncts of that formula. + // + // An example of the first case is an initial predicate Init defined as follows: + // + // VARIABLES x, ... + // F(var) == \/ var \in 0..99 /\ var % 2 = 0 + // \/ var = -1 + // Init == /\ F(x) + // /\ ... + // + // The error would not appear if F were defined by: + // + // F(var) == \/ var \in {i \in 0..99 : i % 2 = 0} + // \/ var = -1 + // + // or if the definition of F(x) were expanded in Init : + // + // Init == /\ \/ x \in 0..99 /\ x % 2 = 0 + // \/ x = -1 + // /\ ... + // + // A similar example holds for case 2 with the same operator F and the + // next-state formula + // + // Next == /\ F(x') + // /\ ... + // + // The workaround is to rewrite the initial predicate or next-state relation so + // it is not in the form that can cause the bug. The simplest way to do that is + // to expand (in-line) the definition of F in the definition of the initial + // predicate or next-state relation. + // + // Note that EvalControl.Init is only set in the scope of this.getInitStates*, + // but not in the scope of methods such as this.isInModel, this.isGoodState... + // which are invoked by DFIDChecker and ModelChecker#doInit and doNext. These + // invocation however don't pose a problem with regards to issue 113 because + // they don't generate the set of initial or next states but get passed fully + // generated/final states. + // + // !EvalControl.isInit(control) means Tool is either processing the spec in + // this.process* as part of initialization or that next-states are being + // generated. The latter case has to restrict usage of cached LazyValue as + // discussed above. + final int level = ((LevelNode) lv.expr).getLevel(); // cast to LevelNode is safe because LV only subclass of SN. + if ((EvalControl.isInit(control) && level <= LevelConstants.ConstantLevel) + || (!EvalControl.isInit(control) && level <= LevelConstants.VariableLevel)) { + // The performance benefits of caching values is generally debatable. The time + // it takes TLC to check a reasonable sized model of the PaxosCommit [1] spec is + // ~2h with, with limited caching due to the fix for issue 113 or without + // caching. There is no measurable performance difference even though the change + // for issue 113 reduces the cache hits from ~13 billion to ~4 billion. This was + // measured with an instrumented version of TLC. + // [1] general/performance/PaxosCommit/ + lv.setValue(res); + } + val = res; + } + } + + } + + Value res = null; + if (val instanceof OpDefNode) { + OpDefNode opDef = (OpDefNode)val; + opcode = BuiltInOPs.getOpCode(opDef.getName()); + if (opcode == 0) { + Context c1 = this.getOpContext(opDef, args, c, true, cm, toolId); + res = this.eval(opDef.getBody(), c1, s0, s1, control, cm); + } + } + else if (val instanceof Value) { + res = (Value)val; + int alen = args.length; + if (alen == 0) { + if (val instanceof MethodValue) { + res = ((MethodValue)val).apply(EmptyArgs, EvalControl.Clear); + } else if (val instanceof EvaluatingValue) { + // Allow EvaluatingValue overwrites to have zero arity. + res = ((EvaluatingValue) val).eval(this, args, c, s0, s1, control, cm); + } + } + else { + if (val instanceof OpValue) { + res = ((OpValue) val).eval(this, args, c, s0, s1, control, cm); + } + } + } + /********************************************************************* + * The following added by Yuan Yu on 13 Nov 2009 to allow theorem an * + * assumption names to be used as expressions. * + * * + * Modified on 23 October 2012 by LL to work if ThmOrAssumpDefNode * + * imported with parameterized instantiation. * + *********************************************************************/ + else if (val instanceof ThmOrAssumpDefNode) { +// Assert.fail("Trying to evaluate the theorem or assumption name `" +// + opNode.getName() + "'. \nUse `" + opNode.getName() +// + "!:' instead.\n" +expr); + ThmOrAssumpDefNode opDef = (ThmOrAssumpDefNode) val ; + Context c1 = this.getOpContext(opDef, args, c, true); + return this.eval(opDef.getBody(), c1, s0, s1, control, cm); + } + else { + Assert.fail(EC.TLC_CONFIG_UNDEFINED_OR_NO_OPERATOR, + new String[] { opNode.getName().toString(), expr.toString() }); + } + if (opcode == 0) { + return res; + } + } + + switch (opcode) { + case OPCODE_bc: // BoundedChoose + { + SemanticNode pred = args[0]; + SemanticNode inExpr = expr.getBdedQuantBounds()[0]; + Value inVal = this.eval(inExpr, c, s0, s1, control, cm); + if (!(inVal instanceof Enumerable)) { + Assert.fail("Attempted to compute the value of an expression of\n" + + "form CHOOSE x \\in S: P, but S was not enumerable.\n" + expr); + } + + // To fix Bugzilla Bug 279 : TLC bug caused by TLC's not preserving the semantics of CHOOSE + // (@see tlc2.tool.BugzillaBug279Test), + // the statement + // + // inVal.normalize(); + // + // was replaced by the following by LL on 7 Mar 2012. This fix has not yet received + // the blessing of Yuan Yu, so it should be considered to be provisional. + // + // Value convertedVal = inVal.ToSetEnum(); + // if (convertedVal != null) { + // inVal = convertedVal; + // } else { + // inVal.normalize(); + // } + // end of fix. + + // MAK 09/22/2018: + // The old fix above has the undesired side effect of enumerating inVal. In + // other words, e.g. a SUBSET 1..8 would be enumerated and normalized into a + // SetEnumValue. This is expensive and especially overkill, if the CHOOSE + // predicate holds for most if not all elements of inVal. In this case, we + // don't want to fully enumerate inVal but instead return the first element + // obtained from Enumerable#elements for which the predicate holds. Thus, + // Enumerable#elements(Ordering) has been added by which we make the requirement + // for elements to be normalized explicit. Implementor of Enumerable, such as + // SubsetValue are then free to implement elements that returns elements in + // normalized order without converting SubsetValue into SetEnumValue first. + + inVal.normalize(); + + ValueEnumeration enumSet = ((Enumerable)inVal).elements(Enumerable.Ordering.NORMALIZED); + FormalParamNode[] bvars = expr.getBdedQuantSymbolLists()[0]; + boolean isTuple = expr.isBdedQuantATuple()[0]; + if (isTuple) { + // Identifier tuple case: + int cnt = bvars.length; + Value val; + while ((val = enumSet.nextElement()) != null) { + TupleValue tv = (TupleValue) val.toTuple(); + if (tv == null || tv.size() != cnt) { + Assert.fail("Attempted to compute the value of an expression of form\n" + + "CHOOSE <<x1, ... , xN>> \\in S: P, but S was not a set\n" + + "of N-tuples.\n" + expr); + } + Context c1 = c; + for (int i = 0; i < cnt; i++) { + c1 = c1.cons(bvars[i], tv.elems[i]); + } + Value bval = this.eval(pred, c1, s0, s1, control, cm); + if (!(bval instanceof BoolValue)) { + Assert.fail(EC.TLC_EXPECTED_VALUE, new String[]{"boolean", expr.toString()}); + } + if (((BoolValue)bval).val) { + return (Value) val; + } + } + } + else { + // Simple identifier case: + SymbolNode name = bvars[0]; + Value val; + while ((val = enumSet.nextElement()) != null) { + Context c1 = c.cons(name, val); + Value bval = this.eval(pred, c1, s0, s1, control, cm); + if (!(bval instanceof BoolValue)) { + Assert.fail(EC.TLC_EXPECTED_VALUE, new String[]{"boolean", expr.toString()}); + } + if (((BoolValue)bval).val) { + return (Value) val; + } + } + } + Assert.fail("Attempted to compute the value of an expression of form\n" + + "CHOOSE x \\in S: P, but no element of S satisfied P.\n" + expr); + return null; // make compiler happy + } + case OPCODE_be: // BoundedExists + { + ContextEnumerator Enum = this.contexts(expr, c, s0, s1, control, cm); + SemanticNode body = args[0]; + Context c1; + while ((c1 = Enum.nextElement()) != null) { + Value bval = this.eval(body, c1, s0, s1, control, cm); + if (!(bval instanceof BoolValue)) { + Assert.fail(EC.TLC_EXPECTED_VALUE, new String[]{"boolean", expr.toString()}); + } + if (((BoolValue)bval).val) { + return BoolValue.ValTrue; + } + } + return BoolValue.ValFalse; + } + case OPCODE_bf: // BoundedForall + { + ContextEnumerator Enum = this.contexts(expr, c, s0, s1, control, cm); + SemanticNode body = args[0]; + Context c1; + while ((c1 = Enum.nextElement()) != null) { + Value bval = this.eval(body, c1, s0, s1, control, cm); + if (!(bval instanceof BoolValue)) { + Assert.fail(EC.TLC_EXPECTED_VALUE, new String[]{"boolean", expr.toString()}); + } + if (!((BoolValue)bval).val) { + return BoolValue.ValFalse; + } + } + return BoolValue.ValTrue; + } + case OPCODE_case: // Case + { + int alen = args.length; + SemanticNode other = null; + for (int i = 0; i < alen; i++) { + OpApplNode pairNode = (OpApplNode)args[i]; + ExprOrOpArgNode[] pairArgs = pairNode.getArgs(); + if (pairArgs[0] == null) { + other = pairArgs[1]; + if (coverage) { cm = cm.get(pairNode); } + } + else { + Value bval = this.eval(pairArgs[0], c, s0, s1, control, coverage ? cm.get(pairNode) : cm); + if (!(bval instanceof BoolValue)) { + Assert.fail("A non-boolean expression (" + bval.getKindString() + + ") was used as a condition of a CASE. " + pairArgs[0]); + } + if (((BoolValue)bval).val) { + return this.eval(pairArgs[1], c, s0, s1, control, coverage ? cm.get(pairNode) : cm); + } + } + } + if (other == null) { + Assert.fail("Attempted to evaluate a CASE with no conditions true.\n" + expr); + } + return this.eval(other, c, s0, s1, control, cm); + } + case OPCODE_cp: // CartesianProd + { + int alen = args.length; + Value[] sets = new Value[alen]; + for (int i = 0; i < alen; i++) { + sets[i] = this.eval(args[i], c, s0, s1, control, cm); + } + return setSource(expr, new SetOfTuplesValue(sets, cm)); + } + case OPCODE_cl: // ConjList + { + int alen = args.length; + for (int i = 0; i < alen; i++) { + Value bval = this.eval(args[i], c, s0, s1, control, cm); + if (!(bval instanceof BoolValue)) { + Assert.fail("A non-boolean expression (" + bval.getKindString() + + ") was used as a formula in a conjunction.\n" + args[i]); + } + if (!((BoolValue)bval).val) { + return BoolValue.ValFalse; + } + } + return BoolValue.ValTrue; + } + case OPCODE_dl: // DisjList + { + int alen = args.length; + for (int i = 0; i < alen; i++) { + Value bval = this.eval(args[i], c, s0, s1, control, cm); + if (!(bval instanceof BoolValue)) { + Assert.fail("A non-boolean expression (" + bval.getKindString() + + ") was used as a formula in a disjunction.\n" + args[i]); + } + if (((BoolValue)bval).val) { + return BoolValue.ValTrue; + } + } + return BoolValue.ValFalse; + } + case OPCODE_exc: // Except + { + int alen = args.length; + Value result = this.eval(args[0], c, s0, s1, control, cm); + // SZ: variable not used ValueExcept[] expts = new ValueExcept[alen-1]; + for (int i = 1; i < alen; i++) { + OpApplNode pairNode = (OpApplNode)args[i]; + ExprOrOpArgNode[] pairArgs = pairNode.getArgs(); + SemanticNode[] cmpts = ((OpApplNode)pairArgs[0]).getArgs(); + + Value[] lhs = new Value[cmpts.length]; + for (int j = 0; j < lhs.length; j++) { + lhs[j] = this.eval(cmpts[j], c, s0, s1, control, coverage ? cm.get(pairNode).get(pairArgs[0]) : cm); + } + Value atVal = result.select(lhs); + if (atVal == null) { + // Do nothing but warn: + MP.printWarning(EC.TLC_EXCEPT_APPLIED_TO_UNKNOWN_FIELD, new String[]{args[0].toString()}); + } + else { + Context c1 = c.cons(EXCEPT_AT, atVal); + Value rhs = this.eval(pairArgs[1], c1, s0, s1, control, coverage ? cm.get(pairNode) : cm); + ValueExcept vex = new ValueExcept(lhs, rhs); + result = (Value) result.takeExcept(vex); + } + } + return result; + } + case OPCODE_fa: // FcnApply + { + Value result = null; + Value fval = this.eval(args[0], c, s0, s1, EvalControl.setKeepLazy(control), cm); + if ((fval instanceof FcnRcdValue) || + (fval instanceof FcnLambdaValue)) { + Applicable fcn = (Applicable)fval; + Value argVal = this.eval(args[1], c, s0, s1, control, cm); + result = fcn.apply(argVal, control); + } + else if ((fval instanceof TupleValue) || + (fval instanceof RecordValue)) { + Applicable fcn = (Applicable)fval; + if (args.length != 2) { + Assert.fail("Attempted to evaluate an expression of form f[e1, ... , eN]" + + "\nwith f a tuple or record and N > 1.\n" + expr); + } + Value aval = this.eval(args[1], c, s0, s1, control, cm); + result = fcn.apply(aval, control); + } + else { + Assert.fail("A non-function (" + fval.getKindString() + ") was applied" + + " as a function.\n" + expr); + } + return result; + } + case OPCODE_fc: // FcnConstructor + case OPCODE_nrfs: // NonRecursiveFcnSpec + case OPCODE_rfs: // RecursiveFcnSpec + { + FormalParamNode[][] formals = expr.getBdedQuantSymbolLists(); + boolean[] isTuples = expr.isBdedQuantATuple(); + ExprNode[] domains = expr.getBdedQuantBounds(); + + Value[] dvals = new Value[domains.length]; + boolean isFcnRcd = true; + for (int i = 0; i < dvals.length; i++) { + dvals[i] = this.eval(domains[i], c, s0, s1, control, cm); + isFcnRcd = isFcnRcd && (dvals[i] instanceof Reducible); + } + FcnParams params = new FcnParams(formals, isTuples, dvals); + + SemanticNode fbody = args[0]; + FcnLambdaValue fval = (FcnLambdaValue) setSource(expr, new FcnLambdaValue(params, fbody, this, c, s0, s1, control, cm)); + if (opcode == OPCODE_rfs) { + SymbolNode fname = expr.getUnbdedQuantSymbols()[0]; + fval.makeRecursive(fname); + isFcnRcd = false; + } + if (isFcnRcd && !EvalControl.isKeepLazy(control)) { + return (Value) fval.toFcnRcd(); + } + return fval; + } + case OPCODE_ite: // IfThenElse + { + Value bval = this.eval(args[0], c, s0, s1, control, cm); + if (!(bval instanceof BoolValue)) { + Assert.fail("A non-boolean expression (" + bval.getKindString() + + ") was used as the condition of an IF.\n" + expr); + } + if (((BoolValue)bval).val) { + return this.eval(args[1], c, s0, s1, control, cm); + } + return this.eval(args[2], c, s0, s1, control, cm); + } + case OPCODE_rc: // RcdConstructor + { + int alen = args.length; + UniqueString[] names = new UniqueString[alen]; + Value[] vals = new Value[alen]; + for (int i = 0; i < alen; i++) { + OpApplNode pairNode = (OpApplNode)args[i]; + ExprOrOpArgNode[] pair = pairNode.getArgs(); + names[i] = ((StringValue)pair[0].getToolObject(toolId)).getVal(); + vals[i] = this.eval(pair[1], c, s0, s1, control, coverage ? cm.get(pairNode) : cm); + } + return setSource(expr, new RecordValue(names, vals, false, cm)); + } + case OPCODE_rs: // RcdSelect + { + Value rval = this.eval(args[0], c, s0, s1, control, cm); + Value sval = (Value) WorkerValue.mux(args[1].getToolObject(toolId)); + if (rval instanceof RecordValue) { + Value result = (Value) ((RecordValue)rval).select(sval); + if (result == null) { + Assert.fail("Attempted to select nonexistent field " + sval + " from the" + + " record\n" + Values.ppr(rval.toString()) + "\n" + expr); + } + return result; + } + else { + FcnRcdValue fcn = (FcnRcdValue) rval.toFcnRcd(); + if (fcn == null) { + Assert.fail("Attempted to select field " + sval + " from a non-record" + + " value " + Values.ppr(rval.toString()) + "\n" + expr); + } + return fcn.apply(sval, control); + } + } + case OPCODE_se: // SetEnumerate + { + int alen = args.length; + ValueVec vals = new ValueVec(alen); + for (int i = 0; i < alen; i++) { + vals.addElement(this.eval(args[i], c, s0, s1, control, cm)); + } + return setSource(expr, new SetEnumValue(vals, false, cm)); + } + case OPCODE_soa: // SetOfAll: {e(x) : x \in S} + { + ValueVec vals = new ValueVec(); + ContextEnumerator Enum = this.contexts(expr, c, s0, s1, control, cm); + SemanticNode body = args[0]; + Context c1; + while ((c1 = Enum.nextElement()) != null) { + Value val = this.eval(body, c1, s0, s1, control, cm); + vals.addElement(val); + // vals.addElement1(val); + } + return setSource(expr, new SetEnumValue(vals, false, cm)); + } + case OPCODE_sor: // SetOfRcds + { + int alen = args.length; + UniqueString names[] = new UniqueString[alen]; + Value vals[] = new Value[alen]; + for (int i = 0; i < alen; i++) { + OpApplNode pairNode = (OpApplNode)args[i]; + ExprOrOpArgNode[] pair = pairNode.getArgs(); + names[i] = ((StringValue)pair[0].getToolObject(toolId)).getVal(); + vals[i] = this.eval(pair[1], c, s0, s1, control, coverage ? cm.get(pairNode) : cm); + } + return setSource(expr, new SetOfRcdsValue(names, vals, false, cm)); + } + case OPCODE_sof: // SetOfFcns + { + Value lhs = this.eval(args[0], c, s0, s1, control, cm); + Value rhs = this.eval(args[1], c, s0, s1, control, cm); + return setSource(expr, new SetOfFcnsValue(lhs, rhs, cm)); + } + case OPCODE_sso: // SubsetOf + { + SemanticNode pred = args[0]; + SemanticNode inExpr = expr.getBdedQuantBounds()[0]; + Value inVal = this.eval(inExpr, c, s0, s1, control, cm); + boolean isTuple = expr.isBdedQuantATuple()[0]; + FormalParamNode[] bvars = expr.getBdedQuantSymbolLists()[0]; + if (inVal instanceof Reducible) { + ValueVec vals = new ValueVec(); + ValueEnumeration enumSet = ((Enumerable)inVal).elements(); + Value elem; + if (isTuple) { + while ((elem = enumSet.nextElement()) != null) { + Context c1 = c; + Value[] tuple = ((TupleValue)elem).elems; + for (int i = 0; i < bvars.length; i++) { + c1 = c1.cons(bvars[i], tuple[i]); + } + Value bval = this.eval(pred, c1, s0, s1, control, cm); + if (!(bval instanceof BoolValue)) { + Assert.fail("Attempted to evaluate an expression of form {x \\in S : P(x)}" + + " when P was " + bval.getKindString() + ".\n" + pred); + } + if (((BoolValue)bval).val) { + vals.addElement(elem); + } + } + } + else { + SymbolNode idName = bvars[0]; + while ((elem = enumSet.nextElement()) != null) { + Context c1 = c.cons(idName, elem); + Value bval = this.eval(pred, c1, s0, s1, control, cm); + if (!(bval instanceof BoolValue)) { + Assert.fail("Attempted to evaluate an expression of form {x \\in S : P(x)}" + + " when P was " + bval.getKindString() + ".\n" + pred); + } + if (((BoolValue)bval).val) { + vals.addElement(elem); + } + } + } + return setSource(expr, new SetEnumValue(vals, inVal.isNormalized(), cm)); + } + else if (isTuple) { + return setSource(expr, new SetPredValue(bvars, inVal, pred, this, c, s0, s1, control, cm)); + } + else { + return setSource(expr, new SetPredValue(bvars[0], inVal, pred, this, c, s0, s1, control, cm)); + } + } + case OPCODE_tup: // Tuple + { + int alen = args.length; + Value[] vals = new Value[alen]; + for (int i = 0; i < alen; i++) { + vals[i] = this.eval(args[i], c, s0, s1, control, cm); + } + return setSource(expr, new TupleValue(vals, cm)); + } + case OPCODE_uc: // UnboundedChoose + { + Assert.fail("TLC attempted to evaluate an unbounded CHOOSE.\n" + + "Make sure that the expression is of form CHOOSE x \\in S: P(x).\n" + + expr); + return null; // make compiler happy + } + case OPCODE_ue: // UnboundedExists + { + Assert.fail("TLC attempted to evaluate an unbounded \\E.\n" + + "Make sure that the expression is of form \\E x \\in S: P(x).\n" + + expr); + return null; // make compiler happy + } + case OPCODE_uf: // UnboundedForall + { + Assert.fail("TLC attempted to evaluate an unbounded \\A.\n" + + "Make sure that the expression is of form \\A x \\in S: P(x).\n" + + expr); + return null; // make compiler happy + } + case OPCODE_lnot: + { + Value arg = this.eval(args[0], c, s0, s1, control, cm); + if (!(arg instanceof BoolValue)) { + Assert.fail("Attempted to apply the operator ~ to a non-boolean\n(" + + arg.getKindString() + ")\n" + expr); + } + return (((BoolValue)arg).val) ? BoolValue.ValFalse : BoolValue.ValTrue; + } + case OPCODE_subset: + { + Value arg = this.eval(args[0], c, s0, s1, control, cm); + return setSource(expr, new SubsetValue(arg, cm)); + } + case OPCODE_union: + { + Value arg = this.eval(args[0], c, s0, s1, control, cm); + return setSource(expr, UnionValue.union(arg)); + } + case OPCODE_domain: + { + Value arg = this.eval(args[0], c, s0, s1, control, cm); + if (!(arg instanceof Applicable)) { + Assert.fail("Attempted to apply the operator DOMAIN to a non-function\n(" + + arg.getKindString() + ")\n" + expr); + } + return setSource(expr, ((Applicable)arg).getDomain()); + } + case OPCODE_enabled: + { + TLCState sfun = TLCStateFun.Empty; + Context c1 = Context.branch(c); + sfun = this.enabled(args[0], ActionItemList.Empty, c1, s0, sfun, cm); + return (sfun != null) ? BoolValue.ValTrue : BoolValue.ValFalse; + } + case OPCODE_eq: + { + Value arg1 = this.eval(args[0], c, s0, s1, control, cm); + Value arg2 = this.eval(args[1], c, s0, s1, control, cm); + return (arg1.equals(arg2)) ? BoolValue.ValTrue : BoolValue.ValFalse; + } + case OPCODE_land: + { + Value arg1 = this.eval(args[0], c, s0, s1, control, cm); + if (!(arg1 instanceof BoolValue)) { + Assert.fail("Attempted to evaluate an expression of form P /\\ Q" + + " when P was\n" + arg1.getKindString() + ".\n" + expr); + } + if (((BoolValue)arg1).val) { + Value arg2 = this.eval(args[1], c, s0, s1, control, cm); + if (!(arg2 instanceof BoolValue)) { + Assert.fail("Attempted to evaluate an expression of form P /\\ Q" + + " when Q was\n" + arg2.getKindString() + ".\n" + expr); + } + return arg2; + } + return BoolValue.ValFalse; + } + case OPCODE_lor: + { + Value arg1 = this.eval(args[0], c, s0, s1, control, cm); + if (!(arg1 instanceof BoolValue)) { + Assert.fail("Attempted to evaluate an expression of form P \\/ Q" + + " when P was\n" + arg1.getKindString() + ".\n" + expr); + } + if (((BoolValue)arg1).val) { + return BoolValue.ValTrue; + } + Value arg2 = this.eval(args[1], c, s0, s1, control, cm); + if (!(arg2 instanceof BoolValue)) { + Assert.fail("Attempted to evaluate an expression of form P \\/ Q" + + " when Q was\n" + arg2.getKindString() + ".\n" + expr); + } + return arg2; + } + case OPCODE_implies: + { + Value arg1 = this.eval(args[0], c, s0, s1, control, cm); + if (!(arg1 instanceof BoolValue)) { + Assert.fail("Attempted to evaluate an expression of form P => Q" + + " when P was\n" + arg1.getKindString() + ".\n" + expr); + } + if (((BoolValue)arg1).val) { + Value arg2 = this.eval(args[1], c, s0, s1, control, cm); + if (!(arg2 instanceof BoolValue)) { + Assert.fail("Attempted to evaluate an expression of form P => Q" + + " when Q was\n" + arg2.getKindString() + ".\n" + expr); + } + return arg2; + } + return BoolValue.ValTrue; + } + case OPCODE_equiv: + { + Value arg1 = this.eval(args[0], c, s0, s1, control, cm); + Value arg2 = this.eval(args[1], c, s0, s1, control, cm); + if (!(arg1 instanceof BoolValue) || !(arg2 instanceof BoolValue)) { + Assert.fail("Attempted to evaluate an expression of form P <=> Q" + + " when P or Q was not a boolean.\n" + expr); + } + BoolValue bval1 = (BoolValue)arg1; + BoolValue bval2 = (BoolValue)arg2; + return (bval1.val == bval2.val) ? BoolValue.ValTrue : BoolValue.ValFalse; + } + case OPCODE_noteq: + { + Value arg1 = this.eval(args[0], c, s0, s1, control, cm); + Value arg2 = this.eval(args[1], c, s0, s1, control, cm); + return arg1.equals(arg2) ? BoolValue.ValFalse : BoolValue.ValTrue; + } + case OPCODE_subseteq: + { + Value arg1 = this.eval(args[0], c, s0, s1, control, cm); + Value arg2 = this.eval(args[1], c, s0, s1, control, cm); + if (!(arg1 instanceof Enumerable)) { + Assert.fail("Attempted to evaluate an expression of form S \\subseteq T," + + " but S was not enumerable.\n" + expr); + } + return ((Enumerable) arg1).isSubsetEq(arg2); + } + case OPCODE_in: + { + Value arg1 = this.eval(args[0], c, s0, s1, control, cm); + Value arg2 = this.eval(args[1], c, s0, s1, control, cm); + return (arg2.member(arg1)) ? BoolValue.ValTrue : BoolValue.ValFalse; + } + case OPCODE_notin: + { + Value arg1 = this.eval(args[0], c, s0, s1, control, cm); + Value arg2 = this.eval(args[1], c, s0, s1, control, cm); + return (arg2.member(arg1)) ? BoolValue.ValFalse : BoolValue.ValTrue; + } + case OPCODE_setdiff: + { + Value arg1 = this.eval(args[0], c, s0, s1, control, cm); + Value arg2 = this.eval(args[1], c, s0, s1, control, cm); + if (arg1 instanceof Reducible) { + return setSource(expr, ((Reducible)arg1).diff(arg2)); + } + return setSource(expr, new SetDiffValue(arg1, arg2)); + } + case OPCODE_cap: + { + Value arg1 = this.eval(args[0], c, s0, s1, control, cm); + Value arg2 = this.eval(args[1], c, s0, s1, control, cm); + if (arg1 instanceof Reducible) { + return setSource(expr, ((Reducible)arg1).cap(arg2)); + } + else if (arg2 instanceof Reducible) { + return setSource(expr, ((Reducible)arg2).cap(arg1)); + } + return setSource(expr, new SetCapValue(arg1, arg2)); + } + case OPCODE_nop: + // Added by LL on 2 Aug 2007 + { + return eval(args[0], c, s0, s1, control, cm); + } + case OPCODE_cup: + { + Value arg1 = this.eval(args[0], c, s0, s1, control, cm); + Value arg2 = this.eval(args[1], c, s0, s1, control, cm); + if (arg1 instanceof Reducible) { + return setSource(expr, ((Reducible)arg1).cup(arg2)); + } + else if (arg2 instanceof Reducible) { + return setSource(expr, ((Reducible)arg2).cup(arg1)); + } + return setSource(expr, new SetCupValue(arg1, arg2, cm)); + } + case OPCODE_prime: + { + // MAK 03/2019: Cannot reproduce this but without this check the nested evaluation + // fails with a NullPointerException which subsequently is swallowed. This makes it + // impossible for a user to diagnose what is going on. Since I cannot reproduce the + // actual expression, I leave this commented for. I recall an expression along the + // lines of: + // ... + // TLCSet(23, CHOOSE p \in pc: pc[p] # pc[p]') + // ... + // The fail statement below is obviously too generic to be useful and needs to be + // clarified if the actual cause has been identified. +// if (s1 == null) { +// Assert.fail("Attempted to evaluate the following expression," + +// " but expression failed to evaluate.\n" + expr); +// } + return this.eval(args[0], c, s1, null, EvalControl.setPrimedIfEnabled(control), cm); + } + case OPCODE_unchanged: + { + Value v0 = this.eval(args[0], c, s0, TLCState.Empty, control, cm); + Value v1 = this.eval(args[0], c, s1, null, EvalControl.setPrimedIfEnabled(control), cm); + return (v0.equals(v1)) ? BoolValue.ValTrue : BoolValue.ValFalse; + } + case OPCODE_aa: // <A>_e + { + Value res = this.eval(args[0], c, s0, s1, control, cm); + if (!(res instanceof BoolValue)) { + Assert.fail("Attempted to evaluate an expression of form <A>_e," + + " but A was not a boolean.\n" + expr); + } + if (!((BoolValue)res).val) { + return BoolValue.ValFalse; + } + Value v0 = this.eval(args[1], c, s0, TLCState.Empty, control, cm); + Value v1 = this.eval(args[1], c, s1, null, EvalControl.setPrimedIfEnabled(control), cm); + return v0.equals(v1) ? BoolValue.ValFalse : BoolValue.ValTrue; + } + case OPCODE_sa: // [A]_e + { + Value res = this.eval(args[0], c, s0, s1, control, cm); + if (!(res instanceof BoolValue)) { + Assert.fail("Attempted to evaluate an expression of form [A]_e," + + " but A was not a boolean.\n" + expr); + } + if (((BoolValue)res).val) { + return BoolValue.ValTrue; + } + Value v0 = this.eval(args[1], c, s0, TLCState.Empty, control, cm); + Value v1 = this.eval(args[1], c, s1, null, EvalControl.setPrimedIfEnabled(control), cm); + return (v0.equals(v1)) ? BoolValue.ValTrue : BoolValue.ValFalse; + } + case OPCODE_cdot: + { + Assert.fail("The current version of TLC does not support action composition."); + /*** + TLCState s01 = TLCStateFun.Empty; + StateVec iss = new StateVec(0); + this.getNextStates(args[0], ActionItemList.Empty, c, s0, s01, iss); + int sz = iss.size(); + for (int i = 0; i < sz; i++) { + s01 = iss.elementAt(i); + this.eval(args[1], c, s01, s1, control); + } + ***/ + return null; // make compiler happy + } + case OPCODE_sf: // SF + { + Assert.fail(EC.TLC_ENCOUNTERED_FORMULA_IN_PREDICATE, new String[]{"SF", expr.toString()}); + return null; // make compiler happy + } + case OPCODE_wf: // WF + { + Assert.fail(EC.TLC_ENCOUNTERED_FORMULA_IN_PREDICATE, new String[]{"WF", expr.toString()}); + return null; // make compiler happy + } + case OPCODE_te: // TemporalExists + { + Assert.fail(EC.TLC_ENCOUNTERED_FORMULA_IN_PREDICATE, new String[]{"\\EE", expr.toString()}); + return null; // make compiler happy + } + case OPCODE_tf: // TemporalForAll + { + Assert.fail(EC.TLC_ENCOUNTERED_FORMULA_IN_PREDICATE, new String[]{"\\AA", expr.toString()}); + return null; // make compiler happy + } + case OPCODE_leadto: + { + Assert.fail(EC.TLC_ENCOUNTERED_FORMULA_IN_PREDICATE, new String[]{"a ~> b", expr.toString()}); + return null; // make compiler happy + } + case OPCODE_arrow: + { + Assert.fail(EC.TLC_ENCOUNTERED_FORMULA_IN_PREDICATE, new String[]{"a -+-> formula", expr.toString()}); + return null; // make compiler happy + } + case OPCODE_box: + { + Assert.fail(EC.TLC_ENCOUNTERED_FORMULA_IN_PREDICATE, new String[]{"[]A", expr.toString()}); + return null; // make compiler happy + } + case OPCODE_diamond: + { + Assert.fail(EC.TLC_ENCOUNTERED_FORMULA_IN_PREDICATE, new String[]{"<>A", expr.toString()}); + return null; // make compiler happy + } + + default: + { + Assert.fail("TLC BUG: could not evaluate this expression.\n" + expr); + return null; + } + } + } + + protected abstract Value setSource(final SemanticNode expr, final Value value); + + /** + * This method determines if the argument is a valid state. A state + * is good iff it assigns legal explicit values to all the global + * state variables. + */ + @Override + public final boolean isGoodState(TLCState state) { + return state.allAssigned(); + } + + /* This method determines if a state satisfies the model constraints. */ + @Override + public final boolean isInModel(TLCState state) throws EvalException { + ExprNode[] constrs = this.getModelConstraints(); + for (int i = 0; i < constrs.length; i++) { + IValue bval = this.eval(constrs[i], Context.Empty, state, CostModel.DO_NOT_RECORD); + if (!(bval instanceof BoolValue)) { + Assert.fail(EC.TLC_EXPECTED_VALUE, new String[]{"boolean", constrs[i].toString()}); + } + if (!((BoolValue)bval).val) return false; + } + return true; + } + + /* This method determines if a pair of states satisfy the action constraints. */ + @Override + public final boolean isInActions(TLCState s1, TLCState s2) throws EvalException { + ExprNode[] constrs = this.getActionConstraints(); + for (int i = 0; i < constrs.length; i++) { + Value bval = this.eval(constrs[i], Context.Empty, s1, s2, EvalControl.Clear, CostModel.DO_NOT_RECORD); + if (!(bval instanceof BoolValue)) { + Assert.fail(EC.TLC_EXPECTED_VALUE, new String[]{"boolean", constrs[i].toString()}); + } + if (!((BoolValue)bval).val) return false; + } + return true; + } + + @Override + public final boolean hasStateOrActionConstraints() { + return this.getModelConstraints().length > 0 || this.getActionConstraints().length > 0; + } + + @Override + public final TLCState enabled(SemanticNode pred, Context c, TLCState s0, TLCState s1) { + return enabled(pred, ActionItemList.Empty, c, s0, s1, CostModel.DO_NOT_RECORD); + } + + @Override + public final TLCState enabled(SemanticNode pred, Context c, TLCState s0, TLCState s1, ExprNode subscript, final int ail) { + ActionItemList acts = (ActionItemList) ActionItemList.Empty.cons(subscript, c, CostModel.DO_NOT_RECORD, ail); + return enabled(pred, acts, c, s0, s1, CostModel.DO_NOT_RECORD); + } + + @Override + public final TLCState enabled(SemanticNode pred, IActionItemList acts, Context c, TLCState s0, TLCState s1) { + return enabled(pred, acts, c, s0, s1, CostModel.DO_NOT_RECORD); + } + + /** + * This method determines if an action is enabled in the given state. + * More precisely, it determines if (act.pred /\ (sub' # sub)) is + * enabled in the state s and context act.con. + */ + @Override + public abstract TLCState enabled(SemanticNode pred, IActionItemList acts, + Context c, TLCState s0, TLCState s1, CostModel cm); + + protected final TLCState enabledImpl(SemanticNode pred, ActionItemList acts, + Context c, TLCState s0, TLCState s1, CostModel cm) { + switch (pred.getKind()) { + case OpApplKind: + { + OpApplNode pred1 = (OpApplNode)pred; + return this.enabledAppl(pred1, acts, c, s0, s1, cm); + } + case LetInKind: + { + LetInNode pred1 = (LetInNode)pred; + OpDefNode[] letDefs = pred1.getLets(); + Context c1 = c; + for (int i = 0; i < letDefs.length; i++) { + OpDefNode opDef = letDefs[i]; + if (opDef.getArity() == 0) { + Value rhs = new LazyValue(opDef.getBody(), c1, cm); + c1 = c1.cons(opDef, rhs); + } + } + return this.enabled(pred1.getBody(), acts, c1, s0, s1, cm); + } + case SubstInKind: + { + SubstInNode pred1 = (SubstInNode)pred; + Subst[] subs = pred1.getSubsts(); + int slen = subs.length; + Context c1 = c; + for (int i = 0; i < slen; i++) { + Subst sub = subs[i]; + c1 = c1.cons(sub.getOp(), this.getVal(sub.getExpr(), c, false, coverage ? sub.getCM() : cm, toolId)); + } + return this.enabled(pred1.getBody(), acts, c1, s0, s1, cm); + } + // Added by LL on 13 Nov 2009 to handle theorem and assumption names. + case APSubstInKind: + { + APSubstInNode pred1 = (APSubstInNode)pred; + Subst[] subs = pred1.getSubsts(); + int slen = subs.length; + Context c1 = c; + for (int i = 0; i < slen; i++) { + Subst sub = subs[i]; + c1 = c1.cons(sub.getOp(), this.getVal(sub.getExpr(), c, false, cm, toolId)); + } + return this.enabled(pred1.getBody(), acts, c1, s0, s1, cm); + } + // LabelKind class added by LL on 13 Jun 2007 + case LabelKind: + { + LabelNode pred1 = (LabelNode)pred; + return this.enabled(pred1.getBody(), acts, c, s0, s1, cm); + } + default: + { + // We should not compute enabled on anything else. + Assert.fail("Attempted to compute ENABLED on a non-boolean expression.\n" + pred); + return null; // make compiler happy + } + } + } + + private final TLCState enabled(ActionItemList acts, TLCState s0, TLCState s1, CostModel cm) { + if (acts.isEmpty()) return s1; + + final int kind = acts.carKind(); + SemanticNode pred = acts.carPred(); + Context c = acts.carContext(); + cm = acts.cm; + ActionItemList acts1 = acts.cdr(); + if (kind > IActionItemList.CONJUNCT) { + TLCState res = this.enabled(pred, acts1, c, s0, s1, cm); + return res; + } + else if (kind == IActionItemList.PRED) { + TLCState res = this.enabled(pred, acts1, c, s0, s1, cm); + return res; + } + if (kind == IActionItemList.UNCHANGED) { + TLCState res = this.enabledUnchanged(pred, acts1, c, s0, s1, cm); + return res; + } + + Value v1 = this.eval(pred, c, s0, TLCState.Empty, EvalControl.Enabled, cm); + // We are now in ENABLED and primed state. Second TLCState parameter being null + // effectively disables LazyValue in evalAppl (same effect as + // EvalControl.setPrimed(EvalControl.Enabled)). + Value v2 = this.eval(pred, c, s1, null, EvalControl.Primed, cm); + + if (v1.equals(v2)) return null; + TLCState res = this.enabled(acts1, s0, s1, cm); + return res; + } + + protected abstract TLCState enabledAppl(OpApplNode pred, ActionItemList acts, Context c, TLCState s0, TLCState s1, CostModel cm); + + protected final TLCState enabledApplImpl(OpApplNode pred, ActionItemList acts, Context c, TLCState s0, TLCState s1, CostModel cm) + { + if (coverage) {cm = cm.get(pred);} + ExprOrOpArgNode[] args = pred.getArgs(); + int alen = args.length; + SymbolNode opNode = pred.getOperator(); + int opcode = BuiltInOPs.getOpCode(opNode.getName()); + + if (opcode == 0) + { + // This is a user-defined operator with one exception: it may + // be substed by a builtin operator. This special case occurs + // when the lookup returns an OpDef with opcode # 0. + Object val = this.lookup(opNode, c, s0, false); + + if (val instanceof OpDefNode) + { + OpDefNode opDef = (OpDefNode) val; + opcode = BuiltInOPs.getOpCode(opDef.getName()); + if (opcode == 0) + { + // Context c1 = this.getOpContext(opDef, args, c, false); + Context c1 = this.getOpContext(opDef, args, c, true, cm, toolId); + return this.enabled(opDef.getBody(), acts, c1, s0, s1, cm); + } + } + + + // Added 13 Nov 2009 by LL to handle theorem or assumption names + /********************************************************************* + * Modified on 23 October 2012 by LL to work if ThmOrAssumpDefNode * + * imported with parameterized instantiation. * + *********************************************************************/ + if (val instanceof ThmOrAssumpDefNode) + { + ThmOrAssumpDefNode opDef = (ThmOrAssumpDefNode) val; + Context c1 = this.getOpContext(opDef, args, c, true); + return this.enabled(opDef.getBody(), acts, c1, s0, s1, cm); + } + + + if (val instanceof LazyValue) + { + LazyValue lv = (LazyValue) val; + return this.enabled(lv.expr, acts, lv.con, s0, s1, lv.cm); + } + + Object bval = val; + if (alen == 0) + { + if (val instanceof MethodValue) + { + bval = ((MethodValue) val).apply(EmptyArgs, EvalControl.Clear); // EvalControl.Clear is ignored by MethodValuea#apply + } else if (val instanceof EvaluatingValue) { + bval = ((EvaluatingValue) val).eval(this, args, c, s0, s1, EvalControl.Enabled, cm); + } + } else + { + if (val instanceof OpValue) + { + bval = ((OpValue) val).eval(this, args, c, s0, s1, EvalControl.Enabled, cm); + } + } + + if (opcode == 0) + { + if (!(bval instanceof BoolValue)) + { + Assert.fail(EC.TLC_EXPECTED_EXPRESSION_IN_COMPUTING, new String[] { "ENABLED", "boolean", + bval.toString(), pred.toString() }); + } + if (((BoolValue) bval).val) + { + return this.enabled(acts, s0, s1, cm); + } + return null; + } + } + + switch (opcode) { + case OPCODE_aa: // AngleAct <A>_e + { + ActionItemList acts1 = (ActionItemList) acts.cons(args[1], c, cm, IActionItemList.CHANGED); + return this.enabled(args[0], acts1, c, s0, s1, cm); + } + case OPCODE_be: // BoundedExists + { + SemanticNode body = args[0]; + ContextEnumerator Enum = this.contexts(pred, c, s0, s1, EvalControl.Enabled, cm); + Context c1; + while ((c1 = Enum.nextElement()) != null) + { + TLCState s2 = this.enabled(body, acts, c1, s0, s1, cm); + if (s2 != null) { + return s2; + } + } + return null; + } + case OPCODE_bf: // BoundedForall + { + SemanticNode body = args[0]; + ContextEnumerator Enum = this.contexts(pred, c, s0, s1, EvalControl.Enabled, cm); + Context c1 = Enum.nextElement(); + if (c1 == null) + { + return this.enabled(acts, s0, s1, cm); + } + ActionItemList acts1 = acts; + Context c2; + while ((c2 = Enum.nextElement()) != null) + { + acts1 = (ActionItemList) acts1.cons(body, c2, cm, IActionItemList.PRED); + } + return this.enabled(body, acts1, c1, s0, s1, cm); + } + case OPCODE_case: // Case + { + SemanticNode other = null; + for (int i = 0; i < alen; i++) + { + OpApplNode pair = (OpApplNode) args[i]; + ExprOrOpArgNode[] pairArgs = pair.getArgs(); + if (pairArgs[0] == null) + { + other = pairArgs[1]; + } else + { + Value bval = this.eval(pairArgs[0], c, s0, s1, EvalControl.Enabled, cm); + if (!(bval instanceof BoolValue)) + { + Assert.fail("In computing ENABLED, a non-boolean expression(" + bval.getKindString() + + ") was used as a guard condition" + " of a CASE.\n" + pairArgs[1]); + } + if (((BoolValue) bval).val) + { + return this.enabled(pairArgs[1], acts, c, s0, s1, cm); + } + } + } + if (other == null) + { + Assert.fail("In computing ENABLED, TLC encountered a CASE with no" + " conditions true.\n" + pred); + } + return this.enabled(other, acts, c, s0, s1, cm); + } + case OPCODE_cl: // ConjList + case OPCODE_land: + { + ActionItemList acts1 = acts; + for (int i = alen - 1; i > 0; i--) + { + acts1 = (ActionItemList) acts1.cons(args[i], c, cm, i); + } + return this.enabled(args[0], acts1, c, s0, s1, cm); + } + case OPCODE_dl: // DisjList + case OPCODE_lor: + { + for (int i = 0; i < alen; i++) + { + TLCState s2 = this.enabled(args[i], acts, c, s0, s1, cm); + if (s2 != null) { + return s2; + } + } + return null; + } + case OPCODE_fa: // FcnApply + { + Value fval = this.eval(args[0], c, s0, s1, EvalControl.setKeepLazy(EvalControl.Enabled), cm); // KeepLazy does not interfere with EvalControl.Enabled in this.evalAppl + if (fval instanceof FcnLambdaValue) + { + FcnLambdaValue fcn = (FcnLambdaValue) fval; + if (fcn.fcnRcd == null) + { + Context c1 = this.getFcnContext(fcn, args, c, s0, s1, EvalControl.Enabled, cm); // EvalControl.Enabled passed on to nested this.evalAppl + return this.enabled(fcn.body, acts, c1, s0, s1, cm); + } + fval = fcn.fcnRcd; + } + if (fval instanceof Applicable) + { + Applicable fcn = (Applicable) fval; + Value argVal = this.eval(args[1], c, s0, s1, EvalControl.Enabled, cm); + Value bval = fcn.apply(argVal, EvalControl.Enabled); // EvalControl.Enabled not taken into account by any subclass of Applicable + if (!(bval instanceof BoolValue)) + { + Assert.fail(EC.TLC_EXPECTED_EXPRESSION_IN_COMPUTING2, new String[] { "ENABLED", "boolean", + pred.toString() }); + } + if (!((BoolValue) bval).val) { + return null; + } + } else + { + Assert.fail("In computing ENABLED, a non-function (" + fval.getKindString() + + ") was applied as a function.\n" + pred); + } + return this.enabled(acts, s0, s1, cm); + } + case OPCODE_ite: // IfThenElse + { + Value guard = this.eval(args[0], c, s0, s1, EvalControl.Enabled, cm); + if (!(guard instanceof BoolValue)) + { + Assert.fail("In computing ENABLED, a non-boolean expression(" + guard.getKindString() + + ") was used as the guard condition" + " of an IF.\n" + pred); + } + int idx = (((BoolValue) guard).val) ? 1 : 2; + return this.enabled(args[idx], acts, c, s0, s1, cm); + } + case OPCODE_sa: // SquareAct [A]_e + { + TLCState s2 = this.enabled(args[0], acts, c, s0, s1, cm); + if (s2 != null) { + return s2; + } + return this.enabledUnchanged(args[1], acts, c, s0, s1, cm); + } + case OPCODE_te: // TemporalExists + case OPCODE_tf: // TemporalForAll + { + Assert.fail("In computing ENABLED, TLC encountered temporal quantifier.\n" + pred); + return null; // make compiler happy + } + case OPCODE_uc: // UnboundedChoose + { + Assert.fail("In computing ENABLED, TLC encountered unbounded CHOOSE. " + + "Make sure that the expression is of form CHOOSE x \\in S: P(x).\n" + pred); + return null; // make compiler happy + } + case OPCODE_ue: // UnboundedExists + { + Assert.fail("In computing ENABLED, TLC encountered unbounded quantifier. " + + "Make sure that the expression is of form \\E x \\in S: P(x).\n" + pred); + return null; // make compiler happy + } + case OPCODE_uf: // UnboundedForall + { + Assert.fail("In computing ENABLED, TLC encountered unbounded quantifier. " + + "Make sure that the expression is of form \\A x \\in S: P(x).\n" + pred); + return null; // make compiler happy + } + case OPCODE_sf: // SF + { + Assert.fail(EC.TLC_ENABLED_WRONG_FORMULA, new String[]{ "SF", pred.toString()}); + return null; // make compiler happy + } + case OPCODE_wf: // WF + { + Assert.fail(EC.TLC_ENABLED_WRONG_FORMULA, new String[] { "WF", pred.toString() }); + return null; // make compiler happy + } + case OPCODE_box: + { + Assert.fail(EC.TLC_ENABLED_WRONG_FORMULA, new String[] { "[]", pred.toString() }); + return null; // make compiler happy + } + case OPCODE_diamond: + { + Assert.fail(EC.TLC_ENABLED_WRONG_FORMULA, new String[] { "<>", pred.toString() }); + return null; // make compiler happy + } + case OPCODE_unchanged: + { + return this.enabledUnchanged(args[0], acts, c, s0, s1, cm); + } + case OPCODE_eq: + { + SymbolNode var = this.getPrimedVar(args[0], c, true); + if (var == null) + { + Value bval = this.eval(pred, c, s0, s1, EvalControl.Enabled, cm); + if (!((BoolValue) bval).val) { + return null; + } + } else + { + UniqueString varName = var.getName(); + IValue lval = s1.lookup(varName); + Value rval = this.eval(args[1], c, s0, s1, EvalControl.Enabled, cm); + if (lval == null) + { + TLCState s2 = s1.bind(var, rval); + return this.enabled(acts, s0, s2, cm); + } else + { + if (!lval.equals(rval)) { + return null; + } + } + } + return this.enabled(acts, s0, s1, cm); + } + case OPCODE_implies: + { + Value bval = this.eval(args[0], c, s0, s1, EvalControl.Enabled, cm); + if (!(bval instanceof BoolValue)) + { + Assert.fail("While computing ENABLED of an expression of the form" + " P => Q, P was " + + bval.getKindString() + ".\n" + pred); + } + if (((BoolValue) bval).val) + { + return this.enabled(args[1], acts, c, s0, s1, cm); + } + return this.enabled(acts, s0, s1, cm); + } + case OPCODE_cdot: + { + Assert.fail("The current version of TLC does not support action composition."); + /*** + TLCState s01 = TLCStateFun.Empty; + StateVec iss = new StateVec(0); + this.getNextStates(args[0], ActionItemList.Empty, c, s0, s01, iss); + int sz = iss.size(); + for (int i = 0; i < sz; i++) { + s01 = iss.elementAt(i); + TLCState s2 = this.enabled(args[1], acts, c, s01, s1); + if (s2 != null) return s2; + } + ***/ + return null; // make compiler happy + } + case OPCODE_leadto: + { + Assert.fail("In computing ENABLED, TLC encountered a temporal formula" + " (a ~> b).\n" + pred); + return null; // make compiler happy + } + case OPCODE_arrow: + { + Assert.fail("In computing ENABLED, TLC encountered a temporal formula" + " (a -+-> formula).\n" + pred); + return null; // make compiler happy + } + case OPCODE_in: + { + SymbolNode var = this.getPrimedVar(args[0], c, true); + if (var == null) + { + Value bval = this.eval(pred, c, s0, s1, EvalControl.Enabled, cm); + if (!((BoolValue) bval).val) { + return null; + } + } else + { + UniqueString varName = var.getName(); + Value lval = (Value) s1.lookup(varName); + Value rval = this.eval(args[1], c, s0, s1, EvalControl.Enabled, cm); + if (lval == null) + { + if (!(rval instanceof Enumerable)) + { + Assert.fail("The right side of \\IN is not enumerable.\n" + pred); + } + ValueEnumeration Enum = ((Enumerable) rval).elements(); + Value val; + while ((val = Enum.nextElement()) != null) + { + TLCState s2 = s1.bind(var, val); + s2 = this.enabled(acts, s0, s2, cm); + if (s2 != null) { + return s2; + } + } + return null; + } else + { + if (!rval.member(lval)) { + return null; + } + } + } + return this.enabled(acts, s0, s1, cm); + } + // The following case added by LL on 13 Nov 2009 to handle subexpression names. + case OPCODE_nop: + { + return this.enabled(args[0], acts, c, s0, s1, cm); + } + + default: + { + // We handle all the other builtin operators here. + Value bval = this.eval(pred, c, s0, s1, EvalControl.Enabled, cm); + if (!(bval instanceof BoolValue)) + { + Assert.fail(EC.TLC_EXPECTED_EXPRESSION_IN_COMPUTING, new String[] { "ENABLED", "boolean", + bval.toString(), pred.toString() }); + } + if (((BoolValue) bval).val) + { + return this.enabled(acts, s0, s1, cm); + } + return null; + } + } + } + + protected abstract TLCState enabledUnchanged(SemanticNode expr, ActionItemList acts, + Context c, TLCState s0, TLCState s1, CostModel cm); + + protected final TLCState enabledUnchangedImpl(SemanticNode expr, ActionItemList acts, + Context c, TLCState s0, TLCState s1, CostModel cm) { + if (coverage) {cm = cm.get(expr);} + SymbolNode var = this.getVar(expr, c, true, toolId); + if (var != null) { + // a state variable, e.g. UNCHANGED var1 + UniqueString varName = var.getName(); + Value v0 = this.eval(expr, c, s0, s1, EvalControl.Enabled, cm); + IValue v1 = s1.lookup(varName); + if (v1 == null) { + s1 = s1.bind(var, v0); + return this.enabled(acts, s0, s1, cm); + } + if (v1.equals(v0)) { + return this.enabled(acts, s0, s1, cm); + } + MP.printWarning(EC.TLC_UNCHANGED_VARIABLE_CHANGED, new String[]{varName.toString() , expr.toString()}); + return null; + } + + if (expr instanceof OpApplNode) { + OpApplNode expr1 = (OpApplNode)expr; + ExprOrOpArgNode[] args = expr1.getArgs(); + int alen = args.length; + SymbolNode opNode = expr1.getOperator(); + UniqueString opName = opNode.getName(); + int opcode = BuiltInOPs.getOpCode(opName); + + if (opcode == OPCODE_tup) { + // a tuple, e.g. UNCHANGED <<var1, var2>> + if (alen != 0) { + ActionItemList acts1 = acts; + for (int i = 1; i < alen; i++) { + acts1 = (ActionItemList) acts1.cons(args[i], c, cm, IActionItemList.UNCHANGED); + } + return this.enabledUnchanged(args[0], acts1, c, s0, s1, cm); + } + return this.enabled(acts, s0, s1, cm); + } + + if (opcode == 0 && alen == 0) { + // a 0-arity operator: + Object val = this.lookup(opNode, c, false); + + if (val instanceof LazyValue) { + LazyValue lv = (LazyValue)val; + return this.enabledUnchanged(lv.expr, acts, lv.con, s0, s1, cm); + } + else if (val instanceof OpDefNode) { + return this.enabledUnchanged(((OpDefNode)val).getBody(), acts, c, s0, s1, cm); + } + else if (val == null) { + Assert.fail("In computing ENABLED, TLC found the undefined identifier\n" + + opName + " in an UNCHANGED expression at\n" + expr); + } + return this.enabled(acts, s0, s1, cm); + } + } + + final Value v0 = this.eval(expr, c, s0, TLCState.Empty, EvalControl.Enabled, cm); + // We are in ENABLED and primed but why pass only primed? This appears to + // be the only place where we call eval from the ENABLED scope without + // additionally passing EvalControl.Enabled. Not passing Enabled allows a + // cached LazyValue could be used (see comments above on line 1384). + // + // The current scope is a nested UNCHANGED in an ENABLED and evaluation is set + // to primed. However, UNCHANGED e equals e' = e , so anything primed in e + // becomes double-primed in ENABLED UNCHANGED e. This makes it illegal TLA+ + // which is rejected by SANY's level checking. A perfectly valid spec - where + // e is not primed - but that also causes this code path to be taken is 23 below: + // + // -------- MODULE 23 --------- + // VARIABLE t + // op(var) == var + // Next == /\ (ENABLED (UNCHANGED op(t))) + // /\ (t'= t) + // Spec == (t = 0) /\ [][Next]_t + // ============================ + // + // However, spec 23 causes the call to this.eval(...) below to throw an + // EvalException either with EvalControl.Primed. The exception's message is + // "In evaluation, the identifier t is either undefined or not an operator." + // indicating that this code path is buggy. + // + // If this bug is ever fixed to make TLC accept spec 23, EvalControl.Primed + // should likely be rewritten to EvalControl.setPrimed(EvalControl.Enabled) + // to disable reusage of LazyValues on line ~1384 above. + final Value v1 = this.eval(expr, c, s1, TLCState.Empty, EvalControl.Primed, cm); + if (!v0.equals(v1)) { + return null; + } + return this.enabled(acts, s0, s1, cm); + } + + /* This method determines if the action predicate is valid in (s0, s1). */ + @Override + public final boolean isValid(Action act, TLCState s0, TLCState s1) { + Value val = this.eval(act.pred, act.con, s0, s1, EvalControl.Clear, act.cm); + if (!(val instanceof BoolValue)) { + Assert.fail(EC.TLC_EXPECTED_VALUE, new String[]{"boolean", act.pred.toString()}); + } + return ((BoolValue)val).val; + } + + /* Returns true iff the predicate is valid in the state. */ + @Override + public final boolean isValid(Action act, TLCState state) { + return this.isValid(act, state, TLCState.Empty); + } + + /* Returns true iff the predicate is valid in the state. */ + @Override + public final boolean isValid(Action act) { + return this.isValid(act, TLCState.Empty, TLCState.Empty); + } + + @Override + public final boolean isValid(ExprNode expr) { + IValue val = this.eval(expr, Context.Empty, TLCState.Empty, CostModel.DO_NOT_RECORD); + if (!(val instanceof BoolValue)) { + Assert.fail(EC.TLC_EXPECTED_VALUE, new String[]{"boolean", expr.toString()}); + } + return ((BoolValue)val).val; + } + + @Override + public final int checkAssumptions() { + final ExprNode[] assumps = getAssumptions(); + final boolean[] isAxiom = getAssumptionIsAxiom(); + for (int i = 0; i < assumps.length; i++) + { + try + { + if ((!isAxiom[i]) && !isValid(assumps[i])) + { + return MP.printError(EC.TLC_ASSUMPTION_FALSE, assumps[i].toString()); + } + } catch (final Exception e) + { + // Assert.printStack(e); + return MP.printError(EC.TLC_ASSUMPTION_EVALUATION_ERROR, + new String[] { assumps[i].toString(), e.getMessage() }); + } + } + return EC.NO_ERROR; + } + + /* Reconstruct the initial state whose fingerprint is fp. */ + @Override + public final TLCStateInfo getState(final long fp) { + class InitStateSelectorFunctor implements IStateFunctor { + private final long fp; + public TLCState state; + public InitStateSelectorFunctor(long fp) { + this.fp = fp; + } + @Override + public Object addElement(TLCState state) { + if (state == null) { + return null; + } else if (this.state != null) { + // Always return the first match found. Do not let later matches override + // this.state. This is in line with the original implementation that called + // getInitStates(). + return null; + } else if (fp == state.fingerPrint()) { + this.state = state; + // TODO Stop generation of initial states preemptively. E.g. make the caller of + // addElement check for a special return value such as this (the functor). + } + return null; + } + } + // Registry a selector that extract out of the (possibly) large set of initial + // states the one identified by fp. The functor pattern has the advantage + // compared to this.getInitStates(), that it kind of streams the initial states + // to the functor whereas getInitStates() stores _all_ init states in a set + // which is traversed afterwards. This is also consistent with + // ModelChecker#DoInitFunctor. Using the functor pattern for the processing of + // init states in ModelChecker#doInit but calling getInitStates() here results + // in a bug during error trace generation when the set of initial states is too + // large for getInitStates(). Earlier TLC would have refused to run the model + // during ModelChecker#doInit. + final InitStateSelectorFunctor functor = new InitStateSelectorFunctor(fp); + this.getInitStates(functor); + if (functor.state != null) { + final String info = "<Initial predicate>"; + final TLCStateInfo tlcStateInfo = new TLCStateInfo(functor.state, info, 1, fp); + return tlcStateInfo; + } + return null; + } + + /** + * Reconstruct the next state of state s whose fingerprint is fp. + * + * @return Returns the TLCState wrapped in TLCStateInfo. TLCStateInfo stores + * the stateNumber (relative to the given sinfo) and a pointer to + * the predecessor. + */ + @Override + public final TLCStateInfo getState(long fp, TLCStateInfo sinfo) { + final TLCStateInfo tlcStateInfo = getState(fp, sinfo.state); + if (tlcStateInfo == null) { + throw new EvalException(EC.TLC_FAILED_TO_RECOVER_NEXT); + } + tlcStateInfo.stateNumber = sinfo.stateNumber + 1; + tlcStateInfo.predecessorState = sinfo; + tlcStateInfo.fp = fp; + return tlcStateInfo; + } + + /* Reconstruct the next state of state s whose fingerprint is fp. */ + @Override + public final TLCStateInfo getState(long fp, TLCState s) { + IdThread.setCurrentState(s); + for (int i = 0; i < this.actions.length; i++) { + Action curAction = this.actions[i]; + StateVec nextStates = this.getNextStates(curAction, s); + for (int j = 0; j < nextStates.size(); j++) { + TLCState state = nextStates.elementAt(j); + long nfp = state.fingerPrint(); + if (fp == nfp) { + state.setPredecessor(s); + return new TLCStateInfo(state, curAction.getLocation()); + } + } + } + return null; + } + + /* Reconstruct the info for s1. */ + @Override + public final TLCStateInfo getState(TLCState s1, TLCState s) { + IdThread.setCurrentState(s); + for (int i = 0; i < this.actions.length; i++) { + Action curAction = this.actions[i]; + StateVec nextStates = this.getNextStates(curAction, s); + for (int j = 0; j < nextStates.size(); j++) { + TLCState state = nextStates.elementAt(j); + if (s1.equals(state)) { + state.setPredecessor(s); + return new TLCStateInfo(state, curAction.getLocation()); + } + } + } + return null; + } + + /* Return the set of all permutations under the symmetry assumption. */ + @Override + public final IMVPerm[] getSymmetryPerms() { + final String name = this.config.getSymmetry(); + if (name.length() == 0) { return null; } + final Object symm = this.unprocessedDefns.get(name); + if (symm == null) { + Assert.fail(EC.TLC_CONFIG_SPECIFIED_NOT_DEFINED, new String[] { "symmetry function", name}); + } + if (!(symm instanceof OpDefNode)) { + Assert.fail("The symmetry function " + name + " must specify a set of permutations."); + } + final OpDefNode opDef = (OpDefNode)symm; + // This calls tlc2.module.TLC.Permutations(Value) and returns a Value of |fcns| + // = n! where n is the capacity of the symmetry set. + final IValue fcns = this.eval(opDef.getBody(), Context.Empty, TLCState.Empty, CostModel.DO_NOT_RECORD); + if (!(fcns instanceof Enumerable) || !(fcns instanceof SetEnumValue)) { + Assert.fail("The symmetry operator must specify a set of functions."); + } + final List<Value> values = ((SetEnumValue)fcns).elements().all(); + for (final Value v : values) { + if (!(v instanceof FcnRcdValue)) { + Assert.fail("The symmetry values must be function records."); + } + } + final ExprOrOpArgNode[] argNodes = ((OpApplNode)opDef.getBody()).getArgs(); + // In the case where the config defines more than one set which is symmetric, they will pass through the + // enumerable size() check even if they are single element sets + final StringBuilder cardinalityOneSetList = new StringBuilder(); + int offenderCount = 0; + if (argNodes.length >= values.size()) { + // If equal, we have as many values as we have permuted sets => we have all 1-element sets; + // if greater than, then we have a heterogenous cardinality of sets, including 0 element sets. + for (final ExprOrOpArgNode node : argNodes) { + addToSubTwoSizedSymmetrySetList(node, cardinalityOneSetList); + offenderCount++; + } + } + + final IMVPerm[] subgroup; + if (offenderCount == 0) { + subgroup = MVPerms.permutationSubgroup((Enumerable)fcns); + final HashSet<ModelValue> subgroupMembers = new HashSet<>(); + for (final IMVPerm imvp : subgroup) { + if (imvp instanceof MVPerm) { // should always be the case + subgroupMembers.addAll(((MVPerm)imvp).getAllModelValues()); + } + } + for (final ExprOrOpArgNode node : argNodes) { + final SetEnumValue enumValue = getSetEnumValueFromArgumentNode(node); + + if (enumValue != null) { + final ValueEnumeration ve = enumValue.elements(); + + boolean found = false; + Value v; + while ((v = ve.nextElement()) != null) { + if ((v instanceof ModelValue) && subgroupMembers.contains(v)) { + found = true; + break; + } + } + + if (!found) { + addToSubTwoSizedSymmetrySetList(node, cardinalityOneSetList); + offenderCount++; + } + } + } + } else { + subgroup = null; + } + + if (offenderCount > 0) { + final String plurality = (offenderCount > 1) ? "s" : ""; + final String antiPlurality = (offenderCount > 1) ? "" : "s"; + final String toHaveConjugation = (offenderCount > 1) ? "have" : "has"; + + MP.printWarning(EC.TLC_SYMMETRY_SET_TOO_SMALL, + new String[] { plurality, cardinalityOneSetList.toString(), toHaveConjugation, antiPlurality }); + } + + return subgroup; + } + + /** + * Teases the original spec name for the set out of node and appends it to the {@code StringBuilder} instance. + */ + private void addToSubTwoSizedSymmetrySetList(final ExprOrOpArgNode node, final StringBuilder cardinalityOneSetList) { + final SyntaxTreeNode tn = (SyntaxTreeNode)node.getTreeNode(); + final String image = tn.getHumanReadableImage(); + final String alias; + if (image.startsWith(TLAConstants.BuiltInOperators.PERMUTATIONS)) { + final int imageLength = image.length(); + alias = image.substring((TLAConstants.BuiltInOperators.PERMUTATIONS.length() + 1), + (imageLength - 1)); + } else { + alias = image; + } + final String specDefinitionName = this.config.getOverridenSpecNameForConfigName(alias); + final String displayDefinition = (specDefinitionName != null) ? specDefinitionName : alias; + + if (cardinalityOneSetList.length() > 0) { + cardinalityOneSetList.append(", and "); + } + + cardinalityOneSetList.append(displayDefinition); + } + + /** + * @param node + * @return if the node represents a permutation, this will return the {@link SetEnumValue} instance contains its + * model values + */ + private SetEnumValue getSetEnumValueFromArgumentNode(final ExprOrOpArgNode node) { + if (node instanceof OpApplNode) { + final OpApplNode permutationNode = (OpApplNode)node; + if (permutationNode.getOperator() instanceof OpDefNode) { + final OpDefNode operator = (OpDefNode)permutationNode.getOperator(); + if (TLAConstants.BuiltInOperators.PERMUTATIONS.equals(operator.getName().toString())) { + final ExprOrOpArgNode[] operands = permutationNode.getArgs(); + if ((operands.length == 1) + && (operands[0] instanceof OpApplNode) + && (((OpApplNode)operands[0]).getOperator() instanceof OpDefOrDeclNode)) { + final Object o = ((OpDefOrDeclNode)((OpApplNode)operands[0]).getOperator()).getToolObject(toolId); + + if (o instanceof SetEnumValue) { + return (SetEnumValue)o; + } else if (o instanceof WorkerValue) { + // If TLC was started with a -workers N specification, N > 1, o will be a WorkerValue instance + final WorkerValue wv = (WorkerValue)o; + final Object unwrapped = WorkerValue.mux(wv); + + if (unwrapped instanceof SetEnumValue) { + return (SetEnumValue)unwrapped; + } + } + } + } + } + } + + return null; + } + + @Override + public final boolean hasSymmetry() { + if (this.config == null) { + return false; + } + final String name = this.config.getSymmetry(); + return name.length() > 0; + } + @Override + public final Context getFcnContext(IFcnLambdaValue fcn, ExprOrOpArgNode[] args, + Context c, TLCState s0, TLCState s1, + final int control) { + return getFcnContext(fcn, args, c, s0, s1, control, CostModel.DO_NOT_RECORD); + } + + @Override + public final Context getFcnContext(IFcnLambdaValue fcn, ExprOrOpArgNode[] args, + Context c, TLCState s0, TLCState s1, + final int control, CostModel cm) { + Context fcon = fcn.getCon(); + int plen = fcn.getParams().length(); + FormalParamNode[][] formals = fcn.getParams().getFormals(); + Value[] domains = (Value[]) fcn.getParams().getDomains(); + boolean[] isTuples = fcn.getParams().isTuples(); + Value argVal = this.eval(args[1], c, s0, s1, control, cm); + + if (plen == 1) { + if (!domains[0].member(argVal)) { + Assert.fail("In applying the function\n" + Values.ppr(fcn.toString()) + + ",\nthe first argument is:\n" + Values.ppr(argVal.toString()) + + "which is not in its domain.\n" + args[0]); + } + if (isTuples[0]) { + FormalParamNode[] ids = formals[0]; + TupleValue tv = (TupleValue) argVal.toTuple(); + if (tv == null || argVal.size() != ids.length) { + Assert.fail("In applying the function\n" + Values.ppr(this.toString()) + + ",\nthe argument is:\n" + Values.ppr(argVal.toString()) + + "which does not match its formal parameter.\n" + args[0]); + } + Value[] elems = tv.elems; + for (int i = 0; i < ids.length; i++) { + fcon = fcon.cons(ids[i], elems[i]); + } + } + else { + fcon = fcon.cons(formals[0][0], argVal); + } + } + else { + TupleValue tv = (TupleValue) argVal.toTuple(); + if (tv == null) { + Assert.fail("Attempted to apply a function to an argument not in its" + + " domain.\n" + args[0]); + } + int argn = 0; + Value[] elems = tv.elems; + for (int i = 0; i < formals.length; i++) { + FormalParamNode[] ids = formals[i]; + Value domain = domains[i]; + if (isTuples[i]) { + if (!domain.member(elems[argn])) { + Assert.fail("In applying the function\n" + Values.ppr(fcn.toString()) + + ",\nthe argument number " + (argn+1) + " is:\n" + + Values.ppr(elems[argn].toString()) + + "\nwhich is not in its domain.\n" + args[0]); + } + TupleValue tv1 = (TupleValue) elems[argn++].toTuple(); + if (tv1 == null || tv1.size() != ids.length) { + Assert.fail("In applying the function\n" + Values.ppr(fcn.toString()) + + ",\nthe argument number " + argn + " is:\n" + + Values.ppr(elems[argn-1].toString()) + + "which does not match its formal parameter.\n" + args[0]); + } + Value[] avals = tv1.elems; + for (int j = 0; j < ids.length; j++) { + fcon = fcon.cons(ids[j], avals[j]); + } + } + else { + for (int j = 0; j < ids.length; j++) { + if (!domain.member(elems[argn])) { + Assert.fail("In applying the function\n" + Values.ppr(fcn.toString()) + + ",\nthe argument number " + (argn+1) + " is:\n" + + Values.ppr(elems[argn].toString()) + + "which is not in its domain.\n" + args[0]); + } + fcon = fcon.cons(ids[j], elems[argn++]); + } + } + } + } + return fcon; + } + + @Override + public final IContextEnumerator contexts(OpApplNode appl, Context c, TLCState s0, + TLCState s1, final int control) { + return contexts(appl, c, s0, s1, control, CostModel.DO_NOT_RECORD); + } + + /* A context enumerator for an operator application. */ + public final ContextEnumerator contexts(OpApplNode appl, Context c, TLCState s0, + TLCState s1, final int control, CostModel cm) { + FormalParamNode[][] formals = appl.getBdedQuantSymbolLists(); + boolean[] isTuples = appl.isBdedQuantATuple(); + ExprNode[] domains = appl.getBdedQuantBounds(); + + int flen = formals.length; + int alen = 0; + for (int i = 0; i < flen; i++) { + alen += (isTuples[i]) ? 1 : formals[i].length; + } + Object[] vars = new Object[alen]; + ValueEnumeration[] enums = new ValueEnumeration[alen]; + int idx = 0; + for (int i = 0; i < flen; i++) { + Value boundSet = this.eval(domains[i], c, s0, s1, control, cm); + if (!(boundSet instanceof Enumerable)) { + Assert.fail("TLC encountered a non-enumerable quantifier bound\n" + + Values.ppr(boundSet.toString()) + ".\n" + domains[i]); + } + FormalParamNode[] farg = formals[i]; + if (isTuples[i]) { + vars[idx] = farg; + enums[idx++] = ((Enumerable)boundSet).elements(); + } + else { + for (int j = 0; j < farg.length; j++) { + vars[idx] = farg[j]; + enums[idx++] = ((Enumerable)boundSet).elements(); + } + } + } + return new ContextEnumerator(vars, enums, c); + } + + // These three are expected by implementing the {@link ITool} interface; they used + // to mirror exactly methods that our parent class ({@link Spec}) implemented + // however those methods have changed signature with refactoring done for + // Issue #393 + @Override + public Context getOpContext(OpDefNode odn, ExprOrOpArgNode[] args, Context ctx, boolean b) { + return getOpContext(odn, args, ctx, b, toolId); + } + + @Override + public Object lookup(SymbolNode opNode, Context con, boolean b) { + return lookup(opNode, con, b, toolId); + } + + @Override + public Object getVal(ExprOrOpArgNode expr, Context con, boolean b) { + return getVal(expr, con, b, toolId); + } +} diff --git a/tlatools/src/tlc2/tool/impl/WorkerValue.java b/tlatools/src/tlc2/tool/impl/WorkerValue.java new file mode 100644 index 0000000000000000000000000000000000000000..7fbe4f7874e8f8fed4864f4aa8d8ea81c63568d3 --- /dev/null +++ b/tlatools/src/tlc2/tool/impl/WorkerValue.java @@ -0,0 +1,157 @@ +/******************************************************************************* + * Copyright (c) 2019 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.impl; + +import tla2sany.semantic.OpDefNode; +import tla2sany.semantic.SemanticNode; +import tlc2.TLCGlobals; +import tlc2.tool.IWorker; +import tlc2.tool.TLCState; +import tlc2.tool.coverage.CostModel; +import tlc2.util.Context; +import tlc2.value.IValue; + +/* +Root Cause: +--------- + +1) [Values](https://github.com/tlaplus/tlaplus/blob/master/tlatools/src/tlc2/value/impl/Value.java) can be (deeply) nested. + +1) Values transition through the following sequence of states: New (instantiated), normalized, and fingerprinted. Transitioning from one state to another is not thread-safe. Non-technical speaking, a value is both the symbolic and the explicit entity. + +2) Normalization of an (enumerable) value causes its elements to be sorted and duplicates to be removed. It does not cause new elements to be generated/enumerated. For example, normalizing the [SetPredValue](https://github.com/tlaplus/tlaplus/blob/master/tlatools/src/tlc2/value/impl/SetPredValue.java), corresponding to the expression ```tla { s \in SUBSET S : s # {} }```, causes ```S``` to be sorted and any duplicate elements to be removed. + +3) Fingerprinting an enumerable value causes its elements to be explicitly enumerated. For example, the SetPredValue from before causes all subsets ```s``` to be enumerated. + +4) At startup, TLC eagerly evaluates zero-arity constant definitions and operators which involves creating values. These values are normalized but not fingerprinted! For example, the operator ```NonEmptySubsS == { s \in SUBSET S : s # {} }``` will be evaluated to a SetPredValue that is stored in the ```NonEmptySubsS``` node of the semantic graph. We denote the set of values in the semantic graph with V<sub>SG</sub>. + +5) Generating a [TLCState](https://github.com/tlaplus/tlaplus/blob/master/tlatools/src/tlc2/tool/TLCTrace.java) during the evaluation of the next-state relation, assigns newly created values to variables. Before a TLCState is enqueued in the [StateQueue](https://github.com/tlaplus/tlaplus/blob/master/tlatools/src/tlc2/tool/queue/StateQueue.java), its values are fingerprinted. Thus, values of TLCStates can safely be shared across [Workers](https://github.com/tlaplus/tlaplus/blob/master/tlatools/src/tlc2/tool/Worker.java). + +Lets assume a spec with one variable ```x``` whose value - in all states - is some set. Furthermore, let ```P``` be the property ```[](x \in NonEmptySubsS)```. For simplicity reasons, we assume: + +1) ```P``` is not checked on the initial states +2) Two Workers check next states simultaneously + +Under these two assumptions, both Workers will race to enumerate (fingerprint) ```NonEmptySubsS```. + +Note that the fact that we had to assume 1) and 2) explains why this race condition didn't surfaced earlier. However, the assumptions are true in real-world executions of TLC. For example, the evaluation of a more complex NonEmptySubsS, with a deeply nested hierarchy of values, may not require the evaluation of nested values in the initial states. + +--------------- + +@xxyzzn came up with another scenario that might lead to the calculation of bogus fingerprints: + +```tla +EXTENDS Integers, Sequences +VARIABLE x, y + +Foo == { s \in SUBSET {1,2,3,4} : s # {} } + +Init == x = << >> /\ y \in 1..2 + +Next == x' \in {<<i, Foo>> : i \in 1..2} /\ UNCHANGED y +``` + +Variable ```y``` only exists so that the state-graph has two initial states (sequentially generated). A single initial state would restrict the first evaluation of Next to a single Worker (only a single initial state to dequeue from the ```StateQueue```) and thus fingerprinting cannot occur concurrently. + +Attempted Fix A: +--------------------- + +[At startup](https://github.com/tlaplus/tlaplus/blob/63bab42e79e750b7ac033cfe7196d8f0b53e861e/tlatools/src/tlc2/tool/impl/SpecProcessor.java#L202-L281), (eagerly) fingerprint all values of V<sub>SG</sub>. + +Problem: Not all values in V<sub>SG</sub> need not be fingerprinted to completely check a spec. For some specs such as [CarTalkPuzzle](https://github.com/tlaplus/tlaplus/issues/361#issuecomment-543256201), this is even prohibitive because explicit enumeration is infeasible: TLC will hang at startup. + +Deferring fingerprinting of V<sub>SG</sub> until after the assumptions have been checked, such as when the initial states are (atomically) generated, is no general solution. + +Proposed Fix A: +-------------------- + +Properly synchronize values such that no race conditions are possible if two or more Workers enumerate them concurrently. + +Disadvantages: +1) It creates a scalability bottleneck: Workers will content for the synchronized ```NonEmptySubs```on every state that is generated. +2) Engineering effort to correctly synchronize the Value type hierarchy without introducing dead-locks. + +Proposed Fix B: +-------------------- + +For each worker, create a copy of V<sub>SG</sub> (technically borrows from the concept of a ```ThreadLocal```). + +Disadvantages: +1) Requires a type check (and potentially cast) on every [lookup](https://github.com/tlaplus/tlaplus/blob/63bab42e79e750b7ac033cfe7196d8f0b53e861e/tlatools/src/tlc2/tool/impl/Spec.java#L448-L497) that occurrs during the [evaluation of the next-state relation](https://github.com/tlaplus/tlaplus/blob/63bab42e79e750b7ac033cfe7196d8f0b53e861e/tlatools/src/tlc2/tool/impl/Tool.java#L782-L814). An optimization would be to [eventually](https://github.com/tlaplus/tlaplus/blob/63bab42e79e750b7ac033cfe7196d8f0b53e861e/tlatools/src/tlc2/tool/AbstractChecker.java#L496) garbage-collect the copies of V<sub>SG</sub> to at least skip the type cast once one copy is completely fingerprinted. +2) [Value#deepCopy](https://github.com/tlaplus/tlaplus/blob/63bab42e79e750b7ac033cfe7196d8f0b53e861e/tlatools/src/tlc2/value/IValue.java#L104) - required to create the copies of V<sub>SG</sub> - is [broken for most value types](https://github.com/tlaplus/tlaplus/blob/63bab42e79e750b7ac033cfe7196d8f0b53e861e/tlatools/src/tlc2/value/impl/SubsetValue.java#L232) (can probably be fixed). +*/ +public class WorkerValue { + + /* + * Demuxing is supposed to be called only once per sn/opDef whereas muxing is called many many times. + */ + + public static Object demux(final OpDefEvaluator spec, final OpDefNode opDef) { + return demux(spec, opDef, opDef); + } + + public static Object demux(final OpDefEvaluator spec, final SemanticNode sn, final OpDefNode opDef) { + final IValue defVal = spec.eval(opDef.getBody(), Context.Empty, TLCState.Empty, CostModel.DO_NOT_RECORD); + defVal.deepNormalize(); + + if (defVal.mutates() && TLCGlobals.getNumWorkers() > 1) { + final IValue[] values = new IValue[TLCGlobals.getNumWorkers()]; + values[0] = defVal; + + for (int i = 1; i < values.length; i++) { + // Ideally, we could invoke IValue#deepCopy here instead of evaluating opDef again. However, + // IValue#deepCopy doesn't create copies for most values. + values[i] = spec.eval(opDef.getBody(), Context.Empty, TLCState.Empty, CostModel.DO_NOT_RECORD); + values[i].deepNormalize(); + } + + return new WorkerValue(values); + } else { + return defVal; + } + } + + public static Object mux(final Object result) { + if (!(result instanceof WorkerValue)) { + return result; + } + + final WorkerValue vp = (WorkerValue) result; + final Thread t = Thread.currentThread(); + if (t instanceof IWorker) { + final IWorker w = (IWorker) t; + return vp.values[w.myGetId()]; + } else { + return vp.values[0]; + } + } + + private final IValue[] values; + + private WorkerValue(IValue[] values) { + this.values = values; + } +} diff --git a/tlatools/src/tlc2/tool/liveness/AbstractDiskGraph.java b/tlatools/src/tlc2/tool/liveness/AbstractDiskGraph.java index b2865fbd0a95d58389e5a29af446c35719fd8e45..c45d7af348b208b14a52eabdb664061fe67c9785 100644 --- a/tlatools/src/tlc2/tool/liveness/AbstractDiskGraph.java +++ b/tlatools/src/tlc2/tool/liveness/AbstractDiskGraph.java @@ -4,14 +4,17 @@ package tlc2.tool.liveness; +import java.io.BufferedWriter; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; +import java.io.FileWriter; import java.io.IOException; import java.util.Collection; import java.util.HashMap; +import java.util.Iterator; import java.util.Map; import tlc2.output.EC; @@ -26,10 +29,11 @@ import util.FileUtil; * * - A {@link DiskGraph} has a 1:1 relationship with {@link OrderOfSolution} * - * - Logically stores the triple of state, tableaux, link (transitions) + * - Logically stores a set of triples that represent the liveness/behavior graph (see Manna/Pnuelli book). + * - Technically, it stores the triple of <<state (fingerprint), tableau node index, link (transitions)>> * -- Technically does *not* store States, but only a state's fingerprints * --- Stores a fingerprint split into 2 ints (low & high part of a fingerprint) - * -- Stores the index of the tableaux, not the tableaux itself + * -- Stores the index of the tableau node, not the tableau node itself * --- The TableauGraphNode (TBGraphNode) instance can be obtained by reading * the DiskGraph triple into a GraphNode instance and calling * GraphNode#getTNode(TBGraph). One obviously has to have access to the TBGraph @@ -69,11 +73,16 @@ public abstract class AbstractDiskGraph { public static final long MAX_LINK = 0x7FFFFFFFFFFFFFFFL; public static boolean isFilePointer(long loc) { + // TODO Does not check >= 0 and thus accepts TableauDiskGraph.UNDONE as + // ptr. return loc < MAX_PTR; } private final String chkptName; protected final String metadir; + /** + * @see tlatools/test/tlc2/tool/liveness/AbstractDiskGraph.JPG + */ protected final BufferedRandomAccessFile nodeRAF; protected final BufferedRandomAccessFile nodePtrRAF; protected final LongVec initNodes; @@ -107,11 +116,26 @@ public abstract class AbstractDiskGraph { return this.initNodes; } + /** + * Creates a fixed size in-memory cache of {@link GraphNode}'s. A disk + * lookup is avoid in {@link AbstractDiskGraph#getNode(long, int, long)} on + * each cache hit. The cache is destroyed by + * {@link AbstractDiskGraph#destroyCache()}. + */ public final void createCache() { - // Make array length a function of the available (heap) memory + // Make array length a function of the available (heap) memory. Could + // approximate the required memory by taking the size of the on-disk + // files into account, but think of hash collisions! this.gnodes = new GraphNode[65536]; } + /** + * Destroys the fixed size in-memory cache created by + * {@link AbstractDiskGraph#createCache()}. This should be done if liveness + * checking wants to destroy in-memory {@link GraphNode} nodes to start a + * new liveness check on them (e.g. to replace SCC link numbers with the + * original disk ptr location). + */ public final void destroyCache() { this.gnodes = null; } @@ -161,11 +185,18 @@ public abstract class AbstractDiskGraph { return ptr; } + /** + * @return true iff the given {@link GraphNode} has already been added to + * this {@link AbstractDiskGraph}. + */ + protected abstract boolean checkDuplicate(GraphNode node); + public abstract GraphNode getNode(long fingerprint, int tableauIdx) throws IOException; /** * @return true iff the given GraphNode belongs to the set of initial - * states. + * states. Inefficient, only use for auxiliary use cases (e.g. + * visualization of the liveness graph (toDotViz())). */ protected boolean isInitState(final GraphNode gnode) { final int numOfInits = initNodes.size(); @@ -199,7 +230,7 @@ public abstract class AbstractDiskGraph { return gnode1; } - public final GraphNode getNodeFromDisk(final long stateFP, final int tidx, final long ptr) throws IOException { + protected final GraphNode getNodeFromDisk(final long stateFP, final int tidx, final long ptr) throws IOException { // If the node is not found in the in-memory cache, the ptr has to be // positive. BufferedRandomAccessFile#seek will throw an IOException due // to "negative seek offset" anyway. Lets catch it early on! @@ -228,20 +259,133 @@ public abstract class AbstractDiskGraph { this.nodePtrRAF.seek(ptr); } + /** + * This methods reads the node PTR file from disk (the ptr file is the + * smaller file ptrs_N of the pair ptrs_N and nodes_N). + * <p> + * The ptr file contains tuples <<fingerprint, tableau idx, ptr location>> + * for all fingerprints times all tableau indices (the corresponding nodes + * file contains the outgoing arcs of the node described in the ptr file). + * <p> + * The reason why the nodePtrTable has to be re-made by calling this method + * prior to running the SCC search, is because the ptr location is + * eventually overwritten with the nodes link number used by SCC search. + * <p> + * makeNodePtrTbl maintains/does not overwrite the isDone state of the node, + * which - iff true - causes SCC search to skip/ignore the node. + * + * @param ptr + * The length of the ptr file up to which this method reads. + * @throws IOException + * Reading the file failed + */ protected abstract void makeNodePtrTbl(final long ptr) throws IOException; - /* Return the link assigned to the node. */ + /* Link information for SCC search */ + + /** + * Return the link assigned to the node via putLink() or -1 if the node has + * no link assigned yet. Unless -1, the link is in interval [ + * {@link AbstractDiskGraph#MAX_PTR}, {@link AbstractDiskGraph#MAX_LINK}] + * + * @param state + * The state's fingerprint + * @param tidx + * The corresponding tableau index + */ public abstract long getLink(long state, int tidx); /** - * Assign link to node. If a link has already been assigned to the node, - * does nothing by simply returning the existing link. Otherwise, add <node, - * link> into the table and return -1. + * Assign link to node during SCC search. If a link has already been + * assigned to the node, does nothing by simply returning the existing link. + * Otherwise, add <node, link> into the table and return -1. The link + * overwrites the previous value of elem (file pointer into nodes_N) in the + * nodePtrTable. + * <p> + * The link has to be in the range [{@link AbstractDiskGraph#MAX_PTR}, + * {@link AbstractDiskGraph#MAX_LINK}). {AbstractDiskGraph#MAX_LINK} is used + * to exclude nodes from being explored by SCC search twice (see + * {@link AbstractDiskGraph#setMaxLink(long, int)}. + * + * @param state + * The state's fingerprint + * @param tidx + * The corresponding tableau index */ public abstract long putLink(long state, int tidx, long link); + /** + * Assigns the maximum possible link number to the given node <state, + * tidx>. This results in that the node is skipped/ignored if it turns up + * as a node during SCC's depth-first-search. + * + * @param state + * The state's fingerprint + * @param tidx + * The corresponding tableau index + */ public abstract void setMaxLink(long state, int tidx); + /* End link information for SCC search */ + + public boolean checkInvariants(final int slen, final int alen) { + // Make sure there are no redundant transitions. + final Iterator<GraphNode> itr = iterator(); + while (itr.hasNext()) { + final GraphNode gn = itr.next(); + if (!gn.checkInvariants(slen, alen)) { + return false; + } + } + return true; + } + + /* start iteration */ + + private Iterator<GraphNode> iterator() { + try { + // reverse ptr file to beginning + this.nodePtrRAF.seek(0); + + final long length = this.nodePtrRAF.length(); + + return new Iterator<GraphNode>() { + + /* (non-Javadoc) + * @see java.util.Iterator#hasNext() + */ + public boolean hasNext() { + return nodePtrRAF.getFilePointer() < length; + } + + /* (non-Javadoc) + * @see java.util.Iterator#next() + */ + public GraphNode next() { + try { + long fp = nodePtrRAF.readLong(); + int tidx = nodePtrRAF.readInt(); + long loc = nodePtrRAF.readLongNat(); + return getNodeFromDisk(fp, tidx, loc); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + /* (non-Javadoc) + * @see java.util.Iterator#remove() + */ + public void remove() { + throw new UnsupportedOperationException("Not supported!"); + } + }; + } catch (IOException e1) { + throw new RuntimeException(e1); + } + } + + /* end iteration */ + /** * Return the shortest path (inclusive and in reverse order) from some * initial state to state. The path is a vector of states <s1, s2, ..., sn>, @@ -261,6 +405,17 @@ public abstract class AbstractDiskGraph { */ public abstract long size(); + /** + * @return The size of both disk files (ptrs and nodes) measured in bytes. + * Can be incorrect during short periods when the graph is being + * recreated ({@link #makeNodePtrTbl()}) or nodes are read from + * disk ({@link #getNodeFromDisk(long, int, long)}). It is up to + * the caller to take this into account. + * @throws IOException + */ + public long getSizeOnDisk() throws IOException { + return this.nodePtrRAF.length() + this.nodeRAF.length(); + } public long getSizeAtLastCheck() { return sizeAtCheck; @@ -280,7 +435,77 @@ public abstract class AbstractDiskGraph { * and call something similar to: 'dot -T svg graphviz.txt -o * "Graphviz.svg"'. It obviously needs Graphviz (http://www.graphviz.org). */ - public abstract String toDotViz(); + public abstract String toDotViz(final OrderOfSolution oos); + + protected String toDotVizLegend(final OrderOfSolution oos) { + final StringBuffer sb = new StringBuffer(); + sb.append("subgraph cluster_legend {"); + sb.append("graph[style=bold];"); + sb.append("label = \"PossibleErrorModel\" style=\"solid\"\n"); + sb.append("node [ labeljust=\"l\",shape=record ]\n"); + + // State checks + int i = 1; + LiveExprNode[] checkState = oos.getCheckState(); + for (LiveExprNode liveExprNode : checkState) { + sb.append(String.format("S%s [label=\"S%s: %s\"]", i, i++, node2dot(liveExprNode))); + sb.append("\n"); + } + // Actions checks + i = 1; + checkState = oos.getCheckAction(); + for (LiveExprNode liveExprNode : checkState) { + sb.append(String.format("A%s [label=\"A%s: %s\"]", i, i++, node2dot(liveExprNode))); + sb.append("\n"); + } + + sb.append("}"); + return sb.toString(); + } + + protected static String node2dot(final LiveExprNode node) { + // Replace "\" with "\\" and """ with "\"". Replace "<" and ">" with "\<" and "\>". + return node.toString().replace("\\", "\\\\").replace("\"", "\\\"").replace("<", "\\<").replace(">", "\\>").trim() + .replace("\n", "\\l"); // Do not remove remaining (i.e. no dangling/leading) "\n". + } + + + /** + * Only useful for debugging. + * + * Writes the current {@link AbstractDiskGraph} to the given {@link File}. + * <p> + * For the Eclipse IDE there exists a handy plug-in that automatically + * renders a .dot file when selected in the package explorer. Just follow + * the installation instructions at + * https://github.com/abstratt/eclipsegraphviz + * + * @param oos + * Length of state checks + * @param alen + * Length of action checks + * @param file + * Destination + */ + public final void writeDotViz(final OrderOfSolution oos, final File file) { + this.createCache(); + + try { + final BufferedWriter bwr = new BufferedWriter(new FileWriter(file)); + + // write contents of StringBuffer to a file + bwr.write(toDotViz(oos)); + + // flush the stream + bwr.flush(); + + // close the stream + bwr.close(); + } catch (IOException e) { + e.printStackTrace(); + } + this.destroyCache(); + } /* Checkpoint. */ public synchronized final void beginChkpt() throws IOException { diff --git a/tlatools/src/tlc2/tool/liveness/AbstractGraphNode.java b/tlatools/src/tlc2/tool/liveness/AbstractGraphNode.java index f22e9b260bebae973731c6e6000c13bebe58fcd0..346fb99f8b7fe7d199fc7fa49a7071ed1a9de003 100644 --- a/tlatools/src/tlc2/tool/liveness/AbstractGraphNode.java +++ b/tlatools/src/tlc2/tool/liveness/AbstractGraphNode.java @@ -40,6 +40,14 @@ public abstract class AbstractGraphNode { return this.checks.get(i); } + public BitVector getCheckAction(int slen, int alen, int nodeIdx) { + final BitVector bv = new BitVector(alen); + for (int j = 0; j < alen; j++) { + bv.set(j, getCheckAction(slen, alen, nodeIdx, j)); + } + return bv; + } + public boolean getCheckAction(int slen, int alen, int nodeIdx, int i) { int pos = slen + alen * nodeIdx + i; return this.checks.get(pos); diff --git a/tlatools/src/tlc2/tool/liveness/AddAndCheckLiveCheck.java b/tlatools/src/tlc2/tool/liveness/AddAndCheckLiveCheck.java new file mode 100644 index 0000000000000000000000000000000000000000..7442f798fbc92e0da0d77f4d30863f4ef49ee09f --- /dev/null +++ b/tlatools/src/tlc2/tool/liveness/AddAndCheckLiveCheck.java @@ -0,0 +1,83 @@ +/******************************************************************************* + * Copyright (c) 2015 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ + +package tlc2.tool.liveness; + +import java.io.IOException; + +import tlc2.output.EC; +import tlc2.output.MP; +import tlc2.tool.ITool; +import tlc2.tool.TLCState; +import tlc2.tool.Worker; +import tlc2.util.SetOfStates; +import tlc2.util.statistics.IBucketStatistics; + +/** + * {@link AddAndCheckLiveCheck} is a subclass of LiveCheck that always runs + * liveness checking after each and every insertion of a new node into the + * behavior graph. It is meant for TESTING ONLY!!! as it severely + * degrades TLC's performance/scalability. + * <p> + * The key difference between {@link AddAndCheckLiveCheck} and {@link LiveCheck} + * is that both methods are synchronized to only allow one {@link Worker} to add + * a state at a time and that liveness checking is after every insertion. + */ +public class AddAndCheckLiveCheck extends LiveCheck { + + public AddAndCheckLiveCheck(ITool tool, String metadir, IBucketStatistics stats) throws IOException { + super(tool, metadir, stats); + MP.printWarning(EC.UNIT_TEST, new String[]{ "!!!WARNING: TLC is running in inefficient unit testing mode!!!", ""} ); + } + + /* (non-Javadoc) + * @see tlc2.tool.liveness.LiveCheck#addInitState(tlc2.tool.TLCState, long) + */ + @Override + public synchronized void addInitState(ITool tool, TLCState state, long stateFP) { + super.addInitState(tool, state, stateFP); + try { + check0(tool, false); + } catch (InterruptedException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + /* (non-Javadoc) + * @see tlc2.tool.liveness.LiveCheck#addNextState(tlc2.tool.TLCState, long, tlc2.util.SetOfStates) + */ + @Override + public synchronized void addNextState(ITool tool, TLCState s0, long fp0, SetOfStates nextStates) throws IOException { + super.addNextState(tool, s0, fp0, nextStates); + try { + check0(tool, false); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } +} diff --git a/tlatools/src/tlc2/tool/liveness/BEGraph.java b/tlatools/src/tlc2/tool/liveness/BEGraph.java index 3a73d7fbb27c9b212c9e63d4fcda0a2270094f1b..ffe148cdecfb5b81db809ebf1b06c9ef5f64ca41 100644 --- a/tlatools/src/tlc2/tool/liveness/BEGraph.java +++ b/tlatools/src/tlc2/tool/liveness/BEGraph.java @@ -15,12 +15,12 @@ public class BEGraph { /** * BEGraph represents the behaviour graph. */ - public Vect initNodes; + public Vect<BEGraphNode> initNodes; public String metadir; public NodeTable allNodes; public BEGraph(String metadir, boolean isBT) { - this.initNodes = new Vect(); + this.initNodes = new Vect<>(); this.metadir = metadir; this.allNodes = new NodeTable(127, isBT); } @@ -95,7 +95,7 @@ public class BEGraph { } } // Get the path following parent pointers: - Vect path = new Vect(); + Vect<BEGraphNode> path = new Vect<>(); BEGraphNode curNode = end; while (curNode != null) { path.addElement(curNode); diff --git a/tlatools/src/tlc2/tool/liveness/DiskGraph.java b/tlatools/src/tlc2/tool/liveness/DiskGraph.java index 060aaf3d3fb0e9a89b53085c9ecd9dcd75f63b82..2db8224739a605f772c2d54fd59f5cabb845ca24 100644 --- a/tlatools/src/tlc2/tool/liveness/DiskGraph.java +++ b/tlatools/src/tlc2/tool/liveness/DiskGraph.java @@ -65,23 +65,31 @@ public class DiskGraph extends AbstractDiskGraph { } /* (non-Javadoc) - * @see tlc2.tool.liveness.DiskGraph#putNode(tlc2.tool.liveness.GraphNode, long) + * @see tlc2.tool.liveness.AbstractDiskGraph#putNode(tlc2.tool.liveness.GraphNode, long) */ protected void putNode(GraphNode node, long ptr) { this.nodePtrTbl.put(node.stateFP, ptr); } /* (non-Javadoc) - * @see tlc2.tool.liveness.DiskGraph#getLink(long, int) + * @see tlc2.tool.liveness.AbstractDiskGraph#checkDuplicate(tlc2.tool.liveness.GraphNode) + */ + protected boolean checkDuplicate(final GraphNode node) { + return this.nodePtrTbl.get(node.stateFP) != -1; + } + + /* (non-Javadoc) + * @see tlc2.tool.liveness.AbstractDiskGraph#getLink(long, int) */ public long getLink(long state, int tidx) { return this.nodePtrTbl.get(state); } /* (non-Javadoc) - * @see tlc2.tool.liveness.DiskGraph#putLink(long, int, long) + * @see tlc2.tool.liveness.AbstractDiskGraph#putLink(long, int, long) */ public long putLink(long state, int tidx, long link) { + assert MAX_PTR <= link && link < MAX_LINK; int loc = this.nodePtrTbl.getLoc(state); long oldLink = this.nodePtrTbl.getByLoc(loc); if (!isFilePointer(oldLink)) { @@ -105,9 +113,7 @@ public class DiskGraph extends AbstractDiskGraph { this.nodePtrRAF.seek(0); while (this.nodePtrRAF.getFilePointer() < ptr) { long fp = this.nodePtrRAF.readLong(); - // SZ Jul 13, 2009: removed to kill the warning - // SZ Feb 20, 2009: variable never read locally - // int tidx = + // skip the tableau idx that is not used by DiskGraph. this.nodePtrRAF.readInt(); long loc = this.nodePtrRAF.readLongNat(); this.nodePtrTbl.put(fp, loc); @@ -168,10 +174,11 @@ public class DiskGraph extends AbstractDiskGraph { } /* (non-Javadoc) - * @see tlc2.tool.liveness.DiskGraph#toDotViz() + * @see tlc2.tool.liveness.AbstractDiskGraph#toDotViz(tlc2.tool.liveness.OrderOfSolution) */ - public final String toDotViz() { - + public final String toDotViz(final OrderOfSolution oos) { + final int slen = oos.getCheckState().length; + final int alen = oos.getCheckAction().length; // The following code relies on gnodes not being null, thus safeguard // against accidental invocations. // Essentially one has to wrap the toDotViz call with @@ -186,6 +193,9 @@ public class DiskGraph extends AbstractDiskGraph { sb.append("digraph DiskGraph {\n"); sb.append("nodesep = 0.7\n"); sb.append("rankdir=LR;\n"); // Left to right rather than top to bottom + sb.append(toDotVizLegend(oos)); + sb.append("subgraph cluster_graph {\n"); + sb.append("color=\"white\";\n"); // no border. long nodePtr = this.nodeRAF.getFilePointer(); long nodePtrPtr = this.nodePtrRAF.getFilePointer(); long len = this.nodePtrRAF.length(); @@ -195,9 +205,9 @@ public class DiskGraph extends AbstractDiskGraph { int tidx = nodePtrRAF.readInt(); long loc = nodePtrRAF.readLongNat(); GraphNode gnode = this.getNode(fp, tidx, loc); - sb.append(gnode.toDotViz(isInitState(gnode), false)); + sb.append(gnode.toDotViz(isInitState(gnode), false, slen, alen)); } - sb.append("}"); + sb.append("}}"); this.nodeRAF.seek(nodePtr); this.nodePtrRAF.seek(nodePtrPtr); } catch (IOException e) { diff --git a/tlatools/src/tlc2/tool/liveness/DotLivenessStateWriter.java b/tlatools/src/tlc2/tool/liveness/DotLivenessStateWriter.java new file mode 100644 index 0000000000000000000000000000000000000000..2ec361f7206238a9351c67932d651579a0d5f16a --- /dev/null +++ b/tlatools/src/tlc2/tool/liveness/DotLivenessStateWriter.java @@ -0,0 +1,125 @@ +/******************************************************************************* + * Copyright (c) 2016 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.liveness; + +import java.io.IOException; + +import tlc2.tool.TLCState; +import tlc2.util.BitVector; +import tlc2.util.DotStateWriter; +import tlc2.util.IStateWriter; + +public class DotLivenessStateWriter extends DotStateWriter implements ILivenessStateWriter { + + public DotLivenessStateWriter(IStateWriter aStateWriter) throws IOException { + super(aStateWriter.getDumpFileName().replace(".dot", "_liveness.dot"), ""); + } + + /* (non-Javadoc) + * @see tlc2.tool.liveness.ILivenessStateWriter#writeState(tlc2.tool.TLCState, tlc2.tool.liveness.TBGraphNode) + */ + public void writeState(TLCState state, TBGraphNode tableauNode) { + + // Marker the state as an initial state by using a filled style. + this.writer.append("\""); + this.writer.append(Long.toString(state.fingerPrint())); + this.writer.append("."); + this.writer.append(Integer.toString(tableauNode.getIndex())); + this.writer.append("\""); + this.writer.append(" [style = filled]"); + this.writer.append(" [label=\""); + this.writer.append(states2dot(state)); + this.writer.append("\n#" + Long.toString(state.fingerPrint()) + "." + + tableauNode.getIndex() + "#"); + this.writer.append("\"]"); + this.writer.append("\n"); + } + + /* (non-Javadoc) + * @see tlc2.tool.liveness.ILivenessStateWriter#writeState(tlc2.tool.TLCState, tlc2.tool.liveness.TBGraphNode, tlc2.tool.TLCState, tlc2.tool.liveness.TBGraphNode, boolean) + */ + public void writeState(TLCState state, TBGraphNode tableauNode, TLCState successor, + TBGraphNode tableauNodeSuccessor, BitVector actionChecks, int from, int length, boolean successorStateIsNew) { + writeState(state, tableauNode, successor, tableauNodeSuccessor, actionChecks, from, length, successorStateIsNew, Visualization.DEFAULT); + } + + /* (non-Javadoc) + * @see tlc2.tool.liveness.ILivenessStateWriter#writeState(tlc2.tool.TLCState, tlc2.tool.liveness.TBGraphNode, tlc2.tool.TLCState, tlc2.tool.liveness.TBGraphNode, boolean, tlc2.util.IStateWriter.Visualization) + */ + public void writeState(TLCState state, TBGraphNode tableauNode, TLCState successor, + TBGraphNode tableauNodeSuccessor, BitVector actionChecks, int from, int length, boolean successorStateIsNew, Visualization visualization) { + + final String successorsFP = Long.toString(successor.fingerPrint()); + + // Write the transition + this.writer.append("\""); + this.writer.append(Long.toString(state.fingerPrint())); + this.writer.append("."); + this.writer.append(Integer.toString(tableauNode.getIndex())); + this.writer.append("\""); + this.writer.append(" -> "); + this.writer.append("\""); + this.writer.append(successorsFP); + this.writer.append("."); + this.writer.append(Integer.toString(tableauNodeSuccessor.getIndex())); + this.writer.append("\""); + if (visualization == Visualization.STUTTERING) { + this.writer.append(" [style=\"dashed\"]"); + } + if (visualization == Visualization.DOTTED) { + this.writer.append(" [style=\"dotted\"]"); + } + if (length > 0) { + this.writer.append(" [label=\"" + actionChecks.toString(from, length, 't', 'f') + "\"]"); + } + this.writer.append(";\n"); + + // If the successor is new, print the state's label. Labels are printed + // when writeState sees the successor. It does not print the label for + // the current state. If it would print the label for the current state, + // the init state labels would be printed twice. + if (successorStateIsNew) { + // Write the successor's label + this.writer.append("\""); + this.writer.append(successorsFP); + this.writer.append("."); + this.writer.append(Integer.toString(tableauNodeSuccessor.getIndex())); + this.writer.append("\""); + this.writer.append(" [label=\""); + this.writer.append(states2dot(successor)); + this.writer.append( + "\n#" + Long.toString(successor.fingerPrint()) + "." + + tableauNodeSuccessor.getIndex() + "#"); + this.writer.append("\"]"); + this.writer.append(";\n"); + } + } + + protected static String tableauNode2dot(final TBGraphNode tableauNode) { + // Replace "\" with "\\" and """ with "\"". + return tableauNode.toString().replace("\\", "\\\\").replace("\"", "\\\"").trim(); + } +} diff --git a/tlatools/src/tlc2/tool/liveness/GraphNode.java b/tlatools/src/tlc2/tool/liveness/GraphNode.java index 69b43598bbf9a84c68e4a885570990c2cadf38f8..1c0371f4464bb74a640b15331a3531e5cc002000 100644 --- a/tlatools/src/tlc2/tool/liveness/GraphNode.java +++ b/tlatools/src/tlc2/tool/liveness/GraphNode.java @@ -6,11 +6,19 @@ package tlc2.tool.liveness; import java.io.IOException; +import java.util.HashSet; +import java.util.Set; import tlc2.util.BitVector; import tlc2.util.BufferedRandomAccessFile; public class GraphNode extends AbstractGraphNode { + /** + * The record size indicates the number of integers used by each transition + * in the array of nnodes (2x32bit to keep the fp and 32bit to keep the tableau + * idx). + */ + private static final int NNODE_RECORD_SIZE = 3; /** * GraphNode is a node in the behaviour graph. We're going to only store * fingerprints of states, rather than actual states. So, as we encounter @@ -77,34 +85,36 @@ public class GraphNode extends AbstractGraphNode { } public final long getStateFP(int i) { - long high = this.nnodes[3 * i]; - long low = this.nnodes[3 * i + 1]; + long high = this.nnodes[NNODE_RECORD_SIZE * i]; + long low = this.nnodes[NNODE_RECORD_SIZE * i + 1]; return (high << 32) | (low & 0xFFFFFFFFL); } public final int getTidx(int i) { - return this.nnodes[3 * i + 2]; + return this.nnodes[NNODE_RECORD_SIZE * i + 2]; } public final int succSize() { - // offset being != -1 indicates that the nnodes array has been + // offset being != NO_FREE_SLOTS indicates that the nnodes array has been // overallocated in preparation to batch-insert transitions but the // transitions have not been added yet. In this case the nnodes.length / - // 3 is *not* the actual number of transitions, offset / 3 is! - if (this.offset != -1) { - return this.offset / 3; + // NNODE_RECORD_SIZE is *not* the actual number of transitions, offset / NNODE_RECORD_SIZE is! + if (this.offset != NO_FREE_SLOTS) { + return this.offset / NNODE_RECORD_SIZE; } - return this.nnodes.length / 3; + return this.nnodes.length / NNODE_RECORD_SIZE; } /** * Points to the first available slot in {@link GraphNode#nnodes} iff free - * slots are available. "-1" indicates no free slots are available. + * slots are available. "NO_FREE_SLOTS" indicates no free slots are available. * * @see GraphNode#allocate(int) */ - private int offset = -1; + private int offset = NO_FREE_SLOTS; + private static final int NO_FREE_SLOTS = -1; + /** * Allocates memory for subsequent * {@link GraphNode#addTransition(long, int, int, int, boolean[])} calls. @@ -135,7 +145,7 @@ public class GraphNode extends AbstractGraphNode { */ private final void allocate(final int transitions) { final int len = this.nnodes.length; - int[] newNodes = new int[len + (3 * transitions)]; + int[] newNodes = new int[len + (NNODE_RECORD_SIZE * transitions)]; System.arraycopy(this.nnodes, 0, newNodes, 0, len); this.nnodes = newNodes; @@ -188,7 +198,7 @@ public class GraphNode extends AbstractGraphNode { } } } - if (this.offset == -1) { + if (this.offset == NO_FREE_SLOTS) { // Have to create a new slot regardless of 0 or negative hint, thus // Math.max... this.allocate(Math.max(allocationHint, 1)); @@ -196,9 +206,9 @@ public class GraphNode extends AbstractGraphNode { this.nnodes[this.offset] = (int) (fp >>> 32); this.nnodes[this.offset + 1] = (int) (fp & 0xFFFFFFFFL); this.nnodes[this.offset + 2] = tidx; - this.offset = this.offset + 3; + this.offset = this.offset + NNODE_RECORD_SIZE; if (this.offset == this.nnodes.length) { - this.offset = -1; + this.offset = NO_FREE_SLOTS; } } @@ -213,14 +223,14 @@ public class GraphNode extends AbstractGraphNode { */ public int realign() { int result = 0; - // It is a noop iff offset == -1 - if (this.offset != -1) { - result = (this.nnodes.length - this.offset) / 3; + // It is a noop iff offset == NO_FREE_SLOTS + if (this.offset != NO_FREE_SLOTS) { + result = (this.nnodes.length - this.offset) / NNODE_RECORD_SIZE; // shrink newNodes to correct size int[] newNodes = new int[this.offset]; System.arraycopy(this.nnodes, 0, newNodes, 0, newNodes.length); this.nnodes = newNodes; - this.offset = -1; + this.offset = NO_FREE_SLOTS; } return result; } @@ -240,14 +250,14 @@ public class GraphNode extends AbstractGraphNode { // been // reached. The free slot detection work with the allocation offset that // points to the end of the filled slots (slots are filled in ascending - // order). If offset is marked invalid ("-1"), the nnodes buffer is + // order). If offset is marked invalid ("NO_FREE_SLOTS"), the nnodes buffer is // completely occupied and has to be searched to the end. - if (this.offset != -1) { + if (this.offset != NO_FREE_SLOTS) { len = offset; } int high = (int) (fp >>> 32); int low = (int) (fp & 0xFFFFFFFFL); - for (int i = 0; i < len; i += 3) { + for (int i = 0; i < len; i += NNODE_RECORD_SIZE) { if (this.nnodes[i] == high && this.nnodes[i + 1] == low && this.nnodes[i + 2] == tidx) { return true; } @@ -255,6 +265,93 @@ public class GraphNode extends AbstractGraphNode { return false; } + public boolean checkInvariants(final int slen, final int alen) { + final Set<Transition> transitions = new HashSet<Transition>(); + for (int i = 0; i < succSize(); i++) { + final Transition t = new Transition(getStateFP(i), getTidx(i), getCheckAction(slen, alen, i)); + transitions.add(t); + } + return transitions.size() == succSize(); + } + + public Set<Transition> getTransition() { + return getTransition(0, 0); + } + + public Set<Transition> getTransition(final int slen, final int alen) { + final Set<Transition> transitions = new HashSet<Transition>(); + for (int i = 0; i < succSize(); i++) { + final BitVector bv = new BitVector(alen); + for (int j = 0; j < alen; j++) { + if (getCheckAction(slen, alen, i, j)) { + bv.set(j); + } + } + transitions.add(new Transition(getStateFP(i), getTidx(i), bv)); + } + return transitions; + } + + public static class Transition { + + private final long fp; + private final int tidx; + private final BitVector bv; + + public Transition(long fp, int tidx, BitVector bv) { + this.fp = fp; + this.tidx = tidx; + this.bv = bv; + } + + /* (non-Javadoc) + * @see java.lang.Object#hashCode() + */ + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((bv == null) ? 0 : bv.hashCode()); + result = prime * result + (int) (fp ^ (fp >>> 32)); + result = prime * result + tidx; + return result; + } + + /* (non-Javadoc) + * @see java.lang.Object#equals(java.lang.Object) + */ + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + Transition other = (Transition) obj; + if (bv == null) { + if (other.bv != null) + return false; + } else if (!bv.equals(other.bv)) + return false; + if (fp != other.fp) + return false; + if (tidx != other.tidx) + return false; + return true; + } + + public BitVector getChecks() { + return bv; + } + + public long getFP() { + return fp; + } + + public int getTidx() { + return tidx; + } + } + /* Return the tableau graph node used by this. */ public final TBGraphNode getTNode(TBGraph tableau) { return tableau.getNode(this.tindex); @@ -268,6 +365,7 @@ public class GraphNode extends AbstractGraphNode { * @throws IOException */ void write(final BufferedRandomAccessFile nodeRAF) throws IOException { + assert offset == NO_FREE_SLOTS; // assert that nnodes hasn't been overallocated. // Write nnodes final int cnt = nnodes.length; nodeRAF.writeNat(cnt); @@ -288,50 +386,105 @@ public class GraphNode extends AbstractGraphNode { // Read checks checks = new BitVector(); checks.read(nodeRAF); + + assert offset == NO_FREE_SLOTS; } public final String toString() { + // A GraphNode does not know the action length. This is kept elsewhere in the code. + return toString(0).replace("[] ", ""); + } + + public final String toString(final int alen) { StringBuffer buf = new StringBuffer(); buf.append("<" + this.stateFP + "," + this.tindex + "> --> "); - int size = this.nnodes.length; - if (size != 0) { - long high = this.nnodes[0]; - long low = this.nnodes[1]; - long fp = (high << 32) | (low & 0xFFFFFFFFL); - buf.append("<" + fp + "," + this.nnodes[2] + ">"); - } - for (int i = 3; i < size; i += 3) { + for (int i = 0; i < succSize(); i++) { + // action checks + buf.append("["); + for (int j = 0; j < alen; j++) { + if (getCheckAction(0, 2, i, j)) { + buf.append("t"); + } else { + buf.append("f"); + } + } + buf.append("] "); + // fingerprint/tableau id + buf.append("<" + getStateFP(i) + "," + getTidx(i) + ">"); buf.append(", "); - long high = this.nnodes[i]; - long low = this.nnodes[i + 1]; - long fp = (high << 32) | (low & 0xFFFFFFFFL); - buf.append("<" + fp + "," + this.nnodes[i + 2] + ">"); } - return buf.toString(); + return buf.substring(0, buf.length() - ", ".length()); // chop off dangling ", " } - public String toDotViz(final boolean isInitState, final boolean hasTableau) { - String id = (""+this.stateFP).substring(0,3); + public String toDotViz(final boolean isInitState, final boolean hasTableau, final int slen, final int alen) { + return toDotViz(isInitState, hasTableau, slen, alen, null); + } + + public String toDotViz(final boolean isInitState, final boolean hasTableau, final int slen, final int alen, TableauNodePtrTable filter) { + // The node's id including its tidx if any. It uses the complete + // fingerprint. + String id = Long.toString(this.stateFP); if (hasTableau) { id += "." + this.tindex; } - + + // Nodes label and a marker if it is an init state. The label is + // shortened to 8 chars max to avoid screen clutter. It's possible + // that the resulting graph will have multiple nodes with an identical + // label iff the first 6 (+2) chars of their fingerprint match. However + // the graph will still contain all nodes regardless of the label + // collision due to id. + String label = Long.toString(this.stateFP).substring(0, 6) + (hasTableau ? "." + this.tindex : ""); + if (slen > 0) { + label += "\n"; + for (int i = 0; i < slen; i++) { + if (getCheckState(i)) { + label += "t"; + } else { + label += "f"; + } + } + } final StringBuffer buf = new StringBuffer(); if (isInitState) { - buf.append(id + " [style = filled]\n"); // node's label + buf.append("\"" + id + "\" [style = filled][label = \"" + label + "\"]\n"); // node's label + } else { + buf.append("\"" + id + "\" [label = \"" + label + "\"]\n"); } - int size = this.nnodes.length; - for (int i = 0; i < size; i += 3) { - buf.append(id + " -> "); - long high = this.nnodes[i]; - long low = this.nnodes[i + 1]; - long fp = (high << 32) | (low & 0xFFFFFFFFL); + + // Each outgoing transition + for (int i = 0; i < succSize(); i++) { + final long stateFP = getStateFP(i); + final int tidx = getTidx(i); + + // If a filter is given, check if this node is in filter + if (filter != null && filter.get(stateFP, tidx) == -1) { + continue; + } + + String fp = Long.toString(stateFP); +// if (fp == this.stateFP) { +// // skip self loops if edge count to large for dotViz to handle. +// continue; +// } + + buf.append("\"" + id + "\" -> "); if (hasTableau) { - buf.append(("" + fp).substring(0, 3) + "." + this.nnodes[i + 2]); + buf.append(("\"" + fp) + "." + tidx + "\""); } else { //Omit tableau index when it's -1 (indicating no tableau) - buf.append(("" + fp).substring(0, 3)); + buf.append(("\"" + fp) + "\""); + } + + buf.append(" [label=\""); + for (int j = 0; j < alen; j++) { + if (getCheckAction(slen, alen, i, j)) { + buf.append("t"); + } else { + buf.append("f"); + } } + buf.append("\"];"); buf.append("\n"); } return buf.toString(); diff --git a/tlatools/src/tlc2/tool/liveness/ILiveCheck.java b/tlatools/src/tlc2/tool/liveness/ILiveCheck.java index 171a00672f42ed8b829429dc06ea9a16959de998..688ad737a155a213b5e0e65621cf5baf0fa92743 100644 --- a/tlatools/src/tlc2/tool/liveness/ILiveCheck.java +++ b/tlatools/src/tlc2/tool/liveness/ILiveCheck.java @@ -1,11 +1,36 @@ +/******************************************************************************* + * Copyright (c) 2015 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ package tlc2.tool.liveness; import java.io.IOException; +import tlc2.tool.ITool; import tlc2.tool.StateVec; import tlc2.tool.TLCState; -import tlc2.tool.Tool; -import tlc2.util.LongVec; +import tlc2.util.SetOfStates; import tlc2.util.statistics.IBucketStatistics; public interface ILiveCheck { @@ -14,35 +39,40 @@ public interface ILiveCheck { * This method records that state is an initial state in the behavior graph. * It is called when a new initial state is generated. */ - void addInitState(TLCState state, long stateFP); + void addInitState(ITool tool, TLCState state, long stateFP); /** * This method adds new nodes into the behavior graph induced by s0. It is * called after the successors of s0 are computed. */ - void addNextState(TLCState s0, long fp0, StateVec nextStates, LongVec nextFPs) throws IOException; + void addNextState(ITool tool, TLCState s0, long fp0, SetOfStates nextStates) throws IOException; + + /** + * true iff a call to {@link ILiveCheck#check(ITool, boolean)} would indeed result in liveness checking. + */ + boolean doLiveCheck(); /** * Check liveness properties for the current (potentially partial) state graph. Returns * true iff it finds no errors. - * * @param forceCheck * Always checks liveness if true, otherwise heuristics about the * partial graph are taken into account if it is worthwhile to * check liveness. - * @return true iff it finds no errors or if liveness has not been checked - * on the partial graph because it was deemed worthless. + * + * @return <code>EC.NO_ERROR</code> iff it finds no errors or if liveness has not been checked + * on the partial graph because it was deemed worthless. Otherwise an EC. */ - boolean check(boolean forceCheck) throws Exception; + int check(ITool tool, boolean forceCheck) throws Exception; /** * No states can be added with add*State once finalCheck has been called. * * @see ILiveCheck#check() - * @return + * @return an error code, or <code>EC.NO_ERROR</code> on success * @throws Exception */ - boolean finalCheck() throws Exception; + int finalCheck(ITool tool) throws Exception; /* simulation mode */ @@ -59,19 +89,17 @@ public interface ILiveCheck { * is done as part of checkTrace. * <p> * checkTrace can be called multiple times until ILiveCheck has been closed (see close()). - * * @param trace + * * @throws IOException * @throws InterruptedException */ - void checkTrace(final StateVec trace) throws IOException, InterruptedException; + void checkTrace(ITool tool, final StateVec trace) throws IOException, InterruptedException; /* auxiliary methods */ String getMetaDir(); - Tool getTool(); - IBucketStatistics getOutDegreeStatistics(); ILiveChecker getChecker(int idx); diff --git a/tlatools/src/tlc2/tool/liveness/ILiveChecker.java b/tlatools/src/tlc2/tool/liveness/ILiveChecker.java index 2c5bb45180eadf427157d006fa25bb7592eb7845..f4b84ca16fdeafca24932bdcf3ea265ae793869f 100644 --- a/tlatools/src/tlc2/tool/liveness/ILiveChecker.java +++ b/tlatools/src/tlc2/tool/liveness/ILiveChecker.java @@ -1,11 +1,37 @@ +/******************************************************************************* + * Copyright (c) 2015 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ + package tlc2.tool.liveness; import java.io.IOException; -import tlc2.tool.StateVec; +import tlc2.tool.ITool; import tlc2.tool.TLCState; import tlc2.util.BitVector; -import tlc2.util.LongVec; +import tlc2.util.SetOfStates; public interface ILiveChecker { @@ -13,17 +39,19 @@ public interface ILiveChecker { * This method records that state is an initial state in the behavior graph. * It is called when a new initial state is generated. */ - void addInitState(TLCState state, long stateFP); + void addInitState(ITool tool, TLCState state, long stateFP); /** * This method adds new nodes into the behavior graph induced by s0. It is * called after the successors of s0 are computed. */ - void addNextState(TLCState s0, long fp0, StateVec nextStates, LongVec nextFPs, BitVector checkActionResults, - boolean[] checkStateResults) throws IOException; + void addNextState(ITool tool, TLCState s0, long fp0, SetOfStates nextStates, + BitVector checkActionResults, boolean[] checkStateResults) throws IOException; AbstractDiskGraph getDiskGraph(); OrderOfSolution getSolution(); + void close() throws IOException; + } \ No newline at end of file diff --git a/tlatools/src/tlc2/tool/liveness/ILivenessStateWriter.java b/tlatools/src/tlc2/tool/liveness/ILivenessStateWriter.java new file mode 100644 index 0000000000000000000000000000000000000000..ee3ea670da4b674559f6fd077dc659b2a8546fe3 --- /dev/null +++ b/tlatools/src/tlc2/tool/liveness/ILivenessStateWriter.java @@ -0,0 +1,38 @@ +/******************************************************************************* + * Copyright (c) 2016 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.liveness; + +import tlc2.tool.TLCState; +import tlc2.util.BitVector; +import tlc2.util.IStateWriter; + +public interface ILivenessStateWriter extends IStateWriter { + void writeState(TLCState state, TBGraphNode tableauNode); + + void writeState(TLCState state, TBGraphNode tableauNode, TLCState successor, TBGraphNode tableauNodeSuccessor, BitVector actionChecks, int from, int length, boolean successorStateIsNew); + + void writeState(TLCState state, TBGraphNode tableauNode, TLCState successor, TBGraphNode tableauNodeSuccessor, BitVector actionChecks, int from, int length, boolean successorStateIsNew, Visualization visulation); +} diff --git a/tlatools/src/tlc2/tool/liveness/LNAction.java b/tlatools/src/tlc2/tool/liveness/LNAction.java index 99dbb03ab6c41aafeab229daec2389117b4c8283..4f8cc8ee220be367044d211c402a66d79cf1f840 100644 --- a/tlatools/src/tlc2/tool/liveness/LNAction.java +++ b/tlatools/src/tlc2/tool/liveness/LNAction.java @@ -8,11 +8,11 @@ package tlc2.tool.liveness; import tla2sany.semantic.ExprNode; import tlc2.output.EC; import tlc2.tool.EvalControl; +import tlc2.tool.ITool; import tlc2.tool.TLCState; -import tlc2.tool.Tool; import tlc2.util.Context; -import tlc2.value.BoolValue; -import tlc2.value.Value; +import tlc2.value.IBoolValue; +import tlc2.value.IValue; import util.Assert; import util.WrongInvocationException; @@ -59,10 +59,10 @@ public class LNAction extends LiveExprNode { return true; } - public final boolean eval(Tool tool, TLCState s1, TLCState s2) { + public final boolean eval(ITool tool, TLCState s1, TLCState s2) { if (this.subscript != null) { - Value v1 = tool.eval(this.subscript, con, s1, TLCState.Empty, EvalControl.Clear); - Value v2 = tool.eval(this.subscript, con, s2, null, EvalControl.Clear); + IValue v1 = tool.eval(this.subscript, con, s1, TLCState.Empty, EvalControl.Clear); + IValue v2 = tool.eval(this.subscript, con, s2, null, EvalControl.Clear); boolean isStut = v1.equals(v2); if (this.isBox) { if (isStut) { @@ -74,11 +74,11 @@ public class LNAction extends LiveExprNode { } } } - Value val = tool.eval(this.body, con, s1, s2, EvalControl.Clear); - if (!(val instanceof BoolValue)) { + IValue val = tool.eval(this.body, con, s1, s2, EvalControl.Clear); + if (!(val instanceof IBoolValue)) { Assert.fail(EC.TLC_LIVE_ENCOUNTERED_NONBOOL_PREDICATE); } - return ((BoolValue) val).val; + return ((IBoolValue) val).getVal(); } public final void toString(StringBuffer sb, String padding) { diff --git a/tlatools/src/tlc2/tool/liveness/LNAll.java b/tlatools/src/tlc2/tool/liveness/LNAll.java index 40c86b77faeab571300745a9e03ea3b72e058771..9223226cb472b296f7df4fd8985f37efd2ddf823 100644 --- a/tlatools/src/tlc2/tool/liveness/LNAll.java +++ b/tlatools/src/tlc2/tool/liveness/LNAll.java @@ -6,8 +6,8 @@ package tlc2.tool.liveness; import tlc2.output.EC; +import tlc2.tool.ITool; import tlc2.tool.TLCState; -import tlc2.tool.Tool; import util.Assert; /** @@ -39,7 +39,7 @@ class LNAll extends LiveExprNode { return this.body.containAction(); } - public final boolean eval(Tool tool, TLCState s1, TLCState s2) { + public final boolean eval(ITool tool, TLCState s1, TLCState s2) { Assert.fail(EC.TLC_LIVE_CANNOT_EVAL_FORMULA, ALWAYS); return false; // make compiler happy } diff --git a/tlatools/src/tlc2/tool/liveness/LNBool.java b/tlatools/src/tlc2/tool/liveness/LNBool.java index b3d5264f3c7d4a439646d0ddc71cdfccb46ccba6..399fbc27b78fc1b3c89f6a8e3018eca999ad0afe 100644 --- a/tlatools/src/tlc2/tool/liveness/LNBool.java +++ b/tlatools/src/tlc2/tool/liveness/LNBool.java @@ -5,8 +5,8 @@ package tlc2.tool.liveness; +import tlc2.tool.ITool; import tlc2.tool.TLCState; -import tlc2.tool.Tool; class LNBool extends LiveExprNode { public static final LNBool TRUE = new LNBool(true); @@ -18,7 +18,7 @@ class LNBool extends LiveExprNode { this.b = b; } - public final boolean eval(Tool tool, TLCState s1, TLCState s2) { + public final boolean eval(ITool tool, TLCState s1, TLCState s2) { return this.b; } diff --git a/tlatools/src/tlc2/tool/liveness/LNConj.java b/tlatools/src/tlc2/tool/liveness/LNConj.java index 4a7b12ee4dcd3bc31f98218667884a953928fe9a..38f0309af3b8e078d27103740d761cdb4ff68279 100644 --- a/tlatools/src/tlc2/tool/liveness/LNConj.java +++ b/tlatools/src/tlc2/tool/liveness/LNConj.java @@ -5,28 +5,28 @@ package tlc2.tool.liveness; +import tlc2.tool.ITool; import tlc2.tool.TLCState; -import tlc2.tool.Tool; import tlc2.util.Vect; class LNConj extends LiveExprNode { - private final Vect conjs; // The conjuncts + private final Vect<LiveExprNode> conjs; // The conjuncts private int info; public LNConj(int size) { - this.conjs = new Vect(size); + this.conjs = new Vect<>(size); this.info = 0; } public LNConj(LiveExprNode n) { - this.conjs = new Vect(1); + this.conjs = new Vect<>(1); this.conjs.addElement(n); int level = n.getLevel(); this.info = n.containAction() ? level + 8 : level; } public LNConj(LiveExprNode n1, LiveExprNode n2) { - this.conjs = new Vect(2); + this.conjs = new Vect<>(2); this.conjs.addElement(n1); this.conjs.addElement(n2); boolean hasAct = n1.containAction() || n2.containAction(); @@ -34,7 +34,7 @@ class LNConj extends LiveExprNode { this.info = hasAct ? level + 8 : level; } - public LNConj(Vect conjs) { + public LNConj(Vect<LiveExprNode> conjs) { this.conjs = conjs; boolean hasAct = false; int level = 0; @@ -76,7 +76,7 @@ class LNConj extends LiveExprNode { return (this.info & 8) > 0; } - public final boolean eval(Tool tool, TLCState s1, TLCState s2) { + public final boolean eval(ITool tool, TLCState s1, TLCState s2) { int sz = this.conjs.size(); for (int i = 0; i < sz; i++) { LiveExprNode item = (LiveExprNode) this.conjs.elementAt(i); @@ -171,7 +171,7 @@ class LNConj extends LiveExprNode { } // We now construct the cross product: - Vect nes = new Vect(count); + Vect<LiveExprNode> nes = new Vect<>(count); int total = 1; for (int i = 0; i < count; i++) { LiveExprNode elem = temp[i]; @@ -194,7 +194,7 @@ class LNConj extends LiveExprNode { return new LNConj(nes); } int nesSize = nes.size(); - Vect res = new Vect(total); + Vect<LiveExprNode> res = new Vect<>(total); for (int i = 0; i < total; i++) { res.addElement(new LNConj(nesSize)); } diff --git a/tlatools/src/tlc2/tool/liveness/LNDisj.java b/tlatools/src/tlc2/tool/liveness/LNDisj.java index cbf32ead1a3683110bd799e61f46750ff2819bf6..9a1a8843635c21e62c52ca049add1637a89e8144 100644 --- a/tlatools/src/tlc2/tool/liveness/LNDisj.java +++ b/tlatools/src/tlc2/tool/liveness/LNDisj.java @@ -5,28 +5,28 @@ package tlc2.tool.liveness; +import tlc2.tool.ITool; import tlc2.tool.TLCState; -import tlc2.tool.Tool; import tlc2.util.Vect; class LNDisj extends LiveExprNode { - private final Vect disjs; // The disjuncts + private final Vect<LiveExprNode> disjs; // The disjuncts private int info; public LNDisj(int size) { - this.disjs = new Vect(size); + this.disjs = new Vect<>(size); this.info = 0; } public LNDisj(LiveExprNode n) { - this.disjs = new Vect(1); + this.disjs = new Vect<>(1); this.disjs.addElement(n); int level = n.getLevel(); this.info = n.containAction() ? level + 8 : level; } public LNDisj(LiveExprNode n1, LiveExprNode n2) { - this.disjs = new Vect(2); + this.disjs = new Vect<>(2); this.disjs.addElement(n1); this.disjs.addElement(n2); boolean hasAct = n1.containAction() || n2.containAction(); @@ -34,7 +34,7 @@ class LNDisj extends LiveExprNode { this.info = hasAct ? level + 8 : level; } - public LNDisj(Vect disjs) { + public LNDisj(Vect<LiveExprNode> disjs) { this.disjs = disjs; boolean hasAct = false; int level = 0; @@ -76,7 +76,7 @@ class LNDisj extends LiveExprNode { return (this.info & 8) > 0; } - public final boolean eval(Tool tool, TLCState s1, TLCState s2) { + public final boolean eval(ITool tool, TLCState s1, TLCState s2) { int sz = disjs.size(); for (int i = 0; i < sz; i++) { LiveExprNode item = (LiveExprNode) disjs.elementAt(i); diff --git a/tlatools/src/tlc2/tool/liveness/LNEven.java b/tlatools/src/tlc2/tool/liveness/LNEven.java index c08ded5e84a88ed8143891f4577d27776d373461..3967a7d47ef30dca6f8264c0ce6afc306b1af1bb 100644 --- a/tlatools/src/tlc2/tool/liveness/LNEven.java +++ b/tlatools/src/tlc2/tool/liveness/LNEven.java @@ -6,8 +6,8 @@ package tlc2.tool.liveness; import tlc2.output.EC; +import tlc2.tool.ITool; import tlc2.tool.TLCState; -import tlc2.tool.Tool; import util.Assert; /** @@ -36,7 +36,7 @@ class LNEven extends LiveExprNode { return this.body.containAction(); } - public final boolean eval(Tool tool, TLCState s1, TLCState s2) { + public final boolean eval(ITool tool, TLCState s1, TLCState s2) { Assert.fail(EC.TLC_LIVE_CANNOT_EVAL_FORMULA, EVENTUALLY); return false; // make compiler happy } diff --git a/tlatools/src/tlc2/tool/liveness/LNNeg.java b/tlatools/src/tlc2/tool/liveness/LNNeg.java index 439023e73abb91c79bdb89bd1cafca4c91f46fed..0602b5e08999043835234798b1d74b003ca35cea 100644 --- a/tlatools/src/tlc2/tool/liveness/LNNeg.java +++ b/tlatools/src/tlc2/tool/liveness/LNNeg.java @@ -5,8 +5,8 @@ package tlc2.tool.liveness; +import tlc2.tool.ITool; import tlc2.tool.TLCState; -import tlc2.tool.Tool; public class LNNeg extends LiveExprNode { private final LiveExprNode body; @@ -27,7 +27,7 @@ public class LNNeg extends LiveExprNode { return this.body.containAction(); } - public final boolean eval(Tool tool, TLCState s1, TLCState s2) { + public final boolean eval(ITool tool, TLCState s1, TLCState s2) { return !this.body.eval(tool, s1, s2); } diff --git a/tlatools/src/tlc2/tool/liveness/LNNext.java b/tlatools/src/tlc2/tool/liveness/LNNext.java index ee09c5146d1c7516f494a25d3f8a161039a043e2..c15ee7002ad21ce46eb5f83fac1c37cbde3b1f30 100644 --- a/tlatools/src/tlc2/tool/liveness/LNNext.java +++ b/tlatools/src/tlc2/tool/liveness/LNNext.java @@ -5,8 +5,8 @@ package tlc2.tool.liveness; +import tlc2.tool.ITool; import tlc2.tool.TLCState; -import tlc2.tool.Tool; class LNNext extends LiveExprNode { private final LiveExprNode body; @@ -27,7 +27,7 @@ class LNNext extends LiveExprNode { return this.body.containAction(); } - public final boolean eval(Tool tool, TLCState s1, TLCState s2) { + public final boolean eval(ITool tool, TLCState s1, TLCState s2) { return this.body.eval(tool, s2, TLCState.Empty); } diff --git a/tlatools/src/tlc2/tool/liveness/LNState.java b/tlatools/src/tlc2/tool/liveness/LNState.java index 59f786a7c4d1c7bd46d5e29dbf2b4bb4e082515d..664bdb36070915d1ae7315360cf31ecd735288a0 100644 --- a/tlatools/src/tlc2/tool/liveness/LNState.java +++ b/tlatools/src/tlc2/tool/liveness/LNState.java @@ -5,8 +5,8 @@ package tlc2.tool.liveness; +import tlc2.tool.ITool; import tlc2.tool.TLCState; -import tlc2.tool.Tool; import tlc2.util.Context; abstract class LNState extends LiveExprNode { @@ -25,7 +25,7 @@ abstract class LNState extends LiveExprNode { return false; } - public final boolean eval(Tool tool, TLCState s) { + public final boolean eval(ITool tool, TLCState s) { return this.eval(tool, s, TLCState.Empty); } diff --git a/tlatools/src/tlc2/tool/liveness/LNStateAST.java b/tlatools/src/tlc2/tool/liveness/LNStateAST.java index c37e787ab93735f879068a6ddcb5ca92cc05d941..2618eb8dcfe1159b8c3e6101dd425c80462a2e2f 100644 --- a/tlatools/src/tlc2/tool/liveness/LNStateAST.java +++ b/tlatools/src/tlc2/tool/liveness/LNStateAST.java @@ -10,11 +10,11 @@ import tla2sany.semantic.ExprNode; import tla2sany.semantic.OpApplNode; import tla2sany.st.TreeNode; import tlc2.output.EC; +import tlc2.tool.ITool; import tlc2.tool.TLCState; -import tlc2.tool.Tool; import tlc2.util.Context; -import tlc2.value.BoolValue; -import tlc2.value.Value; +import tlc2.value.IBoolValue; +import tlc2.value.IValue; import util.Assert; /** @@ -35,12 +35,12 @@ class LNStateAST extends LNState { return this.body; } - public final boolean eval(Tool tool, TLCState s1, TLCState s2) { - Value val = tool.eval(this.body, getContext(), s1); - if (!(val instanceof BoolValue)) { + public final boolean eval(ITool tool, TLCState s1, TLCState s2) { + IValue val = tool.eval(this.body, getContext(), s1); + if (!(val instanceof IBoolValue)) { Assert.fail(EC.TLC_LIVE_STATE_PREDICATE_NON_BOOL); } - return ((BoolValue) val).val; + return ((IBoolValue) val).getVal(); } public final void toString(StringBuffer sb, String padding) { diff --git a/tlatools/src/tlc2/tool/liveness/LNStateEnabled.java b/tlatools/src/tlc2/tool/liveness/LNStateEnabled.java index 8f33d7ee6d484324ba23cfd0288c2e3c45f35732..3f75a50664ef91eccdf251d8300d7ef42ff6012e 100644 --- a/tlatools/src/tlc2/tool/liveness/LNStateEnabled.java +++ b/tlatools/src/tlc2/tool/liveness/LNStateEnabled.java @@ -9,10 +9,10 @@ import tla2sany.parser.SyntaxTreeNode; import tla2sany.semantic.ExprNode; import tla2sany.semantic.OpApplNode; import tla2sany.st.TreeNode; -import tlc2.tool.ActionItemList; +import tlc2.tool.IActionItemList; +import tlc2.tool.ITool; import tlc2.tool.TLCState; import tlc2.tool.TLCStateFun; -import tlc2.tool.Tool; import tlc2.util.Context; /** @@ -41,19 +41,19 @@ class LNStateEnabled extends LNState { this.isBox = isBox; } - public final boolean eval(Tool tool, TLCState s1, TLCState s2) { + public final boolean eval(ITool tool, TLCState s1, TLCState s2) { // Note that s2 is useless. if (this.isBox && this.subscript != null) { return true; } - ActionItemList acts = ActionItemList.Empty; TLCState sfun = TLCStateFun.Empty; Context c1 = Context.branch(getContext()); if (this.subscript != null) { - acts = acts.cons(this.subscript, c1, -3); + sfun = tool.enabled(this.pred, c1, s1, sfun, this.subscript, IActionItemList.CHANGED); + } else { + sfun = tool.enabled(this.pred, c1, s1, sfun); } - sfun = tool.enabled(this.pred, acts, c1, s1, sfun); return sfun != null; } diff --git a/tlatools/src/tlc2/tool/liveness/LiveCheck.java b/tlatools/src/tlc2/tool/liveness/LiveCheck.java index 4aa5c699bbbef22f729027b8c661c26bd6daad1c..f253e135f1c81f10e042d0f2165b50f1341b843a 100644 --- a/tlatools/src/tlc2/tool/liveness/LiveCheck.java +++ b/tlatools/src/tlc2/tool/liveness/LiveCheck.java @@ -9,50 +9,61 @@ import java.util.Arrays; import java.util.Enumeration; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue; - +import java.util.concurrent.CompletionService; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorCompletionService; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; + +import tlc2.TLC; import tlc2.TLCGlobals; import tlc2.output.EC; import tlc2.output.MP; import tlc2.tool.Action; +import tlc2.tool.ITool; +import tlc2.tool.ModelChecker; import tlc2.tool.StateVec; import tlc2.tool.TLCState; -import tlc2.tool.Tool; import tlc2.util.BitVector; -import tlc2.util.FP64; -import tlc2.util.LongVec; -import tlc2.util.statistics.DummyBucketStatistics; +import tlc2.util.IStateWriter; +import tlc2.util.IStateWriter.Visualization; +import tlc2.util.NoopStateWriter; +import tlc2.util.SetOfStates; import tlc2.util.statistics.IBucketStatistics; import util.Assert; -import util.SimpleFilenameToStream; public class LiveCheck implements ILiveCheck { - private final Action[] actions; - private final Tool myTool; private final String metadir; private final IBucketStatistics outDegreeGraphStats; private final ILiveChecker[] checker; - // SZ: fields not read locally - // private static OrderOfSolution currentOOS; - // private static DiskGraph currentDG; - // private static PossibleErrorModel currentPEM; - - public LiveCheck(Tool tool, Action[] acts, String mdir, IBucketStatistics bucketStatistics) throws IOException { - this(tool, acts, Liveness.processLiveness(tool), mdir, bucketStatistics); + public LiveCheck(ITool tool, String mdir, IBucketStatistics bucketStatistics) throws IOException { + this(tool, Liveness.processLiveness(tool), mdir, bucketStatistics, new NoopStateWriter()); + } + + public LiveCheck(ITool tool, String mdir, IBucketStatistics bucketStatistics, IStateWriter stateWriter) throws IOException { + this(tool, Liveness.processLiveness(tool), mdir, bucketStatistics, stateWriter); + } + + public LiveCheck(ITool tool, OrderOfSolution[] solutions, String mdir, IBucketStatistics bucketStatistics) throws IOException { + this(tool, solutions, mdir, bucketStatistics, new NoopLivenessStateWriter()); } - public LiveCheck(Tool tool, Action[] acts, OrderOfSolution[] solutions, String mdir, IBucketStatistics bucketStatistics) throws IOException { - myTool = tool; - actions = acts; + public LiveCheck(ITool tool, OrderOfSolution[] solutions, String mdir, IBucketStatistics bucketStatistics, IStateWriter stateWriter) throws IOException { metadir = mdir; outDegreeGraphStats = bucketStatistics; checker = new ILiveChecker[solutions.length]; for (int soln = 0; soln < solutions.length; soln++) { + final ILivenessStateWriter writer = stateWriter.isNoop() || !stateWriter.isDot() + ? new NoopLivenessStateWriter() + : new DotLivenessStateWriter(stateWriter); if (!solutions[soln].hasTableau()) { - checker[soln] = new LiveChecker(solutions[soln], soln, bucketStatistics); + checker[soln] = new LiveChecker(solutions[soln], soln, bucketStatistics, writer); } else { - checker[soln] = new TableauLiveChecker(solutions[soln], soln, bucketStatistics); + checker[soln] = new TableauLiveChecker(solutions[soln], soln, bucketStatistics, writer); } } } @@ -60,16 +71,16 @@ public class LiveCheck implements ILiveCheck { /* (non-Javadoc) * @see tlc2.tool.liveness.ILiveCheck#addInitState(tlc2.tool.TLCState, long) */ - public void addInitState(TLCState state, long stateFP) { + public void addInitState(ITool tool, TLCState state, long stateFP) { for (int i = 0; i < checker.length; i++) { - checker[i].addInitState(state, stateFP); + checker[i].addInitState(tool, state, stateFP); } } /* (non-Javadoc) - * @see tlc2.tool.liveness.ILiveCheck#addNextState(tlc2.tool.TLCState, long, tlc2.tool.StateVec, tlc2.util.LongVec) + * @see tlc2.tool.liveness.ILiveCheck#addNextState(tlc2.tool.TLCState, long, tlc2.util.SetOfStates) */ - public void addNextState(TLCState s0, long fp0, StateVec nextStates, LongVec nextFPs) throws IOException { + public void addNextState(ITool tool, TLCState s0, long fp0, SetOfStates nextStates) throws IOException { for (int i = 0; i < checker.length; i++) { final ILiveChecker check = checker[i]; final OrderOfSolution oos = check.getSolution(); @@ -83,50 +94,92 @@ public class LiveCheck implements ILiveCheck { // to hold the result and loop over actions x successors twice // (here and down below). This is a little price to pay for significantly // increased concurrency. + // + // The actions have to be checked here because - in the light of + // symmetry - while we still have access to the actual successor + // state rather than just its fingerprint that represents all states + // in the symmetry set. Unless super-symmetry is in place (the + // actions checks for all states in the symmetry set evaluate to the + // same value), the "smallest" (see + // tlc2.tool.TLCStateMut.fingerPrint()) cannot be used as a + // replacement state to check the actions. final BitVector checkActionResults = new BitVector(alen * nextStates.size()); for (int sidx = 0; sidx < nextStates.size(); sidx++) { - final TLCState s1 = nextStates.elementAt(sidx); - oos.checkAction(s0, s1, checkActionResults, alen * sidx); + final TLCState s1 = nextStates.next(); + oos.checkAction(tool, s0, s1, checkActionResults, alen * sidx); } - check.addNextState(s0, fp0, nextStates, nextFPs, checkActionResults, oos.checkState(s0)); + nextStates.resetNext(); + check.addNextState(tool, s0, fp0, nextStates, checkActionResults, oos.checkState(tool, s0)); + + // Write the content of the current graph to a file in GraphViz + // format. Useful when debugging! +// check.getDiskGraph().writeDotViz(oos, new java.io.File( +// metadir + java.io.File.separator + "dgraph_" + i + "_" + System.currentTimeMillis() + ".dot")); } } /* (non-Javadoc) - * @see tlc2.tool.liveness.ILiveCheck#check(boolean) + * @see tlc2.tool.liveness.ILiveCheck#doLiveCheck() */ - public boolean check(boolean forceCheck) throws Exception { + public boolean doLiveCheck() { + for (int i = 0; i < checker.length; i++) { + // If one of the disk graph's size has increased by the given + // percentage, run liveness checking. + // + // TODO Alternatively: + // + // - LL suggest to dedicate a fixed fraction of model checking time + // to liveness checking. + // + // - The level could be taken into account. Unless the level + // (height) of the graph increases, no new cycle won't be found + // anyway (all other aspects of liveness checking are checked as + // part of regular safety checking). + // + // - The authors of the Divine model checker describe an algorithm + // in http://dx.doi.org/10.1109/ASE.2003.1240299 + // that counts the "Back-level Edges" and runs liveness checking upon + // a counter reaching a certain (user defined?!) threshold. + // + final AbstractDiskGraph diskGraph = checker[i].getDiskGraph(); + final long sizeAtLastCheck = diskGraph.getSizeAtLastCheck(); + final long sizeCurrently = diskGraph.size(); + final double delta = (sizeCurrently - sizeAtLastCheck) / (sizeAtLastCheck * 1.d); + if (delta > TLCGlobals.livenessThreshold) { + return true; + } + } + return false; + } + + @Override + public int check(ITool tool, boolean forceCheck) throws Exception { if (forceCheck) { - return check0(false); + return check0(tool, false); + } + if (!TLCGlobals.doLiveness()) { + // The user requested to only check liveness once, on the complete + // state graph. + return EC.NO_ERROR; } for (int i = 0; i < checker.length; i++) { - // If anyone of the disk graphs has increased by the given - // percentage, run liveness checking. This is the best heuristic I - // can come up with quickly. - // - // TODO Alternatively the level could be taken - // into account. Unless the level (height) of the graph increases, - // no new cycle won't be found anyway. All other aspects of liveness - // checking are checked as part of regular safety checking. + // see note in doLiveCheck() above! final AbstractDiskGraph diskGraph = checker[i].getDiskGraph(); final long sizeAtLastCheck = diskGraph.getSizeAtLastCheck(); final long sizeCurrently = diskGraph.size(); final double delta = (sizeCurrently - sizeAtLastCheck) / (sizeAtLastCheck * 1.d); if (delta > TLCGlobals.livenessThreshold) { - return check0(false); + return check0(tool, false); } } - - return true; + return EC.NO_ERROR; } - /* (non-Javadoc) - * @see tlc2.tool.liveness.ILiveCheck#finalCheck() - */ - public boolean finalCheck() throws InterruptedException, IOException { + @Override + public int finalCheck(ITool tool) throws InterruptedException, IOException { // Do *not* re-create the nodePtrTable after the check which takes a // while for larger disk graphs. - return check0(true); + return check0(tool, true); } /** @@ -135,13 +188,17 @@ public class LiveCheck implements ILiveCheck { * liveness check. If this is the final/last check, it's pointless * to re-create the nodePtrTable. */ - private boolean check0(final boolean finalCheck) throws InterruptedException, IOException { + protected int check0(final ITool tool, final boolean finalCheck) throws InterruptedException, IOException { + final long startTime = System.currentTimeMillis(); + + // Sum up the number of nodes in all disk graphs to indicate the amount + // of work to be done by liveness checking. long sum = 0L; for (int i = 0; i < checker.length; i++) { sum += checker[i].getDiskGraph().size(); } - MP.printMessage(EC.TLC_CHECKING_TEMPORAL_PROPS, - new String[] { "current", Long.toString(sum) }); + MP.printMessage(EC.TLC_CHECKING_TEMPORAL_PROPS, new String[] { finalCheck ? "complete" : "current", + Long.toString(sum), checker.length == 1 ? "" : checker.length + " branches of " }); // Copy the array of checkers into a concurrent-enabled queue // that allows LiveWorker threads to easily get the next @@ -160,75 +217,107 @@ public class LiveCheck implements ILiveCheck { final BlockingQueue<ILiveChecker> queue = new ArrayBlockingQueue<ILiveChecker>(checker.length); queue.addAll(Arrays.asList(checker)); - int slen = checker.length; - int wNum = Math.min(slen, TLCGlobals.getNumWorkers()); - - if (wNum == 1) { - LiveWorker worker = new LiveWorker(0, this, queue); - worker.run(); - } else { - final LiveWorker[] workers = new LiveWorker[wNum]; - for (int i = 0; i < wNum; i++) { - workers[i] = new LiveWorker(i, this, queue); - workers[i].start(); - } - for (int i = 0; i < wNum; i++) { - workers[i].join(); + + /* + * A LiveWorker below can either complete a unit of work a) without finding a + * liveness violation, b) finds a violation, or c) fails to check because of an + * exception/error (such as going out of memory). In case an LW fails to check, + * we still wait for all other LWs to complete. A subset of the LWs might have + * found a violation. In other words, the OOM of an LW has lower precedence than + * a violation found by another LW. However, if any LW fails to check, we terminate + * model checking after all LWs completed. + */ + final int wNum = TLCGlobals.doSequentialLiveness() ? 1 : Math.min(checker.length, TLCGlobals.getNumWorkers()); + final ExecutorService pool = Executors.newFixedThreadPool(wNum); + // CS is really just a container around the set of Futures returned by the pool. It saves us from + // creating a low-level array. + final CompletionService<Boolean> completionService = new ExecutorCompletionService<Boolean>(pool); + + for (int i = 0; i < wNum; i++) { + completionService.submit(new LiveWorker(tool, i, wNum, this, queue, finalCheck)); + } + // Wait for all LWs to complete. + pool.shutdown(); + pool.awaitTermination(Long.MAX_VALUE, TimeUnit.DAYS); // wait forever + + // Check if any one of the LWs found a violation (ignore failures for now). + ExecutionException ee = null; + for (int i = 0; i < wNum; i++) { + try { + final Future<Boolean> future = completionService.take(); + if (future.get()) { + MP.printMessage(EC.TLC_CHECKING_TEMPORAL_PROPS_END, + TLC.convertRuntimeToHumanReadable(System.currentTimeMillis() - startTime)); + return EC.TLC_TEMPORAL_PROPERTY_VIOLATED; + } + } catch (final ExecutionException e) { + // handled below! + ee = e; } } - - if (LiveWorker.hasErrFound()) { - return false; + // Terminate if any one of the LWs failed c) + if (ee != null) { + final Throwable cause = ee.getCause(); + if (cause instanceof OutOfMemoryError) { + MP.printError(EC.SYSTEM_OUT_OF_MEMORY_LIVENESS, cause); + } else if (cause instanceof StackOverflowError) { + MP.printError(EC.SYSTEM_STACK_OVERFLOW, cause); + } else if (cause != null) { + MP.printError(EC.GENERAL, cause); + } else { + MP.printError(EC.GENERAL, ee); + } + System.exit(1); } - + // Reset after checking unless it's the final check: if (finalCheck == false) { for (int i = 0; i < checker.length; i++) { checker[i].getDiskGraph().makeNodePtrTbl(); } } - return true; + MP.printMessage(EC.TLC_CHECKING_TEMPORAL_PROPS_END, TLC.convertRuntimeToHumanReadable(System.currentTimeMillis() - startTime)); + + return EC.NO_ERROR; } /* (non-Javadoc) * @see tlc2.tool.liveness.ILiveCheck#checkTrace(tlc2.tool.StateVec) */ - public void checkTrace(final StateVec stateTrace) throws InterruptedException, IOException { + public void checkTrace(ITool tool, final StateVec stateTrace) throws InterruptedException, IOException { // Add the first state to the LiveCheck as the current init state - addInitState(stateTrace.elementAt(0), stateTrace.elementAt(0).fingerPrint()); + addInitState(tool, stateTrace.elementAt(0), stateTrace.elementAt(0).fingerPrint()); // Add the remaining states... - final StateVec successor = new StateVec(2); - final LongVec successorFP = new LongVec(2); + final SetOfStates successors = new SetOfStates(stateTrace.size() * 2); // For all states except last one add the successor // (which is the next state in stateTrace). for (int i = 0; i < stateTrace.size() - 1; i++) { // Empty out old successors. - successor.clear(); - successorFP.reset(); + successors.clear(); // Calculate the current state's fingerprint final TLCState tlcState = stateTrace.elementAt(i); final long fingerPrint = tlcState.fingerPrint(); // Add state itself to allow stuttering - successor.addElement(tlcState); - successorFP.addElement(fingerPrint); + successors.put(tlcState); // Add the successor in the trace - successor.addElement(stateTrace.elementAt(i + 1)); - successorFP.addElement(stateTrace.elementAt(i + 1).fingerPrint()); - addNextState(tlcState, fingerPrint, successor, successorFP); + final TLCState successor = stateTrace.elementAt(i + 1); + successors.put(successor); + addNextState(tool, tlcState, fingerPrint, successors); } // Add last state in trace for which *no* successors have been generated final TLCState lastState = stateTrace.elementAt(stateTrace.size() - 1); - addNextState(lastState, lastState.fingerPrint(), new StateVec(0), new LongVec(0)); + addNextState(tool, lastState, lastState.fingerPrint(), new SetOfStates(0)); // Do *not* re-create the nodePtrTbl when it is thrown away anyway. - if (!check0(true)) { - throw new LiveException(); + final int result = check0(tool, true); + if (result != EC.NO_ERROR) { + throw new LiveException(result); } // We are done with the current subsequence of the behavior. Reset LiveCheck @@ -243,13 +332,6 @@ public class LiveCheck implements ILiveCheck { return metadir; } - /* (non-Javadoc) - * @see tlc2.tool.liveness.ILiveCheck#getTool() - */ - public Tool getTool() { - return myTool; - } - /* (non-Javadoc) * @see tlc2.tool.liveness.ILiveCheck#getOutDegreeStatistics() */ @@ -277,7 +359,7 @@ public class LiveCheck implements ILiveCheck { */ public void close() throws IOException { for (int i = 0; i < checker.length; i++) { - checker[i].getDiskGraph().close(); + checker[i].close(); } } @@ -342,11 +424,14 @@ public class LiveCheck implements ILiveCheck { } static abstract class AbstractLiveChecker implements ILiveChecker { - + + protected final ILivenessStateWriter writer; + protected final OrderOfSolution oos; - public AbstractLiveChecker(OrderOfSolution oos) { + public AbstractLiveChecker(OrderOfSolution oos, ILivenessStateWriter writer) { this.oos = oos; + this.writer = writer; } /* (non-Javadoc) @@ -355,30 +440,41 @@ public class LiveCheck implements ILiveCheck { public OrderOfSolution getSolution() { return oos; } + + /* (non-Javadoc) + * @see tlc2.tool.liveness.ILiveChecker#close() + */ + public void close() throws IOException { + if (!ModelChecker.VETO_CLEANUP) { + this.getDiskGraph().close(); + } + this.writer.close(); + } } private class LiveChecker extends AbstractLiveChecker { private final DiskGraph dgraph; - public LiveChecker(OrderOfSolution oos, int soln, IBucketStatistics bucketStatistics) + public LiveChecker(OrderOfSolution oos, int soln, IBucketStatistics bucketStatistics, ILivenessStateWriter writer) throws IOException { - super(oos); + super(oos, writer); this.dgraph = new DiskGraph(metadir, soln, bucketStatistics); } /* (non-Javadoc) * @see tlc2.tool.liveness.LiveCheck.ILiveChecker#addInitState(tlc2.tool.TLCState, long) */ - public void addInitState(TLCState state, long stateFP) { + public void addInitState(ITool tool, TLCState state, long stateFP) { dgraph.addInitNode(stateFP, -1); + writer.writeState(state); } /* (non-Javadoc) - * @see tlc2.tool.liveness.LiveCheck.ILiveChecker#addNextState(tlc2.tool.TLCState, long, tlc2.tool.StateVec, tlc2.util.LongVec, tlc2.util.BitVector, boolean[]) + * @see tlc2.tool.liveness.ILiveChecker#addNextState(tlc2.tool.TLCState, long, tlc2.util.SetOfStates, tlc2.util.BitVector, boolean[]) */ - public void addNextState(final TLCState s0, final long fp0, final StateVec nextStates, final LongVec nextFPs, - final BitVector checkActionResults, final boolean[] checkStateResults) throws IOException { + public void addNextState(ITool tool, final TLCState s0, final long fp0, + final SetOfStates nextStates, final BitVector checkActionResults, final boolean[] checkStateResults) throws IOException { int cnt = 0; // if there is no tableau ... final int succCnt = nextStates.size(); @@ -388,7 +484,8 @@ public class LiveCheck implements ILiveCheck { final int s = node0.succSize(); node0.setCheckState(checkStateResults); for (int sidx = 0; sidx < succCnt; sidx++) { - final long successor = nextFPs.elementAt(sidx); + final TLCState successorState = nextStates.next(); + final long successor = successorState.fingerPrint(); // Only add the transition if: // a) The successor itself has not been written to disk // TODO Why is an existing successor ignored? @@ -414,17 +511,21 @@ public class LiveCheck implements ILiveCheck { } else { cnt++; } + writer.writeState(s0, successorState, checkActionResults, sidx * alen, alen, ptr1 == -1); } + nextStates.resetNext(); // In simulation mode (see Simulator), it's possible that this // method is called multiple times for the same state (s0/fp0) // but with changing successors caused by the random successor // selection. If the successor is truly new (it has not been // added before), the GraphNode instance has to be updated - // (creating a new record on disk). However, when the the - // successor parameter happens to pass known successors, there - // is no point in adding the GraphNode again. It is assumed that - // it wouldn't invalidate the result, but it wastes disk space. - if (s < node0.succSize()) { + // (creating a new record on disk). However, when the successor + // parameter happens to pass known successors only, there is no + // point in adding the GraphNode again. It would just waste disk + // space. + // The amount of successors is either 0 (no new successor has + // been added) or used to be less than it is now. + if ((s == 0 && s == node0.succSize()) || s < node0.succSize()) { node0.realign(); // see node0.addTransition() hint // Add a node for the current state. It gets added *after* // all transitions have been added because addNode @@ -449,33 +550,34 @@ public class LiveCheck implements ILiveCheck { private final TableauDiskGraph dgraph; - public TableauLiveChecker(OrderOfSolution oos, int soln, IBucketStatistics statistics) + public TableauLiveChecker(OrderOfSolution oos, int soln, IBucketStatistics statistics, ILivenessStateWriter writer) throws IOException { - super(oos); + super(oos, writer); this.dgraph = new TableauDiskGraph(metadir, soln, statistics); } /* (non-Javadoc) * @see tlc2.tool.liveness.LiveChecker#addInitState(tlc2.tool.TLCState, long) */ - public void addInitState(final TLCState state, final long stateFP) { + public void addInitState(final ITool tool, final TLCState state, final long stateFP) { // (state, tnode) is a root node if tnode is an initial tableau // node and tnode is consistent with state. int initCnt = oos.getTableau().getInitCnt(); for (int i = 0; i < initCnt; i++) { TBGraphNode tnode = oos.getTableau().getNode(i); - if (tnode.isConsistent(state, myTool)) { - dgraph.addInitNode(stateFP, tnode.index); - dgraph.recordNode(stateFP, tnode.index); + if (tnode.isConsistent(state, tool)) { + dgraph.addInitNode(stateFP, tnode.getIndex()); + dgraph.recordNode(stateFP, tnode.getIndex()); + writer.writeState(state, tnode); } } } /* (non-Javadoc) - * @see tlc2.tool.liveness.LiveChecker#addNextState(tlc2.tool.TLCState, long, tlc2.tool.StateVec, tlc2.util.LongVec) + * @see tlc2.tool.liveness.ILiveChecker#addNextState(tlc2.tool.TLCState, long, tlc2.util.SetOfStates, tlc2.util.BitVector, boolean[]) */ - public void addNextState(final TLCState s0, final long fp0, final StateVec nextStates, final LongVec nextFPs, - final BitVector checkActionResults, final boolean[] checkStateResults) throws IOException { + public void addNextState(final ITool tool, final TLCState s0, final long fp0, + final SetOfStates nextStates, final BitVector checkActionResults, final boolean[] checkStateResults) throws IOException { int cnt = 0; final int succCnt = nextStates.size(); @@ -487,21 +589,21 @@ public class LiveCheck implements ILiveCheck { // trades speed for additional memory usage (BitVector). final TBGraph tableau = oos.getTableau(); final BitVector consistency = new BitVector(tableau.size() * succCnt); - @SuppressWarnings("unchecked") final Enumeration<TBGraphNode> elements = tableau.elements(); while(elements.hasMoreElements()) { final TBGraphNode tableauNode = elements.nextElement(); for (int sidx = 0; sidx < succCnt; sidx++) { - final TLCState s1 = nextStates.elementAt(sidx); - if(tableauNode.isConsistent(s1, myTool)) { + final TLCState s1 = nextStates.next(); + if(tableauNode.isConsistent(s1, tool)) { // BitVector is divided into a segment for each // tableau node. Inside each segment, addressing is done // via each state. Use identical addressing below // where the lookup is done (plus 1 accounts for // zero-based addressing). - consistency.set((tableauNode.index * succCnt) + sidx); + consistency.set((tableauNode.getIndex() * succCnt) + sidx); } } + nextStates.resetNext(); } // At this point only constant time operations are allowed => @@ -537,40 +639,37 @@ public class LiveCheck implements ILiveCheck { final int s = node0.succSize(); node0.setCheckState(checkStateResults); for (int sidx = 0; sidx < succCnt; sidx++) { - final TLCState s1 = nextStates.elementAt(sidx); - final long successor = nextFPs.elementAt(sidx); + final TLCState s1 = nextStates.next(); + final long successor = s1.fingerPrint(); final boolean isDone = dgraph.isDone(successor); for (int k = 0; k < tnode0.nextSize(); k++) { final TBGraphNode tnode1 = tnode0.nextAt(k); // Check if the successor is new - long ptr1 = dgraph.getPtr(successor, tnode1.index); - if (ptr1 == -1) { - if (consistency.get((tnode1.index * succCnt) + sidx)) { // see note on addressing above - node0.addTransition(successor, tnode1.index, checkStateResults.length, - alen, checkActionResults, sidx * alen, - allocationHint - cnt++); - // Record that we have seen <fp1, - // tnode1>. If fp1 is done, we have - // to compute the next states for <fp1, - // tnode1>. - dgraph.recordNode(successor, tnode1.index); + final long ptr1 = dgraph.getPtr(successor, tnode1.getIndex()); + if (consistency.get((tnode1.getIndex() * succCnt) + sidx) + && (ptr1 == -1 || !node0.transExists(successor, tnode1.getIndex()))) { + node0.addTransition(successor, tnode1.getIndex(), checkStateResults.length, alen, + checkActionResults, sidx * alen, allocationHint - cnt); + writer.writeState(s0, tnode0, s1, tnode1, checkActionResults, sidx * alen, alen, true); + // Record that we have seen <fp1, + // tnode1>. If fp1 is done, we have + // to compute the next states for <fp1, + // tnode1>. + if (ptr1 == -1) { + dgraph.recordNode(successor, tnode1.getIndex()); if (isDone) { - addNextState(s1, successor, tnode1, oos, dgraph); + addNextState(tool, s1, successor, tnode1, oos, dgraph); } } - } else if (!node0.transExists(successor, tnode1.index)) { - node0.addTransition(successor, tnode1.index, checkStateResults.length, - alen, checkActionResults, sidx * alen, allocationHint - - cnt++); - } else { - // Increment cnt even if addTrasition is not called. After all, - // the for loop has completed yet another iteration. - cnt++; } + // Increment cnt even if addTrasition is not called. After all, + // the for loop has completed yet another iteration. + cnt++; } } + nextStates.resetNext(); // See same case in LiveChecker#addNextState - if (s < node0.succSize()) { + if ((s == 0 && s == node0.succSize()) || s < node0.succSize()) { node0.realign(); // see node0.addTransition() hint dgraph.addNode(node0); } else { @@ -584,75 +683,81 @@ public class LiveCheck implements ILiveCheck { } /** - * This method takes care of the case that a new node (s, t) is generated - * after s has been done. In this case, we will have to compute the children - * of (s, t). Hopefully, this case does not occur very frequently. + * This method takes care of the case that a new node <<state, tableau>> + * in the (state X tableau) graph is generated after the state itself + * has been done. Done means that the state has been found during safety + * checking in the state graph already, except that the node <<state, + * tableau>> not been created. + * <p> + * In this case, we will have to generate the state graph successors of + * the state and create the permutation of all successors with all + * tableau nodes . + * <p> + * Hopefully, this case does not occur very frequently because it + * generates successor nodes. */ - private void addNextState(final TLCState s, final long fp, final TBGraphNode tnode, final OrderOfSolution oos, final TableauDiskGraph dgraph) + private void addNextState(final ITool tool, final TLCState s, final long fp, final TBGraphNode tnode, final OrderOfSolution oos, final TableauDiskGraph dgraph) throws IOException { - final boolean[] checkStateRes = oos.checkState(s); + final boolean[] checkStateRes = oos.checkState(tool, s); final int slen = checkStateRes.length; final int alen = oos.getCheckAction().length; - final GraphNode node = dgraph.getNode(fp, tnode.index); + final GraphNode node = dgraph.getNode(fp, tnode.getIndex()); final int numSucc = node.succSize(); node.setCheckState(checkStateRes); // see allocationHint of node.addTransition() invocations below int cnt = 0; - // Add edges induced by s -> s: - final BitVector checkActionResults = oos.checkAction(s, s, new BitVector(alen), 0); - + // Add edges induced by s -> s (self-loop) coming from the tableau + // graph: final int nextSize = tnode.nextSize(); + final BitVector checkActionResults = nextSize > 0 ? oos.checkAction(tool, s, s, new BitVector(alen), 0) : null; for (int i = 0; i < nextSize; i++) { final TBGraphNode tnode1 = tnode.nextAt(i); - final int tidx1 = tnode1.index; + final int tidx1 = tnode1.getIndex(); final long ptr1 = dgraph.getPtr(fp, tidx1); - if (ptr1 == -1) { - if (tnode1.isConsistent(s, myTool)) { - node.addTransition(fp, tidx1, slen, alen, checkActionResults, 0, (nextSize - cnt++)); - dgraph.recordNode(fp, tnode1.index); - addNextState(s, fp, tnode1, oos, dgraph); - } else { - cnt++; + if (tnode1.isConsistent(s, tool) && (ptr1 == -1 || !node.transExists(fp, tidx1))) { + node.addTransition(fp, tidx1, slen, alen, checkActionResults, 0, (nextSize - cnt)); + if (ptr1 == -1) { + dgraph.recordNode(fp, tnode1.getIndex()); + addNextState(tool, s, fp, tnode1, oos, dgraph); } - } else { - node.addTransition(fp, tidx1, slen, alen, checkActionResults, 0, (nextSize - cnt++)); } + cnt++; } - // Add edges induced by s -> s1: + // Add edges induced by s -> s1 (where s1 is a successor of s in the + // state graph): cnt = 0; + final Action[] actions = tool.getActions(); for (int i = 0; i < actions.length; i++) { - final StateVec nextStates = myTool.getNextStates(actions[i], s); + final StateVec nextStates = tool.getNextStates(actions[i], s); final int nextCnt = nextStates.size(); for (int j = 0; j < nextCnt; j++) { final TLCState s1 = nextStates.elementAt(j); - if (myTool.isInModel(s1) && myTool.isInActions(s, s1)) { + if (tool.isInModel(s1) && tool.isInActions(s, s1)) { final long fp1 = s1.fingerPrint(); - final BitVector checkActionRes = oos.checkAction(s, s1, new BitVector(alen), 0); + final BitVector checkActionRes = oos.checkAction(tool, s, s1, new BitVector(alen), 0); boolean isDone = dgraph.isDone(fp1); for (int k = 0; k < tnode.nextSize(); k++) { final TBGraphNode tnode1 = tnode.nextAt(k); - final int tidx1 = tnode1.index; + final int tidx1 = tnode1.getIndex(); long ptr1 = dgraph.getPtr(fp1, tidx1); final int total = actions.length * nextCnt * tnode.nextSize(); - if (ptr1 == -1) { - if (tnode1.isConsistent(s1, myTool)) { - node.addTransition(fp1, tidx1, slen, alen, checkActionRes, 0, (total - cnt++)); - // Record that we have seen <fp1, tnode1>. If - // fp1 is done, we have to compute the next - // states for <fp1, tnode1>. + if (tnode1.isConsistent(s1, tool) && (ptr1 == -1 || !node.transExists(fp1, tidx1))) { + node.addTransition(fp1, tidx1, slen, alen, checkActionRes, 0, (total - cnt)); + writer.writeState(s, tnode, s1, tnode1, checkActionRes, 0, alen, false, Visualization.DOTTED); + // Record that we have seen <fp1, tnode1>. If + // fp1 is done, we have to compute the next + // states for <fp1, tnode1>. + if (ptr1 == -1) { dgraph.recordNode(fp1, tidx1); if (isDone) { - addNextState(s1, fp1, tnode1, oos, dgraph); + addNextState(tool, s1, fp1, tnode1, oos, dgraph); } } - } else if (!node.transExists(fp1, tidx1)) { - node.addTransition(fp1, tidx1, slen, alen, checkActionRes, 0, (total - cnt++)); - } else { - cnt++; } + cnt++; } } else { cnt++; @@ -672,56 +777,4 @@ public class LiveCheck implements ILiveCheck { return dgraph; } } - - // Intended to be used in unit tests only!!! This is not part of the API!!! - static class TestHelper { - - /* - * - EWD840 (with tableau) spec with N = 11 and maxSetSize = 9.000.000 => 12GB nodes file, 46.141.438 distinct states - * - EWD840 (with tableau) spec with N = 12 and maxSetSize = 9.000.000 => 56GB, 201.334.782 dist. states - */ - - // The Eclipse Launch configuration has to set the working directory - // (Arguments tab) to the parent directory of the folder containing the - // nodes_* and ptrs_* files. The parent folder has to contain the spec - // and config file both named "MC". - // metadir is the the name of the folder with the nodes_* and ptrs_* - // relative to the parent directory. The directory does *not* need to contain the - // backing file of the fingerprint set or the state queue files. - public static ILiveCheck recreateFromDisk(final String path) throws Exception { - // Don't know with which Polynomial the FP64 has been initialized, but - // the default is 0. - FP64.Init(0); - - // Most models won't need this, but let's increase this anyway. - TLCGlobals.setBound = 9000000; - - // Re-create the tool to do the init states down below (LiveCheck#init - // doesn't really need tool). - final Tool tool = new Tool("", "MC", "MC", new SimpleFilenameToStream()); - tool.init(true, null); - - // Initialize tool's actions explicitly. LiveCheck#printTrace is - // going to access the actions and fails with a NPE unless - // initialized. - tool.getActions(); - - final ILiveCheck liveCheck = new LiveCheck(tool, null, path, new DummyBucketStatistics()); - - // Calling recover requires a .chkpt file to be able to re-create the - // internal data structures to continue with model checking. However, we - // only want to execute liveness checks on the current static disk - // graph. Thus, we don't need the .chkpt file. - //recover(); - - // After recovery, one has to redo the init states - final StateVec initStates = tool.getInitStates(); - for (int i = 0; i < initStates.size(); i++) { - TLCState state = initStates.elementAt(i); - liveCheck.addInitState(state, state.fingerPrint()); - } - - return liveCheck; - } - } } diff --git a/tlatools/src/tlc2/tool/liveness/LiveCheck1.java b/tlatools/src/tlc2/tool/liveness/LiveCheck1.java index d09308dc91ab06ba25f5c6faa7d55649b96854d0..c4f0811990a1368688b0c8db59f5044d7a3edcb6 100644 --- a/tlatools/src/tlc2/tool/liveness/LiveCheck1.java +++ b/tlatools/src/tlc2/tool/liveness/LiveCheck1.java @@ -7,21 +7,20 @@ package tlc2.tool.liveness; import java.io.IOException; -import tlc2.TLCGlobals; import tlc2.output.EC; import tlc2.output.MP; import tlc2.output.StatePrinter; import tlc2.tool.Action; import tlc2.tool.EvalException; +import tlc2.tool.ITool; import tlc2.tool.StateVec; import tlc2.tool.TLCState; import tlc2.tool.TLCStateInfo; -import tlc2.tool.Tool; import tlc2.util.FP64; import tlc2.util.LongObjTable; -import tlc2.util.LongVec; import tlc2.util.MemObjectStack; import tlc2.util.ObjectStack; +import tlc2.util.SetOfStates; import tlc2.util.Vect; import tlc2.util.statistics.DummyBucketStatistics; import tlc2.util.statistics.IBucketStatistics; @@ -30,7 +29,7 @@ public class LiveCheck1 implements ILiveCheck { /** * Implementation of liveness checking based on MP book. */ - private Tool myTool; + private ITool myTool; private String metadir = ""; private Action[] actions; private OrderOfSolution[] solutions; @@ -72,13 +71,13 @@ public class LiveCheck1 implements ILiveCheck { */ private BEGraphNode initNode = null; - public LiveCheck1(Tool tool) { + public LiveCheck1(ITool tool) { myTool = tool; solutions = Liveness.processLiveness(myTool); bgraphs = new BEGraph[0]; } - public void init(Tool tool, Action[] acts, String mdir) { + public void init(ITool tool, Action[] acts, String mdir) { myTool = tool; metadir = mdir; actions = acts; @@ -115,14 +114,14 @@ public class LiveCheck1 implements ILiveCheck { * state trace (a sequence of states). Assume trace.length > 0. It returns * the set of initial states. */ - Vect constructBEGraph(OrderOfSolution os) { - Vect initNodes = new Vect(1); + Vect<BEGraphNode> constructBEGraph(final ITool tool, OrderOfSolution os) { + Vect<BEGraphNode> initNodes = new Vect<>(1); int slen = os.getCheckState().length; int alen = os.getCheckAction().length; TLCState srcState = stateTrace.elementAt(0); // the initial state long srcFP = srcState.fingerPrint(); - boolean[] checkStateRes = os.checkState(srcState); - boolean[] checkActionRes = os.checkAction(srcState, srcState); + boolean[] checkStateRes = os.checkState(tool, srcState); + boolean[] checkActionRes = os.checkAction(tool, srcState, srcState); if (!os.hasTableau()) { // If there is no tableau, construct begraph with trace. LongObjTable allNodes = new LongObjTable(127); @@ -137,12 +136,12 @@ public class LiveCheck1 implements ILiveCheck { BEGraphNode destNode = (BEGraphNode) allNodes.get(destFP); if (destNode == null) { destNode = new BEGraphNode(destFP); - destNode.setCheckState(os.checkState(srcState)); - destNode.addTransition(destNode, slen, alen, os.checkAction(destState, destState)); - srcNode.addTransition(destNode, slen, alen, os.checkAction(srcState, destState)); + destNode.setCheckState(os.checkState(tool, srcState)); + destNode.addTransition(destNode, slen, alen, os.checkAction(tool, destState, destState)); + srcNode.addTransition(destNode, slen, alen, os.checkAction(tool, srcState, destState)); allNodes.put(destFP, destNode); } else if (!srcNode.transExists(destNode)) { - srcNode.addTransition(destNode, slen, alen, os.checkAction(srcState, destState)); + srcNode.addTransition(destNode, slen, alen, os.checkAction(tool, srcState, destState)); } srcNode = destNode; srcState = destState; @@ -150,16 +149,16 @@ public class LiveCheck1 implements ILiveCheck { } else { // If there is tableau, construct begraph of (tableau X trace). LongObjTable allNodes = new LongObjTable(255); - Vect srcNodes = new Vect(); + Vect<BEGraphNode> srcNodes = new Vect<>(); int initCnt = os.getTableau().getInitCnt(); for (int i = 0; i < initCnt; i++) { TBGraphNode tnode = os.getTableau().getNode(i); if (tnode.isConsistent(srcState, myTool)) { - BEGraphNode destNode = new BTGraphNode(srcFP, tnode.index); + BEGraphNode destNode = new BTGraphNode(srcFP, tnode.getIndex()); destNode.setCheckState(checkStateRes); initNodes.addElement(destNode); srcNodes.addElement(destNode); - allNodes.put(FP64.Extend(srcFP, tnode.index), destNode); + allNodes.put(FP64.Extend(srcFP, tnode.getIndex()), destNode); } } for (int i = 0; i < srcNodes.size(); i++) { @@ -167,7 +166,7 @@ public class LiveCheck1 implements ILiveCheck { TBGraphNode tnode = srcNode.getTNode(os.getTableau()); for (int j = 0; j < tnode.nextSize(); j++) { TBGraphNode tnode1 = tnode.nextAt(j); - long destFP = FP64.Extend(srcFP, tnode1.index); + long destFP = FP64.Extend(srcFP, tnode1.getIndex()); BEGraphNode destNode = (BEGraphNode) allNodes.get(destFP); if (destNode != null) { srcNode.addTransition(destNode, slen, alen, checkActionRes); @@ -175,21 +174,21 @@ public class LiveCheck1 implements ILiveCheck { } } for (int i = 1; i < stateTrace.size(); i++) { - Vect destNodes = new Vect(); + Vect<BEGraphNode> destNodes = new Vect<>(); TLCState destState = stateTrace.elementAt(i); long destStateFP = destState.fingerPrint(); - checkStateRes = os.checkState(destState); - checkActionRes = os.checkAction(srcState, destState); + checkStateRes = os.checkState(myTool, destState); + checkActionRes = os.checkAction(tool, srcState, destState); for (int j = 0; j < srcNodes.size(); j++) { BEGraphNode srcNode = (BEGraphNode) srcNodes.elementAt(j); TBGraphNode tnode = srcNode.getTNode(os.getTableau()); for (int k = 0; k < tnode.nextSize(); k++) { TBGraphNode tnode1 = tnode.nextAt(k); - long destFP = FP64.Extend(destStateFP, tnode1.index); + long destFP = FP64.Extend(destStateFP, tnode1.getIndex()); BEGraphNode destNode = (BEGraphNode) allNodes.get(destFP); if (destNode == null) { if (tnode1.isConsistent(destState, myTool)) { - destNode = new BTGraphNode(destStateFP, tnode1.index); + destNode = new BTGraphNode(destStateFP, tnode1.getIndex()); destNode.setCheckState(checkStateRes); srcNode.addTransition(destNode, slen, alen, checkActionRes); destNodes.addElement(destNode); @@ -200,17 +199,17 @@ public class LiveCheck1 implements ILiveCheck { } } } - checkActionRes = os.checkAction(destState, destState); + checkActionRes = os.checkAction(tool, destState, destState); for (int j = 0; j < destNodes.size(); j++) { BEGraphNode srcNode = (BEGraphNode) destNodes.elementAt(j); TBGraphNode tnode = srcNode.getTNode(os.getTableau()); for (int k = 0; k < tnode.nextSize(); k++) { TBGraphNode tnode1 = tnode.nextAt(k); - long destFP = FP64.Extend(destStateFP, tnode1.index); + long destFP = FP64.Extend(destStateFP, tnode1.getIndex()); BEGraphNode destNode = (BEGraphNode) allNodes.get(destFP); if (destNode == null) { if (tnode1.isConsistent(destState, myTool)) { - destNode = new BTGraphNode(destStateFP, tnode1.index); + destNode = new BTGraphNode(destStateFP, tnode1.getIndex()); destNode.setCheckState(checkStateRes); srcNode.addTransition(destNode, slen, alen, checkActionRes); destNodes.addElement(destNode); @@ -232,14 +231,14 @@ public class LiveCheck1 implements ILiveCheck { * This method adds new nodes into the behavior graph when a new initial * state is generated. */ - public void addInitState(TLCState state, long stateFP) { + public void addInitState(ITool tool, TLCState state, long stateFP) { for (int soln = 0; soln < solutions.length; soln++) { OrderOfSolution os = solutions[soln]; BEGraph bgraph = bgraphs[soln]; int slen = os.getCheckState().length; int alen = os.getCheckAction().length; - boolean[] checkStateRes = os.checkState(state); - boolean[] checkActionRes = os.checkAction(state, state); + boolean[] checkStateRes = os.checkState(tool, state); + boolean[] checkActionRes = os.checkAction(tool, state, state); // Adding nodes and transitions: if (!os.hasTableau()) { // if there is no tableau ... @@ -255,7 +254,7 @@ public class LiveCheck1 implements ILiveCheck { for (int i = 0; i < initCnt; i++) { TBGraphNode tnode = os.getTableau().getNode(i); if (tnode.isConsistent(state, myTool)) { - BTGraphNode destNode = new BTGraphNode(stateFP, tnode.index); + BTGraphNode destNode = new BTGraphNode(stateFP, tnode.getIndex()); destNode.setCheckState(checkStateRes); bgraph.addInitNode(destNode); bgraph.allNodes.putBTNode(destNode); @@ -270,14 +269,15 @@ public class LiveCheck1 implements ILiveCheck { } /* (non-Javadoc) - * @see tlc2.tool.liveness.ILiveCheck#addNextState(tlc2.tool.TLCState, long, tlc2.tool.StateVec, tlc2.util.LongVec) + * @see tlc2.tool.liveness.ILiveCheck#addNextState(tlc2.tool.TLCState, long, tlc2.util.SetOfStates) */ - public void addNextState(TLCState s0, long fp0, StateVec nextStates, LongVec nextFPs) throws IOException { + public void addNextState(final ITool tool, TLCState s0, long fp0, SetOfStates nextStates) throws IOException { for (int i = 0; i < nextStates.size(); i++) { - final TLCState s2 = nextStates.elementAt(i); - final long fp2 = nextFPs.elementAt(i); - addNextState(s0, fp0, s2, fp2); + final TLCState s2 = nextStates.next(); + final long fp2 = s2.fingerPrint(); + addNextState(tool, s0, fp0, s2, fp2); } + nextStates.resetNext(); } /** @@ -285,7 +285,7 @@ public class LiveCheck1 implements ILiveCheck { * generated. The argument s2 is the new state. The argument s1 is parent * state of s2. */ - public synchronized void addNextState(TLCState s1, long fp1, TLCState s2, long fp2) { + public synchronized void addNextState(final ITool tool, TLCState s1, long fp1, TLCState s2, long fp2) { for (int soln = 0; soln < solutions.length; soln++) { OrderOfSolution os = solutions[soln]; BEGraph bgraph = bgraphs[soln]; @@ -298,12 +298,12 @@ public class LiveCheck1 implements ILiveCheck { BEGraphNode node2 = bgraph.allNodes.getBENode(fp2); if (node2 == null) { node2 = new BEGraphNode(fp2); - node2.setCheckState(os.checkState(s2)); - node1.addTransition(node2, slen, alen, os.checkAction(s1, s2)); - node2.addTransition(node2, slen, alen, os.checkAction(s2, s2)); + node2.setCheckState(os.checkState(tool, s2)); + node1.addTransition(node2, slen, alen, os.checkAction(tool, s1, s2)); + node2.addTransition(node2, slen, alen, os.checkAction(tool, s2, s2)); bgraph.allNodes.putBENode(node2); } else if (!node1.transExists(node2)) { - boolean[] checkActionRes = os.checkAction(s1, s2); + boolean[] checkActionRes = os.checkAction(tool, s1, s2); node1.addTransition(node2, slen, alen, checkActionRes); } } else { @@ -315,32 +315,32 @@ public class LiveCheck1 implements ILiveCheck { } boolean[] checkStateRes = null; // Add edges induced by s1 --> s2: - boolean[] checkActionRes = os.checkAction(s1, s2); + boolean[] checkActionRes = os.checkAction(tool, s1, s2); boolean[] checkActionRes1 = null; for (int i = 0; i < srcNodes.length; i++) { BTGraphNode srcNode = srcNodes[i]; TBGraphNode tnode = os.getTableau().getNode(srcNode.getIndex()); for (int j = 0; j < tnode.nextSize(); j++) { TBGraphNode tnode1 = tnode.nextAt(j); - BTGraphNode destNode = bgraph.allNodes.getBTNode(fp2, tnode1.index); + BTGraphNode destNode = bgraph.allNodes.getBTNode(fp2, tnode1.getIndex()); if (destNode == null) { if (tnode1.isConsistent(s2, myTool)) { - destNode = new BTGraphNode(fp2, tnode1.index); + destNode = new BTGraphNode(fp2, tnode1.getIndex()); if (checkStateRes == null) { - checkStateRes = os.checkState(s2); + checkStateRes = os.checkState(tool, s2); } destNode.setCheckState(checkStateRes); srcNode.addTransition(destNode, slen, alen, checkActionRes); int idx = bgraph.allNodes.putBTNode(destNode); // add edges induced by s2 --> s2: if (checkActionRes1 == null) { - checkActionRes1 = os.checkAction(s2, s2); + checkActionRes1 = os.checkAction(tool, s2, s2); } addNodesForStut(s2, fp2, destNode, checkStateRes, checkActionRes1, os, bgraph); // if s2 is done, we have to do something for // destNode: if (bgraph.allNodes.isDone(idx)) { - addNextState(s2, fp2, destNode, os, bgraph); + addNextState(tool, s2, fp2, destNode, os, bgraph); } } } else if (!srcNode.transExists(destNode)) { @@ -363,10 +363,10 @@ public class LiveCheck1 implements ILiveCheck { TBGraphNode tnode = node.getTNode(os.getTableau()); for (int i = 0; i < tnode.nextSize(); i++) { TBGraphNode tnode1 = tnode.nextAt(i); - BTGraphNode destNode = bgraph.allNodes.getBTNode(fp, tnode1.index); + BTGraphNode destNode = bgraph.allNodes.getBTNode(fp, tnode1.getIndex()); if (destNode == null) { if (tnode1.isConsistent(state, myTool)) { - destNode = new BTGraphNode(fp, tnode1.index); + destNode = new BTGraphNode(fp, tnode1.getIndex()); destNode.setCheckState(checkState); node.addTransition(destNode, slen, alen, checkAction); bgraph.allNodes.putBTNode(destNode); @@ -383,7 +383,7 @@ public class LiveCheck1 implements ILiveCheck { * after s has been done. So, we still have to compute the children of (s, * t). Hopefully, this case will not occur very frequently. */ - private void addNextState(TLCState s, long fp, BTGraphNode node, OrderOfSolution os, BEGraph bgraph) { + private void addNextState(final ITool tool, TLCState s, long fp, BTGraphNode node, OrderOfSolution os, BEGraph bgraph) { TBGraphNode tnode = node.getTNode(os.getTableau()); int slen = os.getCheckState().length; int alen = os.getCheckAction().length; @@ -399,30 +399,30 @@ public class LiveCheck1 implements ILiveCheck { boolean[] checkActionRes1 = null; for (int k = 0; k < tnode.nextSize(); k++) { TBGraphNode tnode1 = tnode.nextAt(k); - BTGraphNode destNode = bgraph.allNodes.getBTNode(fp1, tnode1.index); + BTGraphNode destNode = bgraph.allNodes.getBTNode(fp1, tnode1.getIndex()); if (destNode == null) { if (tnode1.isConsistent(s1, myTool)) { - destNode = new BTGraphNode(fp1, tnode1.index); + destNode = new BTGraphNode(fp1, tnode1.getIndex()); if (checkStateRes == null) { - checkStateRes = os.checkState(s1); + checkStateRes = os.checkState(tool, s1); } if (checkActionRes == null) { - checkActionRes = os.checkAction(s, s1); + checkActionRes = os.checkAction(tool, s, s1); } destNode.setCheckState(checkStateRes); node.addTransition(destNode, slen, alen, checkActionRes); if (checkActionRes1 == null) { - checkActionRes1 = os.checkAction(s1, s1); + checkActionRes1 = os.checkAction(tool, s1, s1); } addNodesForStut(s1, fp1, destNode, checkStateRes, checkActionRes1, os, bgraph); int idx = bgraph.allNodes.putBTNode(destNode); if (bgraph.allNodes.isDone(idx)) { - addNextState(s1, fp1, destNode, os, bgraph); + addNextState(tool, s1, fp1, destNode, os, bgraph); } } } else if (!node.transExists(destNode)) { if (checkActionRes == null) { - checkActionRes = os.checkAction(s, s1); + checkActionRes = os.checkAction(tool, s, s1); } node.addTransition(destNode, slen, alen, checkActionRes); } @@ -436,16 +436,24 @@ public class LiveCheck1 implements ILiveCheck { bgraphs[soln].allNodes.setDone(fp); } } + + /* (non-Javadoc) + * @see tlc2.tool.liveness.ILiveCheck#doLiveCheck() + */ + public boolean doLiveCheck() { + return true; + } /** * Checks if the partial behavior graph constructed up to now contains any * "bad" cycle. A "bad" cycle gives rise to a violation of liveness * property. */ - public synchronized boolean check(boolean forceCheck) { + @Override + public synchronized int check(ITool tool, boolean forceCheck) { int slen = solutions.length; if (slen == 0) { - return true; + return EC.NO_ERROR; } for (int soln = 0; soln < slen; soln++) { @@ -469,18 +477,18 @@ public class LiveCheck1 implements ILiveCheck { } // Previous for loop with throw LivenessException anyway, thus no harm // returning true regardless. - return true; + return EC.NO_ERROR; } /** * Checks if the behavior graph constructed from a state trace contains any * "bad" cycle. */ - public void checkTrace(final StateVec trace) { + public synchronized void checkTrace(ITool tool, final StateVec trace) { stateTrace = trace; for (int soln = 0; soln < solutions.length; soln++) { OrderOfSolution os = solutions[soln]; - Vect initNodes = constructBEGraph(os); + Vect<BEGraphNode> initNodes = constructBEGraph(tool, os); // Liveness.printTBGraph(os.tableau); // ToolIO.err.println(os.behavior.toString()); @@ -645,13 +653,14 @@ public class LiveCheck1 implements ILiveCheck { } // Print the prefix: - TLCState lastState = null; + TLCState cycleState = null; for (int i = 0; i < stateNum; i++) { - StatePrinter.printState(states[i], lastState, i + 1); - lastState = states[i].state; + StatePrinter.printState(states[i], cycleState, i + 1); + cycleState = states[i].state; } // Print the cycle: + TLCState lastState = cycleState; int cyclePos = stateNum; long[] fps = new long[cycleStack.size()]; int idx = fps.length; @@ -671,15 +680,14 @@ public class LiveCheck1 implements ILiveCheck { } } if (node.stateFP == lastState.fingerPrint()) { - StatePrinter.printStutteringState(++stateNum); + StatePrinter.printStutteringState(stateNum); } else { - if (TLCGlobals.tool) { - // The parser in Tool mode is picky and does not detect the Back to State unless it's printed via MP.printState. - // See LiveWorker#printTrace(..) - MP.printState(EC.TLC_BACK_TO_STATE, new String[] { "" + cyclePos }, (TLCState) null, -1); - } else { - MP.printMessage(EC.TLC_BACK_TO_STATE, "" + cyclePos); - } + sinfo = myTool.getState(cycleState.fingerPrint(), sinfo); + // The print stmts below claim there is a cycle, thus assert that + // there is indeed one. Index-based lookup into states array is + // reduced by one because cyclePos is human-readable. + assert cycleState.equals(sinfo.state); + StatePrinter.printBackToState(sinfo, cyclePos); } } @@ -766,7 +774,7 @@ public class LiveCheck1 implements ILiveCheck { // April // 2012 } - throw new LiveException("LiveCheck: Found error trace."); + throw new LiveException(EC.TLC_TEMPORAL_PROPERTY_VIOLATED, "LiveCheck: Found error trace."); } /** @@ -801,7 +809,7 @@ public class LiveCheck1 implements ILiveCheck { /* This method checks whether a scc satisfies currentPEM. */ void checkComponent(BEGraphNode node) { - Vect nodes = extractComponent(node); + Vect<BEGraphNode> nodes = extractComponent(node); if (nodes != null) { PossibleErrorModel[] pems = currentOOS.getPems(); for (int i = 0; i < pems.length; i++) { @@ -830,13 +838,13 @@ public class LiveCheck1 implements ILiveCheck { * trivial one. It also assigns a new number to all the nodes in the * component. */ - Vect extractComponent(BEGraphNode node) { + Vect<BEGraphNode> extractComponent(BEGraphNode node) { BEGraphNode node1 = (BEGraphNode) comStack.pop(); if (node == node1 && !node.transExists(node)) { node.setNumber(MAX_FIRST); return null; } - Vect nodes = new Vect(); + Vect<BEGraphNode> nodes = new Vect<>(); numFirstCom = secondNum++; numSecondCom = thirdNum; node1.setNumber(numFirstCom); @@ -906,11 +914,9 @@ public class LiveCheck1 implements ILiveCheck { return false; } - /* (non-Javadoc) - * @see tlc2.tool.liveness.ILiveCheck#finalCheck() - */ - public boolean finalCheck() throws Exception { - return check(true); + @Override + public int finalCheck(ITool tool) throws Exception { + return check(tool, true); } /* (non-Javadoc) @@ -923,7 +929,7 @@ public class LiveCheck1 implements ILiveCheck { /* (non-Javadoc) * @see tlc2.tool.liveness.ILiveCheck#getTool() */ - public Tool getTool() { + public ITool getTool() { return myTool; } diff --git a/tlatools/src/tlc2/tool/liveness/LiveException.java b/tlatools/src/tlc2/tool/liveness/LiveException.java index 83f3ffd45b2b2827e7e9cca148e7cb87cd5303c4..783993063169ea03219367e4f9837ed68151ca83 100644 --- a/tlatools/src/tlc2/tool/liveness/LiveException.java +++ b/tlatools/src/tlc2/tool/liveness/LiveException.java @@ -7,11 +7,15 @@ package tlc2.tool.liveness; public class LiveException extends RuntimeException { - public LiveException() { + public final int errorCode; + + public LiveException(int errorCode) { super(); + this.errorCode = errorCode; } - public LiveException(String msg) { + public LiveException(int errorCode, String msg) { super(msg); + this.errorCode = errorCode; } } diff --git a/tlatools/src/tlc2/tool/liveness/LiveExprNode.java b/tlatools/src/tlc2/tool/liveness/LiveExprNode.java index 9e111bf648ccc7512db3d69a8468d31b43634a42..5322e39bf25c9b6375dc8a79100a10d2952d576e 100644 --- a/tlatools/src/tlc2/tool/liveness/LiveExprNode.java +++ b/tlatools/src/tlc2/tool/liveness/LiveExprNode.java @@ -5,8 +5,8 @@ package tlc2.tool.liveness; +import tlc2.tool.ITool; import tlc2.tool.TLCState; -import tlc2.tool.Tool; /** * LNConj - a conjunction. (contains list of conjuncts) LNDisj - a disjunction. @@ -42,7 +42,13 @@ public abstract class LiveExprNode { /* Returns true iff the expression contains action. */ public abstract boolean containAction(); - public abstract boolean eval(Tool tool, TLCState s1, TLCState s2); + /** + * @param s1 First state + * @param s2 Second (successor) state + * @param tool (Technical Tool implementation) + * @return true iff both states are consistent with this {@link LiveExprNode}. + */ + public abstract boolean eval(ITool tool, TLCState s1, TLCState s2); /* The string representation. */ public final String toString() { diff --git a/tlatools/src/tlc2/tool/liveness/LiveWorker.java b/tlatools/src/tlc2/tool/liveness/LiveWorker.java index 00dffb898622c0ae567a23fb182cea525eb68901..21f23c196f76b94ad51f4a501f4cdc6aacb59a37 100644 --- a/tlatools/src/tlc2/tool/liveness/LiveWorker.java +++ b/tlatools/src/tlc2/tool/liveness/LiveWorker.java @@ -5,59 +5,108 @@ package tlc2.tool.liveness; import java.io.IOException; +import java.util.ArrayList; +import java.util.List; import java.util.concurrent.BlockingQueue; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; import tlc2.TLCGlobals; import tlc2.output.EC; import tlc2.output.MP; import tlc2.output.StatePrinter; import tlc2.tool.EvalException; -import tlc2.tool.TLCState; +import tlc2.tool.ITool; import tlc2.tool.TLCStateInfo; -import tlc2.util.IdThread; +import tlc2.util.IntStack; import tlc2.util.LongVec; import tlc2.util.MemIntQueue; import tlc2.util.MemIntStack; +import tlc2.util.SynchronousDiskIntStack; import tlc2.util.statistics.BucketStatistics; import tlc2.util.statistics.IBucketStatistics; -public class LiveWorker extends IdThread { +/** + * {@link LiveWorker} is doing the heavy lifting of liveness checking: + * <ul> + * <li>Searches for strongly connected components (SCC) a.k.a. cycles in the + * liveness/behavior graph.</li> + * <li>Checks each SCC if it violates the liveness properties.</li> + * <li>In case of a violation, reconstructs and prints the error trace.</li> + * </ul> + */ +public class LiveWorker implements Callable<Boolean> { + + /** + * A marker that is pushed onto the dfsStack during SCC depth-first-search + * to marker an explored nodes on the stack. + * <p> + * A node with a marker is on the comStack. + */ + private static final long SCC_MARKER = -42L; public static final IBucketStatistics STATS = new BucketStatistics("Histogram SCC sizes", LiveWorker.class .getPackage().getName(), "StronglyConnectedComponent sizes"); private static int errFoundByThread = -1; - private static Object workerLock = new Object(); + private static final Object workerLock = new Object(); private OrderOfSolution oos = null; private AbstractDiskGraph dg = null; private PossibleErrorModel pem = null; private final ILiveCheck liveCheck; private final BlockingQueue<ILiveChecker> queue; + private final boolean isFinalCheck; + /** + * Total number of LiveWorkers simultaneously checking liveness. + */ + private final int numWorkers; + + private final ITool tool; - public LiveWorker(int id, final ILiveCheck liveCheck, final BlockingQueue<ILiveChecker> queue) { - super(id); + private final int id; + + public LiveWorker(final ITool tool, int id, int numWorkers, final ILiveCheck liveCheck, final BlockingQueue<ILiveChecker> queue, final boolean finalCheck) { + this.id = id; + this.tool = tool; + this.numWorkers = numWorkers; this.liveCheck = liveCheck; this.queue = queue; + this.isFinalCheck = finalCheck; } - // Returns true iff an error has already found. - public static boolean hasErrFound() { + /** + * Returns true iff an error has already been found. + */ + private static boolean hasErrFound() { synchronized (workerLock) { return (errFoundByThread != -1); } } + // True iff this LiveWorker found a liveness violation.zs + private static boolean hasErrFound(final int id) { + synchronized (workerLock) { + return (errFoundByThread == id); + } + } + /** - * Returns true iff either an error has not found or the error is found by - * this thread. + * Returns true iff either an error has not been found or the error is found + * by this thread. + * <p> + * This is used so that only one of the threads which have found an error + * prints it. */ private/* static synchronized */boolean setErrFound() { synchronized (workerLock) { if (errFoundByThread == -1) { - errFoundByThread = this.myGetId(); // GetId(); + errFoundByThread = this.id; // GetId(); return true; - } else if (errFoundByThread == this.myGetId()) { // (* GetId()) { + } else if (errFoundByThread == this.id) { // (* GetId()) { return true; } return false; @@ -69,34 +118,83 @@ public class LiveWorker extends IdThread { * http://en.wikipedia.org/wiki/Strongly_connected_component), and checks * each of them to see if it contains a counterexample. * <p> - * It seems to be using the Path-based strong component algorithm - * (http://en.wikipedia.org/wiki/Path-based_strong_component_algorithm). - * This assumption however is incorrect! The second stack is not related to - * the SCC algorithm. Thus, it is Tarjan's SCC algorithm at work - * (http://en.wikipedia.org/wiki/Tarjan's_strongly_connected_components_algorithm). + * It is Tarjan's SCC algorithm at work: + * <p> + * The notable differences to the text book algorithm are: + * <ul> + * <li>It is implemented iteratively (probably to prevent StackOverflows) + * </li> + * <li>The lowLink number gets pushed onto the DFS stack</li> + * <li>If a node is on the DFS stack is determined by checking if it has a + * link number assigned</li> + * <li>Once an SCC has been found, it is checked immediately for liveness + * violations (there is no point it searching all SCCs if the first SCC + * found already violates liveness)</li> + * <li>Not all states are added to the set of unexplored nodes initially, + * but only the model checking init states (all successors are known to be + * reachable from the init states).</li> + * <li>Liveness is checked periodically during model checking and thus + * checkSccs runs on a partial graph. Thus some nodes are marked undone. + * Those nodes are skipped by the SCC search.</li> + * </ul> + * @throws ExecutionException + * @throws InterruptedException + * + * @see http://en.wikipedia.org/wiki/Tarjan' + * s_strongly_connected_components_algorithm + * @see http://dx.doi.org/10.1137%2F0201010 + * */ - private final void checkSccs() throws IOException { + private final void checkSccs(final ITool tool) throws IOException, InterruptedException, ExecutionException { // Initialize this.dg: this.dg.makeNodePtrTbl(); // Initialize nodeQueue with initial states. The initial states stored // separately in the DiskGraph are resolved to their pointer location // in the on-disk part of the DiskGraph. - // The pointer location is obviously used to: + // The pointer location generally is obviously used to: // * Speed up disk lookups in the RandomAccessFile(s) backing up the DiskGraph - // * Seems to serve as a marker for when a node during SCCs is fully explored //VERIFY assumption! + // * Is replaced by the SCC link number the moment the node's successors + // are explored during DFS search. At this point the ptr location isn't + // needed anymore. The successors have been resolved. // - final MemIntQueue nodeQueue = new MemIntQueue(liveCheck.getMetaDir(), "root"); + // From each node in nodeQueue the SCC search is started down below, + // which can subsequently add additional nodes into nodeQueue. + // + // Contrary to plain Tarjan, not all vertices are added to the + // nodeQueue of unexplored states, but only the initial states. Since we + // know that all non-initial states are reachable from the set of + // initial states, this is sufficient to start with. final LongVec initNodes = this.dg.getInitNodes(); final int numOfInits = initNodes.size(); + // Allocate space for all initial states, assuming the majority of + // initial nodes will be done. Multiplied by 5 because of + // <<long, int, long>> per "record. + final MemIntQueue nodeQueue = new MemIntQueue(liveCheck.getMetaDir(), "root", (numOfInits / 2) * 5); for (int j = 0; j < numOfInits; j += 2) { final long state = initNodes.elementAt(j); final int tidx = (int) initNodes.elementAt(j + 1); final long ptr = this.dg.getLink(state, tidx); - if (ptr >= 0) { //QUESTION When is a ptr < 0? + // Check if the node <<state, tidx>> s is done. A node s is undone + // if it is an initial state which hasn't been explored yet. This is + // the case if s has been added via LiveChecker#addInitState but not + // yet via LiveChecker#addNextState. LiveChecker#addNextState fully + // explores the given init state s because it has access to s' + // successors. + if (ptr >= 0) { + // Make sure none of the init states has already been assigned a + // link number. That would indicate a bug in makeNodePtrTbl + // which is supposed to reset all link numbers to file ptrs. + assert DiskGraph.isFilePointer(ptr); nodeQueue.enqueueLong(state); nodeQueue.enqueueInt(tidx); nodeQueue.enqueueLong(ptr); + } else { + // If this is the final check on the complete graph, no node is + // allowed to be undone. If it's not the final check, ptr has to + // be UNDONE (a non-UNDONE negative pointer is probably a bug). + // isFinalCheck => ptr # UNDONE + assert !isFinalCheck || ptr != TableauNodePtrTable.UNDONE; } } @@ -104,85 +202,147 @@ public class LiveWorker extends IdThread { final int slen = this.oos.getCheckState().length; final int alen = this.oos.getCheckAction().length; - // Tarjan's stack - final MemIntStack dfsStack = new MemIntStack(liveCheck.getMetaDir(), "dfs"); - // comStack is only being added to during the deep first search. It is passed - // to the checkComponent method while in DFS though. - // - // An Eclipse detailed formatter: - // StringBuffer buf = new StringBuffer(this.size); - // for (int i = 1; i < this.size; i+=5) { - // buf.append("state: "); - // buf.append(peakLong(size - i)); - // buf.append("\n"); - // buf.append(" Tableaux idx: "); - // buf.append(peakInt(size - i - 2)); - // buf.append("\n"); - // buf.append(" ptr loc: "); - // buf.append(peakLong(size - i - 3)); - // buf.append("\n"); - // } - // return buf.toString(); - // - final MemIntStack comStack = new MemIntStack(liveCheck.getMetaDir(), "com"); + // Synchronize all LiveWorker instances to consistently read free + // memory. This method is only called during initialization of SCC + // search, thus synchronization should not cause significant thread + // contention. We want a single LW to successfully allocate both + // dfsStack *and* comStack. + final IntStack dfsStack; + final IntStack comStack; + synchronized (LiveWorker.class) { + // Tarjan's stack + // Append thread id to name for unique disk files during concurrent SCC search + dfsStack = getStack(liveCheck.getMetaDir(), "dfs" + this.id); + + // comStack is only being added to during the deep first search. It is passed + // to the checkComponent method while in DFS though. Note that the nodes pushed + // onto comStack don't necessarily form a strongly connected component (see + // comment above this.checkComponent(...) below for more details). + // + // See tlc2.tool.liveness.LiveWorker.DetailedFormatter.toString(MemIntStack) + // which is useful during debugging. + comStack = getStack(liveCheck.getMetaDir(), "com" + this.id); + } - // Generate the SCCs and check if they contain any "bad" cycle. - while (nodeQueue.length() > 0) { + // Generate the SCCs and check if they contain a "bad" cycle. + while (nodeQueue.size() > 0) { + // Pick one of the unexplored nodes as root and start searching the + // reachable SCCs from it. final long state = nodeQueue.dequeueLong(); final int tidx = nodeQueue.dequeueInt(); final long loc = nodeQueue.dequeueLong(); - // Start computing SCCs with <state, tidx> as the root node: + // Reset (remove all elements) the stack. Logically a new SCC search + // is being started unrelated to the previous one. dfsStack.reset(); + // Push the first node onto the DFS stack which makes it the node + // from which the depth-first-search is being started. dfsStack.pushLong(state); dfsStack.pushInt(tidx); dfsStack.pushLong(loc); + // Push the smallest possible link number (confusingly called + // MAX_PTR here but only because file pointers are < MAX_PTR) as the + // first link number. + // [0, MAX_PTR) for file pointers + // [MAX_PTR, MAX_LINK] for links dfsStack.pushLong(DiskGraph.MAX_PTR); long newLink = DiskGraph.MAX_PTR; - while (dfsStack.size() > 2) { - long lowLink = dfsStack.popLong(); + while (dfsStack.size() >= 7) { + final long lowLink = dfsStack.popLong(); final long curLoc = dfsStack.popLong(); final int curTidx = dfsStack.popInt(); final long curState = dfsStack.popLong(); + // At this point curLoc is still a file pointer (small MAX_PTR) + // and not yet replaced by a link (MAX_PTR < curLoc < MAX_LINK). + assert DiskGraph.isFilePointer(curLoc); // The current node is explored iff curLoc < 0. If it is indeed fully explored, // it means it has potentially found an SCC. Thus, check if this is the case // for the current GraphNode. - if (curLoc < 0) { + // A node is fully explored if the nested loop over its + // successors down below in the else branch has not revealed any + // unexplored successors. + if (curLoc == SCC_MARKER) { + // Check if the current node's link is lowLink which + // indicates that the nodes on comStack up to <<curState, + // curTidx>> form an SCC. + // If curLink # lowLink, continue by pop'ing the next node + // from dfsStack. It can either be: + // - unexplored in which case the else branch is taken and + // DFS continues. + // - be an intermediate node of the SCC and thus curLink # + // lowLink for it too. + // - can be the start of the SCC (curLink = lowLink). final long curLink = this.dg.getLink(curState, curTidx); + assert curLink < AbstractDiskGraph.MAX_LINK; if (curLink == lowLink) { - // The states on the comStack from top to curState form - // a SCC. - // Check for "bad" cycle. - final boolean isOK = this.checkComponent(curState, curTidx, comStack); + // The states on the comStack from "top" to <<curState, + // curTidx>> form an SCC, thus check for "bad" cycle. + // + // The cycle does not necessarily include all states in + // comStack. "top" might very well be curState in which + // case only a single state is checked by + // checkComponent. + // + // The aforementioned case happens regularly when the + // behaviors to check don't have cycles at all (leaving + // single node cycles aside for the moment). The DFS + // followed each behavior from its initial state (see + // nodeQueue) all the way to the behavior's end state at + // which point DFS halts. Since DFS cannot continue + // (there are no successors) it calls checkComponent now + // with the current comStack and the end state as + // <<curState, curTidx>> effectively checking the + // topmost element of comStack. Unless this single state + // violates any liveness properties, it gets removed + // from comStack and DFS continues. Iff DFS still cannot + // continue because the predecessor to endstate + // (endstate - 1) has no more successors to explore + // either, it again calls checkComponent for the single + // element (endstate - 1). This goes on until either the + // initial state is reached or an intermediate state has + // unexplored successors with DFS. + final boolean isOK = this.checkComponent(tool, curState, curTidx, comStack); if (!isOK) { - // Found a "bad" cycle, no point in searching for more SCCs. + // Found a "bad" cycle of one to comStack.size() + // nodes, no point in searching for more SCCs as we + // are only interested in one counter-example at a + // time. + // checkComponent will have printed the + // counter-example by now. return; } } - long plowLink = dfsStack.popLong(); - if (lowLink < plowLink) { - plowLink = lowLink; - } - dfsStack.pushLong(plowLink); + // Replace previous lowLink (plowLink) with the minimum of + // the current lowLink and plowLink on the stack. + final long plowLink = dfsStack.popLong(); + dfsStack.pushLong(Math.min(plowLink, lowLink)); // No SCC found yet } else { // Assign newLink to curState: final long link = this.dg.putLink(curState, curTidx, newLink); + // link is -1 if newLink has been assigned to pair + // <<curState, curTidx>>. If the pair had been assigned a + // link before, the previous link in range [MAX_PTR, + // MAX_LINK] is returned. If the link is not -1, it means + // the node has been explored by this DFS search before. if (link == -1) { // Push curState back onto dfsStack, but make curState // explored: dfsStack.pushLong(lowLink); dfsStack.pushLong(curState); dfsStack.pushInt(curTidx); - dfsStack.pushLong(-1); + // Push a marker onto the stack that, if pop'ed as + // curLoc above causes branching to enter the true case + // of the if block. + dfsStack.pushLong(SCC_MARKER); - // Add curState to comStack: + // Add the tuple <<curState, curTidx, curLoc>> to comStack: comStack.pushLong(curLoc); comStack.pushInt(curTidx); comStack.pushLong(curState); @@ -190,52 +350,185 @@ public class LiveWorker extends IdThread { // Look at all the successors of curState: final GraphNode gnode = this.dg.getNode(curState, curTidx, curLoc); final int succCnt = gnode.succSize(); - long nextLowLink = newLink++; + long nextLowLink = newLink; + // DFS moved on to a new node, thus increment the newLink + // number by 1 for subsequent exploration. + newLink = newLink + 1; for (int i = 0; i < succCnt; i++) { final long nextState = gnode.getStateFP(i); final int nextTidx = gnode.getTidx(i); final long nextLink = this.dg.getLink(nextState, nextTidx); + // If <<nextState, nextTidx>> node's link is < 0 it + // means the node isn't "done" yet (see + // tlc2.tool.liveness.TableauNodePtrTable.UNDONE). + // A successor node t of gnode is undone if it is: + // - An initial state which hasn't been explored yet + // - t has not been added to the liveness disk graph + // itself (only as the successor (transition) of + // gnode). + // + // If it is >= 0, it either is a: + // - file pointer location + // - a previously assigned link (>= MAX_PTR) + // + // Iff nextLink == MAX_PTR, it means that the + // <<nextState, nextTidx>> successor node has been + // processed by checkComponent. The checks below + // will result in the successor node being skipped. + // + // It is possible that <<nextState, nextTidx>> = + // <<curState, curTid>> due to self loops. This is + // intended, as checkAction has to be evaluated for + // self loops too. if (nextLink >= 0) { + // Check if the arc/transition from <<curState, + // curTidx>> to <<nextState, nextTidx>> + // satisfies ("P-satisfiable" MP page 422ff) + // its PEM's EAAction. If it does, 1/3 of the + // conditions for P-satisfiability are + // satisfied. Thus it makes sense to check the + // other 2/3 in checkComponent (AEAction & + // Fulfilling promises). If the EAAction does + // not hold, there is no point in checking the + // other 2/3. All must hold for + // P-satisfiability. + // + // This check is related to the fairness spec. + // Skip to check the other conjuncts of the PossibleErrorModel (PEM) if the + // action check is false (thus does not satisfy the PEM). if (gnode.getCheckAction(slen, alen, i, eaaction)) { + // If the node's nextLink still points to + // disk, it means it has no link assigned + // yet which is the case if this node gets + // explored during DFS search the first + // time. Since it is new, add it to dfsStack + // to have it explored subsequently by DFS. if (DiskGraph.isFilePointer(nextLink)) { dfsStack.pushLong(nextState); dfsStack.pushInt(nextTidx); - dfsStack.pushLong(nextLink); - } else if (nextLink < nextLowLink) { - nextLowLink = nextLink; + dfsStack.pushLong(nextLink); // nextLink is logically a ptr/loc here + // One would expect a (logical) lowLink + // being pushed (additionally to the + // ptr/loc in previous line) onto the + // stack here. However, it is pushed + // down below after all successors are + // on the stack and valid for the + // topmost successor. For the other + // successors below the topmost, a link + // number will be assigned subsequently. + } else { + // The node has been processed + // already, thus use the minimum of its link + // (nextLink) and nextLowLink. + nextLowLink = Math.min(nextLowLink, nextLink); } - } else if (DiskGraph.isFilePointer(nextLink)) { + } else { + // The transition from <<curState, curTidx>> + // to <<nextState, nextTidx>> is not + // P-satisfiable and thus does not need to + // be checkComponent'ed. However, since we + // only added initial but no intermediate + // states to nodeQueue above, we have to add + // <<nextState, nextTidx>> to nodeQueue if + // it's still unprocessed (indicated by its + // on disk state). The current path + // potentially might be the only one by + // which DFS can reach it. + if (DiskGraph.isFilePointer(nextLink)) { nodeQueue.enqueueLong(nextState); nodeQueue.enqueueInt(nextTidx); - nodeQueue.enqueueLong(nextLink); + nodeQueue.enqueueLong(nextLink); // nextLink is logically a ptr/loc here + } } + } else { + // If this is the final check on the complete + // graph, no node is allowed to be undone. If + // it's not the final check, nextLink has to be + // UNDONE (a non-UNDONE negative nextLink is + // probably a bug). + // isFinalCheck => nextLink # UNDONE + assert !isFinalCheck || nextLink != TableauNodePtrTable.UNDONE; } } + // Push the next lowLink onto stack on top of all + // successors. It is assigned to the topmost + // successor only though. dfsStack.pushLong(nextLowLink); } else { - if (link < lowLink) { - lowLink = link; - } - dfsStack.pushLong(lowLink); + // link above wasn't "-1", thus it has to be a valid + // link in the known interval. + assert AbstractDiskGraph.MAX_PTR <= link && link <= AbstractDiskGraph.MAX_LINK; + // Push the minimum of the two links onto the stack. If + // link == DiskGraph.MAX_PTR lowLink will always be the + // minimum (unless this graph has a gigantic amount of + // SCCs exceeding (MAX_LINK - MAX_PTR). + dfsStack.pushLong(Math.min(lowLink, link)); } } } } + // Make sure all nodes on comStack have been checkComponent()'ed + assert comStack.size() == 0; + } - // After completing the checks, clean up: - // dfsStack.cleanup(); - // comStack.cleanup(); + private IntStack getStack(final String metaDir, final String name) throws IOException { + // It is unlikely that the stacks will fit into memory if the + // size of the behavior graph is larger relative to the available + // memory. Also take the total number of simultaneously running + // workers into account that have to share the available memory + // among each other. + try { + final double freeMemoryInBytes = (Runtime.getRuntime().freeMemory() / (numWorkers * 1d)); + final long graphSizeInBytes = this.dg.getSizeOnDisk(); + final double ratio = graphSizeInBytes / freeMemoryInBytes; + if (ratio > TLCGlobals.livenessGraphSizeThreshold) { + // Double SDIS's bufSize/pageSize by how much the graph size + // overshoots the free memory size, but limit page size to 1gb. + // Also, don't allocate more than what is available. + final int capacityInBytes = SynchronousDiskIntStack.BufSize << Math.min((int) ratio, 5); + if (capacityInBytes < freeMemoryInBytes) { + return new SynchronousDiskIntStack(metaDir, name, capacityInBytes); + } else { + // Use default SDIS which is 32mb of in-memory size + return new SynchronousDiskIntStack(metaDir, name); + } + } + // If the disk graph as a whole fits into memory, do not use a + // disk-backed SynchronousDiskIntStack. + return new MemIntStack(metaDir, name); + } catch (final OutOfMemoryError oom) { + System.gc(); + // If the allocation above failed, be more conservative. If it fails to + // allocate even 16mb, TLC will subsequently terminate with a message about insufficient + // memory. + final SynchronousDiskIntStack sdis = new SynchronousDiskIntStack(metaDir, name, + SynchronousDiskIntStack.BufSize / 2); + MP.printWarning(EC.GENERAL, + "Liveness checking will be extremely slow because TLC is running low on memory.\n" + + "Try allocating more memory to the Java pool (heap) in future TLC runs."); + return sdis; + } } /** - * For currentPEM, this method checks if the current scc satisfies its AEs - * and is fulfilling. (We know the current scc satisfies the pem's EA.) If - * satisfiable, this pem contains a counterexample, and this method then - * calls printErrorTrace to print an error trace and returns false. + * For currentPEM, this method checks if the current SCC satisfies its AEs + * and is fulfilling (we know the current SCC satisfies the PEM's EA by the + * nested EAaction in checkSccs() above.) If satisfiable, this PEM + * contains a counterexample, and this method then calls printErrorTrace to + * print an error trace and returns false. + * <p> + * Speaking in words of Manna & Pnueli (Page 422ff), it checks if ~φ + * (which is PEM) is "P-satisfiable" (i.e. is there a computation that + * satisfies ψ). ~φ (called ψ by MP) is the negation of the + * liveness formula φ which has to be "P-valid" for the liveness + * properties to be valid. + * @throws ExecutionException + * @throws InterruptedException */ - private boolean checkComponent(final long state, final int tidx, final MemIntStack comStack) throws IOException { -// final int comStackSize = comStack.size(); -// Assert.check(comStackSize > 0, EC.GENERAL); + private boolean checkComponent(final ITool tool, final long state, final int tidx, final IntStack comStack) throws IOException, InterruptedException, ExecutionException { + final long comStackSize = comStack.size(); + // There is something to pop and each is a well formed tuple <<fp, tidx, loc>> + assert comStackSize >= 5 && comStackSize % 5 == 0; // long + int + long long state1 = comStack.popLong(); int tidx1 = comStack.popInt(); @@ -259,10 +552,20 @@ public class LiveWorker extends IdThread { // to be kept in com. However, it would destroy NodePtrTable's // collision handling. NodePtrTable uses open addressing (see // http://en.wikipedia.org/wiki/Open_addressing). - TableauNodePtrTable com = new TableauNodePtrTable(128); + // + // Initializing the TNPT with 128 buckets/slots is a significant memory + // overhead (especially when comStack contains < 10 elements) which + // regularly results in OutOfMemoryErrors being thrown. To alleviate the + // problem the key-space of the comStack elements could be checked and + // the minimum possible collision-free TNPT size be calculated. + // (Btw. the implementation uses a TNPT in the first place because it is + // passed on to printTrace iff an error is found. The implementation + // here could use a simple java.util.Map or HashTable technically.) + final TableauNodePtrTable com = new TableauNodePtrTable(128); while (true) { // Add <state1, tidx1> into com: com.put(state1, tidx1, loc1); + assert AbstractDiskGraph.isFilePointer(loc1); this.dg.setMaxLink(state1, tidx1); // Get the next node of the component: @@ -275,7 +578,7 @@ public class LiveWorker extends IdThread { loc1 = comStack.popLong(); } // Just parameter node in com OR com subset of comStack -// Assert.check(com.size() <= (comStackSize / 5), EC.GENERAL); + assert com.size() <= (comStackSize / 5); STATS.addSample(com.size()); @@ -288,6 +591,7 @@ public class LiveWorker extends IdThread { final boolean[] AEStateRes = new boolean[aeslen]; final boolean[] AEActionRes = new boolean[aealen]; final boolean[] promiseRes = new boolean[plen]; + final int[] eaaction = this.pem.EAAction; // Extract a node from the nodePtrTable "com". // Note the upper limit is NodePtrTable#getSize() instead of @@ -295,26 +599,46 @@ public class LiveWorker extends IdThread { // NodePtrTable internally hashes the elements to buckets // and isn't filled start to end. Thus, the code // below iterates NodePtrTable front to end skipping null buckets. - int tsz = com.getSize(); + // + // Note that the nodes are processed in random order (depending on a + // node's hash in TableauNodePtrTbl) and not in the order given by + // comStack. This is fine because the all checks have been evaluated + // eagerly during insertion into the liveness graph long before the + // SCC search started. Thus, the code here only has to check the + // check results which can happen in any order. + final int tsz = com.getSize(); for (int ci = 0; ci < tsz; ci++) { - int[] nodes = com.getNodesByLoc(ci); + final int[] nodes = com.getNodesByLoc(ci); if (nodes == null) { // miss in NotePtrTable (null bucket) continue; } state1 = TableauNodePtrTable.getKey(nodes); - for (int nidx = 2; nidx < nodes.length; nidx += com.getElemLength()) { + for (int nidx = 2; nidx < nodes.length; nidx += com.getElemLength()) { // nidx starts with 2 because [0][1] are the long fingerprint state1. tidx1 = TableauNodePtrTable.getTidx(nodes, nidx); loc1 = TableauNodePtrTable.getElem(nodes, nidx); - GraphNode curNode = this.dg.getNode(state1, tidx1, loc1); + final GraphNode curNode = this.dg.getNode(state1, tidx1, loc1); // Check AEState: for (int i = 0; i < aeslen; i++) { + // Only ever set AEStateRes[i] to true, but never to false + // once it was true. It only matters if one state in com + // satisfies PEM's liveness property due to []<>~p (which is + // the inversion of <>[]p). + // + // It obviously has to check all nodes in the component + // (com) if either of them violates AEState unless all + // elements of AEStateRes are true. From that point onwards, + // checking further states wouldn't make a difference. if (!AEStateRes[i]) { int idx = this.pem.AEState[i]; AEStateRes[i] = curNode.getCheckState(idx); + // Can stop checking AEStates the moment AEStateRes + // is completely set to true. However, most of the time + // aeslen is small and the compiler will probably optimize + // out. } } @@ -322,17 +646,65 @@ public class LiveWorker extends IdThread { // between the current node and a successor state. The current // node has n successor states. For each pair, see iff the // successor is in the "com" NodePtrTablecheck, check actions - // and store the results in AEActionRes(ult). - int succCnt = curNode.succSize(); + // and store the results in AEActionRes(ult). Note that the + // actions have long been checked in advance when the node was + // added to the graph and the actual state and not just its + // fingerprint was available. Here, the result is just being + // looked up. + final int succCnt = aealen > 0 ? curNode.succSize() : 0; // No point in looping successors if there are no AEActions to check on them. for (int i = 0; i < succCnt; i++) { - long nextState = curNode.getStateFP(i); - int nextTidx = curNode.getTidx(i); - if (com.getLoc(nextState, nextTidx) != -1) { - for (int j = 0; j < aealen; j++) { - if (!AEActionRes[j]) { - int idx = this.pem.AEAction[j]; - AEActionRes[j] = curNode.getCheckAction(slen, alen, i, idx); - } + final long nextState = curNode.getStateFP(i); + final int nextTidx = curNode.getTidx(i); + // For each successor <<nextState, nextTdix>> of curNode's + // successors check, if it is part of the currently + // processed SCC (com). Successors, which are not part of + // the current SCC have obviously no relevance here. After + // all, we check the SCC. + if (com.getLoc(nextState, nextTidx) == -1) { + continue; + } + // MAK 10/23/2018: + // Line 380 above "if(gnode.getCheckAction)" causes a transition A from state s + // -> t to be skipped even if a belongs to an SCC iff the transition A does not + // satisfy the EA action of the PossibleErrorModel (if the EA action(s) is not + // satisfied, the PEM cannot hold at all). + // However, some state graphs are such that there exists not just the transition + // A from s -> t but a second transition A' from t -> s - which satisfies the EA + // action(s) of the PEM. In the case of a "bidirectional" transition, the states + // s and t will be in the set of states 'com' (which make up the SCC). Thus, the + // transition A from s -> t will be incorrectly traversed here unless it is + // skipped (again). Not skipping the transition A will result in TLC reporting a + // (bogus) counterexample even if the liveness is not violated. + // + // Consider the spec BT for which TLC incorrectly reports a liveness property + // violation and prints a bogus counterexample: + // + // ---- BT ----- + // EXTENDS Naturals + // VARIABLE x + // A == \/ x' = (x + 1) % 3 + // B == x' \in 0..2 + // Spec == (x=0) /\ [][A \/ B]_x/\ WF_x(A) + // Prop == Spec /\ WF_x(A) /\ []<><<A>>_x + // ============= + // + // > Temporal properties were violated. + // > The following behavior constitutes a counter-example: + // > 1: <Initial predicate> + // > x = 0 + // > 2: <A line xx...BT> + // > x = 1 + // > 1: Back to state: <B line xx... BT> + // + // (see tlc2.tool.BidirectionalTransitions1Test and BidirectionalTransitions2Test) + if(!curNode.getCheckAction(slen, alen, i, eaaction)) { + continue; + } + for (int j = 0; j < aealen; j++) { + // Only set false to true, but never true to false. + if (!AEActionRes[j]) { + final int idx = this.pem.AEAction[j]; + AEActionRes[j] = curNode.getCheckAction(slen, alen, i, idx); } } } @@ -340,8 +712,8 @@ public class LiveWorker extends IdThread { // Check that the component is fulfilling. (See MP page 453.) // Note that the promises are precomputed and stored in oos. for (int i = 0; i < plen; i++) { - LNEven promise = this.oos.getPromises()[i]; - TBPar par = curNode.getTNode(this.oos.getTableau()).getPar(); + final LNEven promise = this.oos.getPromises()[i]; + final TBPar par = curNode.getTNode(this.oos.getTableau()).getPar(); if (par.isFulfilling(promise)) { promiseRes[i] = true; } @@ -349,75 +721,381 @@ public class LiveWorker extends IdThread { } } - // We find a counterexample if all three conditions are satisfied. + // We find a counterexample if all three conditions are satisfied. If + // either of the conditions is false, it means the PEM does not hold and + // thus the liveness properties are not violated by the SCC. + // + // All AEState properties, AEActions and promises of PEM must be + // satisfied. If a single one isn't satisfied, the PEM as a whole isn't + // P-satisfiable. That's why it returns on the first false. As stated + // before, EAAction have already been checked if satisfiable. + // checkComponent is only called if the EA actions are satisfiable. + // + // Technically: No error is found if any of the AEStateRes, AEActionRes + // or promiseRes booleans is false. for (int i = 0; i < aeslen; i++) { if (!AEStateRes[i]) { +// writeDotViz(state, tidx, com, new java.io.File(liveCheck.getMetaDir() + java.io.File.separator +// + "pValidSCC" + System.currentTimeMillis() + ".dot")); return true; } } for (int i = 0; i < aealen; i++) { if (!AEActionRes[i]) { +// writeDotViz(state, tidx, com, new java.io.File(liveCheck.getMetaDir() + java.io.File.separator +// + "pValidSCC" + System.currentTimeMillis() + ".dot")); return true; } } for (int i = 0; i < plen; i++) { if (!promiseRes[i]) { +// writeDotViz(state, tidx, com, new java.io.File(liveCheck.getMetaDir() + java.io.File.separator +// + "pValidSCC" + System.currentTimeMillis() + ".dot")); return true; } } // This component must contain a counter-example because all three - // conditions are satisfied. So, print a counter-example! + // conditions are satisfied. So, print a counter-example (if this thread + // is the first one to find a counter-example)! if (setErrFound()) { - this.printTrace(state, tidx, com); + this.printTrace(tool, state, tidx, com); } return false; } /* Check if the node <state, tidx> stutters. */ private boolean isStuttering(long state, int tidx, long loc) throws IOException { - int slen = this.oos.getCheckState().length; - int alen = this.oos.getCheckAction().length; + final int slen = this.oos.getCheckState().length; + final int alen = this.oos.getCheckAction().length; - GraphNode gnode = this.dg.getNode(state, tidx, loc); - int succCnt = gnode.succSize(); + // Find the self loop and check its <>[]action + final GraphNode gnode = this.dg.getNode(state, tidx, loc); + final int succCnt = gnode.succSize(); for (int i = 0; i < succCnt; i++) { - long nextState = gnode.getStateFP(i); - int nextTidx = gnode.getTidx(i); + final long nextState = gnode.getStateFP(i); + final int nextTidx = gnode.getTidx(i); if (state == nextState && tidx == nextTidx) { return gnode.getCheckAction(slen, alen, i, this.pem.EAAction); } } + // <state, tidx> has no self loop, thus cannot stutter return false; } /** - * Print out the error state trace. The method first generates a "bad" cycle - * from the current scc, and then generates a prefix path from some initial - * state to the "bad" cycle in the state graph. The prefix path and the - * "bad" cycle together forms a counter-example. + * Print out the error state trace by finding a cycle in the given SCC. The + * method first generates a "bad" cycle from the current scc, and then + * generates a prefix path from some initial state to the "bad" cycle in the + * state graph. The prefix path and the "bad" cycle together forms a + * counter-example. + * <p> + * Additionally, the first part can be divided into the two while loops A) + * and B). A) re-creates the sub-path of the error trace starting at the + * start state of the SCC as given by the parameters and ends when all + * states have be accumulated that -combined- violate the liveness + * properties. Iff the last state after termination of A) is not equal to + * the start state, there is a gap in the cycle. Thus, B) task is to close + * the gap. + * <p> + * + * @see tlatools/test-model/symmetry/ErrorTraceConstructionPhases.png for a + * sketch. + * @see tlc2.tool.liveness.ErrorTraceConstructionTest which runs a spec that + * exemplifies the three staged error trace composition + * + * @param state + * fingerprint of the state which is the "starting" state of the + * SCC in nodeTbl. + * @param tidx + * tableau index pointing to the {@link TBGraph}. Corresponds to + * the state fingerprint. Combined <<state, tidx>> unique + * identify a node in the liveness/behavior graph. + * @param nodeTbl + * The current SCC which is known to satisfy the + * {@link PossibleErrorModel} and thus violates the liveness + * properties. + * @throws ExecutionException + * @throws InterruptedException */ - private void printTrace(final long state, final int tidx, final TableauNodePtrTable nodeTbl) throws IOException { + private void printTrace(ITool tool, final long state, final int tidx, final TableauNodePtrTable nodeTbl) throws IOException, InterruptedException, ExecutionException { +// writeDotViz(state, tidx, nodeTbl, new java.io.File(liveCheck.getMetaDir() + java.io.File.separator +// + "pSatisfiableSCC_" + System.currentTimeMillis() + ".dot")); MP.printError(EC.TLC_TEMPORAL_PROPERTY_VIOLATED); MP.printError(EC.TLC_COUNTER_EXAMPLE); + + /* + * Use a dedicated thread to concurrently search a prefix-path from some + * initial node to the state identified by <<state, tidx>>. + */ + final ExecutorService executor = Executors.newFixedThreadPool(1); + final Future<List<TLCStateInfo>> future = executor.submit(new Callable<List<TLCStateInfo>>() { + /* (non-Javadoc) + * @see java.util.concurrent.Callable#call() + */ + public List<TLCStateInfo> call() throws Exception { + // Print the error trace. We first construct the prefix that + // led to the bad cycle. The nodes on prefix and cycleStack then + // form the complete counter example. + final LongVec prefix = LiveWorker.this.dg.getPath(state, tidx); + final int plen = prefix.size(); + final List<TLCStateInfo> states = new ArrayList<TLCStateInfo>(plen); + + // Recover the initial state: + //TODO This throws an ArrayIndexOutOfBounds if getPath returned a + // LongVec with just a single element. This happens when the parameter + // state is one of the init states already. + long fp = prefix.elementAt(plen - 1); + TLCStateInfo sinfo = tool.getState(fp); + if (sinfo == null) { + throw new EvalException(EC.TLC_FAILED_TO_RECOVER_INIT); + } + states.add(sinfo); + + // Recover the successor states: + for (int i = plen - 2; i >= 0; i--) { + long curFP = prefix.elementAt(i); + // The prefix might contain duplicates if the path happens to walk + // along two (or more distinct states which differ in the tableau + // idx only (same fingerprint). From the counterexample perspective, + // this is irrelevant iff the identical fingerprints are contiguous. + // It won't be correct to shorten a path <<fp1,fp2,fp1>> to + // <<fp2,fp1>> though. + if (curFP != fp) { + sinfo = tool.getState(curFP, sinfo); + states.add(sinfo); + fp = curFP; + } + } + + // Print the prefix in reverse order of previous loop: + for (int i = 0; i < states.size(); i++) { + StatePrinter.printState(states.get(i)); + } + return states; + } + }); + + /* + * With the executor concurrently working on the prefix, let this thread + * work on the postfix (cycle). + */ + final MemIntStack cycleStack = new MemIntStack(liveCheck.getMetaDir(), "cycle"); + GraphNode curNode = dfsPostFix(state, tidx, nodeTbl, cycleStack); + + /* + * If the cycle is not closed/completed (complete when startState == + * state), continue from the curNode at which the previous while loop + * terminated and follow its successors until the start state shows up. + */ + final LongVec postfix = bfsPostFix(state, tidx, nodeTbl, curNode); + + /* + * At this point the cycle part of the error trace has been constructed. + * cycleStack contains the states from the start state of the SCC up to + * the state that violates all liveness properties. postfix contains the + * suffix from the violating state back to the start state of the SCC. + * Thus, append the reversed cycleStack onto postfix (cycleStack has the + * last state at the top). Postfix then contains the minimal path in the + * SCC that violates the liveness property. + */ + while (cycleStack.size() > 0) { + // Do not filter successive <<fp,tidx,permId>> here but do it below + // when the actual states get printed. See Test3.tla for reason why. + postfix.addElement(cycleStack.popLong()); + cycleStack.popInt(); // ignore tableau idx. The tableau idx is + // irrelevant as <<fpA, tidx1>> and <<fpA, + // tidx2>> both map to the same state in the + // error trace. + } + + // Wait for the prefix-path to be searched/generated and fully printed. + // get() is a blocking call that makes this thread wait for the executor + // to finish its job of searching and printing the prefix-path. + List<TLCStateInfo> states = new ArrayList<>(0); + try { + states = future.get(); + } catch (ExecutionException ee) { + // Do not "leak" ExecutionException to user if root cause is actually an + // EvalException. + if (ee.getCause() instanceof EvalException) { + throw (EvalException) ee.getCause(); + } + throw ee; + } + + /* + * At this point everything from the initial state up to the start state + * of the SCC has been printed. Now, print the states in postfix. Obtain + * the last state from the prefix (which corresponds to <<state, tidx>>) + * to use it to generate the next state. Obviously, we have to wait for + * the prefix thread to be done for two reasons: a) the trace has to be + * printed and b) we need the TLCState instance to generate the + * successor states in the cycle. + */ + final TLCStateInfo cycleState = states.get(states.size() - 1); + + TLCStateInfo sinfo = cycleState; + for (int i = postfix.size() - 1; i >= 0; i--) { + final long curFP = postfix.elementAt(i); + // Only print the state if it differs from its predecessor. We don't + // want to print an identical state twice. This can happen if the + // loops A) and B) above added an identical state multiple times + // into cycleStack/postfix. + // The reason we don't simply compare the actual states is for + // efficiency reason. Regenerating the next state might be + // expensive. + if (curFP != sinfo.fingerPrint()) { + sinfo = tool.getState(curFP, sinfo); + StatePrinter.printState(sinfo); + } + } + + /* All error trace states have been printed (prefix + cycleStack + + * postfix). What is left is to print either the stuttering or the + * back-to-cyclePos marker. + */ + + final int stateNumber = (int) cycleState.stateNumber; // if the cast causes problems the trace won't be comprehensible anyway. + if (sinfo.fingerPrint() == cycleState.fingerPrint()) { + StatePrinter.printStutteringState(stateNumber); + } else { + sinfo = tool.getState(cycleState.fingerPrint(), sinfo); + // The print stmts below claim there is a cycle, thus assert that + // there is indeed one. Index-based lookup into states array is + // reduced by one because cyclePos is human-readable. + assert cycleState.state.equals(sinfo.state); + StatePrinter.printBackToState(sinfo, stateNumber); + } + } + + // BFS search + private LongVec bfsPostFix(final long state, final int tidx, final TableauNodePtrTable nodeTbl, GraphNode curNode) + throws IOException { + final int slen = this.oos.getCheckState().length; + final int alen = this.oos.getCheckAction().length; + final int[] eaaction = this.pem.EAAction; + + final LongVec postfix = new LongVec(16); + final long startState = curNode.stateFP; + final long startTidx = curNode.tindex; + + // B) + if (startState != state || startTidx != tidx) { + final MemIntQueue queue = new MemIntQueue(liveCheck.getMetaDir(), null); + long curState = startState; + int ploc = TableauNodePtrTable.NO_PARENT; + int curLoc = nodeTbl.getNodesLoc(curState); + int[] nodes = nodeTbl.getNodesByLoc(curLoc); + TableauNodePtrTable.setSeen(nodes); + + // B1) + _done: while (true) { + // tloc, the index of the various tableau indices in nodes array + int tloc = TableauNodePtrTable.startLoc(nodes); + // Loop over all GraphNodes (differing by tableau indices only) + // in nodes until at end. When all GraphNodes are explored, the + // next GraphNode to explore is taken from the FIFO/queue and + // the search continues. If one GraphNode happens to match the + // searched for <<fp, tidx>>, the (forward) search stops and + // the reverse path is followed up to the start node (inner + // while loop). + while (tloc != TableauNodePtrTable.END_MARKER) { + final int curTidx = TableauNodePtrTable.getTidx(nodes, tloc); + final long curPtr = TableauNodePtrTable.getPtr(TableauNodePtrTable.getElem(nodes, tloc)); + curNode = this.dg.getNode(curState, curTidx, curPtr); + final int succCnt = curNode.succSize(); + + // for each successor of curNode s, check if s is the + // destination state. + SUCCESSORS: for (int j = 0; j < succCnt; j++) { + final long nextState = curNode.getStateFP(j); + final int nextTidx = curNode.getTidx(j); + + // Ignore self-loop because it cannot close the + // cycle/lasso. The seen state flag partially + // prevents exploring self-loops, but only if + // the tableau idx is the base idx (the seen + // flag ignores the tableau idx entirely). + if (curState == nextState && curTidx == nextTidx) { + assert TableauNodePtrTable.isSeen(nodes); + continue SUCCESSORS; + } + + // Prevent bogus counterexample: Do not close the loop by taking an action which + // does not satisfy the PossibleErrorModel (read more about it on line 640 in + // checkComponent). + if(!curNode.getCheckAction(slen, alen, j, eaaction)) { + continue; + } + + if (nextState == state && nextTidx == tidx) { + // We have found a path from startState to state, + // now backtrack the path the outer loop took to get + // us here and add each state to postfix. + while (curState != startState) { + postfix.addElement(curState); + nodes = nodeTbl.getNodesByLoc(ploc); + curState = TableauNodePtrTable.getKey(nodes); + ploc = TableauNodePtrTable.getParent(nodes); + } + postfix.addElement(startState); + break _done; + } + // s is not equal to the destination state 'startState'. + // If s's successors are still unseen, add s to the + // queue to later explore it as well. Mark it seen + // to not explore it twice. + final int[] nodes1 = nodeTbl.getNodes(nextState); + if (nodes1 != null && !TableauNodePtrTable.isSeen(nodes1)) { + TableauNodePtrTable.setSeen(nodes1); + queue.enqueueLong(nextState); + queue.enqueueInt(curLoc); + } + } + tloc = TableauNodePtrTable.nextLoc(nodes, tloc); + } + // Create a parent pointer to later reverse the path in B2) + TableauNodePtrTable.setParent(nodes, ploc); + // Dequeue the next unexplored state from the queue. + curState = queue.dequeueLong(); + ploc = queue.dequeueInt(); + curLoc = nodeTbl.getNodesLoc(curState); + nodes = nodeTbl.getNodesByLoc(curLoc); + } + } + return postfix; + } + + private GraphNode dfsPostFix(final long state, final int tidx, final TableauNodePtrTable nodeTbl, final MemIntStack cycleStack) throws IOException { // First, find a "bad" cycle from the "bad" scc. - int slen = this.oos.getCheckState().length; - int alen = this.oos.getCheckAction().length; - boolean[] AEStateRes = new boolean[this.pem.AEState.length]; - boolean[] AEActionRes = new boolean[this.pem.AEAction.length]; - boolean[] promiseRes = new boolean[this.oos.getPromises().length]; + final int slen = this.oos.getCheckState().length; + final int alen = this.oos.getCheckAction().length; + // The 3 boolean arrays are used to make sure that the same check result + // is exactly counted once. + final boolean[] AEStateRes = new boolean[this.pem.AEState.length]; + final boolean[] AEActionRes = new boolean[this.pem.AEAction.length]; + final boolean[] promiseRes = new boolean[this.oos.getPromises().length]; + final int[] eaaction = this.pem.EAAction; + // The number/count of all liveness checks. The while loop A) terminates + // once it has accumulated all states that violate all checks (we know + // that the states in nodeTbl have to violate the liveness property + // because we are in printTrace already. checkComponent has already + // determined that there is a violation). int cnt = AEStateRes.length + AEActionRes.length + promiseRes.length; - MemIntStack cycleStack = new MemIntStack(liveCheck.getMetaDir(), "cycle"); - // Mark state as visited: int[] nodes = nodeTbl.getNodes(state); int tloc = nodeTbl.getIdx(nodes, tidx); - long ptr = TableauNodePtrTable.getElem(nodes, tloc); + final long ptr = TableauNodePtrTable.getElem(nodes, tloc); TableauNodePtrTable.setSeen(nodes, tloc); + // A) + // + // Greedy DFS search for a path satisfying the PossibleErrorModel. GraphNode curNode = this.dg.getNode(state, tidx, ptr); + while (cnt > 0) { int cnt0 = cnt; @@ -445,7 +1123,8 @@ public class LiveWorker extends IdThread { break; } - // Check AEAction: + // Check AEAction (which is a check of the out-arc of curNode to + // one of its successors): long nextState1 = 0, nextState2 = 0; int nextTidx1 = 0, nextTidx2 = 0; int tloc1 = -1, tloc2 = -1; @@ -459,18 +1138,26 @@ public class LiveWorker extends IdThread { nodes = nodeTbl.getNodes(nextState); if (nodes != null) { tloc = nodeTbl.getIdx(nodes, nextTidx); - if (tloc != -1) { - // <nextState, nextTidx> is in nodeTbl. - nextState1 = nextState; - nextTidx1 = nextTidx; - tloc1 = tloc; - nodes1 = nodes; - for (int j = 0; j < this.pem.AEAction.length; j++) { - int idx = this.pem.AEAction[j]; - if (!AEActionRes[j] && curNode.getCheckAction(slen, alen, i, idx)) { - AEActionRes[j] = true; - cnt--; - } + // See checkComponent line 637. + if (tloc == -1) { + continue; + } + // Prevent bogus counterexample: Do not close the loop by taking an action which + // does not satisfy the PossibleErrorModel (read more about it on line 640 in + // checkComponent). + if(!curNode.getCheckAction(slen, alen, i, eaaction)) { + continue; + } + // <nextState, nextTidx> is in nodeTbl. + nextState1 = nextState; + nextTidx1 = nextTidx; + tloc1 = tloc; + nodes1 = nodes; + for (int j = 0; j < this.pem.AEAction.length; j++) { + int idx = this.pem.AEAction[j]; + if (!AEActionRes[j] && curNode.getCheckAction(slen, alen, i, idx)) { + AEActionRes[j] = true; + cnt--; } } } @@ -537,181 +1224,188 @@ public class LiveWorker extends IdThread { TableauNodePtrTable.setSeen(nodes2, tloc2); } } - - // All the conditions are satisfied. Find a path from curNode - // to state to form a cycle. Note that: + // All the conditions are satisfied. // 1. curNode has not been pushed on cycleStack. - // 2. nodeTbl is trashed after this operation. + // 2. nodeTbl is trashed after this operation, thus reset. Trashed means + // that some nodes are still marked seen being left-overs from the + // Depth-First search. nodeTbl.resetElems(); - final LongVec postfix = new LongVec(16); - long startState = curNode.stateFP; - - if (startState != state) { - MemIntQueue queue = new MemIntQueue(liveCheck.getMetaDir(), null); - long curState = startState; - int ploc = -1; - int curLoc = nodeTbl.getNodesLoc(curState); - nodes = nodeTbl.getNodesByLoc(curLoc); - TableauNodePtrTable.setSeen(nodes); - - _done: while (true) { - tloc = TableauNodePtrTable.startLoc(nodes); - while (tloc != -1) { - int curTidx = TableauNodePtrTable.getTidx(nodes, tloc); - long curPtr = TableauNodePtrTable.getPtr(TableauNodePtrTable.getElem(nodes, tloc)); - curNode = this.dg.getNode(curState, curTidx, curPtr); - int succCnt = curNode.succSize(); - - for (int j = 0; j < succCnt; j++) { - long nextState = curNode.getStateFP(j); - - if (nextState == state) { - // we have found a path from startState to state: - while (curState != startState) { - postfix.addElement(curState); - nodes = nodeTbl.getNodesByLoc(ploc); - curState = TableauNodePtrTable.getKey(nodes); - ploc = TableauNodePtrTable.getParent(nodes); - } - postfix.addElement(startState); - break _done; - } + + return curNode; + } - int[] nodes1 = nodeTbl.getNodes(nextState); - if (nodes1 != null && !TableauNodePtrTable.isSeen(nodes1)) { - TableauNodePtrTable.setSeen(nodes1); - queue.enqueueLong(nextState); - queue.enqueueInt(curLoc); - } - } - tloc = TableauNodePtrTable.nextLoc(nodes, tloc); - } - TableauNodePtrTable.setParent(nodes, ploc); - curState = queue.dequeueLong(); - ploc = queue.dequeueInt(); - curLoc = nodeTbl.getNodesLoc(curState); - nodes = nodeTbl.getNodesByLoc(curLoc); + public final Boolean call() throws IOException, InterruptedException, ExecutionException { + while (true) { + // Use poll() to get the next checker from the queue or null if + // there is none. Do *not* block when there are no more checkers + // available. Nobody is going to add new checkers to the queue. + final ILiveChecker checker = queue.poll(); + if (checker == null || hasErrFound()) { + // Another thread has either found an error (violation of a + // liveness property) OR there is no more work (checker) to + // be done. + break; } - } - // Now, print the error trace. We first construct the prefix that - // led to the bad cycle. The nodes on prefix and cycleStack then - // form the complete counter example. - int stateNum = 0; - LongVec prefix = this.dg.getPath(state, tidx); - int plen = prefix.size(); - TLCStateInfo[] states = new TLCStateInfo[plen]; - - // Recover the initial state: - //TODO This throws an ArrayIndexOutOfBounds if getPath returned a - // LongVec with just a single element. This happens when the parameter - // state is one of the init states already. - long fp = prefix.elementAt(plen - 1); - TLCStateInfo sinfo = liveCheck.getTool().getState(fp); - if (sinfo == null) { - throw new EvalException(EC.TLC_FAILED_TO_RECOVER_INIT); - } - states[stateNum++] = sinfo; - - // Recover the successor states: - //TODO Check if path.size has elements - for (int i = plen - 2; i >= 0; i--) { - long curFP = prefix.elementAt(i); - // The prefix might contain duplicates if the path happens to walk - // along two distinct states which differ in the tableau idx only - // (same fingerprint). From the counterexample perspective, this is - // irrelevant. - if (curFP != fp) { - sinfo = liveCheck.getTool().getState(curFP, sinfo.state); - if (sinfo == null) { - throw new EvalException(EC.TLC_FAILED_TO_RECOVER_NEXT); + this.oos = checker.getSolution(); + this.dg = checker.getDiskGraph(); + this.dg.createCache(); + PossibleErrorModel[] pems = this.oos.getPems(); + for (int i = 0; i < pems.length; i++) { + if (!hasErrFound()) { + this.pem = pems[i]; + this.checkSccs(tool); } - states[stateNum++] = sinfo; - fp = curFP; } + this.dg.destroyCache(); + // Record the size of the disk graph at the time its checked. This + // information is later used to decide if it it makes sense to + // run the next check on the larger but still *partial* graph. + this.dg.recordSize(); + // If assertions are on (e.g. during unit testing) make sure + // that the disk graph's invariants hold. + assert this.dg.checkInvariants(oos.getCheckState().length, oos.getCheckAction().length); } + return hasErrFound(this.id); + } - // Print the prefix: - TLCState lastState = null; - for (int i = 0; i < stateNum; i++) { - StatePrinter.printState(states[i], lastState, i + 1); - lastState = states[i].state; - } + public String toDotViz(final long state, final int tidx, TableauNodePtrTable tnpt) throws IOException { + final StringBuffer sb = new StringBuffer(tnpt.size() * 10); + sb.append("digraph TableauNodePtrTable {\n"); + sb.append("nodesep = 0.7\n"); + sb.append("rankdir=LR;\n"); // Left to right rather than top to bottom - // Print the cycle: - int cyclePos = stateNum; - long cycleFP = fp; - while (cycleStack.size() > 0) { - postfix.addElement(cycleStack.popLong()); - cycleStack.popInt(); // ignore tableau idx - } - - // Assert.assert(fps.length > 0); - for (int i = postfix.size() - 1; i >= 0; i--) { - long curFP = postfix.elementAt(i); - if (curFP != fp) { - sinfo = liveCheck.getTool().getState(curFP, sinfo.state); - if (sinfo == null) { - throw new EvalException(EC.TLC_FAILED_TO_RECOVER_NEXT); - } - StatePrinter.printState(sinfo, lastState, ++stateNum); - lastState = sinfo.state; - fp = curFP; + final int tsz = tnpt.getSize(); + for (int ci = 0; ci < tsz; ci++) { + final int[] nodes = tnpt.getNodesByLoc(ci); + if (nodes == null) { + // miss in TableauNodePtrTable (null bucket) + continue; } - } - if (fp == cycleFP) { - StatePrinter.printStutteringState(++stateNum); - } else { - sinfo = liveCheck.getTool().getState(cycleFP, sinfo.state); - if (sinfo == null) { - throw new EvalException(EC.TLC_FAILED_TO_RECOVER_NEXT); - } - if (TLCGlobals.tool) { - MP.printState(EC.TLC_BACK_TO_STATE, new String[] { "" + cyclePos }, (TLCState) null, -1); - } else { - MP.printMessage(EC.TLC_BACK_TO_STATE, "" + cyclePos); + long state1 = TableauNodePtrTable.getKey(nodes); + for (int nidx = 2; nidx < nodes.length; nidx += tnpt.getElemLength()) { // nidx starts with 2 because [0][1] are the long fingerprint state1. + int tidx1 = TableauNodePtrTable.getTidx(nodes, nidx); + long loc1 = TableauNodePtrTable.getElem(nodes, nidx); + + final GraphNode curNode = this.dg.getNode(state1, tidx1, loc1); + sb.append(curNode.toDotViz((state1 == state && tidx1 == tidx), true, oos.getCheckState().length, + oos.getCheckAction().length, tnpt)); } } + + sb.append("}"); + return sb.toString(); } - - public final void run() { + + /** + * Write the output of {@link LiveWorker#toDotViz(long, int, TableauNodePtrTable)} to the given file. + * @param state + * @param tidx + * @param tnpt + * @param file + */ + public void writeDotViz(final long state, final int tidx, final TableauNodePtrTable tnpt, final java.io.File file) { + // Ignore trivial SCCs consisting of a single node. + if (tnpt.size() <= 1) { + return; + } + try { - while (true) { - // Use poll() to get the next checker from the queue or null if - // there is none. Do *not* block when there are no more checkers - // available. Nobody is going to add new checkers to the queue. - final ILiveChecker checker = queue.poll(); - if (checker == null || hasErrFound()) { - // Another thread has either found an error (violation of a - // liveness property) OR there is no more work (checker) to - // be done. - break; - } + final java.io.BufferedWriter bwr = new java.io.BufferedWriter(new java.io.FileWriter(file)); - this.oos = checker.getSolution(); - this.dg = checker.getDiskGraph(); - this.dg.createCache(); - PossibleErrorModel[] pems = this.oos.getPems(); - for (int i = 0; i < pems.length; i++) { - if (!hasErrFound()) { - this.pem = pems[i]; - this.checkSccs(); - } - } - this.dg.destroyCache(); - // Record the size of the disk graph at the time its checked. This - // information is later used to decide if it it makes sense to - // run the next check on the larger but still *partial* graph. - this.dg.recordSize(); - } - } catch (Exception e) { - MP.printError(EC.GENERAL, "checking liveness", e); // LL changed - // call 7 April - // 2012 - // Assert.printStack(e); - return; + // write contents of StringBuffer to a file + bwr.write(toDotViz(state, tidx, tnpt)); + + // flush the stream + bwr.flush(); + + // close the stream + bwr.close(); + } catch (IOException e) { + e.printStackTrace(); } } + + /* + * The detailed formatter below can be activated in Eclipse's variable view + * by choosing "New detailed formatter" from the MemIntQueue context menu. + * Insert "LiveWorker.DetailedFormatter.toString(this);". + */ + public static class DetailedFormatter { + public static String toString(final MemIntStack comStack) { + final int size = (int) comStack.size(); + final StringBuffer buf = new StringBuffer(size / 5); + for (int i = 0; i < comStack.size(); i+=5) { + long loc = comStack.peakLong(size - i - 5); + int tidx = comStack.peakInt(size - i - 3); + long state = comStack.peakLong(size - i - 2); + buf.append("state: "); + buf.append(state); + buf.append(" tidx: "); + buf.append(tidx); + buf.append(" loc: "); + buf.append(loc); + buf.append("\n"); + } + return buf.toString(); + } + } + /* + * The detailed formatter below can be activated in Eclipse's variable view + * by choosing "New detailed formatter" from the MemIntQueue context menu. + * Insert "LiveWorker.DFSStackDetailedFormatter.toString(this);". + * Unfortunately it collides with the comStack DetailedFormatter as both use + * the same type MemIntStack. So you have to chose what you want to look at + * while debugging. + * Note that toString treats pops/pushes of nodes and + * states atomically. If called during a node is only partially pushed onto + * the stack, the detailed formatter will crash. + */ + public static class DFSStackDetailedFormatter { + public static String toString(final MemIntStack dfsStack) { + final int size = (int) dfsStack.size(); + final StringBuffer buf = new StringBuffer(size / 7); // approximate the size needed (buf will grow or shrink if needed) + int i = 0; + for (; i < dfsStack.size();) { + // Peak element to see if it's a marker or not + final long topElement = dfsStack.peakLong(size - i - 2); + if (topElement == SCC_MARKER) { + // It is the marker element + buf.append("node ["); + buf.append(" fp: "); + buf.append(dfsStack.peakLong(size - i - 5)); + buf.append(" tidx: "); + buf.append(dfsStack.peakInt(size - i - 3)); + buf.append(" lowLink: "); + buf.append(dfsStack.peakLong(size - i - 7) - DiskGraph.MAX_PTR); + buf.append("]\n"); + // Increase i by the number of elements peaked + i += 7; + } else if (DiskGraph.isFilePointer(topElement)) { + final long location = topElement; + buf.append("succ ["); + buf.append(" fp: "); + buf.append(dfsStack.peakLong(size - i - 5)); + buf.append(" tidx: "); + buf.append(dfsStack.peakInt(size - i - 3)); + buf.append(" location: "); + buf.append(location); + buf.append("]\n"); + // Increase i by the number of elements peaked + i += 5; + } else if (topElement >= DiskGraph.MAX_PTR) { + final long pLowLink = topElement - DiskGraph.MAX_PTR; + buf.append("pLowLink: "); + buf.append(pLowLink); + buf.append("\n"); + i += 2; + } + } + // Assert all elements are used up + assert i == size; + return buf.toString(); + } + } } diff --git a/tlatools/src/tlc2/tool/liveness/Liveness.java b/tlatools/src/tlc2/tool/liveness/Liveness.java index 9396869f9d55dcbe0d9b71621d050bb1fde09db9..77a71cf37aaae2960df20d5020adb62e8050d961 100644 --- a/tlatools/src/tlc2/tool/liveness/Liveness.java +++ b/tlatools/src/tlc2/tool/liveness/Liveness.java @@ -19,29 +19,30 @@ import tlc2.output.EC; import tlc2.output.MP; import tlc2.tool.Action; import tlc2.tool.BuiltInOPs; -import tlc2.tool.ContextEnumerator; import tlc2.tool.EvalControl; -import tlc2.tool.Spec; +import tlc2.tool.IContextEnumerator; +import tlc2.tool.ITool; +import tlc2.tool.ModelChecker; +import tlc2.tool.Specs; import tlc2.tool.TLCState; -import tlc2.tool.Tool; import tlc2.tool.ToolGlobals; import tlc2.util.Context; import tlc2.util.Vect; -import tlc2.value.BoolValue; -import tlc2.value.FcnLambdaValue; -import tlc2.value.Value; +import tlc2.value.IBoolValue; +import tlc2.value.IFcnLambdaValue; +import tlc2.value.IValue; import util.Assert; import util.ToolIO; public class Liveness implements ToolGlobals, ASTConstants { - private static LiveExprNode astToLive(Tool tool, ExprNode expr, Context con, int level) { + private static LiveExprNode astToLive(ITool tool, ExprNode expr, Context con, int level) { if (level == 0) { - Value val = tool.eval(expr, con, TLCState.Empty); - if (!(val instanceof BoolValue)) { + IValue val = tool.eval(expr, con, TLCState.Empty); + if (!(val instanceof IBoolValue)) { Assert.fail(EC.TLC_EXPECTED_VALUE, new String[] { "boolean", expr.toString() }); } - return (((BoolValue) val).val) ? LNBool.TRUE : LNBool.FALSE; + return ((IBoolValue) val).getVal() ? LNBool.TRUE : LNBool.FALSE; } else if (level == 1) { return new LNStateAST(expr, con); } else { @@ -58,7 +59,7 @@ public class Liveness implements ToolGlobals, ASTConstants { * the predicate body with []p. For the moment, we require that arguments to * predicates be computable from its context. */ - private static LiveExprNode astToLive(Tool tool, ExprNode expr, Context con) { + private static LiveExprNode astToLive(ITool tool, ExprNode expr, Context con) { switch (expr.getKind()) { case OpApplKind: { OpApplNode expr1 = (OpApplNode) expr; @@ -80,7 +81,7 @@ public class Liveness implements ToolGlobals, ASTConstants { return astToLive(tool, expr1.getBody(), con1); } default: { - int level = Spec.getLevel(expr, con); + int level = Specs.getLevel(expr, con); if (level > 2) { Assert.fail(EC.TLC_LIVE_CANNOT_HANDLE_FORMULA, expr.toString()); } @@ -89,7 +90,7 @@ public class Liveness implements ToolGlobals, ASTConstants { } } - private static LiveExprNode astToLiveAppl(Tool tool, OpApplNode expr, Context con) { + private static LiveExprNode astToLiveAppl(ITool tool, OpApplNode expr, Context con) { ExprOrOpArgNode[] args = expr.getArgs(); int alen = args.length; SymbolNode opNode = expr.getOperator(); @@ -109,7 +110,7 @@ public class Liveness implements ToolGlobals, ASTConstants { FormalParamNode[] formals = opDef.getParams(); Context con1 = con; for (int i = 0; i < alen; i++) { - Value argVal = tool.eval(args[i], con, TLCState.Empty); + IValue argVal = tool.eval(args[i], con, TLCState.Empty); con1 = con1.cons(formals[i], argVal); } LiveExprNode res = astToLive(tool, opDef.getBody(), con1); @@ -121,12 +122,12 @@ public class Liveness implements ToolGlobals, ASTConstants { } catch (Exception e) { /* SKIP */ } } - } else if (val instanceof BoolValue) { - return (((BoolValue) val).val) ? LNBool.TRUE : LNBool.FALSE; + } else if (val instanceof IBoolValue) { + return ((IBoolValue) val).getVal() ? LNBool.TRUE : LNBool.FALSE; } if (opcode == 0) { - int level = Spec.getLevel(expr, con); + int level = Specs.getLevel(expr, con); if (level > 2) { Assert.fail(EC.TLC_LIVE_CANNOT_HANDLE_FORMULA, expr.toString()); } @@ -139,7 +140,7 @@ public class Liveness implements ToolGlobals, ASTConstants { { ExprNode body = (ExprNode) args[0]; try { - ContextEnumerator Enum = tool.contexts(expr, con, TLCState.Empty, TLCState.Empty, EvalControl.Clear); + IContextEnumerator Enum = tool.contexts(expr, con, TLCState.Empty, TLCState.Empty, EvalControl.Clear); Context con1; LNDisj res = new LNDisj(0); while ((con1 = Enum.nextElement()) != null) { @@ -152,8 +153,9 @@ public class Liveness implements ToolGlobals, ASTConstants { } return astToLive(tool, expr, con, level); } catch (Exception e) { + // Catching Exception here seem dangerous // Assert.printStack(e); - int level = Spec.getLevel(expr, con); + int level = Specs.getLevel(expr, con); if (level > 2) { Assert.fail(EC.TLC_LIVE_CANNOT_HANDLE_FORMULA, expr.toString()); ; @@ -165,7 +167,7 @@ public class Liveness implements ToolGlobals, ASTConstants { { ExprNode body = (ExprNode) args[0]; try { - ContextEnumerator Enum = tool.contexts(expr, con, TLCState.Empty, TLCState.Empty, EvalControl.Clear); + IContextEnumerator Enum = tool.contexts(expr, con, TLCState.Empty, TLCState.Empty, EvalControl.Clear); Context con1; LNConj res = new LNConj(0); while ((con1 = Enum.nextElement()) != null) { @@ -178,11 +180,15 @@ public class Liveness implements ToolGlobals, ASTConstants { } return astToLive(tool, expr, con, level); } catch (Exception e) { + // Catching Exception here seem dangerous // Assert.printStack(e); - int level = Spec.getLevel(expr, con); + int level = Specs.getLevel(expr, con); if (level > 2) { - Assert.fail(EC.TLC_LIVE_CANNOT_HANDLE_FORMULA, expr.toString()); - ; + if (e instanceof Assert.TLCRuntimeException) { + Assert.fail(EC.TLC_LIVE_CANNOT_HANDLE_FORMULA, new String[] {expr.toString(), e.getMessage()}); + } else { + Assert.fail(EC.TLC_LIVE_CANNOT_HANDLE_FORMULA, expr.toString()); + } } return astToLive(tool, expr, con, level); } @@ -216,20 +222,21 @@ public class Liveness implements ToolGlobals, ASTConstants { case OPCODE_fa: // FcnApply { try { - Value fval = tool.eval(args[0], con, TLCState.Empty); - if (fval instanceof FcnLambdaValue) { - FcnLambdaValue fcn = (FcnLambdaValue) fval; - if (fcn.fcnRcd == null) { + IValue fval = tool.eval(args[0], con, TLCState.Empty); + if (fval instanceof IFcnLambdaValue) { + IFcnLambdaValue fcn = (IFcnLambdaValue) fval; + if (!fcn.hasRcd()) { // this could be a bug, since con1 is created but not // used // SZ Jul 13, 2009: removed to kill the warning // SZ Feb 20, 2009: variable never read locally // Context con1 = tool.getFcnContext(fcn, args, con, TLCState.Empty, TLCState.Empty, EvalControl.Clear); - return astToLive(tool, (ExprNode) fcn.body, con); + return astToLive(tool, (ExprNode) fcn.getBody(), con); } } } catch (Exception e) { /* SKIP */ + // Swallowing Exception here seem dangerous } int level = expr.getLevel(); if (level > 2) { @@ -292,8 +299,9 @@ public class Liveness implements ToolGlobals, ASTConstants { } case OPCODE_leadto: { // F ~> G equals [](F => <>G), however TLC does not have an - // implementation for logical implication. Thus, the rule of material - // implication is used to transform it into a disjunct. + // implementation for logical implication. Thus, the rule of + // material implication ("->") is used to transform it into a + // disjunct. LiveExprNode lnLeft = astToLive(tool, (ExprNode) args[0], con); LiveExprNode lnRight = astToLive(tool, (ExprNode) args[1], con); // expand a ~> b into [](-a \/ <>b) @@ -308,6 +316,12 @@ public class Liveness implements ToolGlobals, ASTConstants { LiveExprNode lnArg = astToLive(tool, (ExprNode) args[0], con); return new LNEven(lnArg); } + case OPCODE_aa: { // AngleAct <A>_e + assert Specs.getLevel(expr, con) == 2; + final ExprNode body = (ExprNode) args[0]; // the A in <<A>>_e + final ExprNode subs = (ExprNode) args[1]; // the e in <<A>>_e + return new LNAction(body, con, subs, false); + } // The following case added by LL on 13 Nov 2009 to handle subexpression // names. @@ -315,166 +329,34 @@ public class Liveness implements ToolGlobals, ASTConstants { return astToLive(tool, (ExprNode) args[0], con); } default: { - // We handle all the other built-in operators here. - int level = Spec.getLevel(expr, con); + // We handle all the other built-in operators here. Surprisingly, even OPCODE_aa + // (AngleAct <A>_e) is handled here and not as the dedicated case statement below + // such that e gets passed as subscript to LNAction: + // + // case OPCODE_aa: { // AngleAct <A>_e + // assert Spec.getLevel(expr, con) == 2; + // final ExprNode body = (ExprNode) args[0]; // the A in <<A>>_e + // final ExprNode subscript = (ExprNode) args[1]; // the e in <<A>>_e + // return new LNAction(body, con, subscript, false); + // } + // + // The default handling here results in LNAction#subscript to be null skipping + // the subscript related branch in LNAction#eval(Tool, TLCState, TLCState). This + // poses no problem though because Tool#evalAppl eventually checks if e' = e. + int level = Specs.getLevel(expr, con); if (level > 2) { Assert.fail(EC.TLC_LIVE_CANNOT_HANDLE_FORMULA, expr.toString()); - ; } return astToLive(tool, expr, con, level); } } } - /** - * Given a starting TBGraphNode, constructTableau constructs the tableau for - * it. Read MP for details. It returns a list of all the nodes in the - * tableau graph. - */ - private static TBGraph constructTableau(LiveExprNode tf, int idx) { - TBGraph allnodes = new TBGraph(tf); - TBPar initTerms = new TBPar(1); - initTerms.addElement(tf); - TBParVec pars = particleClosure(initTerms); - - for (int i = 0; i < pars.size(); i++) { - TBGraphNode gn = new TBGraphNode(pars.parAt(i)); - allnodes.addElement(gn); - } - allnodes.setInitCnt(allnodes.size()); - // We now repeatedly compute the outlinks of each node: - for (int i = 0; i < allnodes.size(); i++) { - TBGraphNode gnSrc = (TBGraphNode) allnodes.elementAt(i); - TBPar imps = gnSrc.getPar().impliedSuccessors(); - TBParVec succs = particleClosure(imps); - for (int j = 0; j < succs.size(); j++) { - TBPar par = succs.parAt(j); - TBGraphNode gnDst = findOrCreateNode(allnodes, par); - gnSrc.nexts.addElement(gnDst); - } - } - // Assign each node in the tableau an index. - for (int i = 0; i < allnodes.size(); i++) { - allnodes.getNode(i).index = idx++; - } - return allnodes; - } - - /** - * The method findOrCreateNode, given a list of particles, either finds the - * particle in that list, or creates a new one and puts it in the list. If - * it does create a node, then it also sticks that node into allnodes. - */ - private static TBGraphNode findOrCreateNode(Vect allnodes, TBPar par) { - for (int i = 0; i < allnodes.size(); i++) { - TBGraphNode gn = (TBGraphNode) allnodes.elementAt(i); - if (par.equals(gn.getPar())) { - return gn; - } - } - TBGraphNode gn = new TBGraphNode(par); - allnodes.addElement(gn); - return gn; - } - - /** - * The method particleClosure, given a list of terms (initially just a - * single term), returns a list of all particles containing those terms. - * It's a recursive tree search. - */ - private static TBParVec particleClosure(TBPar terms) { - TBPar positive_closure = terms.positiveClosure(); - Vect alphas = positive_closure.alphaTriples(); - Vect betas = positive_closure.betaTriples(); - return particleClosure(terms, alphas, betas); - } - - private static TBParVec particleClosure(TBPar terms, Vect alphas, Vect betas) { - // if terms is not locally consistent, then terminate. - if (!terms.isLocallyConsistent()) { - return new TBParVec(0); - } - // if terms is not alpha-closed, then close it. - // first, try alpha expansion - TBPar terms1 = terms; - for (int i = 0; i < terms1.size(); i++) { - LiveExprNode ln = terms1.exprAt(i); - LiveExprNode k1 = null, k2 = null; - if (ln instanceof LNAll) { - k1 = ((LNAll) ln).getBody(); - k2 = new LNNext(ln); - } else if (ln instanceof LNConj) { - k1 = ((LNConj) ln).getBody(0); - k2 = ((LNConj) ln).getBody(1); - } - if (k1 != null) { - if (terms1.member(k1)) { - if (!terms1.member(k2)) { - terms1 = terms1.append(k2); - } - } else if (terms1.member(k2)) { - terms1 = terms1.append(k1); - } else { - terms1 = terms1.append(k1, k2); - } - } - } - // second, try alpha^-1 expansion - boolean done; - do { - done = true; - for (int i = 0; i < alphas.size(); i++) { - TBTriple alpha = (TBTriple) alphas.elementAt(i); - if (terms1.member(alpha.getB()) && terms1.member(alpha.getC()) && !terms1.member(alpha.getA())) { - terms1.addElement(alpha.getA()); - done = false; - } - } - } while (!done); - // finally, recurse only when locally consistent - if ((terms1.size() > terms.size()) && (!terms1.isLocallyConsistent())) { - return new TBParVec(0); - } - return particleClosureBeta(terms1, alphas, betas); - } - - private static TBParVec particleClosureBeta(TBPar terms, Vect alphas, Vect betas) { - // try a beta expansion - for (int i = 0; i < terms.size(); i++) { - LiveExprNode ln = terms.exprAt(i); - LiveExprNode k1 = null, k2 = null; - if (ln instanceof LNEven) { - k1 = ((LNEven) ln).getBody(); - k2 = new LNNext(ln); - } else if (ln instanceof LNDisj) { - k1 = ((LNDisj) ln).getBody(0); - k2 = ((LNDisj) ln).getBody(1); - } - if ((k1 != null) && !terms.member(k1) && !terms.member(k2)) { - TBParVec ps1 = particleClosure(terms.append(k1), alphas, betas); - TBParVec ps2 = particleClosure(terms.append(k2), alphas, betas); - return ps1.union(ps2); - } - } - // try a beta^-1 expansion - for (int i = 0; i < betas.size(); i++) { - TBTriple beta = (TBTriple) betas.elementAt(i); - if ((terms.member(beta.getB()) || terms.member(beta.getC())) && !terms.member(beta.getA())) { - return particleClosure(terms.append(beta.getA()), alphas, betas); - } - } - // if there are not any more expansions to do, return the terms - // we've got as the only particle in a list of particles. - TBParVec res = new TBParVec(1); - res.addElement(terms); - return res; - } - /** * Parse the temporals and impliedTemporals given in the config file. It * returns null if there is nothing to check. */ - private static LiveExprNode parseLiveness(Tool tool) { + private static LiveExprNode parseLiveness(ITool tool) { Action[] fairs = tool.getTemporals(); LNConj lnc = new LNConj(fairs.length); for (int i = 0; i < fairs.length; i++) { @@ -512,15 +394,43 @@ public class Liveness implements ToolGlobals, ASTConstants { * manner in which things should ultimately be checked. This method returns * a handle, which can subsequently be passed to the other liveness things. * - * Theory: we're looking for counterexamples to: spec /\ livespec => []inv - * /\ livecheck i.e. (spec /\ livespec /\ <>-inv) \/ (spec /\ livespec /\ - * -livecheck) The first half of this disjunction (inv) is already checked - * by the model checker on the fly. We're converting the second half into - * normal form. We actually omit spec in what we produce. It will be left - * implicit. So, the only job is to turn livespec /\ -livecheck: live1 /\ - * live2 ... /\ (-check1 \/ -check2 ...) into normal form. + * Theory: we're looking for counterexamples to: + * + * <pre> + * spec /\ livespec => []inv /\ livecheck + * </pre> + * + * i.e. + * + * <pre> + * \/ (spec /\ livespec /\ <>-inv) + * \/ (spec /\ livespec /\ -livecheck) + * </pre> + * + * <p> + * The first half of this disjunction (inv) is already checked by the model + * checker on the fly (@see + * {@link ModelChecker#doNext(TLCState, tlc2.util.ObjLongTable)}). + * <p> + * We're converting the second half into <i>normal form</i>. We actually + * omit spec in what we produce. It will be left implicit. So, the only job + * is to turn: + * + * <pre> + * livespec /\ -livecheck + * </pre> + * + * into: + * + * <pre> + * live1 /\ live2 ... /\ (-check1 \/ -check2 ...) + * </pre> + * + * into <i>normal form</i>. livespec corresponds to the spec's + * <i>fairness</i> formulae where check1, check2, ... are the actual + * <i>liveness properties</i> to be checked. */ - public static OrderOfSolution[] processLiveness(Tool tool) { + public static OrderOfSolution[] processLiveness(final ITool tool) { LiveExprNode lexpr = parseLiveness(tool); if (lexpr == null) { @@ -538,20 +448,20 @@ public class Liveness implements ToolGlobals, ASTConstants { if ((lexpr instanceof LNBool) && !((LNBool) lexpr).b) { return new OrderOfSolution[0]; // must be unsatisfiable } - LNDisj dnf = (lexpr instanceof LNDisj) ? (LNDisj) lexpr : (new LNDisj(lexpr)); + final LNDisj dnf = (lexpr instanceof LNDisj) ? (LNDisj) lexpr : (new LNDisj(lexpr)); // Now we will turn DNF into a format that can be tested by the // tableau method. The first step is to collect everything into // pems+lexps: listof-(listof-<>[],[]<> /\ tf) - OSExprPem[] pems = new OSExprPem[dnf.getCount()]; - LiveExprNode[] tfs = new LiveExprNode[dnf.getCount()]; + final OSExprPem[] pems = new OSExprPem[dnf.getCount()]; + final LiveExprNode[] tfs = new LiveExprNode[dnf.getCount()]; for (int i = 0; i < dnf.getCount(); i++) { // Flatten junctions, because DNF may contain singleton junctions - LiveExprNode ln = dnf.getBody(i).flattenSingleJunctions(); - OSExprPem pem = new OSExprPem(); + final LiveExprNode ln = dnf.getBody(i).flattenSingleJunctions(); + final OSExprPem pem = new OSExprPem(); pems[i] = pem; if (ln instanceof LNConj) { - LNConj lnc2 = (LNConj) ln; + final LNConj lnc2 = (LNConj) ln; for (int j = 0; j < lnc2.getCount(); j++) { classifyExpr(lnc2.getBody(j), pem); } @@ -562,7 +472,7 @@ public class Liveness implements ToolGlobals, ASTConstants { if (pem.tfs.size() == 1) { tfs[i] = (LiveExprNode) pem.tfs.elementAt(0); } else if (pem.tfs.size() > 1) { - LNConj lnc2 = new LNConj(pem.tfs.size()); + final LNConj lnc2 = new LNConj(pem.tfs.size()); for (int j = 0; j < pem.tfs.size(); j++) { lnc2.addConj((LiveExprNode) pem.tfs.elementAt(j)); } @@ -579,13 +489,13 @@ public class Liveness implements ToolGlobals, ASTConstants { // haven't done any real rearrangement of them, apart from munging // up \/ and /\ above them. tfbin contains the different tf's. // pembin is a vect of vect-of-pems collecting each tf's pems. - TBPar tfbin = new TBPar(dnf.getCount()); - Vect pembin = new Vect(dnf.getCount()); + final TBPar tfbin = new TBPar(dnf.getCount()); + final Vect<Vect<OSExprPem>> pembin = new Vect<>(dnf.getCount()); for (int i = 0; i < dnf.getCount(); i++) { int found = -1; - LiveExprNode tf = tfs[i]; + final LiveExprNode tf = tfs[i]; for (int j = 0; j < tfbin.size() && found == -1; j++) { - LiveExprNode tf0 = tfbin.exprAt(j); + final LiveExprNode tf0 = tfbin.exprAt(j); if ((tf == null && tf0 == null) || (tf != null && tf0 != null && tf.equals(tf0))) { found = j; } @@ -593,23 +503,23 @@ public class Liveness implements ToolGlobals, ASTConstants { if (found == -1) { found = tfbin.size(); tfbin.addElement(tf); - pembin.addElement(new Vect()); + pembin.addElement(new Vect<OSExprPem>()); } - ((Vect) pembin.elementAt(found)).addElement(pems[i]); + ((Vect<OSExprPem>) pembin.elementAt(found)).addElement(pems[i]); } // We then create an OrderOfSolution for each tf in tfbin. - OrderOfSolution[] oss = new OrderOfSolution[tfbin.size()]; + final OrderOfSolution[] oss = new OrderOfSolution[tfbin.size()]; for (int i = 0; i < tfbin.size(); i++) { - LiveExprNode tf = tfbin.exprAt(i); + final LiveExprNode tf = tfbin.exprAt(i); if (tf == null) { - oss[i] = new OrderOfSolution(new LNEven[0], tool); + oss[i] = new OrderOfSolution(new LNEven[0]); } else { - LiveExprNode tf1 = tf.makeBinary(); - TBPar promises = new TBPar(10); + final LiveExprNode tf1 = tf.makeBinary(); + final TBPar promises = new TBPar(10); tf1.extractPromises(promises); - oss[i] = new OrderOfSolution(constructTableau(tf1, 0), new LNEven[promises.size()], tool); + oss[i] = new OrderOfSolution(new TBGraph(tf1), new LNEven[promises.size()]); for (int j = 0; j < promises.size(); j++) { oss[i].getPromises()[j] = (LNEven) promises.exprAt(j); } @@ -617,16 +527,14 @@ public class Liveness implements ToolGlobals, ASTConstants { // We lump all the pems into a single checkState and checkAct, // and oss[i].pems will simply be integer lookups into them. - Vect stateBin = new Vect(); - Vect actionBin = new Vect(); - Vect tfPems = (Vect) pembin.elementAt(i); + final Vect<LiveExprNode> stateBin = new Vect<>(); + final Vect<LiveExprNode> actionBin = new Vect<>(); + final Vect<OSExprPem> tfPems = (Vect<OSExprPem>) pembin.elementAt(i); oss[i].setPems(new PossibleErrorModel[tfPems.size()]); for (int j = 0; j < tfPems.size(); j++) { - OSExprPem pem = (OSExprPem) tfPems.elementAt(j); - oss[i].getPems()[j] = new PossibleErrorModel(); - oss[i].getPems()[j].AEAction = addToBin(pem.AEAction, actionBin); - oss[i].getPems()[j].AEState = addToBin(pem.AEState, stateBin); - oss[i].getPems()[j].EAAction = addToBin(pem.EAAction, actionBin); + final OSExprPem pem = (OSExprPem) tfPems.elementAt(j); + oss[i].getPems()[j] = new PossibleErrorModel(addToBin(pem.AEAction, actionBin), + addToBin(pem.AEState, stateBin), addToBin(pem.EAAction, actionBin)); } // Finally, store the bins with the order of solution. oss[i].setCheckState(new LiveExprNode[stateBin.size()]); @@ -649,7 +557,7 @@ public class Liveness implements ToolGlobals, ASTConstants { * Given a list of checks, ensures that the checks are in the bin. It * returns an array of index of the checks in the bin. */ - private static int addToBin(LiveExprNode check, Vect bin) { + private static int addToBin(LiveExprNode check, Vect<LiveExprNode> bin) { if (check == null) { return -1; } @@ -667,7 +575,7 @@ public class Liveness implements ToolGlobals, ASTConstants { return idx; } - private static int[] addToBin(Vect checks, Vect bin) { + private static int[] addToBin(Vect<LiveExprNode> checks, Vect<LiveExprNode> bin) { int[] index = new int[checks.size()]; for (int i = 0; i < checks.size(); i++) { LiveExprNode check = (LiveExprNode) checks.elementAt(i); @@ -682,7 +590,14 @@ public class Liveness implements ToolGlobals, ASTConstants { * tests. This method classifies an expression into <>[]act, []<>act, * []<>state, temporal formulas (without actions), or erroneous things. */ + // TODO Explore the idea to syntactically rewrite an LNActions A into a + // ordinary predicate and the next state operator ()A in the tableau. private static void classifyExpr(LiveExprNode ln, OSExprPem pem) { + // TLC is clever enough to optimize the case where some temporal formula + // can be handled WITHOUT a tableau. In this case, the state graph IS + // the behavior graph and thus the overall verification time is reduced. + // Additionally, the tableau generation does not support formulas + // containing (nested) LNActions. if (ln instanceof LNEven) { LiveExprNode ln1 = ((LNEven) ln).getBody(); if (ln1 instanceof LNAll) { @@ -710,6 +625,9 @@ public class Liveness implements ToolGlobals, ASTConstants { if (ln.containAction()) { Assert.fail(EC.TLC_LIVE_WRONG_FORMULA_FORMAT); } + // If we get here (because of a temporal formula), at tableau is + // consequently going to be created. This part corresponds to the + // ideas in the MP book. pem.tfs.addElement(ln); } @@ -726,16 +644,16 @@ public class Liveness implements ToolGlobals, ASTConstants { * PossibleErrorModel and OrderOfSolution. */ private static class OSExprPem { - Vect EAAction; // <>[]action's - Vect AEState; // []<>state's - Vect AEAction; // []<>action's - Vect tfs; // other temp formulae with no actions + Vect<LiveExprNode> EAAction; // <>[]action's + Vect<LiveExprNode> AEState; // []<>state's + Vect<LiveExprNode> AEAction; // []<>action's + Vect<LiveExprNode> tfs; // other temp formulae with no actions public OSExprPem() { - this.EAAction = new Vect(); - this.AEState = new Vect(); - this.AEAction = new Vect(); - this.tfs = new Vect(); + this.EAAction = new Vect<>(); + this.AEState = new Vect<>(); + this.AEAction = new Vect<>(); + this.tfs = new Vect<>(); } } diff --git a/tlatools/src/tlc2/tool/liveness/NoOpLiveCheck.java b/tlatools/src/tlc2/tool/liveness/NoOpLiveCheck.java index 5466402f300ba70f4faf0ad69f290186d745f80c..02d93ca04fd0b8b85b7e7e7c4ae244d050589756 100644 --- a/tlatools/src/tlc2/tool/liveness/NoOpLiveCheck.java +++ b/tlatools/src/tlc2/tool/liveness/NoOpLiveCheck.java @@ -28,20 +28,21 @@ package tlc2.tool.liveness; import java.io.IOException; +import tlc2.output.EC; +import tlc2.tool.ITool; import tlc2.tool.StateVec; import tlc2.tool.TLCState; -import tlc2.tool.Tool; -import tlc2.util.LongVec; +import tlc2.util.SetOfStates; import tlc2.util.statistics.DummyBucketStatistics; import tlc2.util.statistics.IBucketStatistics; public class NoOpLiveCheck implements ILiveCheck { - private final Tool tool; + private final ITool tool; private final String metadir; private final IBucketStatistics stats; - public NoOpLiveCheck(Tool tool, String metadir) { + public NoOpLiveCheck(ITool tool, String metadir) { this.tool = tool; this.metadir = metadir; this.stats = new DummyBucketStatistics(); @@ -50,33 +51,36 @@ public class NoOpLiveCheck implements ILiveCheck { /* (non-Javadoc) * @see tlc2.tool.liveness.ILiveCheck#addInitState(tlc2.tool.TLCState, long) */ - public void addInitState(TLCState state, long stateFP) { + public void addInitState(ITool tool, TLCState state, long stateFP) { } /* (non-Javadoc) - * @see tlc2.tool.liveness.ILiveCheck#addNextState(tlc2.tool.TLCState, long, tlc2.tool.StateVec, tlc2.util.LongVec) + * @see tlc2.tool.liveness.ILiveCheck#addNextState(tlc2.tool.TLCState, long, tlc2.util.SetOfStates) */ - public void addNextState(TLCState s0, long fp0, StateVec nextStates, LongVec nextFPs) throws IOException { + public void addNextState(ITool tool, TLCState s0, long fp0, SetOfStates nextStates) throws IOException { } /* (non-Javadoc) - * @see tlc2.tool.liveness.ILiveCheck#check(boolean) + * @see tlc2.tool.liveness.ILiveCheck#doLiveCheck() */ - public boolean check(boolean forceCheck) throws Exception { - return true; + public boolean doLiveCheck() { + return false; } - /* (non-Javadoc) - * @see tlc2.tool.liveness.ILiveCheck#finalCheck() - */ - public boolean finalCheck() throws Exception { - return true; + @Override + public int check(ITool tool, boolean forceCheck) throws Exception { + return EC.NO_ERROR; + } + + @Override + public int finalCheck(ITool tool) throws Exception { + return EC.NO_ERROR; } /* (non-Javadoc) * @see tlc2.tool.liveness.ILiveCheck#checkTrace(tlc2.tool.StateVec) */ - public void checkTrace(StateVec trace) throws IOException, InterruptedException { + public void checkTrace(ITool tool, StateVec trace) throws IOException, InterruptedException { } /* (non-Javadoc) @@ -89,7 +93,7 @@ public class NoOpLiveCheck implements ILiveCheck { /* (non-Javadoc) * @see tlc2.tool.liveness.ILiveCheck#getTool() */ - public Tool getTool() { + public ITool getTool() { return tool; } diff --git a/tlatools/src/tlc2/tool/liveness/NodePtrTable.java b/tlatools/src/tlc2/tool/liveness/NodePtrTable.java index 7c8a90a87d9eeee3fcdb8b68a7548ae48d6aa8ee..e5a6a1d9368692c613b4b320b00586e62647ac30 100644 --- a/tlatools/src/tlc2/tool/liveness/NodePtrTable.java +++ b/tlatools/src/tlc2/tool/liveness/NodePtrTable.java @@ -4,6 +4,12 @@ package tlc2.tool.liveness; +import tlc2.output.EC; +import tlc2.output.MP; + +/** + * @see TableauNodePtrTable + */ public class NodePtrTable { private int count; @@ -105,20 +111,56 @@ public class NodePtrTable { /* Double the table when the table is full by the threshhold. */ private final void grow() { - this.length = 2 * this.length + 1; - this.thresh = (int) (this.length * 0.75); - this.count = 0; - long[] oldKeys = this.keys; - long[] oldElems = this.elems; - this.keys = new long[this.length]; - this.elems = new long[this.length]; - for (int i = 0; i < this.length; i++) { - this.elems[i] = -1; - } - for (int i = 0; i < oldElems.length; i++) { - long elem = oldElems[i]; - if (elem != -1) { - this.put(oldKeys[i], elem); + final int newLength = 2 * this.length + 1; + grow(newLength); + } + + private final void grow(final int newLength) { + try { + final long[] oldKeys = this.keys; + final long[] oldElems = this.elems; + this.keys = new long[newLength]; + this.elems = new long[newLength]; + for (int i = 0; i < newLength; i++) { + this.elems[i] = -1; + } + this.count = 0; + for (int i = 0; i < oldElems.length; i++) { + final long elem = oldElems[i]; + if (elem != -1) { + int loc = ((int) oldKeys[i] & 0x7FFFFFFF) % newLength; + while (true) { + if (this.elems[loc] == -1) { + this.keys[loc] = oldKeys[i]; + this.elems[loc] = elem; + this.count++; + break; + } + if (this.keys[loc] == oldKeys[i]) { + this.elems[loc] = elem; + break; + } + loc = (loc + 1) % newLength; + } + } + } + this.length = newLength; + this.thresh = (int) (newLength * 0.75); + } catch (OutOfMemoryError t) { + // Handle OOM error locally because grow is on the code path of safety checking + // (LiveCheck#addInit/addNext...). + System.gc(); + if (newLength <= this.length + 1) { + MP.printError(EC.SYSTEM_OUT_OF_MEMORY, t); + System.exit(1); + } + try { + // It doesn't buy us much, but - as fallback - do not grow capacity + // exponentially. + grow(newLength - (newLength >> 2)); + } catch (OutOfMemoryError inner) { + MP.printError(EC.SYSTEM_OUT_OF_MEMORY, inner); + System.exit(1); } } } diff --git a/tlatools/src/tlc2/tool/liveness/NoopLivenessStateWriter.java b/tlatools/src/tlc2/tool/liveness/NoopLivenessStateWriter.java new file mode 100644 index 0000000000000000000000000000000000000000..5ad36cd603101b81cc7b00c3e7ee6e000e0de018 --- /dev/null +++ b/tlatools/src/tlc2/tool/liveness/NoopLivenessStateWriter.java @@ -0,0 +1,137 @@ +/******************************************************************************* + * Copyright (c) 2016 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.liveness; + +import java.io.IOException; + +import tlc2.tool.Action; +import tlc2.tool.TLCState; +import tlc2.util.BitVector; + +public class NoopLivenessStateWriter implements ILivenessStateWriter { + + /* (non-Javadoc) + * @see tlc2.util.StateWriter#writeState(tlc2.tool.TLCState) + */ + public final void writeState(final TLCState state) { + // noop + } + + /* (non-Javadoc) + * @see tlc2.util.StateWriter#writeState(tlc2.tool.TLCState, tlc2.tool.TLCState, boolean) + */ + public final void writeState(final TLCState state, final TLCState successor, final boolean successorStateIsNew) { + // noop + } + + /* (non-Javadoc) + * @see tlc2.util.IStateWriter#writeState(tlc2.tool.TLCState, tlc2.tool.TLCState, boolean, tlc2.util.IStateWriter.Visualization) + */ + public void writeState(TLCState state, TLCState successor, boolean successorStateIsNew, Visualization visualization) { + // noop + } + + /* (non-Javadoc) + * @see tlc2.util.IStateWriter#close() + */ + public final void close() { + // noop + } + + /* (non-Javadoc) + * @see tlc2.tool.liveness.ILivenessStateWriter#writeState(tlc2.tool.TLCState, tlc2.tool.liveness.TBGraphNode) + */ + public final void writeState(TLCState state, TBGraphNode tableauNode) { + // noop + } + + /* (non-Javadoc) + * @see tlc2.tool.liveness.ILivenessStateWriter#writeState(tlc2.tool.TLCState, tlc2.tool.liveness.TBGraphNode, tlc2.tool.TLCState, tlc2.tool.liveness.TBGraphNode, tlc2.util.BitVector, int, int, boolean) + */ + public final void writeState(TLCState state, TBGraphNode tableauNode, TLCState successor, + TBGraphNode tableauNodeSuccessor, BitVector actionChecks, int from, int to, boolean successorStateIsNew) { + // noop + } + + /* (non-Javadoc) + * @see tlc2.tool.liveness.ILivenessStateWriter#writeState(tlc2.tool.TLCState, tlc2.tool.liveness.TBGraphNode, tlc2.tool.TLCState, tlc2.tool.liveness.TBGraphNode, tlc2.util.BitVector, int, int, boolean, tlc2.util.IStateWriter.Visualization) + */ + public final void writeState(TLCState state, TBGraphNode tableauNode, TLCState successor, + TBGraphNode tableauNodeSuccessor, BitVector actionChecks, int from, int to, boolean successorStateIsNew, Visualization visulation) { + // noop + } + + /* (non-Javadoc) + * @see tlc2.util.IStateWriter#writeState(tlc2.tool.TLCState, tlc2.tool.TLCState, tlc2.util.BitVector, int, int, boolean) + */ + public void writeState(TLCState state, TLCState successor, BitVector actionChecks, int from, int to, boolean successorStateIsNew) { + // noop + } + + /* (non-Javadoc) + * @see tlc2.util.IStateWriter#writeState(tlc2.tool.TLCState, tlc2.tool.TLCState, tlc2.util.BitVector, int, int, boolean, tlc2.util.IStateWriter.Visualization) + */ + public void writeState(TLCState state, TLCState successor, BitVector actionChecks, int from, int to, boolean successorStateIsNew, + Visualization visulation) { + // noop + } + + /* (non-Javadoc) + * @see tlc2.util.IStateWriter#writeState(tlc2.tool.TLCState, tlc2.tool.TLCState, boolean, tlc2.tool.Action) + */ + public void writeState(TLCState state, TLCState successor, boolean successorStateIsNew, Action action) { + // noop + } + + /* (non-Javadoc) + * @see tlc2.util.IStateWriter#getDumpFileName() + */ + public String getDumpFileName() { + return ""; + } + + /* (non-Javadoc) + * @see tlc2.util.IStateWriter#isNoop() + */ + public boolean isNoop() { + return true; + } + + /* (non-Javadoc) + * @see tlc2.util.IStateWriter#isDot() + */ + @Override + public boolean isDot() { + return false; + } + + /* (non-Javadoc) + * @see tlc2.util.IStateWriter#snapshot() + */ + @Override + public void snapshot() throws IOException { + } +} diff --git a/tlatools/src/tlc2/tool/liveness/OrderOfSolution.java b/tlatools/src/tlc2/tool/liveness/OrderOfSolution.java index 74b935cd9b0d9ca16de444c098eaec23c492d811..0c0513de92d0ff069a00b1e1e87e0c461619c8da 100644 --- a/tlatools/src/tlc2/tool/liveness/OrderOfSolution.java +++ b/tlatools/src/tlc2/tool/liveness/OrderOfSolution.java @@ -7,8 +7,8 @@ package tlc2.tool.liveness; import java.io.PrintStream; +import tlc2.tool.ITool; import tlc2.tool.TLCState; -import tlc2.tool.Tool; import tlc2.util.BitVector; /* @@ -48,20 +48,29 @@ public class OrderOfSolution { * in the temporal formulas. */ private final TBGraph tableau; // tableau graph + + /** + * A promise φ that a property expressed by a formula will eventually hold. + * + * @see Page 409ff of Manna & Pnueli + * "Temporal Verification of Reactive Systems: Safety" + * <p> + * @see https://books.google.de/books?id=lfIGCAAAQBAJ&lpg=PR5&ots=_YBX09o5tM + * &dq=manna%20pnueli%20temporal%20verification%20of%20reactive% + * 20systems%20safety%20doi&pg=PA409 + */ private final LNEven[] promises; // promises in the tableau private LiveExprNode[] checkState; // state subformula private LiveExprNode[] checkAction; // action subformula private PossibleErrorModel[] pems; - private final Tool tool; - public OrderOfSolution(final LNEven[] livenessEventually, Tool aTool) { - this(null, livenessEventually, aTool); + public OrderOfSolution(final LNEven[] livenessEventually) { + this(null, livenessEventually); } - public OrderOfSolution(final TBGraph aTableau, final LNEven[] livenessEventually, Tool aTool) { + public OrderOfSolution(final TBGraph aTableau, final LNEven[] livenessEventually) { tableau = aTableau; promises = livenessEventually; - this.tool = aTool; } public final void printPromises(PrintStream ps) { @@ -124,7 +133,7 @@ public class OrderOfSolution { return checkState; } - public boolean[] checkState(final TLCState state) { + public boolean[] checkState(ITool tool, final TLCState state) { final boolean[] result = new boolean[checkState.length]; for (int i = 0; i < checkState.length; i++) { result[i] = checkState[i].eval(tool, state, null); @@ -137,7 +146,7 @@ public class OrderOfSolution { } // legacy LiveCheck1 - public boolean[] checkAction(final TLCState state0, final TLCState state1) { + public boolean[] checkAction(ITool tool, final TLCState state0, final TLCState state1) { final boolean[] result = new boolean[checkAction.length]; for (int i = 0; i < checkAction.length; i++) { result[i] = checkAction[i].eval(tool, state0, state1); @@ -145,7 +154,7 @@ public class OrderOfSolution { return result; } - public BitVector checkAction(final TLCState state0, final TLCState state1, final BitVector result, final int offset) { + public BitVector checkAction(ITool tool, final TLCState state0, final TLCState state1, final BitVector result, final int offset) { for (int i = 0; i < checkAction.length; i++) { if (checkAction[i].eval(tool, state0, state1)) { result.set(offset + i); diff --git a/tlatools/src/tlc2/tool/liveness/PossibleErrorModel.java b/tlatools/src/tlc2/tool/liveness/PossibleErrorModel.java index eba04bfdd62248c8407dbac43275b0f64ba6531a..c8310971a2a7561f175212807097dc3345c4cf20 100644 --- a/tlatools/src/tlc2/tool/liveness/PossibleErrorModel.java +++ b/tlatools/src/tlc2/tool/liveness/PossibleErrorModel.java @@ -5,10 +5,59 @@ package tlc2.tool.liveness; +import tlc2.util.BitVector; + +/** + * A {@link PossibleErrorModel} is technically a lookup table into its + * corresponding {@link OrderOfSolution} Eventually Always, Infinitely often + * action checks and infinitely often state checks. The + * {@link PossibleErrorModel} basically selects the set of checks stored in + * {@link OrderOfSolution} that a relevant for this {@link PossibleErrorModel}. + * The mapping is done by storing the integer index of the relevant + * {@link OrderOfSolution} checks in EAAction, AEState and AEAction. + * <p> + * The integer index is used in two places: Lookup the set of relevant OOS + * checks + * <ul> + * <li>When the states and transitions (see + * {@link GraphNode#addTransition(long, int, int, int, BitVector, int, int)}) + * are evaluated during "normal" model checking the PEM index selects the set of + * relevant OOS checks. The result of each check is stored in a per node + * {@link BitVector} index again by the corresponding PEM mapping.</li> + * <li>During liveness checking (see {@link LiveWorker#checkSccs()} when the + * pre-computed check result is being looked up in the node's {@link BitVector}. + * </li> + * </ul> + * <p> + * This class can be seen as a C-like struct (PEM's method are all related to + * OOS#toString). + * <p> + * <p> + * Theorie-wise, a {@link PossibleErrorModel} (there are as many PEMs as + * disjuncts in the normal form (DNF) produced by + * {@link Liveness#processLiveness(tlc2.tool.Tool)}) represents the negation of + * the stated liveness properties. It is then applied onto the discovered + * strongly-connected-components in {@link LiveWorker#checkComponent()} to check + * if the PEM is "P-satisfiable". If it is, it shows that a violation of the + * original liveness properties has been found (meaning they can't be + * "P-valid"). A counter-example can be created. + * <p> + * A simplified example:<br> + * Suppose Spec has the (trivial) liveness property '<>[](x \in Nat)' ( + * "eventually the variable x is always a natural number") defined. This will + * result in a PEM looking for a state such that x \notin Nat. If there exists + * such a behavior (SCC), it would violate the liveness property. + */ public class PossibleErrorModel { - int[] EAAction; // <>[]act's (Eventually Always actions) (Strong fairness) - int[] AEState; // []<>state's (Infinitely Often states) - int[] AEAction; // []<>act's (Infinitely Often actions) (Weak fairness) + final int[] EAAction; // <>[]act's (Eventually Always actions) (Strong fairness) + final int[] AEState; // []<>state's (Infinitely Often states) + final int[] AEAction; // []<>act's (Infinitely Often actions) (Weak fairness) + + public PossibleErrorModel(int[] aeAction, int[] aeState, int[] eaAction) { + this.AEAction = aeAction; + this.AEState = aeState; + this.EAAction = eaAction; + } public final boolean isEmpty() { return (this.EAAction.length == 0 && this.AEState.length == 0 && this.AEAction.length == 0); diff --git a/tlatools/src/tlc2/tool/liveness/TBGraph.java b/tlatools/src/tlc2/tool/liveness/TBGraph.java index c5114097b7cc2d9ae62785ff0fe295e6bc0d7436..ecd714ec7fe1744af1ea26ff3aaf1a4837bd6ec4 100644 --- a/tlatools/src/tlc2/tool/liveness/TBGraph.java +++ b/tlatools/src/tlc2/tool/liveness/TBGraph.java @@ -7,16 +7,69 @@ package tlc2.tool.liveness; import tlc2.util.Vect; -public class TBGraph extends Vect { +@SuppressWarnings("serial") +public class TBGraph extends Vect<TBGraphNode> { /** * TBGraph represents the nodes in the tableau graph. */ public final LiveExprNode tf; private int initCnt; - public TBGraph(LiveExprNode tf) { + public TBGraph() { + this.tf = null; + this.initCnt = 0; + } + + /** + * Given a starting TBGraphNode, constructTableau constructs the tableau for + * it. Read MP for details. It returns a list of all the nodes in the + * tableau graph. + */ + public TBGraph(final LiveExprNode tf) { this.tf = tf; this.initCnt = 0; + + final TBPar initTerms = new TBPar(1); + initTerms.addElement(tf); + final TBParVec pars = initTerms.particleClosure(); + + for (int i = 0; i < pars.size(); i++) { + final TBGraphNode gn = new TBGraphNode(pars.parAt(i)); + this.addElement(gn); + } + this.setInitCnt(this.size()); + // We now repeatedly compute the outlinks of each node: + for (int i = 0; i < this.size(); i++) { + final TBGraphNode gnSrc = (TBGraphNode) this.elementAt(i); + final TBPar imps = gnSrc.getPar().impliedSuccessors(); + final TBParVec succs = imps.particleClosure(); + for (int j = 0; j < succs.size(); j++) { + final TBPar par = succs.parAt(j); + final TBGraphNode gnDst = findOrCreateNode(par); + gnSrc.nexts.addElement(gnDst); + } + } + // Assign each node in the tableau an index. + for (int i = 0; i < this.size(); i++) { + this.getNode(i).setIndex(i); + } + } + + /** + * The method findOrCreateNode, given a list of particles, either finds the + * particle in that list, or creates a new one and puts it in the list. If + * it does create a node, then it also sticks that node into allnodes. + */ + private TBGraphNode findOrCreateNode(final TBPar par) { + for (int i = 0; i < this.size(); i++) { + final TBGraphNode gn = (TBGraphNode) this.elementAt(i); + if (par.equals(gn.getPar())) { + return gn; + } + } + final TBGraphNode gn = new TBGraphNode(par); + this.addElement(gn); + return gn; } public TBGraphNode getNode(int idx) { @@ -30,6 +83,10 @@ public class TBGraph extends Vect { public int getInitCnt() { return this.initCnt; } + + private boolean isInitNode(TBGraphNode aNode) { + return aNode.getIndex() < getInitCnt(); + } public final void toString(StringBuffer sb, String padding) { for (int i = 0; i < this.size(); i++) { @@ -39,7 +96,7 @@ public class TBGraph extends Vect { tnode.getPar().toString(sb, padding); sb.append(" --> "); for (int j = 0; j < tnode.nexts.size(); j++) { - sb.append(tnode.nextAt(j).index + " "); + sb.append(tnode.nextAt(j).getIndex() + " "); } sb.append("\n"); } @@ -60,7 +117,8 @@ public class TBGraph extends Vect { sb.append("nodesep = 0.7\n"); sb.append("rankdir=LR;\n"); // Left to right rather than top to bottom for(int i = 0; i < size(); i++) { - sb.append(getNode(i).toDotViz()); + final TBGraphNode node = getNode(i); + sb.append(node.toDotViz(isInitNode(node))); } sb.append("}"); return sb.toString(); diff --git a/tlatools/src/tlc2/tool/liveness/TBGraphNode.java b/tlatools/src/tlc2/tool/liveness/TBGraphNode.java index 3cd44fa6c3fa300ff509c8bf654ca04e96384471..d3177d8fb3cd5bb16fd728db6f897850d7d1b099 100644 --- a/tlatools/src/tlc2/tool/liveness/TBGraphNode.java +++ b/tlatools/src/tlc2/tool/liveness/TBGraphNode.java @@ -5,8 +5,8 @@ package tlc2.tool.liveness; +import tlc2.tool.ITool; import tlc2.tool.TLCState; -import tlc2.tool.Tool; import tlc2.util.SetOfLong; import tlc2.util.Vect; @@ -19,8 +19,8 @@ public class TBGraphNode { * if a state-node is consistent with a tableau-node. */ private final TBPar par; // particle - public final Vect nexts; // outlinks - public int index; // unique id for this node + public final Vect<TBGraphNode> nexts; // outlinks + private int index; // unique id for this node private final LiveExprNode[] statePreds; // state predicates in the particle public static TBGraphNode dummyNode = new TBGraphNode(); @@ -35,7 +35,7 @@ public class TBGraphNode { public TBGraphNode(TBPar par) { this.par = par; this.index = 0; - this.nexts = new Vect(); + this.nexts = new Vect<>(); TBPar preds = new TBPar(par.size()); for (int i = 0; i < par.size(); i++) { LiveExprNode ln = par.exprAt(i); @@ -54,6 +54,14 @@ public class TBGraphNode { } } + public int getIndex() { + return index; + } + + public void setIndex(final int i) { + this.index = i; + } + public final TBPar getPar() { return this.par; } @@ -76,8 +84,13 @@ public class TBGraphNode { return false; } - /* Checks if this particle node is consistent with a state. */ - public boolean isConsistent(TLCState state, Tool tool) { + /** + * Checks if this particle node is consistent with the given + * {@link TLCState}. In other words, it checks if {@link TLCState}'s truth + * values according to all state predicates of the tableau node. The state + * predicates are deduced from the particles during tableau construction. + */ + public boolean isConsistent(TLCState state, ITool tool) { for (int j = 0; j < this.statePreds.length; j++) { if (!this.statePreds[j].eval(tool, state, null)) { return false; @@ -109,11 +122,14 @@ public class TBGraphNode { /** * @see TBGraph#toDotViz() */ - public String toDotViz() { + public String toDotViz(final boolean isInitNode) { final String label = "\"Id: " + this.index + "\n" + par.toDotViz() + "\""; final StringBuffer buf = new StringBuffer(nextSize()); buf.append(this.index + " [label=" + label + "]\n"); // nodes label + if (isInitNode) { + buf.append("[style = filled]\n"); + } for (int i = 0; i < nextSize(); i++) { final TBGraphNode successor = nextAt(i); buf.append(this.index + " -> " + successor.index); diff --git a/tlatools/src/tlc2/tool/liveness/TBPar.java b/tlatools/src/tlc2/tool/liveness/TBPar.java index 0ef57fd1b4c53b54bdf472a5ab80dff408094b88..fffa55ffb19b3af26a67e42a83e533c046979ff6 100644 --- a/tlatools/src/tlc2/tool/liveness/TBPar.java +++ b/tlatools/src/tlc2/tool/liveness/TBPar.java @@ -27,7 +27,7 @@ import util.Assert; * with p. 43 fig. 0.15), thus it uses the PART-TAB algorithm (p. 456) for the * tableau construction. */ -public class TBPar extends Vect { +public class TBPar extends Vect<LiveExprNode> { public TBPar(int i) { super(i); @@ -102,6 +102,105 @@ public class TBPar extends Vect { return res; } + /** + * The method particleClosure, given a list of terms (initially just a + * single term), returns a list of all particles containing those terms. + * It's a recursive tree search. + */ + public TBParVec particleClosure() { + TBPar positive_closure = this.positiveClosure(); + Vect<TBTriple> alphas = positive_closure.alphaTriples(); + Vect<TBTriple> betas = positive_closure.betaTriples(); + return particleClosure(this, alphas, betas); + } + + private TBParVec particleClosure(TBPar terms, Vect alphas, Vect betas) { + // if terms is not locally consistent, then terminate. + if (!terms.isLocallyConsistent()) { + return new TBParVec(0); + } + // if terms is not alpha-closed, then close it. + // first, try alpha expansion. See MP page 403 + // figure 5.1. for alpha expansion rules. + TBPar terms1 = terms; + for (int i = 0; i < terms1.size(); i++) { + LiveExprNode ln = terms1.exprAt(i); + LiveExprNode kappa1 = null, kappa2 = null; + if (ln instanceof LNAll) { + // Alpha-Expansion: []p expands to p, ()[]p + kappa1 = ((LNAll) ln).getBody(); + kappa2 = new LNNext(ln); + } else if (ln instanceof LNConj) { + // Alpha-Expansion: p /\ q expands to p, q + kappa1 = ((LNConj) ln).getBody(0); + kappa2 = ((LNConj) ln).getBody(1); + } + if (kappa1 != null) { + if (terms1.member(kappa1)) { + if (!terms1.member(kappa2)) { + terms1 = terms1.append(kappa2); + } + } else if (terms1.member(kappa2)) { + terms1 = terms1.append(kappa1); + } else { + terms1 = terms1.append(kappa1, kappa2); + } + } + } + // second, try alpha^-1 expansion + boolean done; + do { + done = true; + for (int i = 0; i < alphas.size(); i++) { + TBTriple alpha = (TBTriple) alphas.elementAt(i); + if (terms1.member(alpha.getB()) && terms1.member(alpha.getC()) && !terms1.member(alpha.getA())) { + terms1.addElement(alpha.getA()); + done = false; + } + } + } while (!done); + // finally, recurse only when locally consistent + if ((terms1.size() > terms.size()) && (!terms1.isLocallyConsistent())) { + return new TBParVec(0); + } + return particleClosureBeta(terms1, alphas, betas); + } + + private TBParVec particleClosureBeta(TBPar terms, Vect alphas, Vect betas) { + // try a beta expansion. See MP page 403 + // figure 5.1. for beta expansion rules. + for (int i = 0; i < terms.size(); i++) { + LiveExprNode ln = terms.exprAt(i); + LiveExprNode kappa1 = null, kappa2 = null; + if (ln instanceof LNEven) { + // Beta-Expansion: <>p expands to p, ()<>p + kappa1 = ((LNEven) ln).getBody(); + kappa2 = new LNNext(ln); + } else if (ln instanceof LNDisj) { + // Beta-Expansion: p \/ expands to p, q + kappa1 = ((LNDisj) ln).getBody(0); + kappa2 = ((LNDisj) ln).getBody(1); + } + if ((kappa1 != null) && !terms.member(kappa1) && !terms.member(kappa2)) { + TBParVec ps1 = particleClosure(terms.append(kappa1), alphas, betas); + TBParVec ps2 = particleClosure(terms.append(kappa2), alphas, betas); + return ps1.union(ps2); + } + } + // try a beta^-1 expansion + for (int i = 0; i < betas.size(); i++) { + TBTriple beta = (TBTriple) betas.elementAt(i); + if ((terms.member(beta.getB()) || terms.member(beta.getC())) && !terms.member(beta.getA())) { + return particleClosure(terms.append(beta.getA()), alphas, betas); + } + } + // if there are not any more expansions to do, return the terms + // we've got as the only particle in a list of particles. + TBParVec res = new TBParVec(1); + res.addElement(terms); + return res; + } + /** * The methods alphaTriples and betaTriples, given a positive closure, * figure out the alpha and beta triples. The way the algorithm works, by @@ -109,8 +208,8 @@ public class TBPar extends Vect { * closed. All junctions must have been binarified at this stage by * makeBinary, otherwise it may give the wrong answer and crash. */ - public final Vect alphaTriples() { - Vect ts = new Vect(); + public final Vect<TBTriple> alphaTriples() { + Vect<TBTriple> ts = new Vect<>(); for (int i = 0; i < this.size(); i++) { LiveExprNode ln = this.exprAt(i); if (ln instanceof LNAll) { @@ -123,8 +222,8 @@ public class TBPar extends Vect { return ts; } - public final Vect betaTriples() { - Vect ts = new Vect(); + public final Vect<TBTriple> betaTriples() { + Vect<TBTriple> ts = new Vect<>(); for (int i = 0; i < this.size(); i++) { LiveExprNode ln = this.exprAt(i); if (ln instanceof LNEven) { @@ -231,7 +330,14 @@ public class TBPar extends Vect { } /** - * This methods returns true iff this particle fulfills the promise. + * This methods returns true iff this particle (TBPar) fulfills the given + * promise. + * <p> + * A particle/atom A is said to fulfill formula φ which promises r if either: + * <ul> + * <li>φ \notin A</li> + * <li>r \in A</li> + * </ul> */ public final boolean isFulfilling(LNEven promise) { return !this.member(promise) || this.member(promise.getBody()); diff --git a/tlatools/src/tlc2/tool/liveness/TBParVec.java b/tlatools/src/tlc2/tool/liveness/TBParVec.java index 76856c648c255217940d1d0b51e35564810ed6e7..a0b39d074ea84f10a365941a4c1ab897b949fc55 100644 --- a/tlatools/src/tlc2/tool/liveness/TBParVec.java +++ b/tlatools/src/tlc2/tool/liveness/TBParVec.java @@ -7,7 +7,7 @@ package tlc2.tool.liveness; import tlc2.util.Vect; -public class TBParVec extends Vect { +public class TBParVec extends Vect<TBPar> { public TBParVec(int size) { super(size); diff --git a/tlatools/src/tlc2/tool/liveness/TableauDiskGraph.java b/tlatools/src/tlc2/tool/liveness/TableauDiskGraph.java index 5ba39a9848a92c5558f5e67aac852137ca921852..e3a19defa82283b6fa9c22005a87fb64bc591666 100644 --- a/tlatools/src/tlc2/tool/liveness/TableauDiskGraph.java +++ b/tlatools/src/tlc2/tool/liveness/TableauDiskGraph.java @@ -30,6 +30,7 @@ import java.io.IOException; import tlc2.output.EC; import tlc2.output.MP; +import tlc2.tool.ITool; import tlc2.util.LongVec; import tlc2.util.MemIntQueue; import tlc2.util.statistics.IBucketStatistics; @@ -69,7 +70,7 @@ public class TableauDiskGraph extends AbstractDiskGraph { * long as it has been recorded with recordNode(). * <p> * A node is logically undone when it's an initial state and added via - * {@link LiveCheck#addInitState(tlc2.tool.TLCState, long)} but not yet + * {@link LiveCheck#addInitState(ITool, tlc2.tool.TLCState, long)} but not yet * added via * {@link LiveCheck#addNextState(tlc2.tool.TLCState, long, tlc2.tool.StateVec, LongVec)} * . A second case is when a successor node in the behavior graph is added @@ -112,11 +113,18 @@ public class TableauDiskGraph extends AbstractDiskGraph { } /* (non-Javadoc) - * @see tlc2.tool.liveness.DiskGraph#putNode(tlc2.tool.liveness.GraphNode, long) + * @see tlc2.tool.liveness.AbstractDiskGraph#putNode(tlc2.tool.liveness.GraphNode, long) */ protected void putNode(GraphNode node, long ptr) { this.nodePtrTbl.put(node.stateFP, node.tindex, ptr); } + + /* (non-Javadoc) + * @see tlc2.tool.liveness.AbstractDiskGraph#checkDuplicate(tlc2.tool.liveness.GraphNode) + */ + protected boolean checkDuplicate(final GraphNode node) { + return this.nodePtrTbl.get(node.stateFP, node.tindex) != -1; + } /** * Get the graph node. Returns a new GraphNode if the node is not in this. @@ -156,16 +164,17 @@ public class TableauDiskGraph extends AbstractDiskGraph { } /* (non-Javadoc) - * @see tlc2.tool.liveness.DiskGraph#getLink(long, int) + * @see tlc2.tool.liveness.AbstractDiskGraph#getLink(long, int) */ public long getLink(long state, int tidx) { return this.nodePtrTbl.get(state, tidx); } /* (non-Javadoc) - * @see tlc2.tool.liveness.DiskGraph#putLink(long, int, long) + * @see tlc2.tool.liveness.AbstractDiskGraph#putLink(long, int, long) */ public long putLink(long state, int tidx, long link) { + assert MAX_PTR <= link && link < MAX_LINK; int[] node = this.nodePtrTbl.getNodes(state); int cloc = this.nodePtrTbl.getIdx(node, tidx); long oldLink = TableauNodePtrTable.getElem(node, cloc); @@ -177,7 +186,7 @@ public class TableauDiskGraph extends AbstractDiskGraph { } /* (non-Javadoc) - * @see tlc2.tool.liveness.DiskGraph#setMaxLink(long, int) + * @see tlc2.tool.liveness.AbstractDiskGraph#setMaxLink(long, int) */ public void setMaxLink(long state, int tidx) { this.nodePtrTbl.put(state, tidx, MAX_LINK); @@ -197,7 +206,7 @@ public class TableauDiskGraph extends AbstractDiskGraph { } /* (non-Javadoc) - * @see tlc2.tool.liveness.DiskGraph#makeNodePtrTbl(long) + * @see tlc2.tool.liveness.AbstractDiskGraph#makeNodePtrTbl(long) */ protected void makeNodePtrTbl(final long ptr) throws IOException { makeNodePtrTbl(ptr, nodePtrTbl); @@ -256,9 +265,11 @@ public class TableauDiskGraph extends AbstractDiskGraph { } /* (non-Javadoc) - * @see tlc2.tool.liveness.DiskGraph#toDotViz() + * @see tlc2.tool.liveness.AbstractDiskGraph#toDotViz(tlc2.tool.liveness.OrderOfSolution) */ - public final String toDotViz() { + public final String toDotViz(final OrderOfSolution oos) { + final int slen = oos.getCheckState().length; + final int alen = oos.getCheckAction().length; // The following code relies on gnodes not being null, thus safeguard // against accidental invocations. @@ -274,6 +285,9 @@ public class TableauDiskGraph extends AbstractDiskGraph { sb.append("digraph DiskGraph {\n"); sb.append("nodesep = 0.7\n"); sb.append("rankdir=LR;\n"); // Left to right rather than top to bottom + sb.append(toDotVizLegend(oos)); + sb.append("subgraph cluster_graph {\n"); + sb.append("color=\"white\";\n"); // no border. //TODO Reading the file front to end potentially yields node duplicates in the output. Better to create a (temporary) nodeptrtable and traverse it instead. long nodePtr = this.nodeRAF.getFilePointer(); long nodePtrPtr = this.nodePtrRAF.getFilePointer(); @@ -284,9 +298,9 @@ public class TableauDiskGraph extends AbstractDiskGraph { int tidx = nodePtrRAF.readInt(); long loc = nodePtrRAF.readLongNat(); GraphNode gnode = this.getNode(fp, tidx, loc); - sb.append(gnode.toDotViz(isInitState(gnode), true)); + sb.append(gnode.toDotViz(isInitState(gnode), true, slen, alen)); } - sb.append("}"); + sb.append("}}"); this.nodeRAF.seek(nodePtr); this.nodePtrRAF.seek(nodePtrPtr); } catch (IOException e) { @@ -380,7 +394,21 @@ public class TableauDiskGraph extends AbstractDiskGraph { continue; } if (nextState == state && nextTidx == tidx) { - // Stop BFS (see MemIntQUEUE above), we found a path to state. + // Stop BFS (see MemIntQUEUE above), we found a path to + // state. The path might not necessarily be the shortest one + // possible. A non-optimal path is returned if the BFS + // search happens to produce a path <<fp1, fp2, fp3>> first + // before it finds <<fp1, fp1, fp2>> (the first two elements + // share the same fingerprint and only differ in the + // discarded tableau id). The caller LiveWorker#printTrace + // collapses contiguous segments (identical fingerprint) in + // the path into a single state. E.g. the second example + // above will be printed as two states: <<State(fp1), + // State(fp2)>>. Hence, it is shorter than <<State(fp1), + // State(fp2), State(fp3)>>. Note that the search does *not* + // stop after the second node from the end in <<fp1, fp1, fp2>> + // because this node - due to its tableau id - does not + // correspond to an initial state in reversablePtrTable. return reconstructReversePath(reversablePtrTable, curState, curTidx, nextState, nextTidx); } @@ -503,6 +531,12 @@ public class TableauDiskGraph extends AbstractDiskGraph { return res; } + /** + * This implementation extends {@link TableauNodePtrTable} to additionally + * store the tableau index of the predecessor node. It is needed to traverse + * the {@link ReverseTraversableTableauNodePtrTable} backwards once a error + * trace path has been created. + */ private class ReverseTraversableTableauNodePtrTable extends TableauNodePtrTable { public ReverseTraversableTableauNodePtrTable(final int size) { @@ -543,9 +577,9 @@ public class TableauDiskGraph extends AbstractDiskGraph { } /* (non-Javadoc) - * @see tlc2.tool.liveness.TableauNodePtrTable#addElem(int[], int, long) + * @see tlc2.tool.liveness.TableauNodePtrTable#appendElem(int[], int, long) */ - protected int[] addElem(final int[] node, final int tidx, final long elem) { + protected int[] appendElem(final int[] node, final int tidx, final long elem) { int len = node.length; int[] newNode = new int[len + getElemLength()]; System.arraycopy(node, 0, newNode, 0, len); diff --git a/tlatools/src/tlc2/tool/liveness/TableauNodePtrTable.java b/tlatools/src/tlc2/tool/liveness/TableauNodePtrTable.java index 1fd7b918351ef7470defea52b8b0347341c75c38..7a5385138aed0e203b419767018d0eb4c983a97a 100644 --- a/tlatools/src/tlc2/tool/liveness/TableauNodePtrTable.java +++ b/tlatools/src/tlc2/tool/liveness/TableauNodePtrTable.java @@ -26,8 +26,62 @@ package tlc2.tool.liveness; +/** + * {@link TableauNodePtrTable} (and its sibling {@link NodePtrTable} for + * tableau-less liveness checking) is a - highly optimized - set of all nodes in + * the behavior graph {@link TableauDiskGraph}. + * <p> + * Each node in the behavior graph consists of the pair <<state, tidx>> (where + * state is a state's fingerprint) and auxiliary information. The auxiliary + * information is: + * <ul> + * <li>An offset into the second set (@see {@link AbstractDiskGraph#nodeRAF}) + * which represents the arcs between the nodes (logically outgoing transitions). + * Technically this is a pointer location into the second disk file of + * {@link TableauDiskGraph}.</li> + * <li>The node's link number during Tarjan's SCC search.</li> + * <li>A flag if the node is done or not (see {@link TableauNodePtrTable#UNDONE} + * below).</li> + * <li>A flag that marks a node an initial node.</li> + * <li>A flag if the node has been seen before during error trace re-creation + * (see {@link LiveWorker#printTrace}.</li> + * </ul> + * <p> + * The last item indicates that this class is used in two scenarios. It's + * primary purpose is to be the backing store of the liveness/behavior disc + * graph. Additionally though, LiveWorker#printTrace independently instantiates + * a new {@link TableauNodePtrTable} to do its work. + * <p> + * To minimize {@link TableauNodePtrTable}'s space/memory requirements, the + * auxiliary information replace each other depending on the phase of liveness + * checking.<br> + * During model checking (safety checking) the auxiliary information is set to a + * pointer location (long) pointing into the arcs set and the high bits of the + * long are used to mark nodes as done or undone.<br> + * As soon as the SCC search starts, the pointer location is replaced by the SCC + * link number.<br> + * Once a liveness violation has been detected, the seen flag is set during the + * error trace path reconstruction. + * <p> + * Internally {@link TableauNodePtrTable} hashes the node's fingerprint to a + * bucket address. In case of hash collision, open addressing is used. + */ public class TableauNodePtrTable { + /** + * A node is marked UNDONE if it is: + * <ul> + * <li>An initial node <b>s0</b> and not yet visited again by + * LiveChecker#addNextState(<b>s0</b>)</li> + * <li>A previously unseen successor node <b>t</b> of a node <b>s</b> that + * is added as an outgoing transition of <b>s</b> when <b>s</b> is being + * added via LiveChecker#addNextState(<b>s</b> )</li> + * </ul> + * <p> + * It logically markers the successor node <b>t</b> to be incomplete which + * can only happen during liveness checking of a <b>partial</b> liveness + * graph. + */ public static final long UNDONE = 0xFFFFFFFE00000000L; private int count; @@ -102,10 +156,10 @@ public class TableauNodePtrTable { int cloc = getIdx(node, tidx); if (cloc == -1) { // The list of nodes does not contain the give tableau idx - // yet, thus add a new element. Technically, it means we + // yet, thus append a new element. Technically, it means we // grow the nodes array by three and insert the tableau idx // and its element. - this.nodes[loc] = addElem(node, tidx, elem); + this.nodes[loc] = appendElem(node, tidx, elem); } else { // Nodes already contains an entry for the given tableau. // Update its element. The element is either a pointer @@ -200,6 +254,7 @@ public class TableauNodePtrTable { return node[3] != -2; } + // Called by addNextState public final int setDone(long k) { if (this.count >= this.thresh) { this.grow(); @@ -235,12 +290,22 @@ public class TableauNodePtrTable { } } + /** + * Clears the seen flag of all records set by static setSeen(..) calls + * earlier. + * <p> + * Post-Condition: None of the records is marked seen. + * + * @see TableauNodePtrTable#setSeen(int[]) + * @see TableauNodePtrTable#setSeen(int[], int) + */ public final void resetElems() { + // Only called when the error trace is being printed. for (int i = 0; i < this.nodes.length; i++) { int[] node = this.nodes[i]; if (node != null) { for (int j = 3; j < node.length; j += getElemLength()) { - node[j] &= 0x7FFFFFFF; + node[j] &= 0x7FFFFFFF; // Clear the MSB set by setSeen(..) } } } @@ -288,7 +353,7 @@ public class TableauNodePtrTable { return node; } - protected int[] addElem(int[] node, int tidx, long elem) { + protected int[] appendElem(int[] node, int tidx, long elem) { int len = node.length; int[] newNode = new int[len + getElemLength()]; System.arraycopy(node, 0, newNode, 0, len); @@ -345,47 +410,80 @@ public class TableauNodePtrTable { return node[loc]; } + /* + * Helper methods used by LiveWorker#printTrace(..) only. Note that + * printTrace does not use the TNPT instance of the DiskGraph but its own + * instance only containing a single SCC. + */ + public static final int END_MARKER = -1; + public static int startLoc(int[] node) { - return (node.length > 2) ? 2 : -1; + return (node.length > 2) ? 2 : END_MARKER; } public static int nextLoc(int[] node, int curLoc) { int loc = curLoc + 3; - return (loc < node.length) ? loc : -1; + return (loc < node.length) ? loc : END_MARKER; } + /** + * @param nodes + * @return True, iff the record at tloc has been marked seen. + * @see TableauNodePtrTable#setSeen(int[], int) + */ public static boolean isSeen(int[] nodes, int tloc) { return getElem(nodes, tloc) < 0; } + /** + * Marks the record at tloc seen. + * + * @param nodes + * @see TableauNodePtrTable#setSeen(int[]) + * @see TableauNodePtrTable#resetElems() + */ public static void setSeen(int[] nodes, int tloc) { long ptr = getElem(nodes, tloc); - putElem(nodes, (ptr | 0x8000000000000000L), tloc); + putElem(nodes, (ptr | 0x8000000000000000L), tloc); // Set the MSB } public static long getPtr(long ptr) { return (ptr & 0x7FFFFFFFFFFFFFFFL); } + /** + * @see TableauNodePtrTable#setSeen(int[]) + * @param nodes + * @return True, iff the record has been marked seen. + */ public static boolean isSeen(int[] nodes) { return nodes[3] < 0; } + /** + * Marks this record seen. + * + * @param nodes + * @see TableauNodePtrTable#resetElems() + */ public static void setSeen(int[] nodes) { - nodes[3] |= 0x80000000; + nodes[3] |= 0x80000000; // Set the MSB } + public static final int NO_PARENT = -1; + public static int getParent(int[] nodes) { return nodes[4]; } public static void setParent(int[] nodes, int loc) { + assert loc >= NO_PARENT && loc <= AbstractDiskGraph.MAX_PTR; nodes[4] = loc; } /* * The detailed formatter below can be activated in Eclipse's variable view - * by choosing "New detailed formatter" from the MemIntQueue context menu. + * by choosing "New detailed formatter" from the nodePtrTable's context menu. * Insert "TableauNodePtrTable.DetailedFormatter.toString(this);". */ public static class DetailedFormatter { @@ -402,8 +500,11 @@ public class TableauNodePtrTable { buf.append(" isDone: " + (node.length == 2 || (node.length > 2 && node[3] != -2))); buf.append("\n"); - // A node maintains n records. Each record logically contains information about a node's successor. - // fingerprint + // A node maintains n records. Each record logically + // contains information - combined with the fingerprint - + // about the full tuple <<fp, tidx, loc>>. + // Depending on the state of the record, the loc might + // also be overwritten by the SCC link number. int j = 2; for (; j < node.length - 1; j+=table.getElemLength()) { // don't miss the ptr at the end buf.append("\t"); @@ -413,7 +514,11 @@ public class TableauNodePtrTable { // element final long elem = getElem(node, j); if (AbstractDiskGraph.isFilePointer(elem)) { - buf.append(" ptr: " + elem); + if (table.isDone(fp)) { + buf.append(" ptr: " + elem); + } else { + buf.append(" ptr: undone"); + } } else if (AbstractDiskGraph.MAX_PTR == elem){ buf.append(" elem: Init State"); } else { diff --git a/tlatools/src/tlc2/tool/management/ModelCheckerMXWrapper.java b/tlatools/src/tlc2/tool/management/ModelCheckerMXWrapper.java index 1a8c8c0c481edbfec2458e614b16e686d7d2ab69..f8f56b3d7f5b7026fd2d8492f85507e59ac5b848 100644 --- a/tlatools/src/tlc2/tool/management/ModelCheckerMXWrapper.java +++ b/tlatools/src/tlc2/tool/management/ModelCheckerMXWrapper.java @@ -2,12 +2,12 @@ package tlc2.tool.management; -import java.io.IOException; - import javax.management.NotCompliantMBeanException; +import tlc2.TLC; import tlc2.TLCGlobals; import tlc2.tool.ModelChecker; +import tlc2.tool.TLCState; import tlc2.tool.distributed.management.TLCStatisticsMXBean; import tlc2.tool.fp.DiskFPSet; @@ -16,14 +16,18 @@ import tlc2.tool.fp.DiskFPSet; */ public class ModelCheckerMXWrapper extends TLCStandardMBean implements TLCStatisticsMXBean { + public static final String OBJ_NAME = "tlc2.tool:type=ModelChecker"; + private final ModelChecker modelChecker; + private final TLC tlc; - public ModelCheckerMXWrapper(final ModelChecker aModelChecker) + public ModelCheckerMXWrapper(final ModelChecker aModelChecker, final TLC tlc) throws NotCompliantMBeanException { super(TLCStatisticsMXBean.class); this.modelChecker = aModelChecker; + this.tlc = tlc; // register all TLCStatisticsMXBeans under the same name - registerMBean("tlc2.tool:type=ModelChecker"); + registerMBean(OBJ_NAME); } /* (non-Javadoc) @@ -51,7 +55,7 @@ public class ModelCheckerMXWrapper extends TLCStandardMBean implements TLCStatis * @see tlc2.tool.distributed.management.TLCStatisticsMXBean#getStateQueueSize() */ public long getStateQueueSize() { - return modelChecker.theStateQueue.size(); + return modelChecker.getStateQueueSize(); } /* (non-Javadoc) @@ -72,13 +76,7 @@ public class ModelCheckerMXWrapper extends TLCStandardMBean implements TLCStatis * @see tlc2.tool.distributed.management.TLCStatisticsMXBean#getProgress() */ public int getProgress() { - try { - return modelChecker.trace.getLevelForReporting(); - } catch (IOException e) { - // The modelchecker trace file might be closed already (e.g. it - // gets closed at the end of the modelchecker run) - return -1; - } + return modelChecker.getProgress(); } /* (non-Javadoc) @@ -102,4 +100,64 @@ public class ModelCheckerMXWrapper extends TLCStandardMBean implements TLCStatis //TODO adapt once Workers can support units of work greater than 1 return 1; } + + /* (non-Javadoc) + * @see tlc2.tool.distributed.management.TLCStatisticsMXBean#getRuntimeRatio() + */ + public double getRuntimeRatio() { + return modelChecker.getRuntimeRatio(); + } + + /* (non-Javadoc) + * @see tlc2.tool.distributed.management.TLCStatisticsMXBean#liveCheck() + */ + public void liveCheck() { + modelChecker.forceLiveCheck(); + } + + /* (non-Javadoc) + * @see tlc2.tool.distributed.management.TLCStatisticsMXBean#getCurrentState() + */ + public String getCurrentState() { + final TLCState state = modelChecker.theStateQueue.sPeek(); + if (state != null) { + return state.toString(); + } + return "N/A"; + } + + /* (non-Javadoc) + * @see tlc2.tool.distributed.management.TLCStatisticsMXBean#getSpecName() + */ + public String getSpecName() { + return tlc.getSpecName(); + } + + /* (non-Javadoc) + * @see tlc2.tool.distributed.management.TLCStatisticsMXBean#getModelName() + */ + public String getModelName() { + return tlc.getModelName(); + } + + /* (non-Javadoc) + * @see tlc2.tool.distributed.management.TLCStatisticsMXBean#stop() + */ + public void stop() { + modelChecker.stop(); + } + + /* (non-Javadoc) + * @see tlc2.tool.distributed.management.TLCStatisticsMXBean#suspend() + */ + public void suspend() { + modelChecker.suspend(); + } + + /* (non-Javadoc) + * @see tlc2.tool.distributed.management.TLCStatisticsMXBean#resume() + */ + public void resume() { + modelChecker.resume(); + } } diff --git a/tlatools/src/tlc2/tool/management/StateMonitor.java b/tlatools/src/tlc2/tool/management/StateMonitor.java new file mode 100644 index 0000000000000000000000000000000000000000..90eb4cd236e827ff46826b1354e739cce54927c1 --- /dev/null +++ b/tlatools/src/tlc2/tool/management/StateMonitor.java @@ -0,0 +1,138 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.management; + +import java.io.IOException; +import java.text.SimpleDateFormat; +import java.util.Comparator; +import java.util.Date; +import java.util.List; +import java.util.Scanner; + +import javax.management.MBeanServerConnection; +import javax.management.MBeanServerInvocationHandler; +import javax.management.MalformedObjectNameException; +import javax.management.ObjectName; +import javax.management.remote.JMXConnector; +import javax.management.remote.JMXConnectorFactory; +import javax.management.remote.JMXServiceURL; + +import com.sun.tools.attach.AttachNotSupportedException; +import com.sun.tools.attach.VirtualMachine; +import com.sun.tools.attach.VirtualMachineDescriptor; + +import tlc2.tool.distributed.management.TLCStatisticsMXBean; + +/* + * This class does not compile in the Eclipse IDE on Ubuntu 18.04 with JaveSE1.8 linked to the + * 8u201-1~webupd8~1 JVM installed from webupd8 and probably fails to compile elsewhere too, + * because Eclipse fails to find the com.sun.tools.attach classes. The fix is to add the JDK's + * tools.jar to the Java installation. On Java 11 this doesn't seem to be an issue. + */ +public class StateMonitor { + + private static final SimpleDateFormat SDF = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); //$NON-NLS-1$ + + public static void main(String[] args) throws IOException, MalformedObjectNameException, InterruptedException, AttachNotSupportedException { + int interval = 10; // 10sec interval by default + if (args.length == 1) { + interval = Integer.valueOf(args[0]); + } + + JMXServiceURL url = null; + try { + final List<VirtualMachineDescriptor> vmds = com.sun.tools.attach.VirtualMachine.list(); + vmds.sort(new Comparator<VirtualMachineDescriptor>() { + @Override + public int compare(VirtualMachineDescriptor o1, VirtualMachineDescriptor o2) { + // Sort those vms higher whose display name contains TLC. + final boolean c1 = o1.displayName().contains("TLC"); + final boolean c2 = o2.displayName().contains("TLC"); + if (c1 ^ c2) { + if (c1) { + return -1; + } + return 1; + } + return 0; + } + }); + + int index = 1; + try (Scanner scanner = new Scanner(System.in)) { + rd: while (true) { + index = 1; + System.out.printf("============\n"); + for (VirtualMachineDescriptor vmd : vmds) { + System.out.printf("[%s]: pid=%s, name=%s\n", index++, vmd.id(), vmd.displayName()); + } + System.out.printf("Please select the number of the Java VM running TLC to connect to:\n"); + if (scanner.hasNextInt()) { + index = scanner.nextInt(); + + // Check index is within bounds. + if (index >= 1 && index <= vmds.size()) { + break rd; + } + } + System.err.printf("Invalid selection %s\n", index); + } + } + final VirtualMachineDescriptor tlcVMD = vmds.get(index - 1); + final VirtualMachine vm = tlcVMD.provider().attachVirtualMachine(tlcVMD); + final String address = vm.startLocalManagementAgent(); + url = new JMXServiceURL(address); + System.out.printf("Connecting to TLC running at %s.\n(Hit Ctrl+c to terminate)\n", url); + } catch (NumberFormatException nfe) { + // If monitored VM has been launched with explicit port, use service url instead + // of CAL: + // -Dcom.sun.management.jmxremote + // -Dcom.sun.management.jmxremote.port=10995 + // -Dcom.sun.management.jmxremote.ssl=false + // -Dcom.sun.management.jmxremote.authenticate=false + // => "service:jmx:rmi:///jndi/rmi://localhost:10995/jmxrmi". + url = new JMXServiceURL("service:jmx:rmi:///jndi/rmi://" + args[0] + "/jmxrmi"); + System.out.printf("Connecting to TLC running at %s.\n(Hit Ctrl+c to terminate)\n", url); + } + + final JMXConnector jmxConnector = JMXConnectorFactory.connect(url); + final MBeanServerConnection mbeanServerConnection = jmxConnector.getMBeanServerConnection(); + // ObjectName should be same as your MBean name + final ObjectName mbeanName = new ObjectName(ModelCheckerMXWrapper.OBJ_NAME); + + // Get MBean proxy instance that will be used to make calls to registered MBean + final TLCStatisticsMXBean mbeanProxy = (TLCStatisticsMXBean) MBeanServerInvocationHandler + .newProxyInstance(mbeanServerConnection, mbeanName, TLCStatisticsMXBean.class, true); + + while (true) { + System.out.printf("############ %s ############\n%s", SDF.format(new Date()), mbeanProxy.getCurrentState()); + Thread.sleep(interval * 1000L); + } + + // No need to close the connection because JVM itself terminates. + //jmxConnector.close(); + } +} diff --git a/tlatools/src/tlc2/tool/management/TLCStandardMBean.java b/tlatools/src/tlc2/tool/management/TLCStandardMBean.java index 52973c84717de1ca64d2600068978d96767d210b..9c3ab9e8f3ae812ba50ba53403282f9a9c984de6 100644 --- a/tlatools/src/tlc2/tool/management/TLCStandardMBean.java +++ b/tlatools/src/tlc2/tool/management/TLCStandardMBean.java @@ -14,6 +14,8 @@ import javax.management.NotCompliantMBeanException; import javax.management.ObjectName; import javax.management.StandardMBean; +import tlc2.TLCGlobals; + /** * @author Markus Alexander Kuppe */ @@ -26,6 +28,17 @@ public abstract class TLCStandardMBean extends StandardMBean { super(mbeanInterface); } + public String getVersion() { + return TLCGlobals.versionOfTLC; + } + + public String getRevision() { + if (TLCGlobals.getRevision() == null) { + return "N/A"; + } + return TLCGlobals.getRevision(); + } + /** * Registers a new MBean with the platform's MBean server after which this MBean will be visible in JMX. * @@ -66,7 +79,9 @@ public abstract class TLCStandardMBean extends StandardMBean { public boolean unregister() { final MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); try { - mbs.unregisterMBean(mxbeanName); + if (mbs.isRegistered(mxbeanName)) { + mbs.unregisterMBean(mxbeanName); + } } catch (MBeanRegistrationException e) { e.printStackTrace(); return false; diff --git a/tlatools/src/tlc2/tool/queue/ByteArrayQueue.java b/tlatools/src/tlc2/tool/queue/ByteArrayQueue.java new file mode 100644 index 0000000000000000000000000000000000000000..7a5228f25b6ec83b973d9eac0a80e422f06a594f --- /dev/null +++ b/tlatools/src/tlc2/tool/queue/ByteArrayQueue.java @@ -0,0 +1,474 @@ +// Copyright (c) 2003 Compaq Corporation. All rights reserved. +// Portions Copyright (c) 2003 Microsoft Corporation. All rights reserved. +// Last modified on Mon 30 Apr 2007 at 13:18:27 PST by lamport +// modified on Tue Feb 13 18:38:24 PST 2001 by yuanyu + +package tlc2.tool.queue; + +import java.io.IOException; + +import tlc2.TLCGlobals; +import tlc2.output.EC; +import tlc2.output.MP; +import tlc2.tool.StateVec; +import tlc2.tool.TLCState; +import tlc2.tool.Worker; + +/* + * This class is identical to StateQueue except that it internally works + * on byte[] whereas StateQueue's internal data is TLCStates. In other words, + * serialization of TLCState to a byte[] occurs outside the critical section. + */ +public abstract class ByteArrayQueue implements IStateQueue { + /** + * In model checking, this is the sequence of states waiting to be explored + * further. When the queue is empty, the checking is completed. + */ + protected long len = 0; // the queue length + private int numWaiting = 0; // the number of waiting threads + private volatile boolean finish = false; // terminate + /** + * Signals {@link Worker} that checkpointing is going happen next. + */ + private boolean stop = false; // suspend all workers. + /** + * Synchronizes between workers and checkpointing. More precisely it is used + * to notify (wake up) the checkpointing thread once the last worker is + * done. + */ + private Object mu = new Object(); + + + private final byte[] toBytes(final TLCState state) { + try { + final DiskByteArrayQueue.ByteValueOutputStream vos = new DiskByteArrayQueue.ByteValueOutputStream(); + state.write(vos); + return vos.toByteArray(); + } catch (IOException notExpectedToHappen) { + // With ByteArrayOutputStream + notExpectedToHappen.printStackTrace(); + } + return null; + } + + private final byte[] toBytes(final TLCState state, final DiskByteArrayQueue.ByteValueOutputStream vos) { + try { + state.write(vos); + return vos.toByteArray(); + } catch (IOException notExpectedToHappen) { + // With ByteArrayOutputStream + notExpectedToHappen.printStackTrace(); + } + return null; + } + + private final TLCState toState(final byte[] bytes) { + try { + final TLCState state = TLCState.Empty.createEmpty(); + state.read(new DiskByteArrayQueue.ByteValueInputStream(bytes)); + return state; + } catch (IOException notExpectedToHappen) { + // With ByteValueInputStream + notExpectedToHappen.printStackTrace(); + } + return null; + } + + /* Enqueues the state. It is not thread-safe. */ + /* (non-Javadoc) + * @see tlc2.tool.queue.IStateQueue#enqueue(tlc2.tool.TLCState) + */ + @Override + public final void enqueue(final TLCState state) { + enqueue(toBytes(state)); + } + + private final void enqueue(final byte[] state) { + this.enqueueInner(state); + this.len++; + } + + /* (non-Javadoc) + * @see tlc2.tool.queue.IStateQueue#dequeue() + */ + @Override + public final TLCState dequeue() { + final byte[] bytes = dequeueRaw(); + if (bytes != null) { + return toState(bytes); + } + return null; + } + + private final byte[] dequeueRaw() { + if (isEmpty()) { + return null; + } + final byte[] state = this.dequeueInner(); + this.len--; + return state; + } + + /* Enqueues a state. Wake up any waiting thread. */ + /* (non-Javadoc) + * @see tlc2.tool.queue.IStateQueue#sEnqueue(tlc2.tool.TLCState) + */ + @Override + public final void sEnqueue(final TLCState state) { + sEnqueue(toBytes(state)); + } + + private final synchronized void sEnqueue(final byte[] state) { + this.enqueueInner(state); + this.len++; + if (this.numWaiting > 0 && !this.stop) { + this.notifyAll(); + } + } + + /* Enqueues a list of states. Wake up any waiting thread. */ + /* (non-Javadoc) + * @see tlc2.tool.queue.IStateQueue#sEnqueue(tlc2.tool.TLCState[]) + */ + @Override + public final void sEnqueue(final TLCState[] states) { + final byte[][] bytes = new byte[states.length][]; + for (int i = 0; i < states.length; i++) { + bytes[i] = toBytes(states[i]); + } + sEnqueue(bytes); + } + + private final synchronized void sEnqueue(final byte[][] states) { + for (int i = 0; i < states.length; i++) { + this.enqueueInner(states[i]); + } + this.len += states.length; + if (this.numWaiting > 0 && !this.stop) { + this.notifyAll(); + } + } + @Override + public final void sEnqueue(final StateVec stateVec) { + sEnqueue(stateVec, stateVec.size()); + } + +// @Override + public final void sEnqueue(final StateVec stateVec, int n) { + if (n == 0) { + return; + } + + final DiskByteArrayQueue.ByteValueOutputStream vos = new DiskByteArrayQueue.ByteValueOutputStream(); + final byte[][] bytes = new byte[n][]; + for (int i = 0; i < stateVec.size(); i++) { + final TLCState state = stateVec.elementAt(i); + if (state != null) { + bytes[n - 1] = toBytes(state, vos); + n--; + } + } + sEnqueue(bytes); + } + + @Override + public final TLCState sPeek() { + final byte[] bytes = sPeekRaw(); + if (bytes != null) { + return toState(bytes); + } + return null; + } + + private final synchronized byte[] sPeekRaw() { + if (this.isAvail()) { + return this.peekInner(); + } + return null; + } + + /* Return the first element in the queue. Wait if empty. */ + /* (non-Javadoc) + * @see tlc2.tool.queue.IStateQueue#sDequeue() + */ + @Override + public final TLCState sDequeue() { + final byte[] bytes = sDequeueRaw(); + if (bytes != null) { + return toState(bytes); + } + return null; + } + + private final synchronized byte[] sDequeueRaw() { + if (this.isAvail()) { + final byte[] state = this.dequeueInner(); + assert state != null : "Null state found on queue"; + this.len--; + return state; + } + return null; + } + + @Override + public final TLCState[] sDequeue(int cnt) { + final byte[][] bytes = sDequeueRaw(cnt); + if (bytes != null) { + final TLCState[] array = new TLCState[cnt]; + for (int i = 0; i < array.length; i++) { + array[i] = toState(bytes[i]); + } + return array; + } + return null; + } + + private final synchronized byte[][] sDequeueRaw(int cnt) { + assert cnt > 0 : "Nonpositive number of states requested."; + if (this.isAvail()) { + if (cnt > len) { + // in this case, casting len to int is safe + cnt = (int) len; + } + final byte[][] states = new byte[cnt][]; + int idx; + for (idx = 0; idx < cnt && this.len > 0; idx++) { + states[idx] = this.dequeueInner(); + this.len--; + } + if (idx == cnt) { + return states; + } + + // cnt >= index, shrink resulting array + // dead code due to resetting cnt == len if cnt > len + final byte[][] res = new byte[idx][]; + for (int i = 0; i < idx; i++) { + res[i] = states[i]; + } + return res; + } + return null; + } + + /** + * Checks if states are available. If no states are available, the callee + * will be put to sleep until new states are available or another callee + * signals work done. This is determined by the fact, that all other workers + * are waiting for states. + * + * @return true if states are available in the queue. + */ + private final boolean isAvail() { + /* + * isAvail is only called from within sDequeue() and sDequeue(..) and + * thus is always synchronized on this. + */ + + if (this.finish) { + return false; + } + while (isEmpty() || this.stop) { + this.numWaiting++; + // the last worker accessing notices that all other workers are + // waiting. This indicates that all work is done. + if (this.numWaiting >= TLCGlobals.getNumWorkers()) { + if (isEmpty()) { + this.numWaiting--; + return false; + } + // TODO what happens if control flow exits without ever + // notifying the checkpoint (mu.wait()) thread? In case + // of distributed TLC, this is the main thread of + // TLCServer. + synchronized (this.mu) { + this.mu.notify(); + } + } + try { + this.wait(); + } catch (Exception e) { + MP.printError(EC.GENERAL, "making a worker wait for a state from the queue", e); // LL changed call 7 April 2012 + System.exit(1); + } + this.numWaiting--; + if (this.finish) { + return false; + } + } + return true; + } + + /* (non-Javadoc) + * @see tlc2.tool.queue.IStateQueue#finishAll() + */ + @Override + public synchronized void finishAll() { + this.finish = true; + // Notify all other worker threads. + this.notifyAll(); + // Need to wake main thread that waits (mu.wait()) to suspend access to + // the squeue (see suspendAll). The main thread might attempt to do its + // periodic work (tlc2.tool.ModelChecker.doPeriodicWork()) the moment + // all worker threads finish. Since suspendAll assumes the main thread + // is woken up (potentially) multiple times from sleeping indefinitely + // in the while loop before it finally returns after locking the + // StateQueue, we have to live up to this assumption. + // + // synchronized(this.mu) does not block when the main thread waits on + // this.mu in suspend all. We merely have to follow proper thread access. + synchronized (this.mu) { + // Technically notify() would do. + this.mu.notify(); + } + } + + /* (non-Javadoc) + * @see tlc2.tool.queue.IStateQueue#suspendAll() + */ + @Override + public final boolean suspendAll() { + boolean needWait = false; + synchronized (this) { + if (this.finish) { + return false; + } + this.stop = true; + needWait = needsWaiting(); + } + // Wait for all worker threads to stop. + while (needWait) { + synchronized (this.mu) { + try { + // finishAll & suspendAll race: + // + // suspendAll from main is on the heels of finishAll, but + // not quite as fast. SuspendAll's synchronized(this.mu) + // is executed one clock tick after finishAll's thus waiting + // for finishAll() to notify all waiters of this.mu. + // With main being the only potential waiter in the system + // nobody gets notified and the lock on this released + // subsequently. + // Now it's suspendAll's turn. It acquires the lock on + // this.mu and immediately after goes to wait on it + // (this.mu) to be waken by worker threads eventually. + // Unfortunately, there are none left in the system. All + // have long finishedAll. + // + // The fix is to check the this.finish variable one more + // time directly after suspendAll acquires the lock this.mu, + // it checks if it still has to wait for workers. To make + // sure it reads the most recent value, it's declared + // volatile to establish a happens-before relation (since + // Java5's memory model) between workers and main. Otherwise + // the VM might decide to let main read a cached value of + // false of this.finish. + if (this.finish) { + return false; + } + // waiting here assumes that subsequently a worker + // is going to wake us up by calling isAvail() or + // this.mu.notify*() + this.mu.wait(); + } catch (Exception e) { + MP.printError(EC.GENERAL, "waiting for a worker to wake up", e); // LL changed call 7 April 2012 + System.exit(1); + } + } + synchronized (this) { + if (this.finish) { + return false; + } + needWait = needsWaiting(); + } + } + return true; + } + + /** + * @return + */ + private boolean needsWaiting() { + //MAK 04/2012: Commented to fix an EOFException when liveness checking is enabled +// // no need to wait without workers present +// if (this.numWaiting < 1) { +// return false; +// } + // if all workers wait at once, it indicates that all work is + // done and suspending all workers can happen right away without + // waiting. + return this.numWaiting < TLCGlobals.getNumWorkers(); + } + + /* (non-Javadoc) + * @see tlc2.tool.queue.IStateQueue#resumeAll() + */ + @Override + public final synchronized void resumeAll() { + this.stop = false; + this.notifyAll(); + } + + /* (non-Javadoc) + * @see tlc2.tool.queue.IStateQueue#resumeAllStuck() + */ + @Override + public void resumeAllStuck() { + // needsWaiting() read might have been incorrect if a TLCWorkerRMI dies + // unexpectedly, thus this recovers a potentially stuck checkpointing + // run. + if (stop) { + synchronized (mu) { + mu.notifyAll(); + } + } + // + if (!stop && !isEmpty() && this.numWaiting > 0) { + synchronized (this) { + this.notifyAll(); + } + } + } + + /* This method returns the size of the state queue. */ + /* (non-Javadoc) + * @see tlc2.tool.queue.IStateQueue#size() + */ + @Override + public final long size() { + return this.len; + } + + /** + * @return true iff the inner queue does not contain states + */ + @Override + public boolean isEmpty() { + return len < 1; + } + + /* This method must be implemented in the subclass. */ + abstract void enqueueInner(byte[] state); + + /* This method must be implemented in the subclass. */ + abstract byte[] dequeueInner(); + + /* This method must be implemented in the subclass. */ + abstract byte[] peekInner(); + + /* Checkpoint. */ + /* (non-Javadoc) + * @see tlc2.tool.queue.IStateQueue#beginChkpt() + */ + public abstract void beginChkpt() throws IOException; + + /* (non-Javadoc) + * @see tlc2.tool.queue.IStateQueue#commitChkpt() + */ + public abstract void commitChkpt() throws IOException; + + /* (non-Javadoc) + * @see tlc2.tool.queue.IStateQueue#recover() + */ + public abstract void recover() throws IOException; +} diff --git a/tlatools/src/tlc2/tool/queue/DiskByteArrayQueue.java b/tlatools/src/tlc2/tool/queue/DiskByteArrayQueue.java new file mode 100644 index 0000000000000000000000000000000000000000..1deadcf6acaa683f2a63c3e2006bac8dffb0af98 --- /dev/null +++ b/tlatools/src/tlc2/tool/queue/DiskByteArrayQueue.java @@ -0,0 +1,945 @@ +// Copyright (c) 2003 Compaq Corporation. All rights reserved. +// Portions Copyright (c) 2003 Microsoft Corporation. All rights reserved. +// Last modified on Mon 30 Apr 2007 at 9:25:48 PST by lamport +// modified on Thu Feb 8 23:32:12 PST 2001 by yuanyu + +package tlc2.tool.queue; + +import java.io.EOFException; +import java.io.File; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.nio.file.Files; +import java.util.Arrays; + +import tlc2.output.EC; +import tlc2.output.MP; +import tlc2.value.IValue; +import tlc2.value.IValueInputStream; +import tlc2.value.IValueOutputStream; +import tlc2.value.ValueConstants; +import tlc2.value.impl.BoolValue; +import tlc2.value.impl.FcnRcdValue; +import tlc2.value.impl.IntValue; +import tlc2.value.impl.IntervalValue; +import tlc2.value.impl.ModelValue; +import tlc2.value.impl.RecordValue; +import tlc2.value.impl.SetEnumValue; +import tlc2.value.impl.StringValue; +import tlc2.value.impl.TupleValue; +import util.Assert; +import util.BufferedDataInputStream; +import util.BufferedDataOutputStream; +import util.FileUtil; +import util.IDataInputStream; +import util.IDataOutputStream; +import util.UniqueString; +import util.WrongInvocationException; + +/** + * A {@link DiskByteArrayQueue} uses the local hard disc as a backing store for + * states. An in-memory buffer of size {@link DiskByteArrayQueue}{@link #BufSize} + */ +public class DiskByteArrayQueue extends ByteArrayQueue { + // TODO dynamic bufsize based on current VM parameters? + private final static int BufSize = Integer.getInteger(DiskStateQueue.class.getName() + ".BufSize", 8192); + + /* + * Invariants: I1. Entries in deqBuf are in the indices: [deqIndex, + * deqBuffer.length ) I2. Entries in enqBuf are in the indices: [ 0, + * enqIndex ) I3. 0 <= deqIndex <= deqBuf.length, where deqIndex == 0 => + * buffer full deqIndex == deqBuf.length => buffer empty I4. 0 <= enqIndex + * <= enqBuf.length, where enqIndex == 0 => buffer empty enqIndex == + * enqBuf.length => buffer full + */ + + /* Fields */ + private final String filePrefix; + protected byte[][] deqBuf, enqBuf; + protected int deqIndex, enqIndex; + protected ByteArrayPoolReader reader; + protected ByteArrayPoolWriter writer; + + /** + * The SPC takes care of deleting swap files on the lower end of the range + * (loPool, hiPool). It terminates, when the first checkpoint is written at + * which point checkpointing itself takes care of removing obsolete swap + * files. + */ + protected final StatePoolCleaner cleaner; + private int loPool, hiPool, lastLoPool, newLastLoPool; + private File loFile; + + // TESTING ONLY! + DiskByteArrayQueue() throws IOException { + this(Files.createTempDirectory("DiskByteArrayQueue").toFile().toString()); + } + + /* Constructors */ + public DiskByteArrayQueue(String diskdir) { + this.deqBuf = new byte[BufSize][]; + this.enqBuf = new byte[BufSize][]; + this.deqIndex = BufSize; + this.enqIndex = 0; + this.loPool = 1; + this.hiPool = 0; + this.lastLoPool = 0; + this.filePrefix = diskdir + FileUtil.separator; + File rFile = new File(this.filePrefix + Integer.toString(0)); + this.reader = new ByteArrayPoolReader(BufSize, rFile); + this.reader.setDaemon(true); + this.loFile = new File(this.filePrefix + Integer.toString(this.loPool)); + this.reader.start(); + this.writer = new ByteArrayPoolWriter(BufSize, this.reader); + this.writer.setDaemon(true); + this.writer.start(); + this.cleaner = new StatePoolCleaner(); + this.cleaner.setDaemon(true); + this.cleaner.start(); + } + + final void enqueueInner(byte[] state) { + if (this.enqIndex == this.enqBuf.length) { + // enqBuf is full; flush it to disk + try { + String pstr = Integer.toString(this.hiPool); + File file = new File(this.filePrefix + pstr); + this.enqBuf = this.writer.doWork(this.enqBuf, file); + this.hiPool++; + this.enqIndex = 0; + } catch (Exception e) { + Assert.fail(EC.SYSTEM_ERROR_WRITING_STATES, + new String[] { "queue", (e.getMessage() == null) ? e.toString() : e.getMessage() }); + } + } + this.enqBuf[this.enqIndex++] = state; + } + + final byte[] dequeueInner() { + if (this.deqIndex == this.deqBuf.length) { + this.fillDeqBuffer(); + } + return this.deqBuf[this.deqIndex++]; + } + + byte[] peekInner() { + if (this.deqIndex == this.deqBuf.length) { + this.fillDeqBuffer(); + } + return this.deqBuf[this.deqIndex]; + } + + private final void fillDeqBuffer() { + try { + if (this.loPool + 1 <= this.hiPool) { + // We are sure there are disk files. + if (this.loPool + 1 >= this.hiPool) { + // potential read-write conflict on a file + this.writer.ensureWritten(); + } + this.deqBuf = this.reader.doWork(this.deqBuf, this.loFile); + this.deqIndex = 0; + this.loPool++; + String pstr = Integer.toString(this.loPool); + this.loFile = new File(this.filePrefix + pstr); + } else { + // We still need to check if a file is buffered. + this.writer.ensureWritten(); + byte[][] buf = this.reader.getCache(this.deqBuf, this.loFile); + if (buf != null) { + this.deqBuf = buf; + this.deqIndex = 0; + this.loPool++; + String pstr = Integer.toString(this.loPool); + this.loFile = new File(this.filePrefix + pstr); + } else { + // copy entries from enqBuf to deqBuf. + this.deqIndex = this.deqBuf.length - this.enqIndex; + System.arraycopy(this.enqBuf, 0, this.deqBuf, this.deqIndex, this.enqIndex); + this.enqIndex = 0; + } + } + // Notify the cleaner to do its job unless its waits for more work + // to pile up. + if ((loPool - lastLoPool) > 100) { //TODO Take BufSize into account. It defines the disc file size. + synchronized (this.cleaner) { + this.cleaner.deleteUpTo = loPool - 1; + this.cleaner.notifyAll(); + } + } + } catch (Exception e) { + Assert.fail(EC.SYSTEM_ERROR_READING_STATES, new String[] { "queue", + (e.getMessage() == null) ? e.toString() : e.getMessage() }); + } + } + + /* Checkpoint. */ + public final void beginChkpt() throws IOException { + synchronized (this.cleaner) { + // Checkpointing takes precedence over periodic cleaning + // (cleaner would otherwise delete checkpoint files as it know + // nothing of checkpoints). + this.cleaner.finished = true; + this.cleaner.notifyAll(); + } + + String filename = this.filePrefix + "queue.tmp"; + final BufferedDataOutputStream vos = new BufferedDataOutputStream(filename); + vos.writeLong(this.len); + vos.writeInt(this.loPool); + vos.writeInt(this.hiPool); + vos.writeInt(this.enqIndex); + vos.writeInt(this.deqIndex); + for (int i = 0; i < this.enqIndex; i++) { + vos.writeInt(this.enqBuf[i].length); + vos.write(this.enqBuf[i]); + } + for (int i = this.deqIndex; i < this.deqBuf.length; i++) { + vos.writeInt(this.deqBuf[i].length); + vos.write(this.deqBuf[i]); + } + vos.close(); + this.newLastLoPool = this.loPool - 1; + } + + public final void commitChkpt() throws IOException { + for (int i = this.lastLoPool; i < this.newLastLoPool; i++) { + String pstr = Integer.toString(i); + File oldPool = new File(this.filePrefix + pstr); + if (!oldPool.delete()) { + String msg = "DiskStateQueue.commitChkpt: cannot delete " + oldPool; + throw new IOException(msg); + } + } + this.lastLoPool = this.newLastLoPool; + File oldChkpt = new File(this.filePrefix + "queue.chkpt"); + File newChkpt = new File(this.filePrefix + "queue.tmp"); + if ((oldChkpt.exists() && !oldChkpt.delete()) || !newChkpt.renameTo(oldChkpt)) { + String msg = "DiskStateQueue.commitChkpt: cannot delete " + oldChkpt; + throw new IOException(msg); + } + } + + public final void recover() throws IOException { + String filename = this.filePrefix + "queue.chkpt"; + final BufferedDataInputStream vis = new BufferedDataInputStream(filename); + this.len = vis.readLong(); + this.loPool = vis.readInt(); + this.hiPool = vis.readInt(); + this.enqIndex = vis.readInt(); + this.deqIndex = vis.readInt(); + this.lastLoPool = this.loPool - 1; + + for (int i = 0; i < this.enqIndex; i++) { + this.enqBuf[i] = new byte[vis.readInt()]; + vis.read(this.enqBuf[i]); + } + for (int i = this.deqIndex; i < this.deqBuf.length; i++) { + this.deqBuf[i] = new byte[vis.readInt()]; + vis.read(this.deqBuf[i]); + } + vis.close(); + File file = new File(this.filePrefix + Integer.toString(this.lastLoPool)); + boolean canRead = (this.lastLoPool < this.hiPool); + this.reader.restart(file, canRead); + String pstr = Integer.toString(this.loPool); + this.loFile = new File(this.filePrefix + pstr); + } + + public void finishAll() { + super.finishAll(); + synchronized (this.writer) { + this.writer.notifyAll(); + } + synchronized (this.reader) { + this.reader.setFinished(); + this.reader.notifyAll(); + } + synchronized (this.cleaner) { + this.cleaner.finished = true; + this.cleaner.notifyAll(); + } + } + + private class StatePoolCleaner extends Thread { + + private volatile boolean finished = false; + public int deleteUpTo; + + private StatePoolCleaner() { + super("RawTLCStatePoolCleaner"); + } + + /* (non-Javadoc) + * @see java.lang.Thread#run() + */ + public void run() { + try { + synchronized (this) { + while (!this.finished) { + this.wait(); + if (this.finished) { + return; + } + + for (int i = lastLoPool; i < deleteUpTo; i++) { + final File oldPoolFile = new File(filePrefix + Integer.toString(i)); + if (!oldPoolFile.delete()) { + // No reason to terminate/kill TLC when the cleanup fails. + // Contrary to StatePoolReader/Write, cleanup is optional + // functionality whose purpose is to prevent the disc from + // filling up. If the cleaner fails, the user can still + // manually delete the files. + MP.printWarning(EC.SYSTEM_ERROR_CLEANING_POOL, oldPoolFile.getCanonicalPath()); + } + } + lastLoPool = deleteUpTo; + } + } + } catch (Exception e) { + // Assert.printStack(e); + MP.printError(EC.SYSTEM_ERROR_CLEANING_POOL, e.getMessage(), e); + System.exit(1); + } + } + } + + private static final class ByteArrayPoolWriter extends Thread { + + private byte[][] buf; + private File poolFile; // the file to be written + private final ByteArrayPoolReader reader; // the consumer if not null + + public ByteArrayPoolWriter(int bufSize, ByteArrayPoolReader reader) { + super("RawTLCStatePoolWriter"); + this.buf = new byte[bufSize][]; + this.poolFile = null; + this.reader = reader; + } + + /* + * This method first completes the preceding write if not started. + * It then notifies this writer to flush enqBuf to file. In practice, + * we expect the preceding write to have been completed. + */ + public final synchronized byte[][] doWork(byte[][] enqBuf, File file) + throws IOException { + if (this.poolFile != null) { + final BufferedDataOutputStream vos = new BufferedDataOutputStream(this.poolFile); + for (int i = 0; i < this.buf.length; i++) { + vos.writeInt(this.buf[i].length); + vos.write(this.buf[i]); + } + vos.close(); + } + byte[][] res = this.buf; + this.buf = enqBuf; + this.poolFile = file; + this.notify(); + return res; + } + + /* Spin waiting for the write to complete. */ + public final void ensureWritten() throws InterruptedException { + synchronized(this) { + while (this.poolFile != null) { + this.wait(); + } + } + } + + public final synchronized void beginChkpt(ObjectOutputStream oos) + throws IOException { + boolean hasFile = (this.poolFile == null) ? false : true; + oos.writeBoolean(hasFile); + if (hasFile) { + oos.writeObject(this.poolFile); + for (int i = 0; i < this.buf.length; i++) { + oos.writeObject(this.buf[i]); + } + } + } + + /* Note this method is not synchronized. */ + public final void recover(ObjectInputStream ois) throws IOException { + boolean hasFile = ois.readBoolean(); + if (hasFile) { + try { + this.poolFile = (File)ois.readObject(); + for (int i = 0; i < this.buf.length; i++) { + this.buf[i] = (byte[])ois.readObject(); + } + } + catch (ClassNotFoundException e) + { + Assert.fail(EC.SYSTEM_CHECKPOINT_RECOVERY_CORRUPT, e); + } + } + else { + this.poolFile = null; + } + } + + /** + * Write "buf" to "poolFile". The objects in the queue are written + * using Java's object serialization facilities. + */ + public void run() { + try { + synchronized(this) { + while (true) { + while (this.poolFile == null) { + this.wait(); + // we are done without ever receiving a pool file + if(this.poolFile == null) { + return; + } + } + final BufferedDataOutputStream vos = new BufferedDataOutputStream(this.poolFile); + for (int i = 0; i < this.buf.length; i++) { + vos.writeInt(this.buf[i].length); + vos.write(this.buf[i]); + } + vos.close(); + this.poolFile = null; + this.notify(); + if (this.reader != null) this.reader.wakeup(); + } + } + } + catch (Exception e) { + // Assert.printStack(e); + MP.printError(EC.SYSTEM_ERROR_WRITING_POOL, e.getMessage(), e); + System.exit(1); + } + } + } + + private static final class ByteArrayPoolReader extends Thread { + + public ByteArrayPoolReader(int bufSize, File file) { + super("RawTLCStatePoolReader"); + this.buf = new byte[bufSize][]; + this.poolFile = file; + this.isFull = false; + this.canRead = false; + } + + private byte[][] buf; + private File poolFile; // the file to be read + private boolean isFull; // true iff the buf is filled + private boolean canRead; // true iff the file can be read + private boolean finished = false; + + public final synchronized void wakeup() { + this.canRead = true; + this.notify(); + } + + public final synchronized void restart(File file, boolean canRead) { + this.poolFile = file; + this.isFull = false; + this.canRead = canRead; + this.notify(); + } + + /* + * In the most common case, this method expects to see the buffer is + * full, it returns its buffer and notifies this reader to read the + * content of the file. + */ + public final synchronized byte[][] doWork(byte[][] deqBuf, File file) + throws IOException, ClassNotFoundException { + if (this.isFull) { + assert this.poolFile == null : EC.SYSTEM_FILE_NULL; + byte[][] res = this.buf; + this.buf = deqBuf; + this.poolFile = file; + this.isFull = false; // <file, false> + this.canRead = true; + this.notify(); + return res; + } + else if (this.poolFile != null) { + final BufferedDataInputStream vis = new BufferedDataInputStream(this.poolFile); + for (int i = 0; i < deqBuf.length; i++) { + deqBuf[i] = new byte[vis.readInt()]; + vis.read(deqBuf[i]); + } + vis.close(); + this.poolFile = file; // <file, false> + this.canRead = true; + this.notify(); + return deqBuf; + } + else { + final BufferedDataInputStream vis = new BufferedDataInputStream(this.poolFile); + for (int i = 0; i < deqBuf.length; i++) { + deqBuf[i] = new byte[vis.readInt()]; + vis.read(deqBuf[i]); + } + vis.close(); // <null, false> + return deqBuf; + } + } + + /* + * Returns the cached buffer if filled. Otherwise, returns null. + */ + public final synchronized byte[][] getCache(byte[][] deqBuf, File file) + throws IOException, ClassNotFoundException { + if (this.isFull) { + assert this.poolFile == null : EC.SYSTEM_FILE_NULL; + byte[][] res = this.buf; + this.buf = deqBuf; + this.poolFile = file; + this.isFull = false; // <file, false> + this.canRead = false; + return res; + } + else if (this.poolFile != null && this.canRead) { + // this should seldom occur. + final BufferedDataInputStream vis = new BufferedDataInputStream(this.poolFile); + for (int i = 0; i < deqBuf.length; i++) { + deqBuf[i] = new byte[vis.readInt()]; + vis.read(deqBuf[i]); + } + vis.close(); + // this.poolFile.delete(); + this.poolFile = file; // <file, false> + this.canRead = false; + return deqBuf; + } + return null; + } + + public final synchronized void beginChkpt(ObjectOutputStream oos) + throws IOException { + boolean hasFile = this.poolFile != null; + oos.writeBoolean(hasFile); + oos.writeBoolean(this.canRead); + oos.writeBoolean(this.isFull); + if (hasFile) { + oos.writeObject(this.poolFile); + } + if (this.isFull) { + for (int i = 0; i < this.buf.length; i++) { + oos.writeObject(this.buf[i]); + } + } + } + + /* Note that this method is not synchronized. */ + public final void recover(ObjectInputStream ois) throws IOException { + boolean hasFile = ois.readBoolean(); + this.canRead = ois.readBoolean(); + this.isFull = ois.readBoolean(); + try { + if (hasFile) { + this.poolFile = (File)ois.readObject(); + } + if (this.isFull) { + for (int i = 0; i < this.buf.length; i++) { + this.buf[i] = (byte[])ois.readObject(); + } + } + } + catch (ClassNotFoundException e) + { + Assert.fail(EC.SYSTEM_CHECKPOINT_RECOVERY_CORRUPT, e); + } + } + + /** + * Read the contents of "poolFile" into "buf". The objects in the + * file are read using Java's object serialization facilities. + */ + public void run() { + try { + synchronized(this) { + while (true) { + while (this.poolFile == null || this.isFull || !this.canRead) { + this.wait(); + if(this.finished ) { + return; + } + } + final BufferedDataInputStream vis = new BufferedDataInputStream(this.poolFile); + for (int i = 0; i < this.buf.length; i++) { + this.buf[i] = new byte[vis.readInt()]; + vis.read(this.buf[i]); + } + vis.close(); + this.poolFile = null; + this.isFull = true; // <null, true> + } + } + } + catch (Exception e) + { + // Assert.printStack(e); + MP.printError(EC.SYSTEM_ERROR_READING_POOL, e.getMessage(), e); + System.exit(1); + } + } + + public void setFinished() { + finished = true; + } + } + + static final class ByteValueOutputStream implements IValueOutputStream, IDataOutputStream { + + private byte[] bytes; + + private int idx; + + public ByteValueOutputStream() { + this.bytes = new byte[16]; // TLCState "header" already has 6 bytes. + this.idx = 0; + } + + private void ensureCapacity(int minCap) { + if (minCap - bytes.length > 0) { + int oldCap = bytes.length; + int newCap = oldCap << 1; // double size + if (newCap - minCap < 0) { + newCap = minCap; + } + bytes = Arrays.copyOf(bytes, newCap); + } + } + + /* (non-Javadoc) + * @see tlc2.value.IValueOutputStream#writeShort(short) + */ + @Override + public final void writeShort(short s) throws IOException { + ensureCapacity(idx + 2); + this.bytes[idx++] = (byte) ((s >>> 8) & 0xff); + this.bytes[idx++] = (byte) (s & 0xff); + } + + /* (non-Javadoc) + * @see tlc2.value.IValueOutputStream#writeInt(int) + */ + @Override + public final void writeInt(int i) throws IOException { + ensureCapacity(idx + 4); + this.bytes[idx++] = (byte) ((i >>> 24) & 0xff); + this.bytes[idx++] = (byte) ((i >>> 16) & 0xff); + this.bytes[idx++] = (byte) ((i >>> 8) & 0xff); + this.bytes[idx++] = (byte) (i & 0xff); + } + + /* (non-Javadoc) + * @see tlc2.value.IValueOutputStream#writeLong(long) + */ + @Override + public final void writeLong(long l) throws IOException { + ensureCapacity(idx + 8); + this.bytes[idx++] = (byte) ((l >>> 56) & 0xff); + this.bytes[idx++] = (byte) ((l >>> 48) & 0xff); + this.bytes[idx++] = (byte) ((l >>> 40) & 0xff); + this.bytes[idx++] = (byte) ((l >>> 32) & 0xff); + this.bytes[idx++] = (byte) ((l >>> 24) & 0xff); + this.bytes[idx++] = (byte) ((l >>> 16) & 0xff); + this.bytes[idx++] = (byte) ((l >>> 8) & 0xff); + this.bytes[idx++] = (byte) (l & 0xff); + } + + /* (non-Javadoc) + * @see tlc2.value.IValueOutputStream#close() + */ + @Override + public final void close() throws IOException { + // No-op + } + + /* (non-Javadoc) + * @see tlc2.value.IValueOutputStream#writeShortNat(short) + */ + @Override + public final void writeShortNat(short x) throws IOException { + if (x > 0x7f) { + this.writeShort((short) -x); + } else { + this.writeByte((byte) x); + } + } + + /* (non-Javadoc) + * @see tlc2.value.IValueOutputStream#writeNat(int) + */ + @Override + public final void writeNat(int x) throws IOException { + if (x > 0x7fff) { + this.writeInt(-x); + } else { + this.writeShort((short) x); + } + } + + /* (non-Javadoc) + * @see tlc2.value.IValueOutputStream#writeLongNat(long) + */ + @Override + public final void writeLongNat(long x) throws IOException { + if (x <= 0x7fffffff) { + this.writeInt((int) x); + } else { + this.writeLong(-x); + } + } + + /* (non-Javadoc) + * @see tlc2.value.IValueOutputStream#writeByte(byte) + */ + @Override + public final void writeByte(byte b) throws IOException { + ensureCapacity(idx + 1); + this.bytes[idx++] = b; + } + + /* (non-Javadoc) + * @see tlc2.value.IValueOutputStream#writeBoolean(boolean) + */ + @Override + public final void writeBoolean(boolean bool) throws IOException { + byte b = (bool ? (byte)1 : (byte)0); + this.writeByte(b); + } + + /* (non-Javadoc) + * @see tlc2.value.IValueOutputStream#getOutputStream() + */ + @Override + public final IDataOutputStream getOutputStream() { + return this; + } + + /* (non-Javadoc) + * @see tlc2.value.IValueOutputStream#put(java.lang.Object) + */ + @Override + public final int put(Object obj) { + return -1; + } + + public final byte[] toByteArray() { + final byte[] copyOf = Arrays.copyOf(bytes, idx); + idx = 0; + return copyOf; + } + + /* (non-Javadoc) + * @see util.IDataOutputStream#writeString(java.lang.String) + */ + @Override + public final void writeString(String str) throws IOException { + final int length = str.length(); + ensureCapacity(idx + length); + + final char[] c = new char[length]; + str.getChars(0, length, c, 0); + + for (int i = 0; i < c.length; i++) { + this.bytes[idx++] = (byte) c[i]; + } + } + } + + static final class ByteValueInputStream implements ValueConstants, IValueInputStream, IDataInputStream { + + private final byte[] bytes; + + private int idx = 0; + + public ByteValueInputStream(byte[] bytes) { + this.bytes = bytes; + } + + /* (non-Javadoc) + * @see tlc2.value.IValueInputStream#read() + */ + @Override + public final IValue read() throws IOException { + final byte kind = this.readByte(); + + switch (kind) { + case BOOLVALUE: { + return (this.readBoolean()) ? BoolValue.ValTrue : BoolValue.ValFalse; + } + case INTVALUE: { + return IntValue.gen(this.readInt()); + } + case STRINGVALUE: { + return StringValue.createFrom(this); + } + case MODELVALUE: { + return ModelValue.mvs[this.readShort()]; + } + case INTERVALVALUE: { + return new IntervalValue(this.readInt(), this.readInt()); + } + case RECORDVALUE: { + return RecordValue.createFrom(this); + } + case FCNRCDVALUE: { + return FcnRcdValue.createFrom(this); + } + case SETENUMVALUE: { + return SetEnumValue.createFrom(this); + } + case TUPLEVALUE: { + return TupleValue.createFrom(this); + } + default: { + throw new WrongInvocationException("ValueInputStream: Can not unpickle a value of kind " + kind); + } + } + } + + private final boolean readBoolean() throws EOFException, IOException { + return (bytes[idx++] != 0); + } + + /* (non-Javadoc) + * @see tlc2.value.IValueInputStream#readShort() + */ + @Override + public final int readShort() throws IOException { + return (short) ((bytes[idx++] << 8) | (bytes[idx++] & 0xff)); + } + + /* (non-Javadoc) + * @see tlc2.value.IValueInputStream#readInt() + */ + @Override + public final int readInt() throws IOException { + int res = bytes[idx++]; + res <<= 8; res |= (bytes[idx++] & 0xff); + res <<= 8; res |= (bytes[idx++] & 0xff); + res <<= 8; res |= (bytes[idx++] & 0xff); + return res; + } + + /* (non-Javadoc) + * @see tlc2.value.IValueInputStream#readLong() + */ + @Override + public final long readLong() throws IOException { + long res = bytes[idx++]; + res <<= 8; res |= (bytes[idx++] & 0xff); + res <<= 8; res |= (bytes[idx++] & 0xff); + res <<= 8; res |= (bytes[idx++] & 0xff); + res <<= 8; res |= (bytes[idx++] & 0xff); + res <<= 8; res |= (bytes[idx++] & 0xff); + res <<= 8; res |= (bytes[idx++] & 0xff); + res <<= 8; res |= (bytes[idx++] & 0xff); + return res; + } + + /* (non-Javadoc) + * @see tlc2.value.IValueInputStream#close() + */ + @Override + public final void close() throws IOException { + // No-op + } + + /* (non-Javadoc) + * @see tlc2.value.IValueInputStream#readNat() + */ + @Override + public final int readNat() throws IOException { + int res = this.readShort(); + if (res >= 0) return res; + res = (res << 16) | (this.readShort() & 0xFFFF); + return -res; + } + + /* (non-Javadoc) + * @see tlc2.value.IValueInputStream#readShortNat() + */ + @Override + public final short readShortNat() throws IOException { + short res = this.readByte(); + if (res >= 0) return res; + return (short) -((res << 8) | (this.readByte() & 0xFF)); + } + + /* (non-Javadoc) + * @see tlc2.value.IValueInputStream#readLongNat() + */ + @Override + public final long readLongNat() throws IOException { + long res = this.readInt(); + if (res >= 0) return res; + res = (res << 32) | ((long)this.readInt() & 0xFFFFFFFFL); + return -res; + } + + /* (non-Javadoc) + * @see tlc2.value.IValueInputStream#readByte() + */ + @Override + public final byte readByte() throws EOFException, IOException { + return bytes[idx++]; + } + + /* (non-Javadoc) + * @see tlc2.value.IValueInputStream#assign(java.lang.Object, int) + */ + @Override + public final void assign(Object obj, int idx) { + // No-op + } + + /* (non-Javadoc) + * @see tlc2.value.IValueInputStream#getIndex() + */ + @Override + public final int getIndex() { + return -1; + } + + /* (non-Javadoc) + * @see tlc2.value.IValueInputStream#getInputStream() + */ + @Override + public final IDataInputStream getInputStream() { + return this; + } + + /* (non-Javadoc) + * @see tlc2.value.IValueInputStream#getValue(int) + */ + @Override + public final UniqueString getValue(int idx) { + throw new WrongInvocationException("Not supported"); + } + + /* (non-Javadoc) + * @see util.IDataInputStream#readString(int) + */ + @Override + public final String readString(int length) throws IOException { + final char[] s = new char[length]; + for (int i = 0; i < s.length; i++) { + s[i] = (char) this.bytes[idx++]; + } + return new String(s); + } + } + + /* (non-Javadoc) + * @see tlc2.tool.queue.IStateQueue#delete() + */ + @Override + public void delete() { + finishAll(); + new File(this.filePrefix).delete(); + } +} diff --git a/tlatools/src/tlc2/tool/queue/DiskStateQueue.java b/tlatools/src/tlc2/tool/queue/DiskStateQueue.java index 14b4e6d17099d31559f125bbd43852e49fb735fb..4acf373684100378ffc4d2fa3208ab5dccdeeccb 100644 --- a/tlatools/src/tlc2/tool/queue/DiskStateQueue.java +++ b/tlatools/src/tlc2/tool/queue/DiskStateQueue.java @@ -7,8 +7,10 @@ package tlc2.tool.queue; import java.io.File; import java.io.IOException; +import java.nio.file.Files; import tlc2.output.EC; +import tlc2.output.MP; import tlc2.tool.TLCState; import tlc2.util.StatePoolReader; import tlc2.util.StatePoolWriter; @@ -23,7 +25,7 @@ import util.FileUtil; */ public class DiskStateQueue extends StateQueue { // TODO dynamic bufsize based on current VM parameters? - private final static int BufSize = Integer.getInteger(DiskStateQueue.class.getName() + ".BufSize", 8192);; + private final static int BufSize = Integer.getInteger(DiskStateQueue.class.getName() + ".BufSize", 8192); /* * Invariants: I1. Entries in deqBuf are in the indices: [deqIndex, @@ -40,9 +42,21 @@ public class DiskStateQueue extends StateQueue { protected int deqIndex, enqIndex; protected StatePoolReader reader; protected StatePoolWriter writer; + /** + * The SPC takes care of deleting swap files on the lower end of the range + * (loPool, hiPool). It terminates, when the first checkpoint is written at + * which point checkpointing itself takes care of removing obsolete swap + * files. + */ + protected final StatePoolCleaner cleaner; private int loPool, hiPool, lastLoPool, newLastLoPool; private File loFile; + // TESTING ONLY! + DiskStateQueue() throws IOException { + this(Files.createTempDirectory("DiskStateQueue").toFile().toString()); + } + /* Constructors */ public DiskStateQueue(String diskdir) { this.deqBuf = new TLCState[BufSize]; @@ -61,6 +75,9 @@ public class DiskStateQueue extends StateQueue { this.writer = new StatePoolWriter(BufSize, this.reader); this.writer.setDaemon(true); this.writer.start(); + this.cleaner = new StatePoolCleaner(); + this.cleaner.setDaemon(true); + this.cleaner.start(); } final void enqueueInner(TLCState state) { @@ -86,6 +103,16 @@ public class DiskStateQueue extends StateQueue { } return this.deqBuf[this.deqIndex++]; } + + /* (non-Javadoc) + * @see tlc2.tool.queue.StateQueue#peekInner() + */ + TLCState peekInner() { + if (this.deqIndex == this.deqBuf.length) { + this.fillDeqBuffer(); + } + return this.deqBuf[this.deqIndex]; + } private final void fillDeqBuffer() { try { @@ -117,6 +144,14 @@ public class DiskStateQueue extends StateQueue { this.enqIndex = 0; } } + // Notify the cleaner to do its job unless its waits for more work + // to pile up. + if ((loPool - lastLoPool) > 100) { //TODO Take BufSize into account. It defines the disc file size. + synchronized (this.cleaner) { + this.cleaner.deleteUpTo = loPool - 1; + this.cleaner.notifyAll(); + } + } } catch (Exception e) { Assert.fail(EC.SYSTEM_ERROR_READING_STATES, new String[] { "queue", (e.getMessage() == null) ? e.toString() : e.getMessage() }); @@ -125,6 +160,14 @@ public class DiskStateQueue extends StateQueue { /* Checkpoint. */ public final void beginChkpt() throws IOException { + synchronized (this.cleaner) { + // Checkpointing takes precedence over periodic cleaning + // (cleaner would otherwise delete checkpoint files as it know + // nothing of checkpoints). + this.cleaner.finished = true; + this.cleaner.notifyAll(); + } + String filename = this.filePrefix + "queue.tmp"; ValueOutputStream vos = new ValueOutputStream(filename); vos.writeLongNat(this.len); @@ -195,6 +238,61 @@ public class DiskStateQueue extends StateQueue { this.reader.setFinished(); this.reader.notifyAll(); } + synchronized (this.cleaner) { + this.cleaner.finished = true; + this.cleaner.notifyAll(); + } } + private class StatePoolCleaner extends Thread { + + private volatile boolean finished = false; + public int deleteUpTo; + + private StatePoolCleaner() { + super("TLCStatePoolCleaner"); + } + + /* (non-Javadoc) + * @see java.lang.Thread#run() + */ + public void run() { + try { + synchronized (this) { + while (!this.finished) { + this.wait(); + if (this.finished) { + return; + } + + for (int i = lastLoPool; i < deleteUpTo; i++) { + final File oldPoolFile = new File(filePrefix + Integer.toString(i)); + if (!oldPoolFile.delete()) { + // No reason to terminate/kill TLC when the cleanup fails. + // Contrary to StatePoolReader/Write, cleanup is optional + // functionality whose purpose is to prevent the disc from + // filling up. If the cleaner fails, the user can still + // manually delete the files. + MP.printWarning(EC.SYSTEM_ERROR_CLEANING_POOL, oldPoolFile.getCanonicalPath()); + } + } + lastLoPool = deleteUpTo; + } + } + } catch (Exception e) { + // Assert.printStack(e); + MP.printError(EC.SYSTEM_ERROR_CLEANING_POOL, e.getMessage(), e); + System.exit(1); + } + } + } + + /* (non-Javadoc) + * @see tlc2.tool.queue.IStateQueue#delete() + */ + @Override + public void delete() { + finishAll(); + new File(this.filePrefix).delete(); + } } diff --git a/tlatools/src/tlc2/tool/queue/IStateQueue.java b/tlatools/src/tlc2/tool/queue/IStateQueue.java index 88f979698e43c0031c538270a088f7bdc3fcd564..e2ed3d6631ba35136454cac1a5b6cdf3a0fc8742 100644 --- a/tlatools/src/tlc2/tool/queue/IStateQueue.java +++ b/tlatools/src/tlc2/tool/queue/IStateQueue.java @@ -2,6 +2,7 @@ package tlc2.tool.queue; import java.io.IOException; +import tlc2.tool.StateVec; import tlc2.tool.TLCState; import tlc2.tool.Worker; @@ -21,9 +22,16 @@ public interface IStateQueue { /* Enqueues a list of states. Wake up any waiting thread. */ public abstract void sEnqueue(final TLCState states[]); - + public abstract void sEnqueue(final StateVec stateVec); + /* Return the first element in the queue. Wait if empty. */ public abstract TLCState sDequeue(); + + /** + * Returns the first element in the queue. Wait if empty. Does not remove the + * element. Can be null and blocks other consumers (sEnqueue and sDequeue). + */ + public abstract TLCState sPeek(); /** * Return (up to) the first count elements in the queue. Wait if empty. @@ -62,7 +70,7 @@ public interface IStateQueue { * free when workers behave correctly except for the single case when a * remote worker dies unexpectedly. * - * @see http://bugzilla.tlaplus.net/show_bug.cgi?id=175 + * @see Bug #175 in general/bugzilla/index.html */ public abstract void resumeAllStuck(); @@ -77,4 +85,10 @@ public interface IStateQueue { public abstract void recover() throws IOException; public abstract boolean isEmpty(); + + /** + * TESTING ONLY! + * Delete disk files if any. + */ + abstract void delete() throws IOException; } \ No newline at end of file diff --git a/tlatools/src/tlc2/tool/queue/MemStateQueue.java b/tlatools/src/tlc2/tool/queue/MemStateQueue.java index 505fca96e2a56f36e7bde0cb0ab63d8d0fa03e21..b5b36c9b37e78dad1c0b590d22fbe7c6d4b20fed 100644 --- a/tlatools/src/tlc2/tool/queue/MemStateQueue.java +++ b/tlatools/src/tlc2/tool/queue/MemStateQueue.java @@ -7,6 +7,7 @@ package tlc2.tool.queue; import java.io.File; import java.io.IOException; +import java.nio.file.Files; import tlc2.output.EC; import tlc2.tool.TLCState; @@ -23,6 +24,13 @@ public final class MemStateQueue extends StateQueue { private int start = 0; private String diskdir; + /** + * TESTING ONLY! + */ + MemStateQueue() throws IOException { + this(Files.createTempDirectory("MemStateQueue").toFile().toString()); + } + public MemStateQueue(String metadir) { this.states = new TLCState[InitialSize]; this.start = 0; @@ -31,7 +39,7 @@ public final class MemStateQueue extends StateQueue { final void enqueueInner(TLCState state) { if (this.len > Integer.MAX_VALUE) { - Assert.fail(EC.SYSTEM_ERROR_WRITING_STATES, "Amount of states exceeds internal storage"); + Assert.fail(EC.SYSTEM_ERROR_WRITING_STATES, new String[]{"queue", "Amount of states exceeds internal storage"}); } if (this.len == this.states.length) { // grow the array @@ -60,6 +68,13 @@ public final class MemStateQueue extends StateQueue { this.start = (this.start + 1) % this.states.length; return res; } + + /* (non-Javadoc) + * @see tlc2.tool.queue.StateQueue#peekInner() + */ + final TLCState peekInner() { + return this.states[this.start]; + } // Checkpoint. public final void beginChkpt() throws IOException { @@ -95,5 +110,4 @@ public final class MemStateQueue extends StateQueue { } vis.close(); } - } diff --git a/tlatools/src/tlc2/tool/queue/StateQueue.java b/tlatools/src/tlc2/tool/queue/StateQueue.java index 0d151ecadd933e9a1733edabc6912a773a1762fe..e37d9287c8da8d00f2d284c4e1faac9ec7a8fe56 100644 --- a/tlatools/src/tlc2/tool/queue/StateQueue.java +++ b/tlatools/src/tlc2/tool/queue/StateQueue.java @@ -10,9 +10,9 @@ import java.io.IOException; import tlc2.TLCGlobals; import tlc2.output.EC; import tlc2.output.MP; +import tlc2.tool.StateVec; import tlc2.tool.TLCState; import tlc2.tool.Worker; -import util.Assert; /** * @@ -84,6 +84,29 @@ public abstract class StateQueue implements IStateQueue { this.notifyAll(); } } + + public final synchronized void sEnqueue(final StateVec stateVec) { + int cnt = 0; + for (int j = 0; j < stateVec.size(); j++) { + TLCState state = stateVec.elementAt(j); + if (state != null) { + this.enqueueInner(state); + cnt++; + } + } + this.len += cnt; + if (this.numWaiting > 0 && !this.stop) { + this.notifyAll(); + } + } + + + public final synchronized TLCState sPeek() { + if (this.isAvail()) { + return this.peekInner(); + } + return null; + } /* Return the first element in the queue. Wait if empty. */ /* (non-Javadoc) @@ -93,7 +116,7 @@ public abstract class StateQueue implements IStateQueue { if (this.isAvail()) { final TLCState state = this.dequeueInner(); // LL modified error message on 7 April 2012 - Assert.check(state != null, "Null state found on queue"); + assert state != null : "Null state found on queue"; this.len--; return state; } @@ -104,7 +127,7 @@ public abstract class StateQueue implements IStateQueue { * @see tlc2.tool.queue.IStateQueue#sDequeue(int) */ public final synchronized TLCState[] sDequeue(int cnt) { - Assert.check(cnt > 0, "Nonpositive number of states requested."); + assert cnt > 0 : "Nonpositive number of states requested."; if (this.isAvail()) { if (cnt > len) { // in this case, casting len to int is safe @@ -327,6 +350,9 @@ public abstract class StateQueue implements IStateQueue { /* This method must be implemented in the subclass. */ abstract TLCState dequeueInner(); + /* This method must be implemented in the subclass. */ + abstract TLCState peekInner(); + /* Checkpoint. */ /* (non-Javadoc) * @see tlc2.tool.queue.IStateQueue#beginChkpt() @@ -342,4 +368,9 @@ public abstract class StateQueue implements IStateQueue { * @see tlc2.tool.queue.IStateQueue#recover() */ public abstract void recover() throws IOException; + + @Override + public void delete() throws IOException { + // no-op + } } diff --git a/tlatools/src/tlc2/util/BitVector.java b/tlatools/src/tlc2/util/BitVector.java index 2f03bc2e0b4d76b7cab91770baf90a637d77c51c..d4230a8494259dbc8f3ce2e3bbd0f2bfa75b07d0 100644 --- a/tlatools/src/tlc2/util/BitVector.java +++ b/tlatools/src/tlc2/util/BitVector.java @@ -128,6 +128,40 @@ public class BitVector implements Serializable { } } + /** + * Returns this {@link BitVector} is a bit string with the MSB to the left + * and the LSB to the right. + * + * @see java.lang.Object#toString() + */ + public String toString() { + final StringBuffer buf = new StringBuffer(this.word.length * 64); + for (int i = this.word.length * 64; i >= 0; i--) { + if (get(i)) { + buf.append("1"); + } else { + buf.append("0"); + } + } + buf.append("]"); + return buf.toString().replaceAll("^0*","["); // Replace leading zeros with "[" + } + public String toString(int start, int length) { + return toString(start, length, '1', '0'); + } + + public String toString(int start, int length, char one, char zero) { + final StringBuffer buf = new StringBuffer(length); + for (int i = 0; i < length; i++) { + if (get(start + i)) { + buf.append(one); + } else { + buf.append(zero); + } + } + return "[" + buf.reverse().toString() + "]"; + } + /** * @return The number of bits set true */ @@ -250,5 +284,4 @@ public class BitVector implements Serializable { return -1; } } - } diff --git a/tlatools/src/tlc2/util/BufferedRandomAccessFile.java b/tlatools/src/tlc2/util/BufferedRandomAccessFile.java index f9ad6dcba94de5c04f906c81cf07221ba89e1c4e..eaa76c9b50a6fadb0b5003fab0c4414380596144 100644 --- a/tlatools/src/tlc2/util/BufferedRandomAccessFile.java +++ b/tlatools/src/tlc2/util/BufferedRandomAccessFile.java @@ -37,8 +37,9 @@ public final class BufferedRandomAccessFile extends java.io.RandomAccessFile { private long maxHi; // this.lo + this.buff.length private boolean hitEOF; // buffer contains last file block? private long diskPos; // disk position + private long mark; - private static Object mu = new Object(); // protects the following fields + private static final Object mu = new Object(); // protects the following fields private static byte[][] availBuffs = new byte[100][]; private static int numAvailBuffs = 0; @@ -175,7 +176,7 @@ public final class BufferedRandomAccessFile extends java.io.RandomAccessFile { } /* Flush any dirty bytes in the buffer to disk. */ - private void flushBuffer() throws IOException { + private boolean flushBuffer() throws IOException { if (this.dirty) { // Assert.check(this.curr > this.lo); if (this.diskPos != this.lo) super.seek(this.lo); @@ -183,7 +184,9 @@ public final class BufferedRandomAccessFile extends java.io.RandomAccessFile { super.write(this.buff, 0, len); this.diskPos = this.curr; this.dirty = false; + return true; } + return false; } /* Read at most "this.buff.length" bytes into "this.buff", @@ -248,7 +251,7 @@ public final class BufferedRandomAccessFile extends java.io.RandomAccessFile { // seeking inside current buffer -- no read required if (pos < this.curr) { // if seeking backwards, we must flush to maintain V4 - this.flushBuffer(); + pageReadNeeded = this.flushBuffer(); } else { pageReadNeeded = false; } @@ -282,9 +285,13 @@ public final class BufferedRandomAccessFile extends java.io.RandomAccessFile { if (this.curr == this.hi) return -1; } // Assert.check(this.curr < this.hi); - byte res = this.buff[(int)(this.curr - this.lo)]; - this.curr++; - return ((int)res) & 0xFF; // convert byte -> int + try { + byte res = this.buff[(int)(this.curr - this.lo)]; + this.curr++; + return ((int)res) & 0xFF; // convert byte -> int + } catch (ArrayIndexOutOfBoundsException e) { + throw new IOException("Read past end of file (increase with setLength)?", e); + } } /* overrides RandomAccessFile.read(byte[]) */ @@ -317,6 +324,13 @@ public final class BufferedRandomAccessFile extends java.io.RandomAccessFile { // Assert.check(this.read(b) == size); return new BigInteger(b); } + + public final int readShortNat() throws IOException { + int res = this.readByte(); + if (res >= 0) return res; + res = (res << 16) | (this.readByte() & 0xff); + return -res; + } public final int readNat() throws IOException { int res = this.readShort(); @@ -351,9 +365,13 @@ public final class BufferedRandomAccessFile extends java.io.RandomAccessFile { } } // Assert.check(this.curr < this.hi); - this.buff[(int)(this.curr - this.lo)] = (byte)b; - this.curr++; - this.dirty = true; + try { + this.buff[(int)(this.curr - this.lo)] = (byte)b; + this.curr++; + this.dirty = true; + } catch (ArrayIndexOutOfBoundsException e) { + throw new IOException("Wrote past end of file (increase with setLength)?", e); + } } /* overrides RandomAccessFile.write(byte[]) */ @@ -377,6 +395,16 @@ public final class BufferedRandomAccessFile extends java.io.RandomAccessFile { // Assert.check(b.length <= size); this.write(b, 0, size); } + + /* Precondition: x is a non-negative short. */ + public final void writeShortNat(int x) throws IOException { + if (x <= 0x7f) { + this.writeByte((short)x); + } + else { + this.writeShort(-x); + } + } /* Precondition: x is a non-negative int. */ public final void writeNat(int x) throws IOException { @@ -434,6 +462,21 @@ public final class BufferedRandomAccessFile extends java.io.RandomAccessFile { setLength(0); this.init(); } + + public long getMark() { + return this.mark; + } + + public long mark() { + final long oldMark = this.mark; + this.mark = getFilePointer(); + return oldMark; + } + + public void seekAndMark(long pos) throws IOException { + this.mark = pos; + this.seek(pos); + } public static void main(String[] args) throws IOException { String name = "xxx"; @@ -449,6 +492,7 @@ public final class BufferedRandomAccessFile extends java.io.RandomAccessFile { System.err.println("len = " + braf.length() + ", pos = " + braf.getFilePointer()); braf.writeLong(x); braf.writeLong(x); braf.writeLong(x); System.err.println("len = " + braf.length() + ", pos = " + braf.getFilePointer()); + braf.close(); } } diff --git a/tlatools/src/tlc2/util/ByteUtils.java b/tlatools/src/tlc2/util/ByteUtils.java index c7f3195b462086e50bb03eefd189cfcfbefcdba0..0eaa6b2248c498d8e5d61b47a15424f0be8da1a9 100644 --- a/tlatools/src/tlc2/util/ByteUtils.java +++ b/tlatools/src/tlc2/util/ByteUtils.java @@ -68,14 +68,14 @@ public class ByteUtils { * The Java Language Specification. */ public static long byteArrayToLong(byte[] b) { - long i0 = (b[0] & 0xFF) << 56; - long i1 = (b[1] & 0xFF) << 48; - long i2 = (b[2] & 0xFF) << 40; - long i3 = (b[3] & 0xFF) << 32; - long i4 = (b[4] & 0xFF) << 24; - long i5 = (b[5] & 0xFF) << 16; - long i6 = (b[6] & 0xFF) << 8; - long i7 = (b[7] & 0xFF); + long i0 = (long) (b[0] & 0xFF) << 56; + long i1 = (long) (b[1] & 0xFF) << 48; + long i2 = (long) (b[2] & 0xFF) << 40; + long i3 = (long) (b[3] & 0xFF) << 32; + long i4 = (long) (b[4] & 0xFF) << 24; + long i5 = (long) (b[5] & 0xFF) << 16; + long i6 = (long) (b[6] & 0xFF) << 8; + long i7 = (long) (b[7] & 0xFF); return (i0 | i1 | i2 | i3 | i4 | i5 | i6 | i7); } diff --git a/tlatools/src/tlc2/util/Combinatorics.java b/tlatools/src/tlc2/util/Combinatorics.java index 3b55d8e16faffb1e8925e2042f30691ea27c2fb4..e8f24057a5148c4567d7375871a7a8fbe1f95c25 100644 --- a/tlatools/src/tlc2/util/Combinatorics.java +++ b/tlatools/src/tlc2/util/Combinatorics.java @@ -10,43 +10,43 @@ import util.Assert; public class Combinatorics { public static final int MAXCHOOSENUM = 62; - private static final int CHOOSETABLESIZE = - (MAXCHOOSENUM-3)*(MAXCHOOSENUM-4)/2+MAXCHOOSENUM-3; - private static long[] CHOOSETABLE = new long[CHOOSETABLESIZE]; + public static final int CHOOSETABLESIZE = (MAXCHOOSENUM - 3) * (MAXCHOOSENUM - 4) / 2 + MAXCHOOSENUM - 3; + public static long[] CHOOSETABLE = new long[CHOOSETABLESIZE]; private static long[] SUMCHOOSETABLE = new long[CHOOSETABLESIZE]; public static long choose(int n, int m) { + if (n < 0 || m < 0) { Assert.check(((m >= 0) && (n >= 0) && (n >= m)), EC.TLC_CHOOSE_ARGUMENTS_WRONG, "choose"); - if (m == 0 || m == n) + } + if (m == 0 || m == n) { return (long)1; - else if (m == 1 || m == n-1) + } else if (m == 1 || m == n - 1) { return (long)n; - else { + } else if (n == 0 || m > n) { + // Cannot choose from zero elements or more elements than present. + return 0; + } else { int j = choosePairToInt(n, m); if (j < CHOOSETABLESIZE) { return CHOOSETABLE[j]; } - Assert.fail(EC.TLC_CHOOSE_UPPER_BOUND, String.valueOf(MAXCHOOSENUM)); - return 0; // make compiler happy + return binomial(n, m); // calculate on demand } } - public static long sumChoose(int n, int m) - { + public static long sumChoose(int n, int m) { Assert.check(((m>=0) && (n>=0) && (n>=m)), EC.TLC_CHOOSE_ARGUMENTS_WRONG, "sumChoose"); - if (m == 0) + if (m == 0) { return (long)1; - else if (m == n) + } else if (m == n) { return ((long)1 << n); - else if (m == 1) + } else if (m == 1) { return (long)n; - else if (m == n-1) + } else if (m == n - 1) { return ((long)2 << n) - n; - else - { + } else { int j = choosePairToInt(n,m); - if (j < CHOOSETABLESIZE) - { + if (j < CHOOSETABLESIZE) { return SUMCHOOSETABLE[j]; } Assert.fail(EC.TLC_CHOOSE_UPPER_BOUND, String.valueOf(MAXCHOOSENUM)); @@ -55,7 +55,7 @@ public class Combinatorics { } } - private static int choosePairToInt(int n, int m) { + public static int choosePairToInt(int n, int m) { return ((n-3)*(n-4))/2 + m -2; } @@ -74,8 +74,7 @@ public class Combinatorics { n++; m = 2; sum = 1+n; - } - else + } else m++; } } @@ -122,6 +121,20 @@ public class Combinatorics { } public static BigInteger bigChoose(int n, int m) { + if (n < MAXCHOOSENUM && m < MAXCHOOSENUM) { + return BigInteger.valueOf(choose(n, m)); + } + + BigInteger binomial = BigInteger.ONE; + for (int i = 1, j = n; i <= m; i++, j--) { + final BigInteger bj = BigInteger.valueOf(j); + final BigInteger bi = BigInteger.valueOf(i); + binomial = binomial.multiply(bj).divide(bi); + } + return binomial; + } + + public static BigInteger slowBigChoose(int n, int m) { BigInteger num = fact(n); BigInteger denom = fact(n - m).multiply(fact(m)); @@ -135,8 +148,7 @@ public class Combinatorics { result = BigInt.BigZero; for (int i = 0; i <= m; i++) result = result.add(bigChoose(n, i)); - } - else { + } else { result = BigInt.BigOne; result = result.shiftLeft(n); for (int i = m+1; i <= n; i++) @@ -154,6 +166,51 @@ public class Combinatorics { return new String(sb); } + // https://blog.plover.com/math/choose.html + public static final long binomial(int n, int k) { + if (k > n) { + return 0; + } + if (k > n - k) { + // Optimize to n choose n - k. + k = n - k; + } + + long binomial = 1L; + for (int i = 1, m = n; i <= k; i++, m--) { + binomial = binomial * m / i; + } + return binomial; + } + + public static long[] pascalTableUpTo(final int maxN, final int maxK) { + if (maxN > MAXCHOOSENUM) { + final long[] ppt = new long[((maxN - MAXCHOOSENUM) * (maxK - 1))]; + int idx = 0; + + // Initialize first "row" of extension table from existing triangle. + int i = MAXCHOOSENUM + 1; + for (int j = 2; j <= maxK; j++) { + ppt[idx++] = choose(i, j); + } + // Subsequent rows initialize from previous row. + final int k = maxK - 1; + for (int j = 1; j < (maxN - MAXCHOOSENUM); j++) { + for (int l = 0; l < k; l++) { + if (l == 0) { + ppt[idx] = i++ + ppt[idx - k]; + } else { + ppt[idx] = ppt[idx - k] + ppt[idx - k - 1]; + } + idx++; + } + } + return ppt; + } + return new long[0]; + } + + // SZ Jul 14, 2009: Dead code. not used. // public static void main(String argv[]) { // int i,j; diff --git a/tlatools/src/tlc2/util/Context.java b/tlatools/src/tlc2/util/Context.java index 65a03219eb8417dd838b6f0a1e83b268309f23bf..0b372a5598e2eb108938003f7ccfed17ee7353fc 100644 --- a/tlatools/src/tlc2/util/Context.java +++ b/tlatools/src/tlc2/util/Context.java @@ -5,8 +5,26 @@ package tlc2.util; +import java.util.function.Function; + import tla2sany.semantic.SymbolNode; +// Context is used two times: +// 1) To determine the level boundedness of the expression appearing in the spec +// (see Tool#getLevelBounds*). This is done once as part of parsing. +// 2) To store the variable/name binding at each scope during state exploration. +// The hierarchy of levels/scopes is represented as a chain of context instances +// (link-list or associative list). The Empty context is the outer most scope +// (first level). +// 1) is not relevant performance-wise, but 2) has - provided non-trivial level - +// a significant performance impact because of excessive lookups. What makes +// optimizations difficult, is the fact that the context chain is recreated each +// time a state is generated, as part of the next state relation. An optimization +// thus has to ensure that it doesn't trade fast - ideally constant - lookups for +// an equally expensive creation complexity. +// +// The contrived spec at the bottom exhibits this problem. Increasing the level, +// the number of lookups go through the roof. public final class Context { /** * A link list of name and value pairs. When adding <name, value> to the @@ -18,7 +36,8 @@ public final class Context { private final Context next; public final static Context Empty = new Context(null, null, null); - public final static Context BaseBranch = new Context(null, null, Empty); + + private final static Context BaseBranch = new Context(null, null, Empty); private Context(SymbolNode name, Object value, final Context next) { this.name = name; @@ -26,6 +45,9 @@ public final class Context { this.next = next; } + // This method is only called within the context of the ENABLED (temporal) + // operator. A branching context is specially handled during lookup below + // if cutoff is true. public static Context branch(Context base) { if (base == Empty) { // Avoid new instance if the next context in the chain is the Empty @@ -60,6 +82,28 @@ public final class Context { } return null; // On Empty Context (end of chain), return null value } + + public final Object lookup(final Function<SymbolNode, Boolean> f) { + Context cur = this; + while (cur != Empty) { + if (f.apply(cur.name)) { + return cur.value; + } + cur = cur.next; + } + return null; + } + + public final SymbolNode lookupName(final Function<SymbolNode, Boolean> f) { + Context cur = this; + while (cur != Empty) { + if (f.apply(cur.name)) { + return cur.name; + } + cur = cur.next; + } + return null; + } /** * @param var @@ -120,3 +164,43 @@ public final class Context { return sb.toString(); } } +/* +----------------------------- MODULE Scoping ----------------------------- +EXTENDS Naturals +CONSTANT Limit +VARIABLE var + +A(a) == TRUE +B(b) == A(b) +C(c) == B(c) +D(d) == C(d) +E(e) == D(e) +F(f) == E(f) +G(g) == F(g) +H(h) == G(h) +I(i) == H(i) +J(j) == I(j) +K(k) == J(k) +L(l) == K(l) +M(m) == L(m) +N(n) == M(n) +O(o) == N(o) +P(p) == O(p) +Q(q) == P(q) +R(r) == Q(r) +S(s) == R(s) +T(t) == S(t) +U(u) == T(u) +V(v) == U(v) +W(w) == V(w) +X(x) == W(x) +Y(y) == X(y) +Z(z) == U(z) + +Next == /\ var' = var + 1 + /\ var < 1 + /\ Z(1) + +Spec == var=0 /\ [][Next]_<<var>> +============================================================================= +*/ diff --git a/tlatools/src/tlc2/util/DiskIntStack.java b/tlatools/src/tlc2/util/DiskIntStack.java index 0fdb6874e644e1e3c2e023bf55c3c7905102b362..1d55b7b5d69b296fa24bcd258ef91a3bcf0580e7 100644 --- a/tlatools/src/tlc2/util/DiskIntStack.java +++ b/tlatools/src/tlc2/util/DiskIntStack.java @@ -14,12 +14,15 @@ import util.BufferedDataOutputStream; import util.FileUtil; /** - * Alternative implementation - * currently not used + * Alternative implementation. Currently not used + * + * This implementation is likely to have never worked and should be regarded as + * a sketch for a how a concurrent implementation could look like. For the + * moment, {@link SynchronousDiskIntStack} does the job just fine. Albeit - as + * the name suggests - in a synchronous fashion. * - * @version $Id$ */ -public final class DiskIntStack { +public final class DiskIntStack implements IntStack { private final static int BufSize = 16384; private long size; @@ -168,4 +171,10 @@ public final class DiskIntStack { } } + /* (non-Javadoc) + * @see tlc2.util.IntStack#reset() + */ + public void reset() { + // TODO Auto-generated method stub + } } diff --git a/tlatools/src/tlc2/util/DotStateWriter.java b/tlatools/src/tlc2/util/DotStateWriter.java new file mode 100644 index 0000000000000000000000000000000000000000..73d895881a2db4e6c19b06cc447a01f06aa1da18 --- /dev/null +++ b/tlatools/src/tlc2/util/DotStateWriter.java @@ -0,0 +1,380 @@ +/******************************************************************************* + * Copyright (c) 2016 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ + +package tlc2.util; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.nio.file.StandardOpenOption; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import tlc2.tool.Action; +import tlc2.tool.TLCState; +import util.FileUtil; + +/** + * Writes the given state in dot notation. + * + * @see https://en.wikipedia.org/wiki/DOT_(graph_description_language) + * + * + * To ASCII-render a graph (on Debian|Ubuntu) install cpanminus, sudo cpanm Graph::Easy and run: + * cat your.dot | graph-easy --from=dot --as_ascii + * (https://stackoverflow.com/questions/3211801/graphviz-and-ascii-output) + */ +public class DotStateWriter extends StateWriter { + + // The Graphviz color scheme that is used for state transition edge colors. See + // https://www.graphviz.org/doc/info/colors.html for more details on color schemes. + private static final String dotColorScheme = "paired12"; + + // A mapping of action names to their assigned color ids. Since states are fed + // into a StateWriter incrementally, one at a time, this table is built up over + // time, adding new actions as we find out about them. + private final Map<String, Integer> actionToColors = new HashMap<>(); + + // A mapping from ranks to nodes. + private final Map<Integer, Set<Long>> rankToNodes = new HashMap<>(); + + // Determines whether or not transition edges should be colorized in the state + // graph. + private final boolean colorize; + + // Determines whether or not transition edges should be labeled with their + // action names. + private final boolean actionLabels; + + // Used for assigning unique color identifiers to each action type. Incremented + // by 1 every time a new color is assigned to an action. + private Integer colorGen = 1; + + // Create a valid fname_snapshot.dot file after a state is written. + private final boolean snapshot; + + public DotStateWriter(final String fname, final String strict) throws IOException { + this(fname, strict, false, false, false); + } + + /** + * @param fname + * @param colorize + * Colorize state transition edges in the DOT state graph. + * @param actionLabels + * Label transition edges in the state graph with the name of the + * associated action. Can potentially add a large amount of visual + * clutter for large graphs with many actions. + * @throws IOException + */ + public DotStateWriter(final String fname, final boolean colorize, final boolean actionLabels, + final boolean snapshot) throws IOException { + this(fname, "strict ", colorize, actionLabels, snapshot); + } + + public DotStateWriter(final String fname, final String strict, final boolean colorize, final boolean actionLabels, + final boolean snapshot) throws IOException { + super(fname); + this.colorize = colorize; + this.actionLabels = actionLabels; + this.snapshot = snapshot; + this.writer.append(strict + "digraph DiskGraph {\n"); // strict removes redundant edges + // Turned off LR because top to bottom provides better results with GraphViz viewer. +// this.writer.append("rankdir=LR;\n"); // Left to right rather than top to bottom + + // Set the color scheme for transition edges if necessary. + if(colorize) { + this.writer.append(String.format("edge [colorscheme=\"%s\"]\n", dotColorScheme)); + } + + // Spread out state nodes a bit more. + this.writer.append("nodesep=0.35;\n"); + + this.writer.append("subgraph cluster_graph {\n"); + this.writer.append("color=\"white\";\n"); // no border. + this.writer.flush(); + } + + /* (non-Javadoc) + * @see tlc2.util.IStateWriter#isDot() + */ + @Override + public boolean isDot() { + return true; + } + + /* (non-Javadoc) + * @see tlc2.util.StateWriter#writeState(tlc2.tool.TLCState) + */ + public synchronized void writeState(final TLCState state) { + // Marker the state as an initial state by using a filled style. + this.writer.append(Long.toString(state.fingerPrint())); + this.writer.append(" [label=\""); + this.writer.append(states2dot(state)); + this.writer.append("\",style = filled]"); + this.writer.append("\n"); + + maintainRanks(state); + + if (snapshot) { + try { + this.snapshot(); + } catch (IOException e) { + // Let's assume this never happens! + e.printStackTrace(); + throw new RuntimeException(e); + } + } + } + + protected void maintainRanks(final TLCState state) { + rankToNodes.computeIfAbsent(state.getLevel(), k -> new HashSet<Long>()).add(state.fingerPrint()); + } + + /* (non-Javadoc) + * @see tlc2.util.StateWriter#writeState(tlc2.tool.TLCState, tlc2.tool.TLCState, boolean) + */ + public synchronized void writeState(TLCState state, TLCState successor, boolean successorStateIsNew) { + writeState(state, successor, successorStateIsNew, Visualization.DEFAULT); + } + + public synchronized void writeState(final TLCState state, final TLCState successor, final boolean successorStateIsNew, Action action) + { + writeState(state, successor, null, 0, 0, successorStateIsNew, Visualization.DEFAULT, action); + } + + /* (non-Javadoc) + * @see tlc2.util.StateWriter#writeState(tlc2.tool.TLCState, tlc2.tool.TLCState, boolean, tlc2.util.IStateWriter.Visualization) + */ + public synchronized void writeState(TLCState state, TLCState successor, boolean successorStateIsNew, Visualization visualization) { + writeState(state, successor, null, 0, 0, successorStateIsNew, visualization, null); + } + + /* (non-Javadoc) + * @see tlc2.util.StateWriter#writeState(tlc2.tool.TLCState, tlc2.tool.TLCState, tlc2.util.BitVector, int, int, boolean) + */ + public synchronized void writeState(TLCState state, TLCState successor, BitVector actionChecks, int from, int length, boolean successorStateIsNew) { + writeState(state, successor, actionChecks, from, length, successorStateIsNew, Visualization.DEFAULT, null); + } + + /* (non-Javadoc) + * @see tlc2.util.StateWriter#writeState(tlc2.tool.TLCState, tlc2.tool.TLCState, java.lang.String, boolean, tlc2.util.IStateWriter.Visualization) + */ + public synchronized void writeState(TLCState state, TLCState successor, BitVector actionChecks, int from, int length, boolean successorStateIsNew, + Visualization visualization, Action action) { + final String successorsFP = Long.toString(successor.fingerPrint()); + + // Write the transition edge. + this.writer.append(Long.toString(state.fingerPrint())); + this.writer.append(" -> "); + this.writer.append(successorsFP); + if (visualization == Visualization.STUTTERING) { + this.writer.append(" [style=\"dashed\"];\n"); + } else { + // Add the transition edge label. + if(action!=null) { + String transitionLabel = this.dotTransitionLabel(state, successor, action); + this.writer.append(transitionLabel); + } + + this.writer.append(";\n"); + + // If the successor is new, print the state's label. Labels are printed + // when writeState sees the successor. It does not print the label for + // the current state. If it would print the label for the current state, + // the init state labels would be printed twice. + if (successorStateIsNew) { + // Write the successor's label. + this.writer.append(successorsFP); + this.writer.append(" [label=\""); + this.writer.append(states2dot(successor)); + this.writer.append("\"]"); + this.writer.append(";\n"); + } + } + + maintainRanks(state); + + if (snapshot) { + try { + this.snapshot(); + } catch (IOException e) { + // Let's assume this never happens! + e.printStackTrace(); + throw new RuntimeException(e); + } + } + } + + /** + * Given an action, returns the associated color identifier for it. The color + * identifier is just an integer suitable for use in a GraphViz color scheme. This + * method updates the (action -> color) mapping if this action has not been seen + * before for this DotStateWriter instance. + * + * @param action + * @return the color identifier for the given action + */ + protected Integer getActionColor(final Action action) { + // Return a default color if the given action is null. + if (action == null) { + return 1; + } else { + String actionName = action.getName().toString(); + // If this action has been seen before, retrieve its color. + if (actionToColors.containsKey(actionName)) { + return actionToColors.get(actionName); + } + // If this action has not been seen yet, get the next available color + // and assign it to this action. + else { + this.colorGen++; + actionToColors.put(actionName, this.colorGen); + return this.colorGen; + } + } + } + + /** + * Creates a DOT label for an edge representing a state transition. + * + * @param state the current state of the transition + * @param successor the next state of the transition + * @param action the action that induced the transition + * @return the DOT label for the edge + */ + protected String dotTransitionLabel(final TLCState state, final TLCState successor, final Action action) { + // Only colorize edges if specified. Default to black otherwise. + final String color = colorize ? this.getActionColor(action).toString() : "black" ; + + // Only add action label if specified. + final String actionName = actionLabels ? action.getName().toString() : "" ; + + final String labelFmtStr = " [label=\"%s\",color=\"%s\",fontcolor=\"%s\"]"; + return String.format(labelFmtStr, actionName, color, color); + } + + + /** + * Creates a DOT legend that maps actions to their corresponding edge color in the state graph. + * + * @param name the title of the legend + * @param actions the set of action names that will be included in the legend + * @return + */ + protected String dotLegend(final String name, final Set<String> actions) { + final StringBuilder sb = new StringBuilder(); + sb.append(String.format("subgraph %s {", "cluster_legend")); + sb.append("graph[style=bold];"); + sb.append("label = \"Next State Actions\" style=\"solid\"\n"); + sb.append(String.format("node [ labeljust=\"l\",colorscheme=\"%s\",style=filled,shape=record ]\n", + dotColorScheme)); + for (String action : actions) { + String str = String.format("%s [label=\"%s\",fillcolor=%d]", action.replaceAll("!", ":"), action, + this.actionToColors.get(action)); + sb.append(str); + sb.append("\n"); + } + sb.append("}"); + return sb.toString(); + } + + /** + * Given a TLC state, generate a string representation suitable for a HTML DOT graph label. + */ + //TODO This cannot handle states with variables such as "active = (0 :> TRUE @@ 1 :> FALSE)". +// protected static String state2html(final TLCState state) { +// final StringBuilder sb = new StringBuilder(); +// final Map<UniqueString, Value> valMap = state.getVals(); +// +// // Generate a string representation of state. +// for (UniqueString key : valMap.keySet()) { +// final String valString = (key.toString() + " = " + valMap.get(key).toString()); +// sb.append(valString); +// // New line between variables. +// sb.append("<br/>"); +// } +// return sb.toString(); +// } + + protected static String states2dot(final TLCState state) { + // Replace "\" with "\\" and """ with "\"". + return state.toString().replace("\\", "\\\\").replace("\"", "\\\"").trim() + .replace("\n", "\\n"); // Do not remove remaining (i.e. no danling/leading) "\n". + } + + /* (non-Javadoc) + * @see tlc2.util.IStateWriter#close() + */ + public void close() { + for (final Set<Long> entry : rankToNodes.values()) { + this.writer.append("{rank = same; "); + for (final Long l : entry) { + this.writer.append(l + ";"); + } + this.writer.append("}\n"); + } + this.writer.append("}\n"); // closes the main subgraph. + // We only need the legend if the edges are colored by action and there is more + // than a single action. + if (colorize && this.actionToColors.size() > 1) { + this.writer.append(dotLegend("DotLegend", this.actionToColors.keySet())); + } + this.writer.append("}"); + super.close(); + } + + /* (non-Javadoc) + * @see tlc2.util.IStateWriter#snapshot() + */ + @Override + public void snapshot() throws IOException { + this.writer.flush(); + + final String snapshot = fname.replace(".dot", "_snapshot" + ".dot"); + FileUtil.copyFile(this.fname, snapshot); + + StringBuffer buf = new StringBuffer(); + for (final Set<Long> entry : rankToNodes.values()) { + buf.append("{rank = same; "); + for (final Long l : entry) { + buf.append(l + ";"); + } + buf.append("}\n"); + } + buf.append("}\n"); + // We only need the legend if the edges are colored by action and there is more + // than a single action. + if (colorize && this.actionToColors.size() > 1) { + buf.append(dotLegend("DotLegend", this.actionToColors.keySet())); + } + buf.append("}"); + + Files.write(Paths.get(snapshot), buf.toString().getBytes(), StandardOpenOption.APPEND); + } +} diff --git a/tlatools/src/tlc2/util/ExpectInlined.java b/tlatools/src/tlc2/util/ExpectInlined.java new file mode 100644 index 0000000000000000000000000000000000000000..535c8f2a0bad69cff9203b700e3e47319ce37c6a --- /dev/null +++ b/tlatools/src/tlc2/util/ExpectInlined.java @@ -0,0 +1,13 @@ +package tlc2.util; + +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +@Retention(RUNTIME) +@Target(METHOD) +public @interface ExpectInlined { + +} \ No newline at end of file diff --git a/tlatools/src/tlc2/util/FP64.java b/tlatools/src/tlc2/util/FP64.java index 87d3a433857ae3ff9fe1db9a68e85a3c441f118e..b02408439640bd9fe8c4cdd81cf8755bac3cf6ba 100644 --- a/tlatools/src/tlc2/util/FP64.java +++ b/tlatools/src/tlc2/util/FP64.java @@ -5,6 +5,7 @@ package tlc2.util; import java.io.IOException; import java.io.InputStream; +import java.util.Random; /** * A 64-bit fingerprint is stored in an instance of the type @@ -127,7 +128,7 @@ public class FP64 { * Extend the fingerprint <code>fp</code> by an * integer <code>x</code>. */ - public static long Extend(long fp, int x) + public static long ExtendLoop(long fp, int x) { long[] mod = ByteModTable_7; for (int i = 0; i < 4; i++) { @@ -138,6 +139,27 @@ public class FP64 { return fp; } + public static long Extend(long fp, int x) + { + final long[] mod = ByteModTable_7; + byte b = (byte)(x & 0xFF); + fp = ((fp >>> 8) ^ (mod[(b ^ ((int)fp)) & 0xFF])); + x = x >>> 8; + + b = (byte)(x & 0xFF); + fp = ((fp >>> 8) ^ (mod[(b ^ ((int)fp)) & 0xFF])); + x = x >>> 8; + + b = (byte)(x & 0xFF); + fp = ((fp >>> 8) ^ (mod[(b ^ ((int)fp)) & 0xFF])); + x = x >>> 8; + + b = (byte)(x & 0xFF); + fp = ((fp >>> 8) ^ (mod[(b ^ ((int)fp)) & 0xFF])); + + return fp; + } + /* * Extend the fingerprint <code>fp</code> by a long * integer <code>fp1</code>. @@ -202,7 +224,16 @@ public class FP64 { /** Unlikely fingerprint? */ public static final long Zero = 0L; - /* This file provides procedures that construct fingerprints of + /* Background Reading: + - Galois Field (GF) or Finite Field + https://en.wikipedia.org/wiki/Finite_field + - GHash/MAC + https://en.wikipedia.org/wiki/Galois/Counter_Mode + - Universal Hashing + https://en.wikipedia.org/wiki/Universal_hashing + ---- + + This file provides procedures that construct fingerprints of strings of bytes via operations in GF[2^64]. GF[64] is represented as the set polynomials of degree 64 with coefficients in Z(2), modulo an irreducible polynomial P of degree 64. The computer @@ -368,13 +399,28 @@ public class FP64 { /* This is the table used for computing fingerprints. The ByteModTable could be hardwired. Note that since we just extend a byte at a time, we need just "ByteModeTable[7]". */ - private static long[] ByteModTable_7; + private static final long[] ByteModTable_7 = new long[256]; /* This is the irreducible polynomial used as seed. */ private static long IrredPoly; public static long getIrredPoly() { return IrredPoly; } + /** + * Initializes {@link FP64#IrredPoly} with a randomly chosen poly from + * {@link FP64#Polys}. + */ + public static void InitRnd() { + Init(new Random().nextInt(Polys.length)); + } + + /** + * Initializes {@link FP64#IrredPoly} the first poly in {@link FP64#Polys}. + */ + public static void Init() { + Init(0); + } + // Initialization code public static void Init(int n) { Init(Polys[n]); @@ -398,7 +444,6 @@ public class FP64 { } // Just need the 7th iteration of the ByteModTable initialization code - ByteModTable_7 = new long[256]; for (int j = 0; j <= 255; j++) { long v = Zero; for (int k = 0; k <= 7; k++) { diff --git a/tlatools/src/tlc2/util/IStateWriter.java b/tlatools/src/tlc2/util/IStateWriter.java new file mode 100644 index 0000000000000000000000000000000000000000..b57111c3cb31b8591f1d8bd804ee56b9abb7d8e6 --- /dev/null +++ b/tlatools/src/tlc2/util/IStateWriter.java @@ -0,0 +1,72 @@ +/******************************************************************************* + * Copyright (c) 2016 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.util; + +import java.io.IOException; + +import tlc2.tool.Action; +import tlc2.tool.TLCState; + +public interface IStateWriter { + + public enum Visualization { + /** + * If successor and the current state are identical and the transition + * is due to stuttering. + */ + STUTTERING, + /** + * No extra visualization hint is given. + */ + DEFAULT, + /** + * A dotted line. + */ + DOTTED; + } + + void writeState(TLCState state); + + void writeState(TLCState state, TLCState successor, boolean successorStateIsNew); + + void writeState(TLCState state, TLCState successor, boolean successorStateIsNew, Action action); + + void writeState(TLCState state, TLCState successor, boolean successorStateIsNew, Visualization visulation); + + void writeState(TLCState state, TLCState successor, BitVector actionChecks, int from, int length, boolean successorStateIsNew); + + void writeState(TLCState state, TLCState successor, BitVector actionChecks, int from, int length, boolean successorStateIsNew, Visualization visulation); + + void close(); + + String getDumpFileName(); + + boolean isNoop(); + + boolean isDot(); + + void snapshot() throws IOException; +} \ No newline at end of file diff --git a/tlatools/src/tlc2/util/IdThread.java b/tlatools/src/tlc2/util/IdThread.java index 33a0b8bf959d1803bfb0119005c14a877947c258..6913e2462b6200861446fd4090c27e99c18fb471 100644 --- a/tlatools/src/tlc2/util/IdThread.java +++ b/tlatools/src/tlc2/util/IdThread.java @@ -2,18 +2,48 @@ // Portions Copyright (c) 2003 Microsoft Corporation. All rights reserved. package tlc2.util; +import tlc2.tool.TLCState; +import tlc2.value.IValue; + /** An <code>IdThread</code> is a <code>Thread</code> with an integer identifier. */ public class IdThread extends Thread { + private static final ThreadLocal<TLCState> currentState = new ThreadLocal<>(); + + /** + * @return null during the generation of initial states (see + * tlc2.tool.ModelChecker.doInit(boolean) and a TLCState during the + * generation of next-states in tlc2.tool.ModelChecker.doNext(TLCState, + * ObjLongTable, Worker). It corresponds to the predecessor state of the + * next-state currently being generated by the worker thread. + */ + public static final TLCState getCurrentState() { + return currentState.get(); + } + public static final void setCurrentState(final TLCState state) { + currentState.set(state); + } + public static final TLCState resetCurrentState() { + final TLCState tlcState = currentState.get(); + currentState.remove(); + return tlcState; + } + private final int id; - + private IValue[] localValues = new IValue[4]; + /** Create a new thread with ID <code>id</code>. */ public IdThread(int id) { this.id = id; } - /** Return this thread's ID. */ + public IdThread(Runnable runnable, String name, int id) { + super(runnable, name); + this.id = id; + } + + /** Return this thread's ID. */ // This method was originally called getId, but a later // version of Java introduced a getId method into the Thread // class which returned a long, and it doesn't allow it to be @@ -40,4 +70,20 @@ public class IdThread extends Thread { Thread th = Thread.currentThread(); return (th instanceof IdThread) ? ((IdThread)th).id : otherId; } + + public IValue getLocalValue(int idx) { + if (idx < this.localValues.length) { + return this.localValues[idx]; + } + return null; + } + + public void setLocalValue(int idx, IValue val) { + if (idx >= this.localValues.length) { + IValue[] vals = new IValue[idx + 1]; + System.arraycopy(this.localValues, 0, vals, 0, this.localValues.length); + this.localValues = vals; + } + this.localValues[idx] = val; + } } diff --git a/tlatools/src/tlc2/util/IntStack.java b/tlatools/src/tlc2/util/IntStack.java new file mode 100644 index 0000000000000000000000000000000000000000..17bb23f92eb305f328927c6f241f747b290a2461 --- /dev/null +++ b/tlatools/src/tlc2/util/IntStack.java @@ -0,0 +1,69 @@ +/******************************************************************************* + * Copyright (c) 2015 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ + +package tlc2.util; + +public interface IntStack { + + /** + * Return the number of items on the stack. + */ + long size(); + + /** + * Push an integer onto the stack. + */ + void pushInt(final int x); + + /** + * Push a long integer onto the stack. + */ + void pushLong(final long x); + + /** + * Pop the integer from the top of the stack. + */ + int popInt(); + + /** + * Pop the long integer from the top of the stack. + */ + long popLong(); + + /** + * Removes all elements from the stack + */ + void reset(); + + // Some implementors support peak operations. + default long peakLong() { + throw new UnsupportedOperationException("Not implemented"); + } + + default int peakInt() { + throw new UnsupportedOperationException("Not implemented"); + } +} \ No newline at end of file diff --git a/tlatools/src/tlc2/util/LongVec.java b/tlatools/src/tlc2/util/LongVec.java index 703e71c6367b50ea162b45d526e73767177f7b48..44706322da05c66ff1f53474b3bebbb3bc28bf6c 100644 --- a/tlatools/src/tlc2/util/LongVec.java +++ b/tlatools/src/tlc2/util/LongVec.java @@ -8,14 +8,10 @@ import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; +import java.util.Arrays; import util.FileUtil; -/** - * - * - * @version $Id$ - */ public class LongVec implements Cloneable, Serializable { private static final long serialVersionUID = 2406899362740899071L; protected long[] elementData; @@ -28,6 +24,11 @@ public class LongVec implements Cloneable, Serializable { this.elementData = new long[initialCapacity]; } + private LongVec(final LongVec other) { + this.elementCount = other.elementCount; + this.elementData = Arrays.copyOfRange(other.elementData, 0, other.elementCount); + } + public final void addElement(long x) { if (this.elementCount == this.elementData.length) { ensureCapacity(this.elementCount + 1); @@ -132,4 +133,26 @@ public class LongVec implements Cloneable, Serializable { System.err.println(vec1.elementAt(2)); } + private LongVec reverse0() { + int left = 0; + int right = elementData.length - 1; + + while (left < right) { + long temp = elementData[left]; + elementData[left] = elementData[right]; + elementData[right] = temp; + + left++; + right--; + } + return this; + } + + public LongVec reverse() { + try { + return new LongVec(this).reverse0(); + } catch (Exception e) { + throw e; + } + } } diff --git a/tlatools/src/tlc2/util/MemBasedSet.java b/tlatools/src/tlc2/util/MemBasedSet.java new file mode 100644 index 0000000000000000000000000000000000000000..c9b4c119a6fa369cb52727ca2a1b4b2ab25a207f --- /dev/null +++ b/tlatools/src/tlc2/util/MemBasedSet.java @@ -0,0 +1,76 @@ +/******************************************************************************* + * Copyright (c) 2015 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ + +package tlc2.util; + +public abstract class MemBasedSet { + + protected int size; + + protected int[] elems; + + public MemBasedSet(final int minCapacity) { + this.size = 0; + this.elems = new int[minCapacity]; + } + + protected int[] ensureCapacity(final int minCapacity) { + // If the internal storage reaches its capacity, double the size of + // the internal storage. This strategy is great for as long as size + // is smaller than 2^31. Once size is >= 2^31, doubling it means it + // becomes negative resulting in a NegativeArraySizeException. Additionally + // if size gets larger and larger, an OutOfMemory exception becomes + // more likely when MemIntStacks memory requirements get doubled. + // From the literature and popular implementations (e.g. Java's ArrayList), + // a growth factor of 1.5 seems to be practical. + final int newSize = (int) ((this.size * 3L) / 2) + 1; // multiply with *long* 3L to stay positive + // If newSize is negative (happens when size become too large for + // int), just increase the array by MIN_CAPACITY. This obviously + // doesn't prevent a NegativeArraySizeException either, but it + // postpones it to the moment when the int-sized array will be + // completely full. At this point, nothing can be done except using + // two MemIntStack instances which requires changes in the places + // where MemIntStack is used or swapping MemIntStack to disk (which - + // from looking at the ctor's parameters - appears to be the preferred + // solution of the original MemIntStack authors). + // + // Performance obviously goes south the moment the array is + // increased in MIN_CAPACITY steps. It's a trade off between risking + // an OutOfMemory exception and performance. + return new int[Math.max(newSize, this.size + minCapacity)]; + } + + /** + * Return the number of items on the stack. + */ + public final long size() { + return this.size; + } + + public final boolean hasElements() { + return this.size > 0; + } +} diff --git a/tlatools/src/tlc2/util/MemIntQueue.java b/tlatools/src/tlc2/util/MemIntQueue.java index ad119d5abca7fc01dfb1acc1db3e05a2a43fd38c..257a9bb0765c36133d8fde777c8b8b8b9b27c5b9 100644 --- a/tlatools/src/tlc2/util/MemIntQueue.java +++ b/tlatools/src/tlc2/util/MemIntQueue.java @@ -13,137 +13,143 @@ import util.BufferedDataInputStream; import util.BufferedDataOutputStream; import util.FileUtil; -/** - * - * @version $Id$ - */ -public final class MemIntQueue { - private final static int InitialSize = 4096; - private final static int GrowthFactor = 2; - - private int len; - private int[] elems; - private int start; - private String diskdir; - private String filename; - - public MemIntQueue(String metadir, String filename) { - this.len = 0; - this.elems = new int[InitialSize]; - this.start = 0; - this.diskdir = metadir; - this.filename = filename; - } - - public final int length() { return this.len; } - - public final boolean hasElements() { return this.len > 0; } - - public final void enqueueInt(int elem) { - if (this.len == this.elems.length) { - // grow the array - int newLen = Math.max(1, this.len * GrowthFactor); - int[] newElems = new int[newLen]; - int copyLen = this.elems.length - this.start; - System.arraycopy(this.elems, this.start, newElems, 0, copyLen); - System.arraycopy(this.elems, 0, newElems, copyLen, this.start); - this.elems = newElems; - this.start = 0; - } - int last = (this.start + this.len) % this.elems.length; - this.elems[last] = elem; - this.len++; - } - - public final void enqueueLong(long elem) { - this.enqueueInt((int)(elem >>> 32)); - this.enqueueInt((int)(elem & 0xFFFFFFFFL)); - } - - public final int dequeueInt() { - // Assert.check(this.len > 0); - if (this.len < 1) { - throw new NoSuchElementException(); +public final class MemIntQueue extends MemBasedSet { + private final static int InitialSize = 4096; + + private int start; + private String diskdir; + private String filename; + + public MemIntQueue(String metadir, String filename) { + this(metadir, filename, InitialSize); + } + + public MemIntQueue(String metadir, String filename, final int initialCapacity) { + super(initialCapacity); + this.start = 0; + this.diskdir = metadir; + this.filename = filename; + } + + public final void enqueueInt(int elem) { + if (this.size == this.elems.length) { + // grow the array + final int[] newElems = ensureCapacity(InitialSize); + // copy old content to new array + int copyLen = this.elems.length - this.start; + System.arraycopy(this.elems, this.start, newElems, 0, copyLen); + System.arraycopy(this.elems, 0, newElems, copyLen, this.start); + this.elems = newElems; + this.start = 0; + } + int last = (this.start + this.size) % this.elems.length; + this.elems[last] = elem; + this.size++; + } + + public final void enqueueLong(long elem) { + this.enqueueInt((int) (elem >>> 32)); + this.enqueueInt((int) (elem & 0xFFFFFFFFL)); + } + + public final int dequeueInt() { + // Assert.check(this.len > 0); + if (this.size < 1) { + throw new NoSuchElementException(); + } + int res = this.elems[this.start]; + this.size--; + this.start = (this.start + 1) % this.elems.length; + return res; + } + + public final long dequeueLong() { + long high = this.dequeueInt(); + long low = this.dequeueInt(); + return (high << 32) | (low & 0xFFFFFFFFL); + } + + public int popInt() { + // Assert.check(this.len > 0); + if (this.size < 1) { + throw new NoSuchElementException(); + } + return this.elems[--this.size]; + } + + public long popLong() { + long low = this.popInt(); + long high = this.popInt(); + return (high << 32) | (low & 0xFFFFFFFFL); + } + + // Checkpoint. + public final void beginChkpt() throws IOException { + String tmpName = this.diskdir + FileUtil.separator + this.filename + ".tmp"; + BufferedDataOutputStream bos = new BufferedDataOutputStream(tmpName); + bos.writeInt(this.size); + int index = this.start; + for (int i = 0; i < this.size; i++) { + bos.writeInt(this.elems[index++]); + if (index == this.elems.length) + index = 0; + } + bos.close(); + } + + public final void commitChkpt() throws IOException { + String oldName = this.diskdir + FileUtil.separator + this.filename + ".chkpt"; + File oldChkpt = new File(oldName); + String newName = this.diskdir + FileUtil.separator + this.filename + ".tmp"; + File newChkpt = new File(newName); + if ((oldChkpt.exists() && !oldChkpt.delete()) || !newChkpt.renameTo(oldChkpt)) { + throw new IOException("MemStateQueue.commitChkpt: cannot delete " + oldChkpt); + } } - int res = this.elems[this.start]; - this.len--; - this.start = (this.start + 1) % this.elems.length; - return res; - } - - public final long dequeueLong() { - long high = this.dequeueInt(); - long low = this.dequeueInt(); - return (high << 32) | (low & 0xFFFFFFFFL); - } - - // Checkpoint. - public final void beginChkpt() throws IOException { - String tmpName = this.diskdir + FileUtil.separator + this.filename + ".tmp"; - BufferedDataOutputStream bos = new BufferedDataOutputStream(tmpName); - bos.writeInt(this.len); - int index = this.start; - for (int i = 0; i < this.len; i++) { - bos.writeInt(this.elems[index++]); - if (index == this.elems.length) index = 0; - } - bos.close(); - } - - public final void commitChkpt() throws IOException { - String oldName = this.diskdir + FileUtil.separator + this.filename + ".chkpt"; - File oldChkpt = new File(oldName); - String newName = this.diskdir + FileUtil.separator + this.filename + ".tmp"; - File newChkpt = new File(newName); - if ((oldChkpt.exists() && !oldChkpt.delete()) || - !newChkpt.renameTo(oldChkpt)) { - throw new IOException("MemStateQueue.commitChkpt: cannot delete " + oldChkpt); - } - } - - public final void recover() throws IOException { - String chkptName = this.diskdir + FileUtil.separator + this.filename + ".chkpt"; - BufferedDataInputStream bis = new BufferedDataInputStream(chkptName); - this.len = bis.readInt(); - for (int i = 0; i < this.len; i++) { - this.elems[i] = bis.readInt(); - } - bis.close(); - } - - /* + + public final void recover() throws IOException { + String chkptName = this.diskdir + FileUtil.separator + this.filename + ".chkpt"; + BufferedDataInputStream bis = new BufferedDataInputStream(chkptName); + this.size = bis.readInt(); + for (int i = 0; i < this.size; i++) { + this.elems[i] = bis.readInt(); + } + bis.close(); + } + + /* * The detailed formatter below can be activated in Eclipse's variable view * by choosing "New detailed formatter" from the MemIntQueue context menu. * Insert "MemIntQueue.DetailedFormatter.fpAndtidxAndptr(this);". */ - public static class DetailedFormatter { - - // An Eclipse detailed formatter for when this Queue holds pairs of long - // (fp) and int (tableau idx) - public static String fpAndtidx(MemIntQueue aQueue) { - final StringBuffer buf = new StringBuffer(aQueue.len / 3); - for (int i = 0; i < aQueue.len; i+=3) { - final long fp = ((long) aQueue.elems[i] << 32) | ((long) (aQueue.elems[i + 1]) & 0xFFFFFFFFL); - buf.append("fp: " + fp); - buf.append(" tidx: " + aQueue.elems[i + 2]); - buf.append("\n"); - } - return buf.toString(); - } - - // An Eclipse detailed formatter for when this Queue holds pairs of long - // (fp), int (tableau idx) and long (disk graph pointer). - public static String fpAndtidxAndptr(MemIntQueue aQueue) { - final StringBuffer buf = new StringBuffer(aQueue.len / 5); - for (int i = 0; i < aQueue.len; i += 5) { - final long fp = ((long) aQueue.elems[i] << 32) | ((long) (aQueue.elems[i + 1]) & 0xFFFFFFFFL); - buf.append("fp: " + fp); - buf.append(" tidx: " + aQueue.elems[i + 2]); - final long ptr = ((long) aQueue.elems[i + 3] << 32) | ((long) (aQueue.elems[i+ 4] ) & 0xFFFFFFFFL); - buf.append(" ptr: " + ptr); - buf.append("\n"); - } - return buf.toString(); - } - } + public static class DetailedFormatter { + + // An Eclipse detailed formatter for when this Queue holds pairs of long + // (fp) and int (tableau idx) + public static String fpAndtidx(MemIntQueue aQueue) { + final StringBuffer buf = new StringBuffer(aQueue.size / 3); + for (int i = 0; i < aQueue.size; i += 3) { + final long fp = ((long) aQueue.elems[i] << 32) | ((long) (aQueue.elems[i + 1]) & 0xFFFFFFFFL); + buf.append("fp: " + fp); + buf.append(" tidx: " + aQueue.elems[i + 2]); + buf.append("\n"); + } + return buf.toString(); + } + + // An Eclipse detailed formatter for when this Queue holds pairs of long + // (fp), int (tableau idx) and long (disk graph pointer). + public static String fpAndtidxAndptr(MemIntQueue aQueue) { + final StringBuffer buf = new StringBuffer(aQueue.size / 5); + for (int i = 0; i < aQueue.size; i += 5) { + final long fp = ((long) aQueue.elems[i] << 32) | ((long) (aQueue.elems[i + 1]) & 0xFFFFFFFFL); + buf.append("fp: " + fp); + buf.append(" tidx: " + aQueue.elems[i + 2]); + final long ptr = ((long) aQueue.elems[i + 3] << 32) | ((long) (aQueue.elems[i + 4]) & 0xFFFFFFFFL); + buf.append(" ptr: " + ptr); + buf.append("\n"); + } + return buf.toString(); + } + } } diff --git a/tlatools/src/tlc2/util/MemIntStack.java b/tlatools/src/tlc2/util/MemIntStack.java index e8dae0c97e9b29d63886a947de86a67aee9088e5..ce74548de265fcfa4d66699d3002def008d29808 100644 --- a/tlatools/src/tlc2/util/MemIntStack.java +++ b/tlatools/src/tlc2/util/MemIntStack.java @@ -5,66 +5,74 @@ package tlc2.util; -public final class MemIntStack { - private int size; - private int[] elems; - - public MemIntStack(String diskdir, String name) { - this.size = 0; - this.elems = new int[1024]; - } +public final class MemIntStack extends MemBasedSet implements IntStack { + private static final int MIN_CAPACITY = 1024; - /* Return the number of items on the stack. */ - public final int size() { return this.size; } - - /* Push an integer onto the stack. */ - public final synchronized void pushInt(int x) { - if (this.size == this.elems.length) { - int[] elems1 = new int[2*this.size]; - System.arraycopy(elems, 0, elems1, 0, this.size); - this.elems = elems1; - } - this.elems[this.size] = x; - this.size++; - } - - /* Push a long integer onto the stack. */ - public final synchronized void pushLong(long x) { - this.pushInt((int)(x & 0xFFFFFFFFL)); - this.pushInt((int)(x >>> 32)); - } + public MemIntStack(String diskdir, String name) { + super(MIN_CAPACITY); + } - /* Pop the integer on top of the stack. */ - public final synchronized int popInt() { - return this.elems[--this.size]; - } + /* (non-Javadoc) + * @see tlc2.util.IntStack#pushInt(int) + */ + public final synchronized void pushInt(int x) { + if (this.size == this.elems.length) { + final int[] newElems = ensureCapacity(MIN_CAPACITY); + System.arraycopy(elems, 0, newElems, 0, this.size); + this.elems = newElems; + } + this.elems[this.size] = x; + this.size++; + } - public final synchronized int peakInt() { - return peakInt(size - 1); - } + /* (non-Javadoc) + * @see tlc2.util.IntStack#pushLong(long) + */ + public final synchronized void pushLong(long x) { + this.pushInt((int) (x & 0xFFFFFFFFL)); + this.pushInt((int) (x >>> 32)); + } - public final synchronized int peakInt(int pos) { - return this.elems[pos]; -} + /* (non-Javadoc) + * @see tlc2.util.IntStack#popInt() + */ + public final synchronized int popInt() { + return this.elems[--this.size]; + } + + public final synchronized int peakInt() { + return peakInt(size - 1); + } + + public final synchronized int peakInt(int pos) { + return this.elems[pos]; + } - /* Pop the long integer on top of the stack. */ - public final synchronized long popLong() { - long high = this.popInt(); - long low = this.popInt(); - return (high << 32) | (low & 0xFFFFFFFFL); - } + /* (non-Javadoc) + * @see tlc2.util.IntStack#popLong() + */ + public final synchronized long popLong() { + long high = this.popInt(); + long low = this.popInt(); + return (high << 32) | (low & 0xFFFFFFFFL); + } - public final synchronized long peakLong() { - long high = this.peakInt(); - long low = this.peakInt(); - return (high << 32) | (low & 0xFFFFFFFFL); - } + public final synchronized long peakLong() { + long high = this.peakInt(); + long low = this.peakInt(); + return (high << 32) | (low & 0xFFFFFFFFL); + } - public final synchronized long peakLong(int pos) { - long high = this.peakInt(pos); - long low = this.peakInt(pos + 1); - return (high << 32) | (low & 0xFFFFFFFFL); - } + public final synchronized long peakLong(int pos) { + long high = this.peakInt(pos + 1); + long low = this.peakInt(pos); + return (high << 32) | (low & 0xFFFFFFFFL); + } - public final void reset() { this.size = 0; } + /* (non-Javadoc) + * @see tlc2.util.IntStack#reset() + */ + public final void reset() { + this.size = 0; + } } diff --git a/tlatools/src/tlc2/util/NoopStateWriter.java b/tlatools/src/tlc2/util/NoopStateWriter.java new file mode 100644 index 0000000000000000000000000000000000000000..4d3dce4cdea66bf803c7b04fc41fd9983d854db1 --- /dev/null +++ b/tlatools/src/tlc2/util/NoopStateWriter.java @@ -0,0 +1,113 @@ +/******************************************************************************* + * Copyright (c) 2016 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.util; + +import java.io.IOException; + +import tlc2.tool.Action; +import tlc2.tool.TLCState; + +public final class NoopStateWriter implements IStateWriter { + + /* (non-Javadoc) + * @see tlc2.util.StateWriter#writeState(tlc2.tool.TLCState) + */ + public final void writeState(final TLCState state) { + // noop + } + + /* (non-Javadoc) + * @see tlc2.util.StateWriter#writeState(tlc2.tool.TLCState, tlc2.tool.TLCState, boolean) + */ + public final void writeState(final TLCState state, final TLCState successor, final boolean successorStateIsNew) { + // noop + } + + /* (non-Javadoc) + * @see tlc2.util.IStateWriter#writeState(tlc2.tool.TLCState, tlc2.tool.TLCState, boolean, tlc2.util.IStateWriter.Visualization) + */ + public void writeState(TLCState state, TLCState successor, boolean successorStateIsNew, Visualization visualization) { + // noop + } + + /* (non-Javadoc) + * @see tlc2.util.IStateWriter#close() + */ + public final void close() { + // noop + } + + /* (non-Javadoc) + * @see tlc2.util.IStateWriter#writeState(tlc2.tool.TLCState, tlc2.tool.TLCState, tlc2.util.BitVector, int, int, boolean) + */ + public void writeState(TLCState state, TLCState successor, BitVector actionChecks, int from, int to, boolean successorStateIsNew) { + // noop + } + + /* (non-Javadoc) + * @see tlc2.util.IStateWriter#writeState(tlc2.tool.TLCState, tlc2.tool.TLCState, tlc2.util.BitVector, int, int, boolean, tlc2.util.IStateWriter.Visualization) + */ + public void writeState(TLCState state, TLCState successor, BitVector actionChecks, int from, int to, boolean successorStateIsNew, + Visualization visulation) { + // noop + } + + /* (non-Javadoc) + * @see tlc2.util.IStateWriter#writeState(tlc2.tool.TLCState, tlc2.tool.TLCState, boolean, tlc2.tool.Action) + */ + public void writeState(TLCState state, TLCState successor, boolean successorStateIsNew, Action action) { + // noop + } + + /* (non-Javadoc) + * @see tlc2.util.IStateWriter#isNoop() + */ + public boolean isNoop() { + return true; + } + + /* (non-Javadoc) + * @see tlc2.util.IStateWriter#isDot() + */ + @Override + public boolean isDot() { + return false; + } + + /* (non-Javadoc) + * @see tlc2.util.IStateWriter#getDumpFileName() + */ + public String getDumpFileName() { + return ""; + } + + /* (non-Javadoc) + * @see tlc2.util.IStateWriter#snapshot() + */ + @Override + public void snapshot() throws IOException { + } +} diff --git a/tlatools/src/tlc2/util/ObjLongTable.java b/tlatools/src/tlc2/util/ObjLongTable.java index 7001d7893dcc90844fdf1e960feed93cd8cebd67..0b894d096ac6ea0e3116a5fd316e827ee685795e 100644 --- a/tlatools/src/tlc2/util/ObjLongTable.java +++ b/tlatools/src/tlc2/util/ObjLongTable.java @@ -5,38 +5,40 @@ package tlc2.util; -public final class ObjLongTable { +public final class ObjLongTable<T> { private int count; private int length; private int thresh; - private Object[] keys; + private T[] keys; private long[] elems; + @SuppressWarnings("unchecked") public ObjLongTable(int size) { - this.keys = new Object[size]; + this.keys = (T[]) new Object[size]; this.elems = new long[size]; this.count = 0; this.length = size; this.thresh = this.length / 2; } + @SuppressWarnings("unchecked") private final void grow() { Object[] oldKeys = this.keys; long[] oldElems = this.elems; this.count = 0; this.length = 2 * this.length + 1; this.thresh = this.length / 2; - this.keys = new Object[this.length]; + this.keys = (T[]) new Object[this.length]; this.elems = new long[this.length]; for (int i = 0; i < oldKeys.length; i++) { - Object key = oldKeys[i]; + T key = (T) oldKeys[i]; if (key != null) this.put(key, oldElems[i]); } } public final int size() { return this.count; } - public final int put(Object k, long elem) { + public final int put(T k, long elem) { if (count >= thresh) this.grow(); int loc = ((int)k.hashCode() & 0x7FFFFFFF) % this.length; while (true) { @@ -55,7 +57,7 @@ public final class ObjLongTable { } } - public final int add(Object k, long elem) { + public final int add(T k, long elem) { if (count >= thresh) this.grow(); int loc = ((int)k.hashCode() & 0x7FFFFFFF) % this.length; while (true) { @@ -84,41 +86,46 @@ public final class ObjLongTable { } } - public final String[] sortStringKeys() { - String[] res = new String[this.count]; - int idx = -1; - for (int i = 0; i < this.length; i++) { - String key = (String)this.keys[i]; - if (key != null) { - int j = idx; - while (j >= 0) { - if (res[j].compareTo(key) <= 0) break; - res[j+1] = res[j]; - j--; + /** + * Merges the keys and longs of into this instance. If this instance already + * contains an entry with a given key, the long values will be accumulated. + */ + public ObjLongTable<T> mergeInto(final ObjLongTable<T> other) { + T key; + final ObjLongTable<T>.Enumerator<T> keys2 = other.keys(); + while ((key = keys2.nextElement()) != null) { + add(key, other.get(key)); + } + return this; } - res[j+1] = key; - idx++; - } - } - return res; - } - - public final Enumerator keys() { return new Enumerator(); } + @SuppressWarnings("unchecked") + public T[] toArray(T[] a) { + a = (T[]) java.lang.reflect.Array.newInstance(a.getClass().getComponentType(), count); + ObjLongTable<T>.Enumerator<T> keys2 = keys(); + T e = null; + int i = 0; + while((e = keys2.nextElement()) != null) { + a[i++] = e; + } + return a; + } + + public final Enumerator<T> keys() { return new Enumerator<T>(); } - public final class Enumerator { + @SuppressWarnings("hiding") + public final class Enumerator<T> { int index = 0; - public final Object nextElement() { + @SuppressWarnings("unchecked") + public final T nextElement() { while (this.index < keys.length) { if (keys[this.index] != null) { - return keys[this.index++]; + return (T) keys[this.index++]; } this.index++; } return null; } } - - } diff --git a/tlatools/src/tlc2/util/SetOfLong.java b/tlatools/src/tlc2/util/SetOfLong.java index cfc2dd305a8a11086414aa75474d1c5d9714710e..d124389626b9770143ddc01f0c4a780dffa669c1 100644 --- a/tlatools/src/tlc2/util/SetOfLong.java +++ b/tlatools/src/tlc2/util/SetOfLong.java @@ -86,7 +86,7 @@ public final class SetOfLong { public final long sizeof() { return 20 + (8 * this.length); } - public final double checkFPs() { + public final long checkFPs() { int cnt = 0; for (int i = 0; i < this.length; i++) { long x = this.table[i]; @@ -107,7 +107,7 @@ public final class SetOfLong { dis = Math.min(dis, this.table[i]-x); x = this.table[i]; } - return (1.0/dis); + return dis; } public final void beginChkpt(DataOutputStream dos) throws IOException { diff --git a/tlatools/src/tlc2/util/SetOfStates.java b/tlatools/src/tlc2/util/SetOfStates.java new file mode 100644 index 0000000000000000000000000000000000000000..ace08cc903306d11d3c36eb1aeb14da44ebdae77 --- /dev/null +++ b/tlatools/src/tlc2/util/SetOfStates.java @@ -0,0 +1,160 @@ +/******************************************************************************* + * Copyright (c) 2015 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ + +package tlc2.util; + +import tlc2.tool.ModelChecker; +import tlc2.tool.TLCState; + +/** + * A {@link SetOfStates} is a hash set with open addressing that is intended to + * be used in TLC's {@link ModelChecker#getNextStates()} implementation. In this + * are the number of {@link TLCState}s generated is relatively small and thus + * the likelihood of consecutive ranges in the fingerprint domain low. In turn, + * this means that the {@link TLCState}s in {@link SetOfStates} are evenly + * distributed assuming the {@link SetOfStates#length} is sufficiently large. + */ +public final class SetOfStates { + + private TLCState[] states; + private int count; + private int length; + private int thresh; + + public SetOfStates() { + this(16); + } + + public SetOfStates(final int size) { + this.count = 0; + this.length = size; + this.thresh = length / 2; + this.states = new TLCState[length]; + } + + public final void clear() { + this.count = 0; + this.states = new TLCState[length]; + } + + private final void grow() { + final TLCState[] old = states; + this.count = 0; + this.length = 2 * this.length + 1; + this.thresh = this.length / 2; + this.states = new TLCState[this.length]; + for (int i = 0; i < old.length; i++) { + final TLCState s = old[i]; + // This is where we have to redundantly compute the state's + // fingerprint. Thus, try to minimize the number of grow operations. + if (s != null) { + this.put(s.fingerPrint(), s); + } + } + } + + public final boolean put(final TLCState aState) { + return put(aState.fingerPrint(), aState); + } + + public final boolean put(final long fingerprint, final TLCState aState) { + if (count >= thresh) { + this.grow(); + } + int loc = ((int) fingerprint & 0x7FFFFFFF) % this.length; + // This loop keep going until either a match or a null bucket is found. + while (true) { + final TLCState ent = this.states[loc]; + if (ent == null) { + states[loc] = aState; + count++; + return false; + } + // Compare with equals here to correctly handle symmetry where two + // symmetric states will be hashed to neighboring buckets. The + // assumption is that we will end up with only doing a few equality + // checks because the primary comparison is still the fingerprint + // and that the states[] is sparsely populated. + if (aState.equals(ent)) { + return true; + } + loc = (loc + 1) % this.length; + } + } + + /** + * @return The current capacity of this set. [](capacity > size) + */ + public final int capacity() { + return this.length; + } + + /** + * @return The number of {@link TLCState}s in this set. [](capacity > size) + */ + public final int size() { + return this.count; + } + + /* (non-Javadoc) + * @see java.lang.Object#toString() + */ + public String toString() { + final StringBuffer buf = new StringBuffer("{"); + for (int i = 0; i < states.length; i++) { + final TLCState tlcState = states[i]; + if (tlcState != null) { + buf.append("<<"); + buf.append(tlcState.fingerPrint()); + buf.append(","); + final String toStr = tlcState.toString(); + buf.append(toStr.substring(0, toStr.length() - 1)); // chop off "\n" + buf.append(">>,\n"); + } + } + buf.append("}"); + return buf.toString(); + } + + /* + * Iterate (avoids creating an iterator object at the price of the mandatory + * resetNext() method). + */ + + private int iteratorIndex = 0; + + public final TLCState next() { + TLCState next = null; + while ((next = this.states[iteratorIndex++]) == null) { + // No-op loop + } + return next; + } + + public void resetNext() { + iteratorIndex = 0; + } +} diff --git a/tlatools/src/tlc2/util/StatePoolReader.java b/tlatools/src/tlc2/util/StatePoolReader.java index 7690191f2beeb7e847c19bcc795ed125cd2d5bfa..670e3eb1983f22b16f08b9769ff278a4eabc4df6 100644 --- a/tlatools/src/tlc2/util/StatePoolReader.java +++ b/tlatools/src/tlc2/util/StatePoolReader.java @@ -23,6 +23,7 @@ public class StatePoolReader extends Thread { } public StatePoolReader(int bufSize, File file) { + super("TLCStatePoolReader"); this.buf = new TLCState[bufSize]; this.poolFile = file; this.isFull = false; @@ -55,7 +56,7 @@ public class StatePoolReader extends Thread { public final synchronized TLCState[] doWork(TLCState[] deqBuf, File file) throws IOException, ClassNotFoundException { if (this.isFull) { - Assert.check(this.poolFile == null, EC.SYSTEM_FILE_NULL); + assert this.poolFile == null : EC.SYSTEM_FILE_NULL; TLCState[] res = this.buf; this.buf = deqBuf; this.poolFile = file; @@ -93,7 +94,7 @@ public class StatePoolReader extends Thread { public final synchronized TLCState[] getCache(TLCState[] deqBuf, File file) throws IOException, ClassNotFoundException { if (this.isFull) { - Assert.check(this.poolFile == null, EC.SYSTEM_FILE_NULL); + assert this.poolFile == null : EC.SYSTEM_FILE_NULL; TLCState[] res = this.buf; this.buf = deqBuf; this.poolFile = file; diff --git a/tlatools/src/tlc2/util/StatePoolWriter.java b/tlatools/src/tlc2/util/StatePoolWriter.java index 2f4a45a26fae73d0433fc41d770a4ff55a752fa7..b01461bfd405cab8f741586c67517329c79e9d25 100644 --- a/tlatools/src/tlc2/util/StatePoolWriter.java +++ b/tlatools/src/tlc2/util/StatePoolWriter.java @@ -28,6 +28,7 @@ public class StatePoolWriter extends Thread { } public StatePoolWriter(int bufSize, StatePoolReader reader) { + super("TLCStatePoolWriter"); this.buf = new TLCState[bufSize]; this.poolFile = null; this.reader = reader; diff --git a/tlatools/src/tlc2/util/StateWriter.java b/tlatools/src/tlc2/util/StateWriter.java index a0021b4f82a1ddd62ff118ef2d7cb098d7652f79..3efc5a0754bf1efc43998448b0e7b2c35aa9c8d2 100644 --- a/tlatools/src/tlc2/util/StateWriter.java +++ b/tlatools/src/tlc2/util/StateWriter.java @@ -3,26 +3,52 @@ package tlc2.util; import java.io.IOException; import java.io.PrintWriter; +import tlc2.tool.Action; import tlc2.tool.TLCState; import util.FileUtil; /** * State writer * @author Simon Zambrovski - * @version $Id$ */ -public class StateWriter +public class StateWriter implements IStateWriter { - private PrintWriter writer; - private int stateNum; + protected final PrintWriter writer; + protected int stateNum; + protected String fname; public StateWriter(String fname) throws IOException { - // SZ Feb 24, 2009: stream creation moved + this.fname = fname; this.writer = new PrintWriter(FileUtil.newBFOS(fname)); this.stateNum = 1; } + /* (non-Javadoc) + * @see tlc2.util.IStateWriter#getDumpFileName() + */ + public String getDumpFileName() { + return this.fname; + } + + /* (non-Javadoc) + * @see tlc2.util.IStateWriter#isNoop() + */ + public boolean isNoop() { + return false; + } + + /* (non-Javadoc) + * @see tlc2.util.IStateWriter#isDot() + */ + @Override + public boolean isDot() { + return false; + } + + /* (non-Javadoc) + * @see tlc2.util.IStateWriter#writeState(tlc2.tool.TLCState) + */ public synchronized void writeState(TLCState state) { this.writer.println("State " + this.stateNum + ":"); @@ -30,8 +56,61 @@ public class StateWriter this.stateNum++; } - public final void close() + /* (non-Javadoc) + * @see tlc2.util.IStateWriter#writeState(tlc2.tool.TLCState, tlc2.tool.TLCState, boolean) + */ + public synchronized void writeState(final TLCState state, final TLCState successor, final boolean successorStateIsNew) + { + if (successorStateIsNew) { + this.writeState(successor); + } + } + + public synchronized void writeState(final TLCState state, final TLCState successor, final boolean successorStateIsNew, Action action) + { + if (successorStateIsNew) { + this.writeState(successor); + } + } + + /* (non-Javadoc) + * @see tlc2.util.IStateWriter#writeState(tlc2.tool.TLCState, tlc2.tool.TLCState, boolean, tlc2.util.IStateWriter.Visualization) + */ + public void writeState(TLCState state, TLCState successor, final boolean successorStateIsNew, final Visualization visualization) { + if (successorStateIsNew) { + this.writeState(successor); + } + } + + /* (non-Javadoc) + * @see tlc2.util.IStateWriter#close() + */ + public void close() { this.writer.close(); } + + /* (non-Javadoc) + * @see tlc2.util.IStateWriter#writeState(tlc2.tool.TLCState, tlc2.tool.TLCState, tlc2.util.BitVector, int, int, boolean) + */ + public void writeState(TLCState state, TLCState successor, BitVector actionChecks, int from, int to, boolean successorStateIsNew) { + if (successorStateIsNew) { + this.writeState(state); + } + } + + /* (non-Javadoc) + * @see tlc2.util.IStateWriter#writeState(tlc2.tool.TLCState, tlc2.tool.TLCState, tlc2.util.BitVector, int, int, boolean, tlc2.util.IStateWriter.Visualization) + */ + public void writeState(TLCState state, TLCState successor, BitVector actionChecks, int from, int to, boolean successorStateIsNew, + Visualization visulation) { + if (successorStateIsNew) { + this.writeState(state); + } + } + + @Override + public void snapshot() throws IOException { + // No-op unless DotStateWriter. + } } diff --git a/tlatools/src/tlc2/util/Striped.java b/tlatools/src/tlc2/util/Striped.java index 7f6ddbdb648b2ebd4a8ef1086156e540b5127216..ba46ff1d6451009d8aaa5e6dad981ce0582a04e3 100644 --- a/tlatools/src/tlc2/util/Striped.java +++ b/tlatools/src/tlc2/util/Striped.java @@ -4,7 +4,7 @@ package tlc2.util; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; -public class Striped { +public final class Striped { public static Striped readWriteLock(final int lockCnt) { return new Striped(lockCnt); @@ -19,21 +19,21 @@ public class Striped { } } - public ReadWriteLock getAt(int lockIndex) { + public final ReadWriteLock getAt(int lockIndex) { return this.locks[lockIndex]; } - public int size() { + public final int size() { return locks.length; } - public void releaseAllLocks() { + public final void releaseAllLocks() { for (int i = size() - 1; i >= 0; i--) { this.locks[i].writeLock().unlock(); } } - public void acquireAllLocks() { + public final void acquireAllLocks() { //TODO find way to do this more efficiently for (int i = 0; i < size(); i++) { this.locks[i].writeLock().lock(); diff --git a/tlatools/src/tlc2/util/SynchronousDiskIntStack.java b/tlatools/src/tlc2/util/SynchronousDiskIntStack.java new file mode 100644 index 0000000000000000000000000000000000000000..b66120433be9da7b4676c741617549739c626013 --- /dev/null +++ b/tlatools/src/tlc2/util/SynchronousDiskIntStack.java @@ -0,0 +1,150 @@ +/******************************************************************************* + * Copyright (c) 2015 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ + +package tlc2.util; + +import java.io.File; + +import tlc2.output.EC; +import util.Assert; +import util.BufferedDataInputStream; +import util.BufferedDataOutputStream; +import util.FileUtil; + +public class SynchronousDiskIntStack implements IntStack { + + public final static int BufSize = 8388608; // ~32mb + private final static int BufSizeMax = BufSize << 5; // ~1024mb + + private final int bufSize; + private final String filePrefix; + + private long size = 0L; + private int index = 0; + + private int hiPool = 0; + + private int[] buf; + + public SynchronousDiskIntStack(String diskdir, String name) { + this(diskdir, name, BufSize); + } + + public SynchronousDiskIntStack(String diskdir, String name, int capacity) { + // Hard-limit capacity to 1gb per page file + capacity = Math.min(BufSizeMax, capacity); + this.filePrefix = diskdir + FileUtil.separator + name; + this.bufSize = capacity; + this.buf = new int[capacity]; + } + + /* (non-Javadoc) + * @see tlc2.util.IntStack#size() + */ + public long size() { + return this.size; + } + + /* (non-Javadoc) + * @see tlc2.util.IntStack#pushInt(int) + */ + public void pushInt(int x) { + if (this.index == bufSize) { + // flush to disk + try { + final File poolFile = new File(this.filePrefix + Integer.toString(this.hiPool)); + poolFile.deleteOnExit(); + final BufferedDataOutputStream bdos = FileUtil.newBdFOS(false, poolFile); + final int len = buf.length; + for (int i = 0; i < len; i++) { + bdos.writeInt(buf[i]); + } + bdos.close(); + this.hiPool++; + this.index = 0; + } catch (Exception e) { + Assert.fail(EC.SYSTEM_ERROR_WRITING_STATES, new String[] { "stack", e.getMessage() }); + } + } + this.buf[this.index++] = x; + this.size++; + } + + /* (non-Javadoc) + * @see tlc2.util.IntStack#pushLong(long) + */ + public void pushLong(long x) { + this.pushInt((int) (x & 0xFFFFFFFFL)); + this.pushInt((int) (x >>> 32)); + } + + /* (non-Javadoc) + * @see tlc2.util.IntStack#popInt() + */ + public int popInt() { + if (this.index == 0 && hasPool()) { + // fill buffer + try { + final File poolFile = new File(this.filePrefix + Integer.toString(this.hiPool - 1)); + final BufferedDataInputStream bdis = FileUtil.newBdFIS(false, poolFile); + final int len = buf.length; + for (int i = 0; i < len; i++) { + buf[i] = bdis.readInt(); + } + bdis.close(); + this.hiPool--; + this.index = len; + } catch (Exception e) { + Assert.fail(EC.SYSTEM_ERROR_WRITING_STATES, new String[] { "stack", e.getMessage() }); + } + } + this.size--; + return this.buf[--this.index]; + } + + private boolean hasPool() { + return this.hiPool >= 0; + } + + /* (non-Javadoc) + * @see tlc2.util.IntStack#popLong() + */ + public long popLong() { + long high = this.popInt(); + long low = this.popInt(); + return (high << 32) | (low & 0xFFFFFFFFL); + } + + /* (non-Javadoc) + * @see tlc2.util.IntStack#reset() + */ + public void reset() { + this.size = 0L; + this.index = 0; + + this.hiPool = 0; + } +} diff --git a/tlatools/src/tlc2/util/Vect.java b/tlatools/src/tlc2/util/Vect.java index 7557aa1bc240af131ab32e8029eb034306f8e1ae..8d4907820ed4d1fbc12c8195a6839c1cfa95d02d 100644 --- a/tlatools/src/tlc2/util/Vect.java +++ b/tlatools/src/tlc2/util/Vect.java @@ -9,21 +9,27 @@ import java.util.Enumeration; import java.util.NoSuchElementException; import java.util.Vector; -public class Vect implements Cloneable, Serializable { - private Object[] elementData; +/** + * TODO it's unclear why we've written our own list implementation; we should consider using existing framework code for this; + * we've also written our own "Vector" class in SANY... + */ +@SuppressWarnings("unchecked") +public class Vect<E> implements Cloneable, Serializable { + private static final long serialVersionUID = 1L; + + + private E[] elementData; private int elementCount; - static private final Object[] empty = new Object[0]; - - final class Enumerator implements Enumeration { + final class Enumerator implements Enumeration<E> { int index = 0; public final boolean hasMoreElements () { return (this.index < elementCount); } - public final Object nextElement() { - return elementData[index++]; + public final E nextElement() { + return (E) elementData[index++]; } } @@ -32,14 +38,14 @@ public class Vect implements Cloneable, Serializable { public Vect(int initialCapacity) { this.elementCount = 0; if (initialCapacity == 0) { - this.elementData = empty ; + this.elementData = (E[]) new Object[0]; } else { - this.elementData = new Object[initialCapacity]; + this.elementData = (E[]) new Object[initialCapacity]; } } - public Vect(Vector v) { + public Vect(Vector<E> v) { this(v.size()); int sz = v.size(); for (int i = 0; i < sz; i++) { @@ -47,15 +53,15 @@ public class Vect implements Cloneable, Serializable { } } - public final void addElement(Object obj) { + public final void addElement(E obj) { if (this.elementCount == this.elementData.length) { this.ensureCapacity(this.elementCount+1); } this.elementData[this.elementCount++] = obj; } - public final Vect concat(Vect elems) { - Vect v = new Vect(); + public final Vect<E> concat(Vect<E> elems) { + Vect<E> v = new Vect<>(); for (int i = 0; i < this.elementCount; i++) { v.addElement(this.elementData[i]); } @@ -68,7 +74,7 @@ public class Vect implements Cloneable, Serializable { public int capacity() { return this.elementData.length; } public Object clone() { - Vect v = new Vect(this.elementData.length); + Vect<E> v = new Vect<>(this.elementData.length); System.arraycopy(this.elementData, 0, v.elementData, 0, this.elementCount); v.elementCount = this.elementCount; return v; @@ -82,11 +88,12 @@ public class Vect implements Cloneable, Serializable { System.arraycopy(this.elementData, 0, anArray, 0, this.elementCount); } - public final Object elementAt(int index) { + public final E elementAt(int index) { return this.elementData[index]; } - public Enumeration elements() { return new Vect.Enumerator(); } + @SuppressWarnings("rawtypes") + public Enumeration<E> elements() { return new Vect.Enumerator(); } public final void ensureCapacity(int minCapacity) { if (this.elementData.length < minCapacity) { @@ -95,7 +102,7 @@ public class Vect implements Cloneable, Serializable { newCapacity = minCapacity; } Object oldBuffer[] = elementData; - elementData = new Object[newCapacity]; + elementData = (E[]) new Object[newCapacity]; System.arraycopy( oldBuffer, 0, elementData, 0, elementCount); } } @@ -113,7 +120,7 @@ public class Vect implements Cloneable, Serializable { return -1; } - public final void insertElementAt(Object obj, int index) { + public final void insertElementAt(E obj, int index) { if (elementCount == elementData.length) ensureCapacity(elementCount+1); @@ -142,7 +149,7 @@ public class Vect implements Cloneable, Serializable { this.elementData[this.elementCount] = null; } - public final void setElementAt(Object obj, int index) { + public final void setElementAt(E obj, int index) { this.elementData[index] = obj; } @@ -165,7 +172,7 @@ public class Vect implements Cloneable, Serializable { return elem; } - public final void push(Object elem) { + public final void push(E elem) { this.addElement(elem); } @@ -181,7 +188,7 @@ public class Vect implements Cloneable, Serializable { public final boolean equals(Object obj) { if (!(obj instanceof Vect)) return false; - Vect v = (Vect)obj; + Vect<E> v = (Vect<E>)obj; if (v.size() != this.elementCount) return false; for (int i = 0; i < this.elementCount; i++) { if (!this.elementData[i].equals(v.elementAt(i))) return false; diff --git a/tlatools/src/tlc2/util/statistics/AbstractBucketStatistics.java b/tlatools/src/tlc2/util/statistics/AbstractBucketStatistics.java new file mode 100644 index 0000000000000000000000000000000000000000..bdea80b92ec7b0c3c954fb09f79b9bc2859c9f81 --- /dev/null +++ b/tlatools/src/tlc2/util/statistics/AbstractBucketStatistics.java @@ -0,0 +1,221 @@ +/******************************************************************************* + * Copyright (c) 2017 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.util.statistics; + +import java.util.Iterator; +import java.util.Map.Entry; +import java.util.NavigableMap; + +import javax.management.NotCompliantMBeanException; + +import tlc2.output.EC; +import tlc2.output.MP; +import tlc2.tool.management.TLCStandardMBean; +import tlc2.util.statistics.management.BucketStatisticsMXWrapper; + +public abstract class AbstractBucketStatistics implements IBucketStatistics { + + /** + * Human readable statistics name (used in toString()) + */ + protected final String title; + + public AbstractBucketStatistics(String aTitle) { + super(); + this.title = aTitle; + } + + /** + * @param aTitle + * A title for console pretty printing + * @param pkg + * A package name for this statistics, e.g. tlc2.tool.liveness + * for stats are from classes in the liveness package. + * @param name + * The (class) name of the source of the statistics. + */ + public AbstractBucketStatistics(final String aTitle, final String pkg, final String name) { + this(aTitle); + try { + //TODO unregister somehow + new BucketStatisticsMXWrapper(this, name, pkg); + } catch (NotCompliantMBeanException e) { + // not expected to happen would cause JMX to be broken, hence just log and + // continue + MP.printWarning( + EC.GENERAL, + "Failed to create MBean wrapper for BucketStatistics. No statistics/metrics will be avaiable.", + e); + TLCStandardMBean.getNullTLCStandardMBean(); + } + } + + public String toString() { + final StringBuffer buf = new StringBuffer(); + buf.append("============================%n"); + buf.append("=" + title + "=%n"); + buf.append("============================%n"); + buf.append(String.format("Observations: %d%n", getObservations())); + buf.append(String.format("Min: %d%n", getMin())); + buf.append(String.format("Max: %d%n", getMax())); + buf.append(String.format("Mean: %.2f%n", getMean())); + buf.append(String.format("Median: %d%n", getMedian())); + buf.append(String.format("Standard deviation: %.2f%n", getStdDev())); + buf.append(String.format("75%%: %.2f%n", getPercentile(0.75d))); + buf.append(String.format("95%%: %.2f%n", getPercentile(0.95d))); + buf.append(String.format("98%%: %.2f%n", getPercentile(0.98d))); + buf.append(String.format("99%%: %.2f%n", getPercentile(0.99d))); + buf.append(String.format("99.9%%: %.2f%n", getPercentile(0.999d))); + buf.append("numEdges/occurrences (log scale)%n"); + buf.append("--------------------------------%n"); + final Iterator<Entry<Integer, Long>> iterator = getSamples().entrySet().iterator(); + while(iterator.hasNext()) { + Entry<Integer, Long> next = iterator.next(); + long amount = next.getValue(); + int i = next.getKey(); + buf.append(String.format("%02d", i)); + buf.append(":"); + buf.append(String.format("%02d", amount)); + buf.append(" "); + for (int j = 0; j < Math.log(amount); j++) { + buf.append("#"); + } + buf.append("%n"); + } + buf.append("============================"); + return buf.toString(); + } + + public int getMedian() { + long l = getObservations(); + if (l <= 0) { + return -1; + } + // skip forward for as many elements as 1/2 observations. The + // corresponding bucket is the median. + long sum = 0L; + final Iterator<Entry<Integer, Long>> iterator = getSamples().entrySet().iterator(); + while(iterator.hasNext()) { + final Entry<Integer, Long> next = iterator.next(); + sum += next.getValue(); + if (sum > (l / 2)) { + return next.getKey(); + } + } + // make compiler happy + throw new RuntimeException("bug, shoud not get here"); + } + + public double getMean() { + long sum = 0L; + // Sum up values and count + final Iterator<Entry<Integer, Long>> iterator = getSamples().entrySet().iterator(); + while(iterator.hasNext()) { + final Entry<Integer, Long> next = iterator.next(); + final long value = next.getValue(); + final int i = next.getKey(); + sum += value * i; + } + if (getObservations() > 0) { + return (sum / (getObservations() * 1.0d)); + } else { + // No mean yet + return -1; + } + } + + public int getMin() { + if (getObservations() <= 0) { + return -1; + } + return getSamples().firstKey(); + } + + public int getMax() { + if (getObservations() <= 0) { + return -1; + } + return getSamples().lastKey(); + } + + public double getStdDev() { + final long N = getObservations(); + if (N <= 0) { + return -1.0d; + } + final double mean = getMean() * 1.0d; + double sum = 0.0d; + final Iterator<Entry<Integer, Long>> iterator = getSamples().entrySet().iterator(); + while(iterator.hasNext()) { + Entry<Integer, Long> next = iterator.next(); + double Xi = next.getKey() * 1.0d; + double diff = Xi - mean; + sum += (diff * diff) * ((next.getValue() * 1.0d)); // diff^2 + } + double variance = sum / (N * 1.0d); + double stdDev = Math.sqrt(variance); + return stdDev; + } + + public double getPercentile(double quantile) { + if (Double.isNaN(quantile)) { + throw new IllegalArgumentException("NaN"); + } + final long obsv = getObservations(); + if (obsv <= 0) { + return -1.0; + } + // adjust values to valid range + quantile = Math.min(1.0, quantile); + quantile = Math.max(0, quantile); + + final NavigableMap<Integer, Long> samples = getSamples(); + + // calculate the elements position for the + // given quantile + final int pos = (int) ((obsv * 1.0d) * quantile); + if (pos > obsv) { + return samples.size(); + } + if (pos < 0) { + return 0; + } + + // advance to the bucket at position + long cnt = 0l; + final Iterator<Entry<Integer, Long>> iterator = samples.entrySet().iterator(); + while(iterator.hasNext()) { + Entry<Integer, Long> next = iterator.next(); + int i = next.getKey(); + cnt += next.getValue(); + if (cnt > pos) { + return i; + } + } + return quantile; + } + +} \ No newline at end of file diff --git a/tlatools/src/tlc2/util/statistics/BucketStatistics.java b/tlatools/src/tlc2/util/statistics/BucketStatistics.java index f74fe27f021af9589c850b2fc0aa9c3a41af26c0..049930161ef1a2620388570966d9b8b5e33c21fd 100644 --- a/tlatools/src/tlc2/util/statistics/BucketStatistics.java +++ b/tlatools/src/tlc2/util/statistics/BucketStatistics.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2015 Microsoft Research. All rights reserved. + * Copyright (c) 2017 Microsoft Research. All rights reserved. * * The MIT License (MIT) * @@ -23,29 +23,21 @@ * Contributors: * Markus Alexander Kuppe - initial API and implementation ******************************************************************************/ - package tlc2.util.statistics; -import java.util.Iterator; +import java.util.HashMap; +import java.util.Map; import java.util.Map.Entry; -import java.util.concurrent.ConcurrentNavigableMap; -import java.util.concurrent.ConcurrentSkipListMap; -import java.util.concurrent.atomic.AtomicLong; - -import javax.management.NotCompliantMBeanException; +import java.util.NavigableMap; +import java.util.TreeMap; -import tlc2.output.EC; -import tlc2.output.MP; -import tlc2.tool.management.TLCStandardMBean; -import tlc2.util.statistics.management.BucketStatisticsMXWrapper; +public class BucketStatistics extends AbstractBucketStatistics implements IBucketStatistics { -public class BucketStatistics implements IBucketStatistics { - /** * The amount of samples seen by this statistics. It's identical * to the sum of the value of all buckets. */ - protected final AtomicLong observations = new AtomicLong(0); + private long observations; /** * Instead of using an ever-growing list of samples, identical @@ -54,270 +46,66 @@ public class BucketStatistics implements IBucketStatistics { * seen two times. * The map is thread safe, so are the values. */ - protected final ConcurrentNavigableMap<Integer, AtomicLong> buckets = new ConcurrentSkipListMap<Integer, AtomicLong>(); - - /** - * Human readable statistics name (used in toString()) - */ - private final String title; - - /** - * Upper limit for the number of buckets available for sampling. If a sample - * exceeds maximum, it will go into the very last bucket. It serves as an - * "overflow" bucket. - * Useful if samples flatten out to the right and at many buckets with just - * a single sample would be added. - */ - private final int maximum; + private final Map<Integer, Long> buckets; - // Used for unit testing only - BucketStatistics() { - this("Historgram"); - } - - /** - * @see {@link BucketStatistics#BucketStatistics(String, int)} - */ - public BucketStatistics(final String aTitle) { - this(aTitle, Integer.MAX_VALUE); - } - - /** - * @see {@link BucketStatistics#BucketStatistics(String, int, String, String)} - */ - public BucketStatistics(final String aTitle, final String pkg, final String name) { - this(aTitle, Integer.MAX_VALUE, pkg, name); - } - - /** - * @param aTitle - * A title for console pretty printing - * @param aMaxmimum - * see {@link BucketStatistics#maximum} - */ - public BucketStatistics(final String aTitle, final int aMaxmimum) { - this.title = aTitle; - this.maximum = aMaxmimum; + public BucketStatistics(String aTitle) { + super(aTitle); + this.buckets = new HashMap<Integer, Long>(); } - /** - * @param aTitle - * A title for console pretty printing - * @param aMaxmimum - * see {@link BucketStatistics#maximum} - * @param pkg - * A package name for this statistics, e.g. tlc2.tool.liveness - * for stats are from classes in the liveness package. - * @param name - * The (class) name of the source of the statistics. - */ - public BucketStatistics(final String aTitle, final int aMaxmimum, final String pkg, final String name) { - this(aTitle, aMaxmimum); - - TLCStandardMBean graphStatsMXWrapper; - try { - //TODO unregister somehow - graphStatsMXWrapper = new BucketStatisticsMXWrapper(this, name, pkg); - } catch (NotCompliantMBeanException e) { - // not expected to happen - // would cause JMX to be broken, hence just log and continue - MP.printWarning( - EC.GENERAL, - "Failed to create MBean wrapper for BucketStatistics. No statistics/metrics will be avaiable.", - e); - graphStatsMXWrapper = TLCStandardMBean.getNullTLCStandardMBean(); - } + public BucketStatistics(String aTitle, final String pkg, final String name) { + super(aTitle, pkg, name); + this.buckets = new HashMap<Integer, Long>(); } - + /* (non-Javadoc) * @see tlc2.util.statistics.IBucketStatistics#addSample(int) */ - public void addSample(final int amount) { + public void addSample(int amount) { if (amount < 0) { throw new IllegalArgumentException("Negative amount invalid"); } - // If the amount exceeds the fixed maximum, increment the overflow - // bucket. The overflow bucket is the very last bucket. - final int idx = Math.min(maximum, amount); - - final AtomicLong atomicLong = buckets.get(idx); - if(atomicLong == null) { - buckets.putIfAbsent(idx, new AtomicLong(1)); + Long l = buckets.get(amount); + if(l == null) { + buckets.put(amount, 1L); } else { - atomicLong.incrementAndGet(); + buckets.replace(amount, ++l); } - observations.getAndIncrement(); - } - - /* (non-Javadoc) - * @see tlc2.util.statistics.IBucketStatistics#getObservations() - */ - public long getObservations() { - return observations.get(); - } - - /* (non-Javadoc) - * @see java.lang.Object#toString() - */ - public String toString() { - final StringBuffer buf = new StringBuffer(); - buf.append("============================\n"); - buf.append("=" + title + "=\n"); - buf.append("============================\n"); - buf.append(String.format("Observations: %d\n", observations.get())); - buf.append(String.format("Min: %d\n", getMin())); - buf.append(String.format("Max: %d\n", getMax())); - buf.append(String.format("Mean: %.2f\n", getMean())); - buf.append(String.format("Median: %d\n", getMedian())); - buf.append(String.format("Standard deviation: %.2f\n", getStdDev())); - buf.append(String.format("75%%: %.2f\n", getPercentile(0.75d))); - buf.append(String.format("95%%: %.2f\n", getPercentile(0.95d))); - buf.append(String.format("98%%: %.2f\n", getPercentile(0.98d))); - buf.append(String.format("99%%: %.2f\n", getPercentile(0.99d))); - buf.append(String.format("99.9%%: %.2f\n", getPercentile(0.999d))); - buf.append("numEdges/occurrences (log scale)\n"); - buf.append("--------------------------------\n"); - final Iterator<Entry<Integer, AtomicLong>> iterator = buckets.entrySet().iterator(); - while(iterator.hasNext()) { - Entry<Integer, AtomicLong> next = iterator.next(); - long amount = next.getValue().get(); - int i = next.getKey(); - buf.append(String.format("%02d", i)); - buf.append(":"); - buf.append(String.format("%02d", amount)); - buf.append(" "); - for (int j = 0; j < Math.log(amount); j++) { - buf.append("#"); - } - buf.append("\n"); - } - buf.append("============================"); - return buf.toString(); - } - - /* (non-Javadoc) - * @see tlc2.util.statistics.IBucketStatistics#getMedian() - */ - public int getMedian() { - long l = observations.get(); - if (l <= 0) { - return -1; - } - // skip forward for as many elements as 1/2 observations. The - // corresponding bucket is the median. - long sum = 0L; - final Iterator<Entry<Integer, AtomicLong>> iterator = buckets.entrySet().iterator(); - while(iterator.hasNext()) { - final Entry<Integer, AtomicLong> next = iterator.next(); - sum += next.getValue().get(); - if (sum > (l / 2)) { - return next.getKey(); - } - } - // make compiler happy - throw new RuntimeException("bug, shoud not get here"); - } - - /* (non-Javadoc) - * @see tlc2.util.statistics.IBucketStatistics#getMean() - */ - public double getMean() { - long sum = 0L; - // Sum up values and count - final Iterator<Entry<Integer, AtomicLong>> iterator = buckets.entrySet().iterator(); - while(iterator.hasNext()) { - final Entry<Integer, AtomicLong> next = iterator.next(); - final long value = next.getValue().get(); - final int i = next.getKey(); - sum += value * i; - } - if (observations.get() > 0) { - return (sum / (observations.get() * 1.0d)); - } else { - // No mean yet - return -1; - } - } - - /* (non-Javadoc) - * @see tlc2.util.statistics.IBucketStatistics#getMin() - */ - public int getMin() { - if (observations.get() <= 0) { - return -1; - } - return buckets.firstKey(); + observations++; } /* (non-Javadoc) - * @see tlc2.util.statistics.IBucketStatistics#getMax() + * @see tlc2.util.statistics.AbstractBucketStatistics#getObservations() */ - public int getMax() { - if (observations.get() <= 0) { - return -1; - } - return buckets.lastKey(); + public long getObservations() { + return observations; } /* (non-Javadoc) - * @see tlc2.util.statistics.IBucketStatistics#getStdDev() + * @see tlc2.util.statistics.IBucketStatistics#getSamples() */ - public double getStdDev() { - final long N = observations.get(); - if (N <= 0) { - return -1.0d; - } - final double mean = getMean() * 1.0d; - double sum = 0.0d; - final Iterator<Entry<Integer, AtomicLong>> iterator = buckets.entrySet().iterator(); - while(iterator.hasNext()) { - Entry<Integer, AtomicLong> next = iterator.next(); - double Xi = next.getKey() * 1.0d; - double diff = Xi - mean; - sum += (diff * diff) * ((next.getValue().get() * 1.0d)); // diff² + public NavigableMap<Integer, Long> getSamples() { + final NavigableMap<Integer, Long> res = new TreeMap<Integer, Long>(); + for (Entry<Integer, Long> entry : this.buckets.entrySet()) { + res.put(entry.getKey(), entry.getValue()); } - double variance = sum / (N * 1.0d); - double stdDev = Math.sqrt(variance); - return stdDev; + return res; } - /* (non-Javadoc) - * @see tlc2.util.statistics.IBucketStatistics#getPercentile(double) + /** + * Adds the observations of <code>stat</code> to this {@link BucketStatistics}. */ - public double getPercentile(double quantile) { - if (Double.isNaN(quantile)) { - throw new IllegalArgumentException("NaN"); - } - final long obsv = observations.get(); - if (obsv <= 0) { - return -1.0; - } - // adjust values to valid range - quantile = Math.min(1.0, quantile); - quantile = Math.max(0, quantile); + public void add(final IBucketStatistics stat) { + this.observations += stat.getObservations(); - // calculate the elements position for the - // given quantile - final int pos = (int) ((obsv * 1.0d) * quantile); - if (pos > obsv) { - return buckets.size(); - } - if (pos < 0) { - return 0; - } - - // advance to the bucket at position - long cnt = 0l; - final Iterator<Entry<Integer, AtomicLong>> iterator = buckets.entrySet().iterator(); - while(iterator.hasNext()) { - Entry<Integer, AtomicLong> next = iterator.next(); - int i = next.getKey(); - cnt += next.getValue().get(); - if (cnt > pos) { - return i; + for (Entry<Integer, Long> entry : stat.getSamples().entrySet()) { + final Long l = this.buckets.get(entry.getKey()); + if (l == null) { + this.buckets.put(entry.getKey(), entry.getValue()); + } else { + this.buckets.replace(entry.getKey(), l + entry.getValue()); } } - return quantile; } } diff --git a/tlatools/src/tlc2/util/statistics/ConcurrentBucketStatistics.java b/tlatools/src/tlc2/util/statistics/ConcurrentBucketStatistics.java new file mode 100644 index 0000000000000000000000000000000000000000..a69e0eb765a38e21eeb8f83309ed0800322fa13c --- /dev/null +++ b/tlatools/src/tlc2/util/statistics/ConcurrentBucketStatistics.java @@ -0,0 +1,105 @@ +/******************************************************************************* + * Copyright (c) 2015 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.util.statistics; + +import java.util.Map.Entry; +import java.util.NavigableMap; +import java.util.TreeMap; +import java.util.concurrent.ConcurrentNavigableMap; +import java.util.concurrent.ConcurrentSkipListMap; +import java.util.concurrent.atomic.AtomicLong; +import java.util.concurrent.atomic.LongAdder; + +public class ConcurrentBucketStatistics extends AbstractBucketStatistics implements IBucketStatistics { + + /** + * The amount of samples seen by this statistics. It's identical + * to the sum of the value of all buckets. + */ + private final LongAdder observations = new LongAdder(); + + /** + * Instead of using an ever-growing list of samples, identical + * samples are counted in a bucket. E.g. the sample 5 is stored + * in a bucket with key 5 and a value of 2 if the sample has been + * seen two times. + * The map is thread safe, so are the values. + */ + private final ConcurrentNavigableMap<Integer, AtomicLong> buckets = new ConcurrentSkipListMap<Integer, AtomicLong>(); + + ConcurrentBucketStatistics() { + super("Concurrent Historgram"); + } + + /** + * @see {@link BucketStatistics#BucketStatistics(String, int)} + */ + public ConcurrentBucketStatistics(final String aTitle) { + super(aTitle); + } + + /** + * @see {@link BucketStatistics#BucketStatistics(String, int, String, String)} + */ + public ConcurrentBucketStatistics(final String aTitle, final String pkg, final String name) { + super(aTitle, pkg, name); + } + + /* (non-Javadoc) + * @see tlc2.util.statistics.IBucketStatistics#addSample(int) + */ + public void addSample(final int amount) { + if (amount < 0) { + throw new IllegalArgumentException("Negative amount invalid"); + } + + final AtomicLong atomicLong = buckets.get(amount); + if(atomicLong == null) { + buckets.putIfAbsent(amount, new AtomicLong(1)); + } else { + atomicLong.incrementAndGet(); + } + observations.increment(); + } + + /* (non-Javadoc) + * @see tlc2.util.statistics.AbstractBucketStatistics#getObservations() + */ + public long getObservations() { + return observations.sum(); + } + + /* (non-Javadoc) + * @see tlc2.util.statistics.IBucketStatistics#getSamples() + */ + public NavigableMap<Integer, Long> getSamples() { + final NavigableMap<Integer, Long> res = new TreeMap<Integer, Long>(); + for (Entry<Integer, AtomicLong> entry : buckets.entrySet()) { + res.put(entry.getKey(), entry.getValue().get()); + } + return res; + } +} diff --git a/tlatools/src/tlc2/util/statistics/CounterStatistic.java b/tlatools/src/tlc2/util/statistics/CounterStatistic.java new file mode 100644 index 0000000000000000000000000000000000000000..39fe31a71138a19d24539c5d09068d7782013b41 --- /dev/null +++ b/tlatools/src/tlc2/util/statistics/CounterStatistic.java @@ -0,0 +1,88 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.util.statistics; + +import java.util.concurrent.atomic.LongAdder; +import java.util.function.BooleanSupplier; + +public abstract class CounterStatistic { + + public static CounterStatistic getInstance(final BooleanSupplier s) { + if (s.getAsBoolean()) { + return new LongAdderCounterStatistic(); + } else { + return new NoopCounterStatistic(); + } + } + + public abstract void increment(); + + public abstract void add(final long evalCount); + + public abstract long getCount(); + + private CounterStatistic() { + // private ctor + } + + private static class NoopCounterStatistic extends CounterStatistic { + + @Override + public final long getCount() { + return 0; + } + + @Override + public final void increment() { + // noop + } + + @Override + public void add(long evalCount) { + // noop + } + } + + private static class LongAdderCounterStatistic extends CounterStatistic { + + private final LongAdder adder = new LongAdder(); + + @Override + public final long getCount() { + return this.adder.sum(); + } + + @Override + public final void increment() { + adder.increment(); + } + + @Override + public void add(long evalCount) { + adder.add(evalCount); + } + } +} diff --git a/tlatools/src/tlc2/util/statistics/DummyBucketStatistics.java b/tlatools/src/tlc2/util/statistics/DummyBucketStatistics.java index ddecbf410538c29ef6cbfa4cc85e5eb9ea4469e8..72880929f53ffbc9db5ba8ea8d3d9f5506ae670d 100644 --- a/tlatools/src/tlc2/util/statistics/DummyBucketStatistics.java +++ b/tlatools/src/tlc2/util/statistics/DummyBucketStatistics.java @@ -26,6 +26,9 @@ package tlc2.util.statistics; +import java.util.NavigableMap; +import java.util.TreeMap; + /** * Doesn't really do a thing. Can be instantiated multiple times contrary to * BucketStatistics which registers itself via JMX. @@ -87,4 +90,17 @@ public class DummyBucketStatistics implements IBucketStatistics { public double getPercentile(double quantile) { return 0; } + + /* (non-Javadoc) + * @see tlc2.util.statistics.IBucketStatistics#getSamples() + */ + public NavigableMap<Integer, Long> getSamples() { + return new TreeMap<Integer, Long>(); + } + + /* (non-Javadoc) + * @see tlc2.util.statistics.IBucketStatistics#addSamples(tlc2.util.statistics.IBucketStatistics) + */ + public void addSamples(IBucketStatistics stat) { + } } diff --git a/tlatools/src/tlc2/util/statistics/FixedSizedBucketStatistics.java b/tlatools/src/tlc2/util/statistics/FixedSizedBucketStatistics.java new file mode 100644 index 0000000000000000000000000000000000000000..c1743379e705b7c87f39a0d29df682a76115a128 --- /dev/null +++ b/tlatools/src/tlc2/util/statistics/FixedSizedBucketStatistics.java @@ -0,0 +1,95 @@ +/******************************************************************************* + * Copyright (c) 2017 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.util.statistics; + +import java.util.NavigableMap; +import java.util.TreeMap; + +public class FixedSizedBucketStatistics extends AbstractBucketStatistics implements IBucketStatistics { + + /** + * The amount of samples seen by this statistics. It's identical + * to the sum of the value of all buckets. + */ + private long observations; + + /** + * Instead of using an ever-growing list of samples, identical + * samples are counted in a bucket. E.g. the sample 5 is stored + * in a bucket with key 5 and a value of 2 if the sample has been + * seen two times. + * The map is thread safe, so are the values. + */ + private final long[] buckets; + + /** + * @param aTitle + * A title for console pretty printing + * @param aMaxmimum + * see {@link BucketStatistics#maximum} + */ + public FixedSizedBucketStatistics(final String aTitle, final int aMaxmimum) { + super(aTitle); + this.buckets = new long[aMaxmimum]; + } + + /* (non-Javadoc) + * @see tlc2.util.statistics.IBucketStatistics#addSample(int) + */ + public void addSample(int amount) { + if (amount < 0) { + throw new IllegalArgumentException("Negative amount invalid"); + } + + // If the amount exceeds the fixed maximum, increment the overflow + // bucket. The overflow bucket is the very last bucket. + final int idx = Math.min(this.buckets.length - 1, amount); + + this.buckets[idx]++; + this.observations++; + } + + /* (non-Javadoc) + * @see tlc2.util.statistics.AbstractBucketStatistics#getObservations() + */ + public long getObservations() { + return observations; + } + + /* (non-Javadoc) + * @see tlc2.util.statistics.IBucketStatistics#getSamples() + */ + public NavigableMap<Integer, Long> getSamples() { + final NavigableMap<Integer, Long> res = new TreeMap<Integer, Long>(); + for (int i = 0; i < this.buckets.length; i++) { + long value = this.buckets[i]; + if (value > 0) { + res.put(i, value); + } + } + return res; + } +} diff --git a/tlatools/src/tlc2/util/statistics/FixedSizedConcurrentBucketStatistics.java b/tlatools/src/tlc2/util/statistics/FixedSizedConcurrentBucketStatistics.java new file mode 100644 index 0000000000000000000000000000000000000000..c6da9197785319d207a01792246e3087d8307326 --- /dev/null +++ b/tlatools/src/tlc2/util/statistics/FixedSizedConcurrentBucketStatistics.java @@ -0,0 +1,97 @@ +/******************************************************************************* + * Copyright (c) 2017 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.util.statistics; + +import java.util.NavigableMap; +import java.util.TreeMap; +import java.util.concurrent.atomic.AtomicLongArray; +import java.util.concurrent.atomic.LongAdder; + +public class FixedSizedConcurrentBucketStatistics extends AbstractBucketStatistics implements IBucketStatistics { + + /** + * The amount of samples seen by this statistics. It's identical + * to the sum of the value of all buckets. + */ + private final LongAdder observations = new LongAdder(); + + /** + * Instead of using an ever-growing list of samples, identical + * samples are counted in a bucket. E.g. the sample 5 is stored + * in a bucket with key 5 and a value of 2 if the sample has been + * seen two times. + * The map is thread safe, so are the values. + */ + protected final AtomicLongArray buckets; + + /** + * @param aTitle + * A title for console pretty printing + * @param aMaxmimum + * see {@link BucketStatistics#maximum} + */ + public FixedSizedConcurrentBucketStatistics(final String aTitle, final int aMaxmimum) { + super(aTitle); + this.buckets = new AtomicLongArray(aMaxmimum); + } + + /* (non-Javadoc) + * @see tlc2.util.statistics.IBucketStatistics#addSample(int) + */ + public void addSample(final int amount) { + if (amount < 0) { + throw new IllegalArgumentException("Negative amount invalid"); + } + + // If the amount exceeds the fixed maximum, increment the overflow + // bucket. The overflow bucket is the very last bucket. + final int idx = Math.min(this.buckets.length() - 1, amount); + + this.buckets.incrementAndGet(idx); + this.observations.increment(); + } + + /* (non-Javadoc) + * @see tlc2.util.statistics.AbstractBucketStatistics#getObservations() + */ + public long getObservations() { + return observations.sum(); + } + + /* (non-Javadoc) + * @see tlc2.util.statistics.IBucketStatistics#getSamples() + */ + public NavigableMap<Integer, Long> getSamples() { + final NavigableMap<Integer, Long> res = new TreeMap<Integer, Long>(); + for (int i = 0; i < this.buckets.length(); i++) { + long value = this.buckets.get(i); + if (value > 0) { + res.put(i, value); + } + } + return res; + } +} diff --git a/tlatools/src/tlc2/util/statistics/IBucketStatistics.java b/tlatools/src/tlc2/util/statistics/IBucketStatistics.java index 05bda1b1f06ca1144191efe56b3b526da0acc109..490dcecda8235ad32a5a3f8df834bb4651b00957 100644 --- a/tlatools/src/tlc2/util/statistics/IBucketStatistics.java +++ b/tlatools/src/tlc2/util/statistics/IBucketStatistics.java @@ -1,10 +1,15 @@ package tlc2.util.statistics; +import java.util.NavigableMap; + +/** + * Keeps statistics about any samples added. + */ public interface IBucketStatistics { /** * @param amount - * Add a sample to the stastics. Allowed range is 0 <= sample <= + * Add a sample to the statistics. Allowed range is 0 <= sample <= * Integer.MAX_VALUE */ void addSample(int amount); @@ -45,4 +50,5 @@ public interface IBucketStatistics { */ double getPercentile(double quantile); + NavigableMap<Integer, Long> getSamples(); } \ No newline at end of file diff --git a/tlatools/src/tlc2/value/BoolValue.java b/tlatools/src/tlc2/value/BoolValue.java deleted file mode 100644 index 3df9f9784c6f1e12077d6a69efe0c38bcd347b7a..0000000000000000000000000000000000000000 --- a/tlatools/src/tlc2/value/BoolValue.java +++ /dev/null @@ -1,104 +0,0 @@ -// Copyright (c) 2003 Compaq Corporation. All rights reserved. -// Portions Copyright (c) 2003 Microsoft Corporation. All rights reserved. -// Last modified on Sat 23 February 2008 at 10:01:16 PST by lamport -// modified on Fri Aug 10 15:07:07 PDT 2001 by yuanyu - -package tlc2.value; - -import tlc2.util.FP64; -import util.Assert; - -public class BoolValue extends Value { - public boolean val; // the boolean - - /* Constructor */ - public BoolValue(boolean b) { this.val = b; } - - public final byte getKind() { return BOOLVALUE; } - - public final int compareTo(Object obj) { - if (obj instanceof BoolValue) { - int x = this.val ? 1 : 0; - int y = ((BoolValue)obj).val ? 1 : 0; - return x - y; - } - if (!(obj instanceof ModelValue)) { - Assert.fail("Attempted to compare boolean " + ppr(this.toString()) + - " with non-boolean:\n" + ppr(obj.toString())); - } - return 1; - } - - public final boolean equals(Object obj) { - if (obj instanceof BoolValue) { - return this.val == ((BoolValue)obj).val; - } - if (!(obj instanceof ModelValue)) { - Assert.fail("Attempted to compare equality of boolean " + ppr(this.toString()) + - " with non-boolean:\n" + ppr(obj.toString())); - } - return ((ModelValue) obj).modelValueEquals(this) ; - } - - public final boolean member(Value elem) { - Assert.fail("Attempted to check if the value:\n" + ppr(elem.toString()) + - "\nis an element of the boolean " + ppr(this.toString())); - return false; // make compiler happy - } - - public final boolean isFinite() { - Assert.fail("Attempted to check if the boolean " + ppr(this.toString()) + - " is a finite set."); - return false; // make compiler happy - } - - public final Value takeExcept(ValueExcept ex) { - if (ex.idx < ex.path.length) { - Assert.fail("Attempted to apply EXCEPT construct to the boolean " + - ppr(this.toString()) + "."); - } - return ex.value; - } - - public final Value takeExcept(ValueExcept[] exs) { - if (exs.length != 0) { - Assert.fail("Attempted to apply EXCEPT construct to the boolean " + - ppr(this.toString()) + "."); - } - return this; - } - - public final int size() { - Assert.fail("Attempted to compute the number of elements in the boolean " + - ppr(this.toString()) + "."); - return 0; // make compiler happy - } - - public final boolean isNormalized() { return true; } - - public final void normalize() { /*nop*/ } - - public final boolean isDefined() { return true; } - - public final Value deepCopy() { return this; } - - public final boolean assignable(Value val) { - return ((val instanceof BoolValue) && - this.val == ((BoolValue)val).val); - } - - /* The fingerprint method */ - public final long fingerPrint(long fp) { - fp = FP64.Extend(fp, BOOLVALUE) ; - fp = FP64.Extend(fp, (this.val) ? 't' : 'f') ; - return fp ; - } - - public final Value permute(MVPerm perm) { return this; } - - /* The string representation */ - public final StringBuffer toString(StringBuffer sb, int offset) { - return sb.append((this.val) ? "TRUE" : "FALSE"); - } - -} diff --git a/tlatools/src/tlc2/value/Enumerable.java b/tlatools/src/tlc2/value/Enumerable.java deleted file mode 100644 index 810f4173b50f7fd813dcd2709cff877f4d0cdcee..0000000000000000000000000000000000000000 --- a/tlatools/src/tlc2/value/Enumerable.java +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright (c) 2003 Compaq Corporation. All rights reserved. -// Portions Copyright (c) 2003 Microsoft Corporation. All rights reserved. -// Last modified on Mon 30 Apr 2007 at 13:20:54 PST by lamport -// modified on Thu Mar 11 21:25:20 PST 1999 by yuanyu - -package tlc2.value; - -public interface Enumerable { - - public int size(); - public boolean member(Value elem); - public ValueEnumeration elements(); - -} - diff --git a/tlatools/src/tlc2/value/FcnLambdaValue.java b/tlatools/src/tlc2/value/FcnLambdaValue.java deleted file mode 100644 index 6846a525f33cafd29b969dfa3aaf83d0a1d7aaf8..0000000000000000000000000000000000000000 --- a/tlatools/src/tlc2/value/FcnLambdaValue.java +++ /dev/null @@ -1,522 +0,0 @@ -// Copyright (c) 2003 Compaq Corporation. All rights reserved. -// Portions Copyright (c) 2003 Microsoft Corporation. All rights reserved. -// Last modified on Wed 4 Jul 2007 at 17:31:23 PST by lamport -// modified on Thu Dec 6 21:46:34 PST 2001 by yuanyu - -package tlc2.value; - -import java.io.IOException; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; - -import tla2sany.semantic.FormalParamNode; -import tla2sany.semantic.SemanticNode; -import tla2sany.semantic.SymbolNode; -import tlc2.tool.EvalControl; -import tlc2.tool.EvalException; -import tlc2.tool.TLCState; -import tlc2.tool.Tool; -import tlc2.util.Context; -import util.Assert; - -public class FcnLambdaValue extends Value implements Applicable { - public FcnParams params; // the function formals - public SemanticNode body; // the function body - public ValueExcept[] excepts; // the EXCEPTs - public Tool tool; - public Context con; - public TLCState state; - public TLCState pstate; - public int control; - public FcnRcdValue fcnRcd; - - /* Constructor */ - public FcnLambdaValue(FcnParams params, SemanticNode body, Tool tool, - Context c, TLCState s0, TLCState s1, int control) { - this.params = params; - this.body = body; - this.excepts = null; - this.tool = tool; - this.con = c; - this.state = s0.copy(); // copy() added 12 Mar 2010 by Yuan Yu. - if (s1 != null) { // see SetPredValue constructor. - this.pstate = s1.copy(); - } else { - this.pstate = null; - } - - this.control = control; - this.fcnRcd = null; - } - - public FcnLambdaValue(FcnLambdaValue fcn) { - this.params = fcn.params; - this.body = fcn.body; - this.excepts = fcn.excepts; - this.tool = fcn.tool; - this.con = fcn.con; - this.state = fcn.state; - this.pstate = fcn.pstate; - this.control = fcn.control; - this.fcnRcd = fcn.fcnRcd; - } - - public final byte getKind() { return FCNLAMBDAVALUE; } - - public final void makeRecursive(SymbolNode fname) { - this.con = this.con.cons(fname, this); - this.control = EvalControl.setKeepLazy(this.control); - } - - public final int compareTo(Object obj) { - FcnRcdValue fcn = FcnRcdValue.convert(this); - return fcn.compareTo(obj); - } - - public final boolean equals(Object obj) { - FcnRcdValue fcn = FcnRcdValue.convert(this); - return fcn.equals(obj); - } - - public final boolean member(Value elem) { - Assert.fail("Attempted to check if the value:\n" + ppr(elem.toString()) + - "\nis an element of the function " + ppr(this.toString())); - return false; // make compiler happy - } - - public final boolean isFinite() { - Assert.fail("Attempted to check if the function:\n" + ppr(this.toString()) + - "\nis a finite set."); - return false; // make compiler happy - } - - /* Apply this function to the arguments given by args. */ - public final Value apply(Value args, int control) throws EvalException { - if (this.fcnRcd != null) { - return this.fcnRcd.apply(args, control); - } - - // First, find all the excepts that match args. - Value res = null; - int num = 0; - ValueExcept[] excepts1 = null; - if (this.excepts != null) { - int exlen = this.excepts.length; - for (int i = exlen-1; i >= 0; i--) { - ValueExcept ex = this.excepts[i]; - Value arg = ex.current(); - boolean inExcept = true; - inExcept = arg.equals(args); - if (inExcept) { - if (ex.isLast()) { res = ex.value; break; } - if (excepts1 == null) excepts1 = new ValueExcept[exlen]; - excepts1[num++] = new ValueExcept(ex, ex.idx+1); - } - } - } - - // Second, evaluate the function application. - if (res == null) { - Context c1 = this.con; - FormalParamNode[][] formals = this.params.formals; - Value[] domains = this.params.domains; - boolean[] isTuples = this.params.isTuples; - int plen = this.params.length(); - - if (plen == 1) { - if (!domains[0].member(args)) { - Assert.fail("In applying the function\n" + Value.ppr(this.toString()) + - ",\nthe first argument is:\n" + Value.ppr(args.toString()) + - "\nwhich is not in its domain.\n"); - } - if (isTuples[0]) { - FormalParamNode[] ids = formals[0]; - TupleValue argVal = TupleValue.convert(args); - if (argVal == null) { - Assert.fail("In applying the function\n" + Value.ppr(this.toString()) + - ",\nthe first argument is:\n" + Value.ppr(args.toString()) + - "\nwhich does not match its formal parameter.\n"); - } - if (argVal.size() != ids.length) return null; - Value[] elems = argVal.elems; - for (int i = 0; i < ids.length; i++) { - c1 = c1.cons(ids[i], elems[i]); - } - } - else { - c1 = c1.cons(formals[0][0], args); - } - } - else { - TupleValue tv = TupleValue.convert(args); - if (tv == null) { - Assert.fail("In applying the function\n" + Value.ppr(this.toString()) + - ",\nthe argument list is:\n" + Value.ppr(args.toString()) + - "\nwhich does not match its formal parameter.\n"); - } - Value[] elems = tv.elems; - int argn = 0; - for (int i = 0; i < formals.length; i++) { - FormalParamNode[] ids = formals[i]; - Value domain = domains[i]; - if (isTuples[i]) { - if (!domain.member(elems[argn])) { - Assert.fail("In applying the function\n" + Value.ppr(this.toString()) + - ",\nthe argument number " + (argn+1) + " is:\n" + - Value.ppr(elems[argn].toString()) + - "\nwhich is not in its domain.\n"); - } - TupleValue tv1 = TupleValue.convert(elems[argn++]); - if (tv1 == null || tv1.size() != ids.length) { - Assert.fail("In applying the function\n" + Value.ppr(this.toString()) + - ",\nthe argument number " + argn + " is:\n" + - Value.ppr(elems[argn-1].toString()) + - "\nwhich does not match its formal parameter.\n"); - } - Value[] avals = tv1.elems; - for (int j = 0; j < ids.length; j++) { - c1 = c1.cons(ids[j], avals[j]); - } - } - else { - for (int j = 0; j < ids.length; j++) { - if (!domain.member(elems[argn])) { - Assert.fail("In applying the function\n" + Value.ppr(this.toString()) + - ",\nthe argument number " + (argn+1) + " is:\n" + - Value.ppr(elems[argn].toString()) + "\nwhich is not in its domain.\n"); - } - c1 = c1.cons(ids[j], elems[argn++]); - } - } - } - } - res = this.tool.eval(this.body, c1, this.state, this.pstate, control); - } - - // Finally, apply the matching excepts on the result. - if (num == 0) return res; - ValueExcept[] excepts2 = new ValueExcept[num]; - for (int i = 0; i < num; i++) { - excepts2[num-1-i] = excepts1[i]; - } - return res.takeExcept(excepts2); - } - - /* This one does not seem to be needed anymore. */ - public final Value apply(Value[] args, int control) throws EvalException { - return this.apply(new TupleValue(args), control); - } - - public final Value select(Value arg) { - if (this.fcnRcd != null) { - return this.fcnRcd.select(arg); - } - - // First, find all the excepts that match arg. - Value res = null; - int num = 0; - ValueExcept[] excepts1 = null; - if (this.excepts != null) { - int exlen = this.excepts.length; - for (int i = exlen-1; i >= 0; i--) { - ValueExcept ex = this.excepts[i]; - Value exArg = ex.current(); - boolean inExcept = exArg.equals(arg); - if (inExcept) { - if (ex.isLast()) { res = ex.value; break; } - if (excepts1 == null) excepts1 = new ValueExcept[exlen]; - excepts1[num++] = new ValueExcept(ex, ex.idx+1); - } - } - } - - // Second, evaluate the function application. - if (res == null) { - Context c1 = this.con; - FormalParamNode[][] formals = this.params.formals; - Value[] domains = this.params.domains; - boolean[] isTuples = this.params.isTuples; - int plen = this.params.length(); - - if (plen == 1) { - if (!domains[0].member(arg)) return null; - if (isTuples[0]) { - FormalParamNode[] ids = formals[0]; - TupleValue argVal = TupleValue.convert(arg); - /* - * SZA: Changed from argVal.toString() to arg.toString() to prevent a NullPointerException - */ - if (argVal == null) { - Assert.fail("In applying the function\n" + Value.ppr(this.toString()) + - ",\nthe first argument is:\n" + Value.ppr(arg.toString()) + - "\nwhich does not match its formal parameter.\n"); - } - if (argVal.size() != ids.length) return null; - Value[] elems = argVal.elems; - for (int i = 0; i < ids.length; i++) { - c1 = c1.cons(ids[i], elems[i]); - } - } - else { - c1 = c1.cons(formals[0][0], arg); - } - } - else { - TupleValue tv = TupleValue.convert(arg); - if (tv == null) { - Assert.fail("In applying the function\n" + Value.ppr(this.toString()) + - ",\nthe argument list is:\n" + Value.ppr(arg.toString()) + - "\nwhich does not match its formal parameter.\n"); - } - Value[] elems = tv.elems; - int argn = 0; - for (int i = 0; i < formals.length; i++) { - FormalParamNode[] ids = formals[i]; - Value domain = domains[i]; - if (isTuples[i]) { - if (!domain.member(elems[argn])) return null; - TupleValue tv1 = TupleValue.convert(elems[argn++]); - if (tv1 == null) { - Assert.fail("In applying the function\n" + Value.ppr(this.toString()) + - ",\nthe argument number " + argn + " is:\n" + - Value.ppr(elems[argn-1].toString()) + - "\nwhich does not match its formal parameter.\n"); - } - if (tv1.size() != ids.length) return null; - Value[] avals = tv1.elems; - for (int j = 0; j < ids.length; j++) { - c1 = c1.cons(ids[j], avals[j]); - } - } - else { - for (int j = 0; j < ids.length; j++) { - if (!domain.member(elems[argn])) return null; - c1 = c1.cons(ids[j], elems[argn++]); - } - } - } - } - res = this.tool.eval(this.body, c1, this.state, this.pstate, this.control); - } - - // Finally, apply the matching excepts on the result. - if (num == 0) return res; - ValueExcept[] excepts2 = new ValueExcept[num]; - for (int i = 0; i < num; i++) { - excepts2[num-1-i] = excepts1[i]; - } - return res.takeExcept(excepts2); - } - - /* This method returns a new function value by taking except. */ - public final Value takeExcept(ValueExcept ex) { - if (ex.idx >= ex.path.length) return ex.value; - - if (this.fcnRcd != null) { - return this.fcnRcd.takeExcept(ex); - } - FcnLambdaValue fcn = new FcnLambdaValue(this); - if (this.excepts == null) { - fcn.excepts = new ValueExcept[1]; - fcn.excepts[0] = ex; - } - else { - int exlen = this.excepts.length; - fcn.excepts = new ValueExcept[exlen+1]; - for (int i = 0; i < exlen; i++) { - fcn.excepts[i] = this.excepts[i]; - } - fcn.excepts[exlen] = ex; - } - return fcn; - } - - /* This method returns a new function value by taking excepts. */ - public final Value takeExcept(ValueExcept[] exs) { - if (this.fcnRcd != null) { - return this.fcnRcd.takeExcept(exs); - } - FcnLambdaValue fcn = new FcnLambdaValue(this); - int exslen = exs.length; - if (exslen != 0) { - int i = 0; - for (i = exs.length-1; i >= 0; i--) { - if (exs[i].idx >= exs[i].path.length) break; - } - if (i >= 0) { - int xlen = exslen-i-1; - fcn.excepts = new ValueExcept[xlen]; - System.arraycopy(exs, i+1, fcn.excepts, 0, xlen); - } - else if (this.excepts == null) { - fcn.excepts = new ValueExcept[exslen]; - System.arraycopy(exs, 0, fcn.excepts, 0, exslen); - } - else { - int len = this.excepts.length; - fcn.excepts = new ValueExcept[len + exslen]; - System.arraycopy(this.excepts, 0, fcn.excepts, 0, len); - System.arraycopy(exs, 0, fcn.excepts, len, exslen); - } - } - return fcn; - } - - public final Value getDomain() { - if (this.fcnRcd != null) { - return this.fcnRcd.getDomain(); - } - int len = this.params.length(); - if (len == 1) { - return this.params.domains[0]; - } - Value[] sets = new Value[len]; - int dlen = this.params.domains.length; - boolean[] isTuples = this.params.isTuples; - int idx = 0; - for (int i = 0; i < dlen; i++) { - FormalParamNode[] formal = this.params.formals[i]; - Value domain = this.params.domains[i]; - if (isTuples[i]) { - sets[idx++] = domain; - } - else { - for (int j = 0; j < formal.length; j++) { - sets[idx++] = domain; - } - } - } - return new SetOfTuplesValue(sets); - } - - public final int size() { - if (this.fcnRcd == null) { - return this.params.size(); - } - return this.fcnRcd.size(); - } - - public final boolean isDefined() { return true; } - - public final Value deepCopy() { - FcnLambdaValue fcn = new FcnLambdaValue(this); - // A bug occured when printing a function whose domain is a Cartesian product because this.fcnRcd - // is null at this point. On 5 Mar 2012, LL wrapped the following null test around the assignment. - if (this.fcnRcd != null) { - fcn.fcnRcd = (FcnRcdValue)this.fcnRcd.deepCopy(); - } - return fcn; - } - - public final boolean assignable(Value val) { - return (val instanceof FcnLambdaValue); - } - - private void readObject(ObjectInputStream ois) - throws IOException, ClassNotFoundException { - this.fcnRcd = (FcnRcdValue)ois.readObject(); - } - - private void writeObject(ObjectOutputStream oos) throws IOException { - FcnRcdValue res = this.toFcnRcd(); - oos.writeObject(res); - } - - public final boolean isNormalized() { - if (this.fcnRcd == null) { - return false; - } - return this.fcnRcd.isNormalized(); - } - - public final void normalize() { - if (this.fcnRcd != null) { - this.fcnRcd.normalize(); - } - } - - public final FcnRcdValue toFcnRcd() { - if (this.fcnRcd == null) { - int sz = this.params.size(); - FormalParamNode[][] formals = this.params.formals; - boolean[] isTuples = this.params.isTuples; - - Value[] domain = new Value[sz]; - Value[] values = new Value[sz]; - int idx = 0; - ValueEnumeration Enum = this.params.elements(); - Value arg; - if (this.params.length() == 1) { - while ((arg = Enum.nextElement()) != null) { - domain[idx] = arg; - Context c1 = this.con; - if (isTuples[0]) { - FormalParamNode[] ids = formals[0]; - Value[] avals = ((TupleValue)arg).elems; - for (int j = 0; j < ids.length; j++) { - c1 = c1.cons(ids[j], avals[j]); - } - } - else { - c1 = c1.cons(formals[0][0], arg); - } - values[idx++] = this.tool.eval(this.body, c1, this.state, this.pstate, this.control); - } - } - else { - while ((arg = Enum.nextElement()) != null) { - domain[idx] = arg; - Value[] argList = ((TupleValue)arg).elems; - int argn = 0; - Context c1 = this.con; - for (int i = 0; i < formals.length; i++) { - FormalParamNode[] ids = formals[i]; - if (isTuples[i]) { - Value[] avals = ((TupleValue)argList[argn++]).elems; - for (int j = 0; j < ids.length; j++) { - c1 = c1.cons(ids[j], avals[j]); - } - } - else { - for (int j = 0; j < ids.length; j++) { - c1 = c1.cons(ids[j], argList[argn++]); - } - } - } - values[idx++] = this.tool.eval(this.body, c1, this.state, this.pstate, this.control); - } - } - this.fcnRcd = new FcnRcdValue(domain, values, false); - if (this.excepts != null) { - this.fcnRcd = (FcnRcdValue)fcnRcd.takeExcept(this.excepts); - } - } - return this.fcnRcd; - } - - /* The fingerprint methods. */ - public final long fingerPrint(long fp) { - FcnRcdValue fcn = FcnRcdValue.convert(this); - return fcn.fingerPrint(fp); - } - - public final Value permute(MVPerm perm) { - FcnRcdValue fcn = FcnRcdValue.convert(this); - return fcn.permute(perm); - } - - /* The string representation of this function. */ - public final StringBuffer toString(StringBuffer sb, int offset) { - if (expand || this.params == null) { - try { - Value val = FcnRcdValue.convert(this); - return val.toString(sb, offset); - } - catch (Throwable e) { /*SKIP*/ } - } - sb.append("[" + this.params.toString()); - sb.append(" |-> <expression " + this.body + ">]"); - return sb; - } - -} diff --git a/tlatools/src/tlc2/value/FcnRcdValue.java b/tlatools/src/tlc2/value/FcnRcdValue.java deleted file mode 100644 index f47940cd5531193837de4ec085e21ad7ba1af540..0000000000000000000000000000000000000000 --- a/tlatools/src/tlc2/value/FcnRcdValue.java +++ /dev/null @@ -1,627 +0,0 @@ -// Copyright (c) 2003 Compaq Corporation. All rights reserved. -// Portions Copyright (c) 2003 Microsoft Corporation. All rights reserved. -// Last modified on Sat 23 February 2008 at 10:07:06 PST by lamport -// modified on Fri Aug 10 15:07:25 PDT 2001 by yuanyu - -package tlc2.value; - -import java.util.Arrays; - -import tlc2.tool.EvalControl; -import tlc2.util.FP64; -import util.Assert; - -public class FcnRcdValue extends Value implements Applicable { - public Value[] domain; - public IntervalValue intv; - public Value[] values; - private boolean isNorm; - private int[] indexTbl; // speed up function application - - /* Constructor */ - public FcnRcdValue(Value[] domain, Value[] values, boolean isNorm) { - this.domain = domain; - this.values = values; - this.intv = null; - this.isNorm = isNorm; - this.indexTbl = null; - } - - public FcnRcdValue(IntervalValue intv, Value[] values) { - this.intv = intv; - this.values = values; - this.domain = null; - this.isNorm = true; - this.indexTbl = null; - } - - private FcnRcdValue(FcnRcdValue fcn, Value[] values) { - this.domain = fcn.domain; - this.intv = fcn.intv; - this.values = values; - this.isNorm = fcn.isNorm; - this.indexTbl = fcn.indexTbl; - } - - public final byte getKind() { return FCNRCDVALUE; } - - /* We create an index only when the domain is not very small. */ - private final void createIndex() { - if (this.domain != null && this.domain.length > 10) { - int len = this.domain.length * 2 + 1; - - int[] tbl = new int[len]; - Arrays.fill(tbl, -1); - - synchronized(this) { - for (int i = 0; i < this.domain.length; i++) { - int loc = (this.domain[i].hashCode() & 0x7FFFFFFF) % len; - while (tbl[loc] != -1) { - loc = (loc + 1) % len; - } - tbl[loc] = i; - } - } - - synchronized(this) { this.indexTbl = tbl; } - } - } - - private final int lookupIndex(Value arg) { - int len = this.indexTbl.length; - int loc = (arg.hashCode() & 0x7FFFFFFF) % len; - while (true) { - int idx = this.indexTbl[loc]; - if (idx == -1) { - Assert.fail("Attempted to apply function:\n" + ppr(this.toString()) + - "\nto argument " + ppr(arg.toString()) + - ", which is not in the domain of the function."); - } - if (this.domain[idx].equals(arg)) { - return idx; - } - loc = (loc + 1) % len; - } - } - - public final int compareTo(Object obj) { - FcnRcdValue fcn = convert(obj); - if (fcn == null) { - if (obj instanceof ModelValue) return 1; - Assert.fail("Attempted to compare the function " + ppr(this.toString()) + - " with the value:\n" + ppr(obj.toString())); - } - this.normalize(); - fcn.normalize(); - - int result = this.values.length - fcn.values.length; - if (result != 0) return result; - - if (this.intv != null) { - if (fcn.intv != null) { - result = this.intv.low - fcn.intv.low; - if (result != 0) return result; - for (int i = 0; i < this.values.length; i++) { - result = this.values[i].compareTo(fcn.values[i]); - if (result != 0) return result; - } - } - else { - for (int i = 0; i < fcn.domain.length; i++) { - Value dElem = fcn.domain[i]; - if (!(dElem instanceof IntValue)) { - Assert.fail("Attempted to compare integer with non-integer:\n" + - ppr(dElem.toString()) + "."); - } - result = this.intv.low + i - ((IntValue)dElem).val; - if (result != 0) return result; - result = this.values[i].compareTo(fcn.values[i]); - if (result != 0) return result; - } - } - } - else { - if (fcn.intv != null) { - for (int i = 0; i < this.domain.length; i++) { - Value dElem = this.domain[i]; - if (!(dElem instanceof IntValue)) { - Assert.fail("Attempted to compare integer with non-integer\n" + - ppr(dElem.toString()) + "."); - } - result = ((IntValue)dElem).val - (fcn.intv.low + i); - if (result != 0) return result; - result = this.values[i].compareTo(fcn.values[i]); - if (result != 0) return result; - } - } - else { - for (int i = 0; i < this.domain.length; i++) { - result = this.domain[i].compareTo(fcn.domain[i]); - if (result != 0) return result; - result = this.values[i].compareTo(fcn.values[i]); - if (result != 0) return result; - } - } - } - return 0; - } - - public final boolean equals(Object obj) { - FcnRcdValue fcn = convert(obj); - if (fcn == null) { - if (obj instanceof ModelValue) - return ((ModelValue) obj).modelValueEquals(this) ; - Assert.fail("Attempted to check equality of the function " + ppr(this.toString()) + - " with the value:\n" + ppr(obj.toString())); - } - this.normalize(); - fcn.normalize(); - - if (this.intv != null) { - if (fcn.intv != null) { - if (!this.intv.equals(fcn.intv)) return false; - for (int i = 0; i < this.values.length; i++) { - if (!this.values[i].equals(fcn.values[i])) - return false; - } - } - else { - if (fcn.domain.length != this.intv.size()) return false; - for (int i = 0; i < fcn.domain.length; i++) { - Value dElem = fcn.domain[i]; - if (!(dElem instanceof IntValue)) { - Assert.fail("Attempted to compare an integer with non-integer:\n" + - ppr(dElem.toString()) + "."); - } - if (((IntValue)dElem).val != (this.intv.low + i) || - !this.values[i].equals(fcn.values[i])) { - return false; - } - } - } - } - else { - if (this.values.length != fcn.values.length) return false; - if (fcn.intv != null) { - for (int i = 0; i < this.domain.length; i++) { - Value dElem = this.domain[i]; - if (!(dElem instanceof IntValue)) { - Assert.fail("Attempted to compare an integer with non-integer:\n" + - ppr(dElem.toString()) + "."); - } - if (((IntValue)dElem).val != (fcn.intv.low + i) || - !this.values[i].equals(fcn.values[i])) { - return false; - } - } - } - else { - for (int i = 0; i < this.domain.length; i++) { - if (!this.domain[i].equals(fcn.domain[i]) || - !this.values[i].equals(fcn.values[i])) { - return false; - } - } - } - } - return true; - } - - public final boolean member(Value elem) { - Assert.fail("Attempted to check if the value:\n" + ppr(elem.toString()) + - "\nis an element of the function " + ppr(this.toString())); - return false; // make compiler happy - } - - public final boolean isFinite() { return true; } - - public final Value apply(Value arg, int control) { - Value result = this.select(arg); - if (result == null) { - Assert.fail("Attempted to apply function:\n" + ppr(this.toString()) + - "\nto argument " + ppr(arg.toString()) + ", which is" + - " not in the domain of the function."); - } - return result; - } - - /* This one does not seem to be needed anymore. */ - public final Value apply(Value[] args, int control) { - return this.apply(new TupleValue(args), EvalControl.Clear); - } - - public final Value select(Value arg) { - if (this.intv != null) { - // domain is represented as an integer interval: - if (!(arg instanceof IntValue)) { - Assert.fail("Attempted to apply function with integer domain to" + - " the non-integer argument " + ppr(arg.toString())); - } - int idx = ((IntValue)arg).val; - if ((idx >= this.intv.low) && (idx <= this.intv.high)) { - return this.values[idx - this.intv.low]; - } - } - else { - // domain is represented as an array of values: - if (this.indexTbl != null) { - return this.values[this.lookupIndex(arg)]; - } - if (this.isNorm) this.createIndex(); - - if (this.indexTbl != null) { - return this.values[this.lookupIndex(arg)]; - } - else { - int len = this.domain.length; - for (int i = 0; i < len; i++) { - if (this.domain[i].equals(arg)) { - return this.values[i]; - } - } - } - } - return null; - } - - public final boolean assign(Value[] args, Value val) { - if (this.intv != null) { - // domain is represented as an integer interval: - if (args.length != 1) { - Assert.fail("Wrong number of function arguments."); - } - if (args[0] instanceof IntValue) { - int idx = ((IntValue)args[0]).val; - if ((idx >= this.intv.low) && (idx <= this.intv.high)) { - int vIdx = idx - this.intv.low; - if (this.values[vIdx] == ValUndef || - this.values[vIdx].equals(val)) { - this.values[vIdx] = val; - return true; - } - return false; - } - } - } - else { - // domain is represented as an array of values: - Value argv = new TupleValue(args); - int len = this.domain.length; - for (int i = 0; i < len; i++) { - if (this.domain[i].equals(argv)) { - if (this.values[i] == ValUndef || - this.values[i].equals(val)) { - this.values[i] = val; - return true; - } - return false; - } - } - } - Assert.fail("Function initialization out of domain."); - return false; // make compiler happy - } - - public final Value takeExcept(ValueExcept ex) { - if (ex.idx >= ex.path.length) return ex.value; - - int flen = this.values.length; - Value[] newValues = new Value[flen]; - for (int i = 0; i < flen; i++) { - newValues[i] = this.values[i]; - } - Value arg = ex.path[ex.idx]; - - if (this.intv != null) { - // domain is represented as an integer interval: - if (arg instanceof IntValue) { - int idx = ((IntValue)arg).val; - if ((idx >= this.intv.low) && (idx <= this.intv.high)) { - int vidx = idx - this.intv.low; - ex.idx++; - newValues[idx] = this.values[vidx].takeExcept(ex); - } - return new FcnRcdValue(this.intv, newValues); - } - } - else { - // domain is represented as an array of values: - for (int i = 0; i < flen; i++) { - if (arg.equals(this.domain[i])) { - ex.idx++; - newValues[i] = newValues[i].takeExcept(ex); - Value[] newDomain = this.domain; - if (!this.isNorm) { - newDomain = new Value[flen]; - for (int j = 0; j < flen; j++) { - newDomain[j] = this.domain[j]; - } - } - return new FcnRcdValue(newDomain, newValues, this.isNorm); - } - } - } - return this; - } - - public final Value takeExcept(ValueExcept[] exs) { - Value res = this; - for (int i = 0; i < exs.length; i++) { - res = res.takeExcept(exs[i]); - } - return res; - } - - public final Value getDomain() { - if (this.intv != null) { - return this.intv; - } - this.normalize(); - return new SetEnumValue(this.domain, true); - } - - public final int size() { - this.normalize(); - return this.values.length; - } - - /* - * This method converts a value to a function value. It returns - * null if the conversion fails. - */ - public static final FcnRcdValue convert(Object val) { - switch (((Value)val).getKind()) { - case FCNRCDVALUE: - { - return (FcnRcdValue)val; - } - case FCNLAMBDAVALUE: - { - return ((FcnLambdaValue)val).toFcnRcd(); - } - case RECORDVALUE: - { - RecordValue rcd = (RecordValue)val; - rcd.normalize(); - Value[] dom = new Value[rcd.names.length]; - for (int i = 0; i < rcd.names.length; i++) { - dom[i] = new StringValue(rcd.names[i]); - } - return new FcnRcdValue(dom, rcd.values, rcd.isNormalized()); - } - case TUPLEVALUE: - { - Value[] elems = ((TupleValue)val).elems; - IntervalValue intv = new IntervalValue(1, elems.length); - return new FcnRcdValue(intv, elems); - } - default: - // return null if not convertable - return null; - } - } - - /* Return true iff this function is in normal form. */ - public final boolean isNormalized() { return this.isNorm; } - - /* This method normalizes (destructively) this function. */ - public final void normalize() { - if (!this.isNorm) { - // Assert.check(this.domain != null) - int dlen = this.domain.length; - for (int i = 1; i < dlen; i++) { - int cmp = this.domain[0].compareTo(this.domain[i]); - if (cmp == 0) { - Assert.fail("The value\n" + this.domain[i] + - "\noccurs multiple times in the function domain."); - } - else if (cmp > 0) { - Value tv = this.domain[0]; - this.domain[0] = this.domain[i]; - this.domain[i] = tv; - tv = this.values[0]; - this.values[0] = this.values[i]; - this.values[i] = tv; - } - } - for (int i = 2; i < dlen; i++) { - Value d = this.domain[i]; - Value v = this.values[i]; - int j = i; - int cmp; - while ((cmp = d.compareTo(this.domain[j-1])) < 0) { - this.domain[j] = this.domain[j-1]; - this.values[j] = this.values[j-1]; - j--; - } - if (cmp == 0) { - Assert.fail("The value\n" + this.domain[i] + - "\noccurs multiple times in the function domain."); - } - this.domain[j] = d; - this.values[j] = v; - } - this.isNorm = true; - } - } - - public final boolean isDefined() { - boolean defined = true; - if (this.intv == null) { - for (int i = 0; i < this.values.length; i++) { - defined = defined && this.domain[i].isDefined(); - } - } - for (int i = 0; i < this.values.length; i++) { - defined = defined && this.values[i].isDefined(); - } - return defined; - } - - public final Value deepCopy() { - Value[] vals = new Value[this.values.length]; - for (int i = 0; i < vals.length; i++) { - vals[i] = this.values[i].deepCopy(); - } - return new FcnRcdValue(this, vals); - } - - public final boolean assignable(Value val) { - boolean canAssign = ((val instanceof FcnRcdValue) && - this.values.length == ((FcnRcdValue)val).values.length); - if (!canAssign) return false; - FcnRcdValue fcn = (FcnRcdValue)val; - for (int i = 0; i < this.values.length; i++) { - canAssign = (canAssign && - this.domain[i].equals(fcn.domain[i]) && - this.values[i].assignable(fcn.values[i])); - } - return canAssign; - } - - /* The fingerprint method. */ - public final long fingerPrint(long fp) { - this.normalize(); - int flen = this.values.length; - fp = FP64.Extend(fp, FCNRCDVALUE); - fp = FP64.Extend(fp, flen); - if (this.intv == null) { - for (int i = 0; i < flen; i++) { - fp = this.domain[i].fingerPrint(fp); - fp = this.values[i].fingerPrint(fp); - } - } - else { - for (int i = 0; i < flen; i++) { - fp = FP64.Extend(fp, INTVALUE); - fp = FP64.Extend(fp, i + this.intv.low); - fp = this.values[i].fingerPrint(fp); - } - } - return fp; - } - - public final Value permute(MVPerm perm) { - this.normalize(); - int flen = this.domain.length; - Value[] vals = new Value[flen]; - - boolean vchanged = false; - for (int i = 0; i < flen; i++) { - vals[i] = this.values[i].permute(perm); - vchanged = vchanged || (vals[i] != this.values[i]); - } - - if (this.intv == null) { - Value[] dom = new Value[flen]; - boolean dchanged = false; - for (int i = 0; i < flen; i++) { - dom[i] = this.domain[i].permute(perm); - dchanged = dchanged || (dom[i] != this.domain[i]); - } - - if (dchanged) { - return new FcnRcdValue(dom, vals, false); - } - else if (vchanged) { - return new FcnRcdValue(this.domain, vals, true); - } - } - else { - if (vchanged) { - return new FcnRcdValue(this.intv, vals); - } - } - return this; - } - - private static final boolean isName(String name) { - int len = name.length(); - boolean hasLetter = false; - - for (int idx = 0; idx < len; idx++) { - char ch = name.charAt(idx); - if (ch == '_') continue; - if (!Character.isLetterOrDigit(ch)) return false; - hasLetter = hasLetter || Character.isLetter(ch); - } - - return hasLetter && (len < 4 || (!name.startsWith("WF_") && !name.startsWith("SF_"))); - } - - private final boolean isRcd() { - if (this.intv != null) return false; - for (int i = 0; i < this.domain.length; i++) { - Value dval = this.domain[i]; - boolean isName = ((dval instanceof StringValue) && - isName(((StringValue)dval).val.toString())); - if (!isName) return false; - } - return true; - } - - private final boolean isTuple() { - if (this.intv != null) { - return (this.intv.low == 1); - } - for (int i = 0; i < this.domain.length; i++) { - if (!(this.domain[i] instanceof IntValue)) { - return false; - } - } - this.normalize(); - for (int i = 0; i < this.domain.length; i++) { - if (((IntValue)this.domain[i]).val != (i+1)) { - return false; - } - } - return true; - } - - /* The string representation of the value. */ - public final StringBuffer toString(StringBuffer sb, int offset) { - int len = this.values.length; - if (len == 0) { - sb.append("<< >>"); - } - else if (this.isRcd()) { - sb.append("["); - sb.append(((StringValue)this.domain[0]).val + " |-> "); - sb = this.values[0].toString(sb, offset); - - for (int i = 1; i < len; i++) { - sb.append(", "); - sb.append(((StringValue)this.domain[i]).val + " |-> "); - sb = this.values[i].toString(sb, offset); - } - sb.append("]"); - } - else if (this.isTuple()) { - // It is actually a sequence: - sb = sb.append("<<"); - sb = this.values[0].toString(sb, offset); - - for (int i = 1; i < len; i++) { - sb.append(", "); - sb = this.values[i].toString(sb, offset); - } - sb.append(">>"); - } - else { - sb = sb.append("("); - sb = this.domain[0].toString(sb, offset); - sb.append(" :> "); - sb = this.values[0].toString(sb, offset); - - for (int i = 1; i < len; i++) { - sb.append(" @@ "); - sb = this.domain[i].toString(sb, offset); - sb.append(" :> "); - sb = this.values[i].toString(sb, offset); - } - sb.append(")"); - } - return sb; - } - -} diff --git a/tlatools/src/tlc2/value/IBoolValue.java b/tlatools/src/tlc2/value/IBoolValue.java new file mode 100644 index 0000000000000000000000000000000000000000..552782b693c3cd6505cafe1347c33345bf17c786 --- /dev/null +++ b/tlatools/src/tlc2/value/IBoolValue.java @@ -0,0 +1,31 @@ +/******************************************************************************* + * Copyright (c) 2019 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.value; + +public interface IBoolValue extends IValue { + + boolean getVal(); +} \ No newline at end of file diff --git a/tlatools/src/tlc2/value/IFcnLambdaValue.java b/tlatools/src/tlc2/value/IFcnLambdaValue.java new file mode 100644 index 0000000000000000000000000000000000000000..b91d82fef09e099887269b6356d5cc9dfec9864a --- /dev/null +++ b/tlatools/src/tlc2/value/IFcnLambdaValue.java @@ -0,0 +1,44 @@ +/******************************************************************************* + * Copyright (c) 2019 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ + +package tlc2.value; + +import tla2sany.semantic.SemanticNode; +import tlc2.util.Context; + +public interface IFcnLambdaValue { + + SemanticNode getBody(); + + IFcnRcdValue getRcd(); + + IFcnParams getParams(); + + Context getCon(); + + boolean hasRcd(); + +} diff --git a/tlatools/src/tlc2/value/IFcnParams.java b/tlatools/src/tlc2/value/IFcnParams.java new file mode 100644 index 0000000000000000000000000000000000000000..40082374a6c1e904da86877165fe615bda73895a --- /dev/null +++ b/tlatools/src/tlc2/value/IFcnParams.java @@ -0,0 +1,40 @@ +/******************************************************************************* + * Copyright (c) 2019 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.value; + +import tla2sany.semantic.FormalParamNode; + +public interface IFcnParams { + + int length(); + + FormalParamNode[][] getFormals(); + + IValue[] getDomains(); + + boolean[] isTuples(); + +} diff --git a/tlatools/src/tlc2/value/IFcnRcdValue.java b/tlatools/src/tlc2/value/IFcnRcdValue.java new file mode 100644 index 0000000000000000000000000000000000000000..d2c3b3bdbfe01b16a05daa2b01d0a1d1f0dc2ecd --- /dev/null +++ b/tlatools/src/tlc2/value/IFcnRcdValue.java @@ -0,0 +1,30 @@ +/******************************************************************************* + * Copyright (c) 2019 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.value; + +public interface IFcnRcdValue { + +} diff --git a/tlatools/src/tlc2/value/IMVPerm.java b/tlatools/src/tlc2/value/IMVPerm.java new file mode 100644 index 0000000000000000000000000000000000000000..2682c3c1f0e0f972b14a189f6fab121e3e8bfaca --- /dev/null +++ b/tlatools/src/tlc2/value/IMVPerm.java @@ -0,0 +1,38 @@ +/******************************************************************************* + * Copyright (c) 2019 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.value; + +public interface IMVPerm { + + IValue get(IValue value); + + void put(IModelValue dval, IModelValue rval); + + int size(); + + IMVPerm compose(IMVPerm elementAt); + +} diff --git a/tlatools/src/tlc2/value/IModelValue.java b/tlatools/src/tlc2/value/IModelValue.java new file mode 100644 index 0000000000000000000000000000000000000000..61d69866eea38a77654936ae087a75342186ef43 --- /dev/null +++ b/tlatools/src/tlc2/value/IModelValue.java @@ -0,0 +1,30 @@ +/******************************************************************************* + * Copyright (c) 2019 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.value; + +public interface IModelValue { + +} diff --git a/tlatools/src/tlc2/value/ITupleValue.java b/tlatools/src/tlc2/value/ITupleValue.java new file mode 100644 index 0000000000000000000000000000000000000000..6525574b3c5448aa4a7d2d26250a7675be0148e8 --- /dev/null +++ b/tlatools/src/tlc2/value/ITupleValue.java @@ -0,0 +1,36 @@ +/******************************************************************************* + * Copyright (c) 2019 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.value; + +public interface ITupleValue extends IValue { + + IValue getElem(int i); + + IValue[] getElems(); + + @Override + int size(); +} \ No newline at end of file diff --git a/tlatools/src/tlc2/value/IValue.java b/tlatools/src/tlc2/value/IValue.java new file mode 100644 index 0000000000000000000000000000000000000000..30e7a4addfc2052935bc77e8b80301130db20f94 --- /dev/null +++ b/tlatools/src/tlc2/value/IValue.java @@ -0,0 +1,139 @@ +/******************************************************************************* + * Copyright (c) 2019 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.value; + +import java.io.IOException; + +import tla2sany.semantic.SemanticNode; +import tlc2.tool.coverage.CostModel; +import tlc2.value.impl.BoolValue; +import tlc2.value.impl.IntValue; +import tlc2.value.impl.ModelValue; +import tlc2.value.impl.StringValue; + +public interface IValue extends Comparable<Object> { + + /* This method compares this with val. */ + @Override + int compareTo(Object val); + + void write(IValueOutputStream vos) throws IOException; + + IValue setCostModel(CostModel cm); + + CostModel getCostModel(); + + void setSource(SemanticNode semanticNode); + + SemanticNode getSource(); + + boolean hasSource(); + + /* MAK 09/17/2019: Introduced to guarantee that Value instances are + * fully initialized when created by the SpecProcessor (as opposed + * to by workers during state space exploration). + */ + /** + * Fully initialize this instance which includes: + * - deep normalization + * - conversion and caching iff defined by the sub-class + * + * No further mutation of this instance should be required + * for any evaluation whatsoever. + * + * Afterwards, isNormalized below returns true (it does not + * return true for all sub-classes when only deepNormalized + * is executed)! + * + * see comment in UnionValue#deepNormalize too + */ + default IValue initialize() { + this.deepNormalize(); + // Execute fingerprint code path to internally trigger convertAndCache iff + // defined (0L parameter is not relevant) + this.fingerPrint(0L); + return this; + } + + /** + * This method normalizes (destructively) the representation of + * the value. It is essential for equality comparison. + */ + boolean isNormalized(); + + /* Fully normalize this (composite) value. */ + void deepNormalize(); + + /* This method returns the fingerprint of this value. */ + long fingerPrint(long fp); + + /** + * This method returns the value permuted by the permutation. It + * returns this if nothing is permuted. + */ + IValue permute(IMVPerm perm); + + /* This method returns true iff the value is finite. */ + boolean isFinite(); + + /* This method returns the size of the value. */ + int size(); + + /* This method returns true iff the value is fully defined. */ + boolean isDefined(); + + /* This method makes a real deep copy of this. */ + IValue deepCopy(); + + /** + * This abstract method returns a string representation of this + * value. Each subclass must provide its own implementation. + */ + StringBuffer toString(StringBuffer sb, int offset, boolean swallow); + + /* The string representation of this value */ + String toString(); + + String toString(String delim); + + String toUnquotedString(); + + default boolean isAtom() { + if (this instanceof ModelValue || this instanceof IntValue || this instanceof StringValue + || this instanceof BoolValue) { + return true; + } + return false; + } + + /** + * @return true if a value mutates as part of normalization or fingerprinting. + */ + default boolean mutates() { + return true; + } + +} \ No newline at end of file diff --git a/tlatools/src/tlc2/value/IValueInputStream.java b/tlatools/src/tlc2/value/IValueInputStream.java new file mode 100644 index 0000000000000000000000000000000000000000..3c1413f967bc0700713b2025f32ea8d65e35ec36 --- /dev/null +++ b/tlatools/src/tlc2/value/IValueInputStream.java @@ -0,0 +1,61 @@ +/******************************************************************************* + * Copyright (c) 2019 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.value; + +import java.io.EOFException; +import java.io.IOException; + +import util.IDataInputStream; +import util.UniqueString; + +public interface IValueInputStream { + + IValue read() throws IOException; + + int readShort() throws IOException; + + int readInt() throws IOException; + + long readLong() throws IOException; + + void close() throws IOException; + + int readNat() throws IOException; + + short readShortNat() throws IOException; + + long readLongNat() throws IOException; + + byte readByte() throws EOFException, IOException; + + void assign(Object obj, int idx); + + int getIndex(); + + IDataInputStream getInputStream(); + + UniqueString getValue(int idx); +} \ No newline at end of file diff --git a/tlatools/src/tlc2/value/IValueOutputStream.java b/tlatools/src/tlc2/value/IValueOutputStream.java new file mode 100644 index 0000000000000000000000000000000000000000..cc5cc51cf1f0713ff4101c21b3fe43da817165c6 --- /dev/null +++ b/tlatools/src/tlc2/value/IValueOutputStream.java @@ -0,0 +1,86 @@ +/******************************************************************************* + * Copyright (c) 2019 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.value; + +import java.io.IOException; + +import util.IDataOutputStream; + +public interface IValueOutputStream { + + void writeShort(short x) throws IOException; + + void writeInt(int x) throws IOException; + + void writeLong(long x) throws IOException; + + void close() throws IOException; + + /* Precondition: x is a non-negative short. */ + void writeShortNat(short x) throws IOException; + + /* Precondition: x is a non-negative int. */ + void writeNat(int x) throws IOException; + + /* Precondition: x is a non-negative long. */ + void writeLongNat(long x) throws IOException; + + void writeByte(byte b) throws IOException; + + void writeBoolean(boolean b) throws IOException; + + IDataOutputStream getOutputStream(); + + /** + * Check if another TLCState - which is currently also being serialized to the + * same storage (i.e. disk file) - has/contains an identical Value. If yes, do + * not serialize the Value instance again but make this TLCState point to the + * Value instance previously serialized for the other TLCState. In other words, + * this is a custom-tailored compression/de-duplication mechanism for Value + * instances. + * <p> + * This approach only works because both TLCStates are serialized to the same + * storage and thus de-serialized as part of the same operation (same + * Value*Stream instance). + * <p> + * The purpose of this approach appears to be: + * <ul> + * <li>Reduce serialization efforts and storage size</li> + * <li>Reduce the number of Value instances created during de-serialization</li> + * <li>Allow identity comparison on Value instances (AFAICT not used by Value + * explicitly, just UniqueString) to speed up check. Value#equals internally + * likely uses identity comparison as first check.</li> + * </ul> + * <p> + * A disadvantage is the cost of maintaining the internal HandleTable which can + * grow to thousands of elements during serialization/de-serialization (in + * ValueInputStream). Since serialization suspends the DiskStateQueue and thus + * blocks tlc2.tool.Workers from exploring the state space, this might has + * adverse effects. + */ + int put(Object obj); + +} \ No newline at end of file diff --git a/tlatools/src/tlc2/value/IntValue.java b/tlatools/src/tlc2/value/IntValue.java deleted file mode 100644 index cbba09d9eb1cb01f0322138d4d16f58b579c8a34..0000000000000000000000000000000000000000 --- a/tlatools/src/tlc2/value/IntValue.java +++ /dev/null @@ -1,129 +0,0 @@ -// Copyright (c) 2003 Compaq Corporation. All rights reserved. -// Portions Copyright (c) 2003 Microsoft Corporation. All rights reserved. -// Last modified on Sat 23 February 2008 at 10:08:05 PST by lamport -// modified on Fri Aug 10 15:07:30 PDT 2001 by yuanyu - -package tlc2.value; - -import tlc2.util.FP64; -import util.Assert; - -public class IntValue extends Value { - private static final IntValue[] cache; - - public int val; - - private IntValue(int i) { this.val = i; } - - static { - cache = new IntValue[10]; - for (int i = 0; i < cache.length; i++) { - cache[i] = new IntValue(i); - } - } - - public final byte getKind() { return INTVALUE; } - - public static final int nbits(int tmp) { - int nb = 0; - while(tmp != 0 && tmp != -1) { - nb++; - tmp >>= 1; - } - return nb + 1; - } - - // the number of bits needed to encode the value of this int - public final int nbits() { - return nbits(this.val); - } - - public static IntValue gen(int i) { - if (i >= 0 && i < cache.length) { - return cache[i]; - } - return new IntValue(i); - } - - public final int compareTo(Object obj) { - if (obj instanceof IntValue) { - return this.val - ((IntValue)obj).val; - } - if (!(obj instanceof ModelValue)) { - Assert.fail("Attempted to compare integer " + ppr(this.toString()) + - " with non-integer:\n" + ppr(obj.toString())); - } - return 1; - } - - public final boolean equals(Object obj) { - if (obj instanceof IntValue) { - return this.val == ((IntValue)obj).val; - } - if (!(obj instanceof ModelValue)) { - Assert.fail("Attempted to check equality of integer " + ppr(this.toString()) + - " with non-integer:\n" + ppr(obj.toString())); - } - return ((ModelValue) obj).modelValueEquals(this) ; - } - - public final boolean member(Value elem) { - Assert.fail("Attempted to check if the value:\n" + ppr(elem.toString()) + - "\nis an element of the integer " + ppr(this.toString())); - return false; // make compiler happy - } - - public final boolean isFinite() { - Assert.fail("Attempted to check if the integer " + ppr(this.toString()) + - " is a finite set."); - return false; // make compiler happy - } - - public final Value takeExcept(ValueExcept ex) { - if (ex.idx < ex.path.length) { - Assert.fail("Attempted to appy EXCEPT construct to the integer " + - ppr(this.toString()) + "."); - } - return ex.value; - } - - public final Value takeExcept(ValueExcept[] exs) { - if (exs.length != 0) { - Assert.fail("Attempted to apply EXCEPT construct to the integer " + - ppr(this.toString()) + "."); - } - return this; - } - - public final int size() { - Assert.fail("Attempted to compute the number of elements in the integer " + - ppr(this.toString()) + "."); - return 0; // make compiler happy - } - - public final boolean isNormalized() { return true; } - - public final void normalize() { /*nop*/ } - - public final boolean isDefined() { return true; } - - public final Value deepCopy() { return this; } - - public final boolean assignable(Value val) { - return ((val instanceof IntValue) && - this.val == ((IntValue)val).val); - } - - /* The fingerprint methods */ - public final long fingerPrint(long fp) { - return FP64.Extend(FP64.Extend(fp, INTVALUE), this.val); - } - - public final Value permute(MVPerm perm) { return this; } - - /* The string representation. */ - public final StringBuffer toString(StringBuffer sb, int offset) { - return sb.append(this.val); - } - -} diff --git a/tlatools/src/tlc2/value/IntervalValue.java b/tlatools/src/tlc2/value/IntervalValue.java deleted file mode 100644 index cece9ce89a480202614391f410cc730e9fb1d6da..0000000000000000000000000000000000000000 --- a/tlatools/src/tlc2/value/IntervalValue.java +++ /dev/null @@ -1,176 +0,0 @@ -// Copyright (c) 2003 Compaq Corporation. All rights reserved. -// Portions Copyright (c) 2003 Microsoft Corporation. All rights reserved. -// Last modified on Sat 23 February 2008 at 10:12:59 PST by lamport -// modified on Fri Aug 10 15:07:36 PDT 2001 by yuanyu - -package tlc2.value; - -import tlc2.util.FP64; -import util.Assert; - -public class IntervalValue extends Value -implements Enumerable, Reducible { - public int low, high; // the integer interval [low, high] - - /* Constructor */ - public IntervalValue(int low, int high) { - this.low = low; - this.high = high; - } - - public final byte getKind() { return INTERVALVALUE; } - - public final int compareTo(Object obj) { - if (obj instanceof IntervalValue) { - IntervalValue intv = (IntervalValue)obj; - int cmp = this.size() - intv.size(); - if (cmp != 0) return cmp; - if (this.size() == 0) return 0; - return this.low - intv.low; - } - // Well, we have to convert them to sets and compare. - return SetEnumValue.convert(this).compareTo(obj); - } - - public final boolean equals(Object obj) { - if (obj instanceof IntervalValue) { - IntervalValue intv = (IntervalValue)obj; - if (this.size() == 0) return intv.size() == 0; - return (this.low == intv.low) && (this.high == intv.high); - } - // Well, we have to convert them to sets and compare. - return SetEnumValue.convert(this).equals(obj); - } - - public final boolean member(Value elem) { - if (elem instanceof IntValue) { - int x = ((IntValue)elem).val; - return (x >= low) && (x <= high); - } - if ( (this.low <= this.high) - && ( !(elem instanceof ModelValue) - || (((ModelValue) elem).type != 0)) ) { - Assert.fail("Attempted to check if the value:\n" + ppr(elem.toString()) + - "\nis in the integer interval " + ppr(this.toString())); - } - return false; - } - - public final boolean isFinite() { return true; } - - public final int size() { - if (this.high < this.low) return 0; - return this.high - this.low + 1; - } - - /* Return this - val. */ - public final Value diff(Value val) { - ValueVec diffElems = new ValueVec(); - for (int i = this.low; i <= this.high; i++) { - Value elem = IntValue.gen(i); - if (!val.member(elem)) diffElems.addElement(elem); - } - return new SetEnumValue(diffElems, true); - } - - /* Return this \cap val. */ - public final Value cap(Value val) { - ValueVec capElems = new ValueVec(); - for (int i = this.low; i <= this.high; i++) { - Value elem = IntValue.gen(i); - if (val.member(elem)) capElems.addElement(elem); - } - return new SetEnumValue(capElems, true); - } - - /* Return this \cup val. */ - public final Value cup(Value set) { - if (this.size() == 0) return set; - - if (set instanceof Reducible) { - ValueVec cupElems = new ValueVec(); - for (int i = this.low; i <= this.high; i++) { - cupElems.addElement(IntValue.gen(i)); - } - ValueEnumeration Enum = ((Enumerable)set).elements(); - Value elem; - while ((elem = Enum.nextElement()) != null) { - if (!this.member(elem)) cupElems.addElement(elem); - } - return new SetEnumValue(cupElems, false); - } - return new SetCupValue(this, set); - } - - public final Value takeExcept(ValueExcept ex) { - if (ex.idx < ex.path.length) { - Assert.fail("Attempted to apply EXCEPT construct to the interval value " + - ppr(this.toString()) + "."); - } - return ex.value; - } - - public final Value takeExcept(ValueExcept[] exs) { - if (exs.length != 0) { - Assert.fail("Attempted to apply EXCEPT construct to the interval value " + - ppr(this.toString()) + "."); - } - return this; - } - - public final boolean isNormalized() { return true; } - - public final void normalize() { /*nop*/ } - - public final boolean isDefined() { return true; } - - public final Value deepCopy() { return this; } - - public final boolean assignable(Value val) { - return ((val instanceof IntervalValue) && - this.high == ((IntervalValue)val).high && - this.low == ((IntervalValue)val).low); - } - - /* The fingerprint method */ - public final long fingerPrint(long fp) { - fp = FP64.Extend(fp, SETENUMVALUE); - fp = FP64.Extend(fp, this.size()) ; - for (int i = this.low; i <= this.high; i++) { - fp = FP64.Extend(fp, INTVALUE); - fp = FP64.Extend(fp, i); - } - return fp; - } - - public final Value permute(MVPerm perm) { - return this; - } - - /* The string representation */ - public final StringBuffer toString(StringBuffer sb, int offset) { - if (this.low <= this.high) { - return sb.append(this.low).append("..").append(this.high); - } - return sb.append("{").append("}"); - } - - public final ValueEnumeration elements() { - return new Enumerator(); - } - - final class Enumerator implements ValueEnumeration { - int index = low; - - public final void reset() { this.index = low; } - - public final Value nextElement() { - if (this.index <= high) { - return IntValue.gen(this.index++); - } - return null; - } - - } - -} diff --git a/tlatools/src/tlc2/value/LazyValue.java b/tlatools/src/tlc2/value/LazyValue.java deleted file mode 100644 index 453e67ef61fed807c99c6cd23f928e5e7f163135..0000000000000000000000000000000000000000 --- a/tlatools/src/tlc2/value/LazyValue.java +++ /dev/null @@ -1,152 +0,0 @@ -// Copyright (c) 2003 Compaq Corporation. All rights reserved. -// Portions Copyright (c) 2003 Microsoft Corporation. All rights reserved. -// Last modified on Mon 30 Apr 2007 at 15:30:08 PST by lamport -// modified on Thu Feb 8 21:23:55 PST 2001 by yuanyu - -package tlc2.value; - -import java.io.IOException; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; - -import tla2sany.semantic.SemanticNode; -import tlc2.util.Context; -import util.Assert; - -public class LazyValue extends Value { - /** - * The field val is the result of evaluating expr in context con and - * a pair of states. If val is null, then the value has not been - * computed, but when computed, the value can be cached in the field - * val. If val is ValUndef, then the value has not been computed, - * and when computed, it can not be cached in the field val. - */ - - public SemanticNode expr; - public Context con; - public Value val; - - public LazyValue(SemanticNode expr, Context con) { - this.expr = expr; - this.con = con; - this.val = null; - } - - public final void setUncachable() { this.val = ValUndef; } - - public final byte getKind() { return LAZYVALUE; } - - public final int compareTo(Object obj) { - if (this.val == null || this.val == ValUndef) { - Assert.fail("Error(TLC): Attempted to compare lazy values."); - } - return this.val.compareTo(obj); - } - - public final boolean equals(Object obj) { - if (this.val == null || this.val == ValUndef) { - Assert.fail("Error(TLC): Attempted to check equality of lazy values."); - } - return this.val.equals(obj); - } - - public final boolean member(Value elem) { - if (this.val == null || this.val == ValUndef) { - Assert.fail("Error(TLC): Attempted to check set membership of lazy values."); - } - return this.val.member(elem); - } - - public final boolean isFinite() { - if (this.val == null || this.val == ValUndef) { - Assert.fail("Error(TLC): Attempted to check if a lazy value is a finite set."); - } - return this.val.isFinite(); - } - - public final Value takeExcept(ValueExcept ex) { - if (this.val == null || this.val == ValUndef) { - Assert.fail("Error(TLC): Attempted to apply EXCEPT construct to lazy value."); - } - return this.val.takeExcept(ex); - } - - public final Value takeExcept(ValueExcept[] exs) { - if (this.val == null || this.val == ValUndef) { - Assert.fail("Error(TLC): Attempted to apply EXCEPT construct to lazy value."); - } - return this.val.takeExcept(exs); - } - - public final int size() { - if (this.val == null || this.val == ValUndef) { - Assert.fail("Error(TLC): Attempted to compute size of lazy value."); - } - return this.val.size(); - } - - private void readObject(ObjectInputStream ois) - throws IOException, ClassNotFoundException { - this.val = (Value)ois.readObject(); - } - - private void writeObject(ObjectOutputStream oos) throws IOException { - if (this.val == null || this.val == ValUndef) { - Assert.fail("Error(TLC): Attempted to serialize lazy value."); - } - oos.writeObject(this.val); - } - - /* Nothing to normalize. */ - public final boolean isNormalized() { - if (this.val == null || this.val == ValUndef) { - Assert.fail("Error(TLC): Attempted to normalize lazy value."); - } - return this.val.isNormalized(); - } - - public final void normalize() { - if (this.val == null || this.val == ValUndef) { - Assert.fail("Error(TLC): Attempted to normalize lazy value."); - } - this.val.normalize(); - } - - public final boolean isDefined() { return true; } - - public final Value deepCopy() { - if (this.val == null || this.val == ValUndef) return this; - return this.val.deepCopy(); - } - - public final boolean assignable(Value val) { - if (this.val == null || this.val == ValUndef) { - Assert.fail("Error(TLC): Attempted to call assignable on lazy value."); - } - return this.val.assignable(val); - } - - /* The fingerprint method */ - public final long fingerPrint(long fp) { - if (this.val == null || this.val == ValUndef) { - Assert.fail("Error(TLC): Attempted to fingerprint a lazy value."); - } - return this.val.fingerPrint(fp); - } - - public final Value permute(MVPerm perm) { - if (this.val == null || this.val == ValUndef) { - Assert.fail("Error(TLC): Attempted to apply permutation to lazy value."); - } - return this.val.permute(perm); - } - - /* The string representation of the value. */ - public final StringBuffer toString(StringBuffer sb, int offset) { - if (this.val == null || this.val == ValUndef) { - return sb.append("<LAZY " + this.expr + ">"); - } - return this.val.toString(sb, offset); - } - -} diff --git a/tlatools/src/tlc2/value/MVPerm.java b/tlatools/src/tlc2/value/MVPerm.java deleted file mode 100644 index 1f3b3e24ed6754652f2e0cd0821f366864c80701..0000000000000000000000000000000000000000 --- a/tlatools/src/tlc2/value/MVPerm.java +++ /dev/null @@ -1,166 +0,0 @@ -// Copyright (c) 2003 Compaq Corporation. All rights reserved. -// Portions Copyright (c) 2003 Microsoft Corporation. All rights reserved. -// Last modified on Mon 30 Apr 2007 at 13:42:27 PST by lamport -// modified on Thu Nov 16 15:53:30 PST 2000 by yuanyu - -package tlc2.value; - -import java.util.Enumeration; - -import tlc2.util.Vect; -import util.Assert; -import util.Set; - -public final class MVPerm { - private ModelValue[] elems; - private int count; - - public MVPerm() { - this.elems = new ModelValue[ModelValue.mvs.length]; - this.count = 0; - } - - public final boolean equals(Object obj) { - if (obj instanceof MVPerm) { - MVPerm perm = (MVPerm)obj; - for (int i = 0; i < this.elems.length; i++) { - if (this.elems[i] == null) { - if (perm.elems[i] != null) { - return false; - } - } - else if (!this.elems[i].equals(perm.elems[i])) { - return false; - } - } - return true; - } - return false; - } - - public final int hashCode() { - int res = 0; - for (int i = 0; i < this.elems.length; i++) { - ModelValue mv = this.elems[i]; - if (mv != null) { - res = 31*res + mv.val.hashCode(); - } - } - return res; - } - - public final int size() { return this.count; } - - public final ModelValue get(ModelValue k) { - return this.elems[k.index]; - } - - public final void put(ModelValue k, ModelValue elem) { - if (!k.equals(elem) && this.elems[k.index] == null) { - this.elems[k.index] = elem; - this.count++; - } - } - - public final void put(int i, ModelValue elem) { - if (this.elems[i] == null && elem != null) { - this.elems[i] = elem; - this.count++; - } - } - - public final MVPerm compose(MVPerm perm) { - MVPerm res = new MVPerm(); - for (int i = 0; i < this.elems.length; i++) { - ModelValue mv = this.elems[i]; - if (mv == null) { - res.put(i, perm.elems[i]); - } - else { - ModelValue mv1 = perm.elems[mv.index]; - if (mv1 == null) { - res.put(i, mv); - } - else if (!ModelValue.mvs[i].equals(mv1)) { - res.put(i, mv1); - } - } - } - return res; - } - - public static final MVPerm[] permutationSubgroup(ValueEnumeration Enum) { - Set perms = new Set(20); - Vect permVec = new Vect(20); - // Compute the group generators: - Value elem; - while ((elem = Enum.nextElement()) != null) { - FcnRcdValue fcn = FcnRcdValue.convert(elem); - if (fcn == null) { - Assert.fail("The symmetry operator must specify a set of functions."); - } - MVPerm perm = new MVPerm(); - for (int i = 0; i < fcn.domain.length; i++) { - Value dval = fcn.domain[i]; - Value rval = fcn.values[i]; - if ((dval instanceof ModelValue) && (rval instanceof ModelValue)) { - perm.put((ModelValue)dval, (ModelValue)rval); - } - else { - Assert.fail("Symmetry function must have model values as domain and range."); - } - } - if (perm.size() > 0 && perms.put(perm) == null) { - permVec.addElement(perm); - } - } - // Compute the group generated by the generators: - int gsz = permVec.size(); - int sz0 = 0; - while (true) { - int sz1 = permVec.size(); - for (int i = 0; i < gsz; i++) { - MVPerm perm1 = (MVPerm)permVec.elementAt(i); - for (int j = sz0; j < sz1; j++) { - MVPerm perm = perm1.compose((MVPerm)permVec.elementAt(j)); - if (perm.size() > 0 && perms.put(perm) == null) { - permVec.addElement(perm); - } - } - } - if (sz1 == permVec.size()) break; - sz0 = sz1; - } - // Finally, put all the elements in an array ready for use: - MVPerm[] res = new MVPerm[permVec.size()]; - Enumeration permEnum = permVec.elements(); - for (int i = 0; i < res.length; i++) { - res[i] = (MVPerm)permEnum.nextElement(); - } - return res; - } - - public final String toString() { - StringBuffer sb = new StringBuffer("["); - int i = 0; - for (i = 0; i < this.elems.length; i++) { - if (this.elems[i] != null) { - sb.append(ModelValue.mvs[i].toString()); - sb.append(" -> "); - sb.append(this.elems[i].toString()); - break; - } - } - for (int j = i+1; j < this.elems.length; j++) { - if (this.elems[j] != null) { - sb.append(", "); - sb.append(ModelValue.mvs[j].toString()); - sb.append(" -> "); - sb.append(this.elems[j].toString()); - } - } - sb.append("]"); - return sb.toString(); - } - -} diff --git a/tlatools/src/tlc2/value/MethodValue.java b/tlatools/src/tlc2/value/MethodValue.java deleted file mode 100644 index ae68732b5410e7c4aff2222f04428bc1fa4a2bc8..0000000000000000000000000000000000000000 --- a/tlatools/src/tlc2/value/MethodValue.java +++ /dev/null @@ -1,121 +0,0 @@ -// Copyright (c) 2003 Compaq Corporation. All rights reserved. -// Portions Copyright (c) 2003 Microsoft Corporation. All rights reserved. -// Last modified on Mon 30 Apr 2007 at 13:21:00 PST by lamport -// modified on Fri Sep 22 13:18:45 PDT 2000 by yuanyu - -package tlc2.value; - -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; - -import tlc2.output.EC; -import tlc2.tool.EvalException; -import util.Assert; -import util.WrongInvocationException; - -public class MethodValue extends OpValue implements Applicable { - public Method md; - - /* Constructor */ - public MethodValue(Method md) { this.md = md; } - - public final byte getKind() { return METHODVALUE; } - - public final int compareTo(Object obj) { - Assert.fail("Attempted to compare operator " + this.toString() + - " with value:\n" + obj == null ? "null" : ppr(obj.toString())); - return 0; // make compiler happy - } - - public final boolean equals(Object obj) { - Assert.fail("Attempted to check equality of operator " + this.toString() + - " with value:\n" + obj == null ? "null" : ppr(obj.toString())); - return false; // make compiler happy - } - - public final boolean member(Value elem) { - Assert.fail("Attempted to check if the value:\n" + elem == null ? "null" : ppr(elem.toString()) + - "\nis an element of operator " + this.toString()); - return false; // make compiler happy - } - - public final boolean isFinite() { - Assert.fail("Attempted to check if the operator " + this.toString() + - " is a finite set."); - return false; // make compiler happy - } - - public final Value apply(Value arg, int control) { - throw new WrongInvocationException("It is a TLC bug: Should use the other apply method."); - } - - public final Value apply(Value[] args, int control) { - Value res = null; - try - { - res = (Value)this.md.invoke(null, (Object[]) args); - } catch (Exception e) - { - if (e instanceof InvocationTargetException) - { - Throwable targetException = ((InvocationTargetException)e).getTargetException(); - throw new EvalException(EC.TLC_MODULE_VALUE_JAVA_METHOD_OVERRIDE, new String[]{this.md.toString(), targetException.getMessage()}); - } else - { - Assert.fail(EC.TLC_MODULE_VALUE_JAVA_METHOD_OVERRIDE, new String[]{this.md.toString(), e.getMessage()}); - } - } - return res; - } - - public final Value select(Value arg) { - throw new WrongInvocationException("It is a TLC bug: Attempted to call MethodValue.select()."); - } - - public final Value takeExcept(ValueExcept ex) { - Assert.fail("Attempted to appy EXCEPT construct to the operator " + - this.toString() + "."); - return null; // make compiler happy - } - - public final Value takeExcept(ValueExcept[] exs) { - Assert.fail("Attempted to apply EXCEPT construct to the operator " + - this.toString() + "."); - return null; // make compiler happy - } - - public final Value getDomain() { - Assert.fail("Attempted to compute the domain of the operator " + - this.toString() + "."); - return EmptySet; // make compiler happy - } - - public final int size() { - Assert.fail("Attempted to compute the number of elements in the operator " + - this.toString() + "."); - return 0; // make compiler happy - } - - /* Should never normalize an operator. */ - public final boolean isNormalized() { - throw new WrongInvocationException("It is a TLC bug: Attempted to normalize an operator."); - } - - public final void normalize() { - throw new WrongInvocationException("It is a TLC bug: Attempted to normalize an operator."); - } - - public final boolean isDefined() { return true; } - - public final Value deepCopy() { return this; } - - public final boolean assignable(Value val) { - throw new WrongInvocationException("It is a TLC bug: Attempted to initialize an operator."); - } - - /* String representation of the value. */ - public final StringBuffer toString(StringBuffer sb, int offset) { - return sb.append("<Java Method: " + this.md + ">"); - } - -} diff --git a/tlatools/src/tlc2/value/ModelValue.java b/tlatools/src/tlc2/value/ModelValue.java deleted file mode 100644 index 38fe2acf4b8106c03cf1f487b954d117a16f73f8..0000000000000000000000000000000000000000 --- a/tlatools/src/tlc2/value/ModelValue.java +++ /dev/null @@ -1,229 +0,0 @@ -// Copyright (c) 2003 Compaq Corporation. All rights reserved. -// Portions Copyright (c) 2003 Microsoft Corporation. All rights reserved. -// Last modified on Sat 23 February 2008 at 10:32:25 PST by lamport -// modified on Fri Aug 10 15:07:47 PDT 2001 by yuanyu - -/*************************************************************************** -* Change to model values made by LL on 23 Feb 2008: * -* ------ * -* A model value whose name is "x_str" for any character x and string str * -* is said to have TYPE 'x'. Otherwise, it is said to be untyped. It is * -* an error to test if a typed model value is equal to any value except a * -* model value that is either untyped or has the same type. (As before, * -* an untyped model value is unequal to any value except itself.) * -* * -* This was implemented by adding a `type' field to a ModelValue object * -* and making changes to the following classes: * -* * -* changed member method * -* module/Integers.java * -* module/Naturals.java * -* module/Sequences.java * -* module/Strings.java * -* value/IntervalValue.java CHECK THIS with mv \notin 1..2 * -* value/SetOfFcnsValue.java * -* value/SetOfRcdsValue.java * -* value/SetOfTuplesValue.java * -* value/StringValue.java * -* changed equals method * -* value/BoolValue.java * -* value/FcnRcdValue.java * -* value/IntValue.java * -* value/RecordValue.java * -* value/SetEnumValue.java * -***************************************************************************/ - -package tlc2.value; - -import java.util.Enumeration; -import java.util.Hashtable; - -import tlc2.util.FP64; -import util.Assert; -import util.UniqueString; - -public class ModelValue extends Value { - - /** - * A method to reset the model values - * All callers should make sure that the model value class has been initialized - */ - public static void init() - { - count = 0; - mvTable = new Hashtable(); - mvs = null; - } - - /** - * Workround to the static usage - */ - static - { - init(); - } - - private static int count; - private static Hashtable mvTable; - // SZ Mar 9, 2009: public accessed field, this will cause troubles - public static ModelValue[] mvs; - - public UniqueString val; - public int index; - public char type; // type = 0 means untyped. - - /* Constructor */ - private ModelValue(String val) { - // SZ 11.04.2009: changed access method - this.val = UniqueString.uniqueStringOf(val); - this.index = count++; - if ( (val.length() > 2) - && (val.charAt(1) == '_')) { - this.type = val.charAt(0) ; - } - else { this.type = 0 ; } ; - } - - /* Make str a new model value, if it is not one yet. */ - public static ModelValue make(String str) { - ModelValue mv = (ModelValue)mvTable.get(str); - if (mv != null) return mv; - mv = new ModelValue(str); - mvTable.put(str, mv); - return mv; - } - - /* Collect all the model values defined thus far. */ - public static void setValues() { - mvs = new ModelValue[mvTable.size()]; - Enumeration Enum = mvTable.elements(); - while (Enum.hasMoreElements()) { - ModelValue mv = (ModelValue)Enum.nextElement(); - mvs[mv.index] = mv; - } - } - - public final byte getKind() { return MODELVALUE; } - - public final int compareTo(Object obj) { - if (obj instanceof ModelValue) { - return this.val.compareTo(((ModelValue)obj).val); - } - return -1; - } - - public final boolean equals(Object obj) { - if (this.type == 0) { - return (obj instanceof ModelValue && - this.val.equals(((ModelValue)obj).val)); - }; - if (obj instanceof ModelValue) { - ModelValue mobj = (ModelValue) obj ; - if ( (mobj.type == this.type) - || (mobj.type == 0) ) { - return mobj.val == this.val ; - } - else { - Assert.fail("Attempted to check equality " - + "of the differently-typed model values " - + ppr(this.toString()) + " and " - + ppr(mobj.toString())); - } ; - } ; - Assert.fail("Attempted to check equality of typed model value " - + ppr(this.toString()) + " and non-model value\n" - + ppr(obj.toString())) ; - return false; // make compiler happy - } - - /************************************************************************* - * The following two methods are used used to check if this model value * - * equal to or a member of non-model value obj. They return false if * - * this model value is untyped and raise an exception if it is typed. * - *************************************************************************/ - public final boolean modelValueEquals(Object obj){ - if (this.type != 0) { - Assert.fail("Attempted to check equality of the typed model value " - + ppr(this.toString()) + " and the non-model value\n" - + ppr(obj.toString())) ; - - } ; - return false ; - } - - public final boolean modelValueMember(Object obj){ - if (this.type != 0) { - Assert.fail("Attempted to check if the typed model value " - + ppr(this.toString()) - + " is an element of\n" - + ppr(obj.toString())) ; - - } ; - return false ; - } - - public final boolean member(Value elem) { - Assert.fail("Attempted to check if the value:\n" + ppr(elem.toString()) + - "\nis an element of the model value " + ppr(this.toString())); - return false; // make compiler happy - } - - public final boolean isFinite() { - Assert.fail("Attempted to check if the model value " + ppr(this.toString()) + - " is a finite set."); - return false; // make compiler happy - } - - public final Value takeExcept(ValueExcept ex) { - if (ex.idx < ex.path.length) { - Assert.fail("Attempted to apply EXCEPT construct to the model value " + - ppr(this.toString()) + "."); - } - return ex.value; - } - - public final Value takeExcept(ValueExcept[] exs) { - if (exs.length != 0) { - Assert.fail("Attempted to apply EXCEPT construct to the model value " + - ppr(this.toString()) + "."); - } - return this; - } - - public final int size() { - Assert.fail("Attempted to compute the number of elements in the model value " + - ppr(this.toString()) + "."); - return 0; // make compiler happy - } - - public final boolean isNormalized() { return true; } - - public final void normalize() { /*nop*/ } - - public final boolean isDefined() { return true; } - - public final Value deepCopy() { return this; } - - public final boolean assignable(Value val) { - return ((val instanceof ModelValue) && - this.val.equals(((ModelValue)val).val)); - } - - /* The fingerprint methods */ - public final long fingerPrint(long fp) { - return this.val.fingerPrint(FP64.Extend(fp, MODELVALUE)); - } - - public final Value permute(MVPerm perm) { - Value res = perm.get(this); - if (res == null) return this; - return res; - } - - /* The string representation. */ - public final StringBuffer toString(StringBuffer sb, int offset) { - return sb.append(this.val); - } - - -} diff --git a/tlatools/src/tlc2/value/OpLambdaValue.java b/tlatools/src/tlc2/value/OpLambdaValue.java deleted file mode 100644 index c7b35723637609a71d3fd9c401ff765eba2cced7..0000000000000000000000000000000000000000 --- a/tlatools/src/tlc2/value/OpLambdaValue.java +++ /dev/null @@ -1,129 +0,0 @@ -// Copyright (c) 2003 Compaq Corporation. All rights reserved. -// Portions Copyright (c) 2003 Microsoft Corporation. All rights reserved. -// Last modified on Mon 30 Apr 2007 at 15:30:09 PST by lamport -// modified on Fri Sep 22 13:18:45 PDT 2000 by yuanyu - -package tlc2.value; - -import tla2sany.semantic.FormalParamNode; -import tla2sany.semantic.OpDefNode; -import tlc2.tool.TLCState; -import tlc2.tool.Tool; -import tlc2.util.Context; -import util.Assert; -import util.WrongInvocationException; - -public class OpLambdaValue extends OpValue implements Applicable { - public OpDefNode opDef; // the operator definition. - public Tool tool; - public Context con; - public TLCState state; - public TLCState pstate; - - /* Constructor */ - public OpLambdaValue(OpDefNode op, Tool tool, Context con, - TLCState state, TLCState pstate) { - this.opDef = op; - this.tool = tool; - this.state = state; - this.con = con; - this.pstate = pstate; - } - - public final byte getKind() { return OPLAMBDAVALUE; } - - public final int compareTo(Object obj) { - Assert.fail("Attempted to compare operator " + ppr(this.toString()) + - " with value:\n" + ppr(obj.toString())); - return 0; // make compiler happy - } - - public final boolean equals(Object obj) { - Assert.fail("Attempted to check equality of operator " + ppr(this.toString()) + - " with value:\n" + ppr(obj.toString())); - return false; // make compiler happy - } - - public final boolean member(Value elem) { - Assert.fail("Attempted to check if the value:\n" + ppr(elem.toString()) + - "\nis an element of operator " + ppr(this.toString())); - return false; // make compiler happy - } - - public final boolean isFinite() { - Assert.fail("Attempted to check if the operator " + ppr(this.toString()) + - " is a finite set."); - return false; // make compiler happy - } - - public final Value apply(Value arg, int control) { - throw new WrongInvocationException("Should use the other apply method."); - } - - public final Value apply(Value[] args, int control) { - int alen = this.opDef.getArity(); - if (alen != args.length) { - Assert.fail("Applying the operator " + ppr(this.toString()) + - " with wrong number of arguments."); - } - Context c1 = this.con; - FormalParamNode[] formals = this.opDef.getParams(); - for (int i = 0; i < alen; i++) { - c1 = c1.cons(formals[i], args[i]); - } - return this.tool.eval(this.opDef.getBody(), c1, this.state, this.pstate, - control); - } - - public final Value select(Value arg) { - throw new WrongInvocationException("Error(TLC): attempted to call OpLambdaValue.select()."); - } - - public final Value takeExcept(ValueExcept ex) { - Assert.fail("Attempted to appy EXCEPT construct to the operator " + - ppr(this.toString()) + "."); - return null; // make compiler happy - } - - public final Value takeExcept(ValueExcept[] exs) { - Assert.fail("Attempted to apply EXCEPT construct to the operator " + - ppr(this.toString()) + "."); - return null; // make compiler happy - } - - public final Value getDomain() { - Assert.fail("Attempted to compute the domain of the operator " + - ppr(this.toString()) + "."); - return EmptySet; // make compiler happy - } - - public final int size() { - Assert.fail("Attempted to compute the number of elements in the operator " + - ppr(this.toString()) + "."); - return 0; // make compiler happy - } - - /* Should never normalize an operator. */ - public final boolean isNormalized() { - throw new WrongInvocationException("Should not normalize an operator."); - } - - public final void normalize() { - throw new WrongInvocationException("Should not normalize an operator."); - } - - public final boolean isDefined() { return true; } - - public final Value deepCopy() { return this; } - - public final boolean assignable(Value val) { - throw new WrongInvocationException("Should not initialize an operator."); - } - - /* String representation of the value. */ - public final StringBuffer toString(StringBuffer sb, int offset) { - String opName = this.opDef.getName().toString(); - return sb.append("<Operator ").append(opName).append(">"); - } - -} diff --git a/tlatools/src/tlc2/value/OpRcdValue.java b/tlatools/src/tlc2/value/OpRcdValue.java deleted file mode 100644 index fe25a594cf148fc35178e8b691bd513f50440c06..0000000000000000000000000000000000000000 --- a/tlatools/src/tlc2/value/OpRcdValue.java +++ /dev/null @@ -1,173 +0,0 @@ -// Copyright (c) 2003 Compaq Corporation. All rights reserved. -// Portions Copyright (c) 2003 Microsoft Corporation. All rights reserved. -// Last modified on Mon 30 Apr 2007 at 13:21:01 PST by lamport -// modified on Sat Nov 13 12:43:44 PST 1999 by yuanyu - -package tlc2.value; - -import tlc2.util.Vect; -import util.Assert; -import util.WrongInvocationException; - -public class OpRcdValue extends OpValue implements Applicable { - public Vect domain; - public Vect values; - - /* Constructor */ - public OpRcdValue() { - this.domain = new Vect(); - this.values = new Vect(); - } - - public OpRcdValue(Vect domain, Vect values) { - this.domain = domain; - this.values = values; - } - - public final byte getKind() { return OPRCDVALUE; } - - public final int compareTo(Object obj) { - Assert.fail("Attempted to compare operator " + ppr(this.toString()) + - " with value:\n" + ppr(obj.toString())); - return 0; // make compiler happy - } - - public final boolean equals(Object obj) { - Assert.fail("Attempted to check equality of operator " + ppr(this.toString()) + - " with value:\n" + ppr(obj.toString())); - return false; // make compiler happy - } - - public final boolean member(Value elem) { - Assert.fail("Attempted to check if the value:\n" + ppr(elem.toString()) + - "\nis an element of operator " + ppr(this.toString())); - return false; // make compiler happy - } - - public final boolean isFinite() { - Assert.fail("Attempted to check if the operator " + ppr(this.toString()) + - " is a finite set."); - return false; // make compiler happy - } - - public final void addLine(Vect vs) { - int len = vs.size(); - Value[] args = new Value[len-2]; - for (int i = 0; i < len-2; i++) { - args[i] = (Value)vs.elementAt(i+1); - } - this.domain.addElement(args); - this.values.addElement(vs.elementAt(len-1)); - } - - public final Value apply(Value arg, int control) { - throw new WrongInvocationException("Should use the other apply method."); - } - - public final Value apply(Value[] args, int control) { - int sz = this.domain.size(); - for (int i = 0; i < sz; i++) { - Value[] vals = (Value[])this.domain.elementAt(i); - if (args.length != vals.length) { - Assert.fail("Attempted to apply the operator " + ppr(this.toString()) + - "\nwith wrong number of arguments."); - } - boolean matched = true; - for (int j = 0; j < vals.length; j++) { - matched = vals[j].equals(args[j]); - if (!matched) break; - } - if (matched) { - return (Value)this.values.elementAt(i); - } - } - // Generate the error message: - String msg = "Attempted to apply operator:\n" + ppr(this.toString()) + - "\nto arguments ("; - if (args.length > 0) msg += args[0]; - for (int i = 1; i < args.length; i++) { - msg += ", " + args[i]; - } - Assert.fail(msg + "), which is undefined."); - return null; // make compiler happy - } - - public final Value select(Value arg) { - Assert.fail("Attempted to call OpRcdValue.select(). This is a TLC bug."); - return null; // make compiler happy - } - - public final Value takeExcept(ValueExcept ex) { - Assert.fail("Attempted to appy EXCEPT construct to the operator " + - ppr(this.toString()) + "."); - return null; // make compiler happy - } - - public final Value takeExcept(ValueExcept[] exs) { - Assert.fail("Attempted to apply EXCEPT construct to the operator " + - ppr(this.toString()) + "."); - return null; // make compiler happy - } - - public final Value getDomain() { - Assert.fail("Attempted to compute the domain of the operator " + - ppr(this.toString()) + "."); - return EmptySet; // make compiler happy - } - - public final int size() { - Assert.fail("Attempted to compute the number of elements in the operator " + - ppr(this.toString()) + "."); - return 0; // make compiler happy - } - - /* Should never normalize an operator. */ - public final boolean isNormalized() { - throw new WrongInvocationException("Should not normalize an operator."); - } - - public final void normalize() { - throw new WrongInvocationException("Should not normalize an operator."); - } - - public final boolean isDefined() { - boolean defined = true; - for (int i = 0; i < this.values.size(); i++) { - defined = defined && ((Value)this.values.elementAt(i)).isDefined(); - } - return defined; - } - - public final Value deepCopy() { return this; } - - public final boolean assignable(Value val) { - throw new WrongInvocationException("Should not initialize an operator."); - } - - /* Pretty-printing */ - public final StringBuffer toString(StringBuffer sb, int offset) { - sb.append("{ "); - if (this.values.size() != 0) { - sb.append("<"); - Value[] args = (Value[])this.domain.elementAt(0); - for (int j = 0; j < args.length; j++) { - sb = args[j].toString(sb, offset); - sb.append(", "); - } - sb = ((Value)this.values.elementAt(0)).toString(sb, offset); - sb.append(">"); - } - for (int i = 1; i < this.values.size(); i++) { - sb.append(", <"); - Value[] args = (Value[])this.domain.elementAt(i); - for (int j = 0; j < args.length; j++) { - sb = args[j].toString(sb, offset); - sb.append(", "); - } - sb = ((Value)this.values.elementAt(i)).toString(sb, offset); - sb.append(">"); - } - return sb.append("}"); - } - -} diff --git a/tlatools/src/tlc2/value/OpValue.java b/tlatools/src/tlc2/value/OpValue.java deleted file mode 100644 index 1aa0c0a3a31d69e0da656f28e83012e49e4b0e70..0000000000000000000000000000000000000000 --- a/tlatools/src/tlc2/value/OpValue.java +++ /dev/null @@ -1,10 +0,0 @@ -// Copyright (c) 2003 Compaq Corporation. All rights reserved. -// Portions Copyright (c) 2003 Microsoft Corporation. All rights reserved. -// Last modified on Mon 30 Apr 2007 at 13:21:02 PST by lamport -// modified on Fri Sep 22 13:18:45 PDT 2000 by yuanyu - -package tlc2.value; - -public abstract class OpValue extends Value implements Applicable { - -} diff --git a/tlatools/src/tlc2/value/RandomEnumerableValues.java b/tlatools/src/tlc2/value/RandomEnumerableValues.java new file mode 100644 index 0000000000000000000000000000000000000000..9d7d3f97f5e71369d1328dc961083587fd3556ad --- /dev/null +++ b/tlatools/src/tlc2/value/RandomEnumerableValues.java @@ -0,0 +1,157 @@ +/******************************************************************************* + * Copyright (c) 2019 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + * Ian Morris Nieves - added support for fingerprint stack trace + ******************************************************************************/ + +package tlc2.value; + +import java.util.Random; + +import tlc2.TLCGlobals; +import tlc2.tool.ModelChecker; +import tlc2.tool.TLCState; +import tlc2.util.IdThread; + +public abstract class RandomEnumerableValues { + + /* Randomization for sets */ + + private static long randomSeed; + + public static long getSeed() { + return randomSeed; + } + + /** + * Initialize Random with the given seed value. + **/ + public static void setSeed(final long seed) { + randomSeed = seed; + reset(); + } + + /** + * Re-Initialize Random with the recorded seed value. + **/ + public static void reset() { + RANDOMS.remove(); + } + + public static Random get() { + return RANDOMS.get(); + } + + private static final ThreadLocal<Random> RANDOMS = new ThreadLocal<Random>() { + @Override + protected Random initialValue() { + if (TLCGlobals.mainChecker != null && ModelChecker.class.equals(TLCGlobals.mainChecker.getClass())) { + // In order to recreate the error trace in BFS mode - which essentially + // corresponds to rerunning state exploration following a given path - we have + // to recreate the same random values too. Otherwise, TLC will fail to recreate + // the trace because the recreated TLCState does not match the fingerprint in + // the error trace/path. Therefore, we maintain one RNG per IdThread (IWorker) + // which - for the initial states - is seeded with enumFractionSeed and - in the + // scope of next-state - gets seeded with the fingerprint of the predecessor + // state. + return new TLCStateRandom(randomSeed); + } else { + // DFS or Simulation need no special handling because: + // - DFS does not re-create the error trace (just prints the current trace). + // - Simulation mode is intended to allow users to gather statistics on + // behaviors generated with specified probabilities of different transitions. + // (For example, to find the expected time for a message to be delivered with + // retransmission as a function of the probability of message loss). For this, + // it's important that the random choice have no correlation with the state in + // which the choice is made. + return new DefaultRandom(randomSeed); + } + } + + @Override + public Random get() { + // Hook to re-initialize random (no-op for DFS and simulation). + return ((EnumerableValueRandom) super.get()).initialize(); + } + }; + + private interface EnumerableValueRandom { + Random initialize(); + } + + @SuppressWarnings("serial") + private static final class DefaultRandom extends Random implements EnumerableValueRandom { + public DefaultRandom(long randomSeed) { + super(randomSeed); + } + + @Override + public final Random initialize() { + // Noop + return this; + } + } + + @SuppressWarnings("serial") + private static final class TLCStateRandom extends Random implements EnumerableValueRandom { + + private TLCState state; + + public TLCStateRandom(long randomSeed) { + super(randomSeed); + } + + private void initializedFor(final TLCState state) { + // XOR the state's fingerprint with the initial randomSeed value. This is done + // so that two identical states (same fingerprint) of two different + // specifications do not produce the same random value. + final long seed = state.fingerPrint() ^ randomSeed; + this.setSeed(seed); + this.state = state; + } + + private boolean isInitializedFor(final TLCState another) { + return state == another; + } + + @Override + public Random initialize() { + final TLCState state = IdThread.getCurrentState(); + // state is null during the generation of initial states and non-null in the + // scope of the next-state relation (however, state can be an initial state). + // Thus, an RNG is seeded with randomSeed during the generation of initial + // states and seeded with randomSeed ^ predecessor's fingerprint during the + // generation of next states. + // Do not re-initialize random for the same TLCState twice to produce two + // distinct values with high probability with a next-state such as: + // Next == x' = RandomElement(0..2) /\ y' = RandomElement(0..2) + // If random was to be re-initialized/re-seeded, RandomElement(0..2) for x' and y' + // would be identical values (also see tlc2.tool.RandomElementXandYTest). + if (state != null && !isInitializedFor(state)) { + initializedFor(state); + } + return this; + } + } +} diff --git a/tlatools/src/tlc2/value/RecordValue.java b/tlatools/src/tlc2/value/RecordValue.java deleted file mode 100644 index 3dd1281069177fe6296207d949e2a23908dfcbe0..0000000000000000000000000000000000000000 --- a/tlatools/src/tlc2/value/RecordValue.java +++ /dev/null @@ -1,328 +0,0 @@ -// Copyright (c) 2003 Compaq Corporation. All rights reserved. -// Portions Copyright (c) 2003 Microsoft Corporation. All rights reserved. -// Last modified on Sat 23 February 2008 at 10:15:47 PST by lamport -// modified on Fri Aug 10 15:09:07 PDT 2001 by yuanyu - -package tlc2.value; - -import tlc2.output.EC; -import tlc2.output.MP; -import tlc2.util.FP64; -import util.Assert; -import util.UniqueString; - -public class RecordValue extends Value implements Applicable { - public UniqueString[] names; // the field names - public Value[] values; // the field values - private boolean isNorm; - - /* Constructor */ - public RecordValue(UniqueString[] names, Value[] values, boolean isNorm) { - this.names = names; - this.values = values; - this.isNorm = isNorm; - } - - public final byte getKind() { return RECORDVALUE; } - - public final int compareTo(Object obj) { - RecordValue rcd = convert(obj); - if (rcd == null) { - if (obj instanceof ModelValue) return 1; - Assert.fail("Attempted to compare record:\n" + ppr(this.toString()) + - "\nwith non-record\n" + ppr(obj.toString())); - } - this.normalize(); - rcd.normalize(); - int len = this.names.length; - int cmp = len - rcd.names.length; - if (cmp == 0) { - for (int i = 0; i < len; i++) { - cmp = this.names[i].compareTo(rcd.names[i]); - if (cmp != 0) break; - cmp = this.values[i].compareTo(rcd.values[i]); - if (cmp != 0) break; - } - } - return cmp; - } - - public final boolean equals(Object obj) { - RecordValue rcd = convert(obj); - if (rcd == null) { - if (obj instanceof ModelValue) - return ((ModelValue) obj).modelValueEquals(this) ; - Assert.fail("Attempted to check equality of record:\n" + ppr(this.toString()) + - "\nwith non-record\n" + ppr(obj.toString())); - } - this.normalize(); - rcd.normalize(); - int len = this.names.length; - if (len != rcd.names.length) return false; - for (int i = 0; i < len; i++) { - if ((!(this.names[i].equals(rcd.names[i]))) || - (!(this.values[i].equals(rcd.values[i])))) - return false; - } - return true; - } - - public final boolean member(Value elem) { - Assert.fail("Attempted to check if element:\n" + ppr(elem.toString()) + - "\nis in the record:\n" + ppr(this.toString())); - return false; // make compiler happy - } - - public final boolean isFinite() { return true; } - - public final Value takeExcept(ValueExcept ex) { - if (ex.idx < ex.path.length) { - int rlen = this.names.length; - Value[] newValues = new Value[rlen]; - Value arcVal = ex.path[ex.idx]; - if (arcVal instanceof StringValue) { - UniqueString arc = ((StringValue)arcVal).val; - for (int i = 0; i < rlen; i++) { - if (this.names[i].equals(arc)) { - ex.idx++; - newValues[i] = this.values[i].takeExcept(ex); - } - else { - newValues[i] = this.values[i]; - } - } - UniqueString[] newNames = this.names; - if (!this.isNorm) { - newNames = new UniqueString[rlen]; - for (int i = 0; i < rlen; i++) { - newNames[i] = this.names[i]; - } - } - return new RecordValue(newNames, newValues, this.isNorm); - } - else { - MP.printWarning(EC.TLC_WRONG_RECORD_FIELD_NAME, new String[]{ppr(arcVal.toString())}); - } - } - return ex.value; - } - - public final Value takeExcept(ValueExcept[] exs) { - Value res = this; - for (int i = 0; i < exs.length; i++) { - res = res.takeExcept(exs[i]); - } - return res; - } - - /* - * This method converts a value to a function value. It returns - * null if the conversion fails. - */ - public static final RecordValue convert(Object val) { - if (val instanceof RecordValue) { - return (RecordValue)val; - } - else if (val instanceof FcnRcdValue || - val instanceof FcnLambdaValue) { - FcnRcdValue fcn = FcnRcdValue.convert(val); - if (fcn == null || fcn.domain == null) return null; - fcn.normalize(); - UniqueString[] vars = new UniqueString[fcn.domain.length]; - for (int i = 0; i < fcn.domain.length; i++) { - if (!(fcn.domain[i] instanceof StringValue)) { - return null; - } - vars[i] = ((StringValue)fcn.domain[i]).getVal(); - } - return new RecordValue(vars, fcn.values, fcn.isNormalized()); - } - else if ((val instanceof TupleValue) && - ((TupleValue)val).size() == 0) { - return EmptyRcd; - } - // return null if not convertable - return null; - } - - public final int size() { return this.names.length; } - - public final Value apply(Value arg, int control) { - if (!(arg instanceof StringValue)) { - Assert.fail("Attempted to apply record to a non-string value " + - ppr(arg.toString()) + "."); - } - UniqueString name = ((StringValue)arg).getVal(); - int rlen = this.names.length; - for (int i = 0; i < rlen; i++) { - if (name.equals(this.names[i])) { - return this.values[i]; - } - } - Assert.fail("Attempted to apply the record\n" + ppr(this.toString()) + - "\nto nonexistent record field " + name + "."); - return null; // make compiler happy - } - - public final Value apply(Value[] args, int control) { - if (args.length != 1) { - Assert.fail("Attempted to apply record to more than one arguments."); - } - return this.apply(args[0], control); - } - - /* This method returns the named component of the record. */ - public final Value select(Value arg) { - if (!(arg instanceof StringValue)) { - Assert.fail("Attempted to apply record to a non-string argument " + - ppr(arg.toString()) + "."); - } - UniqueString name = ((StringValue)arg).getVal(); - int rlen = this.names.length; - for (int i = 0; i < rlen; i++) { - if (name.equals(this.names[i])) { - return this.values[i]; - } - } - return null; - } - - public final Value getDomain() { - Value[] dElems = new Value[this.names.length]; - for (int i = 0; i < this.names.length; i++) { - dElems[i] = new StringValue(this.names[i]); - } - return new SetEnumValue(dElems, this.isNormalized()); - } - - public final boolean assign(UniqueString name, Value val) { - for (int i = 0; i < this.names.length; i++) { - if (name.equals(this.names[i])) { - if (this.values[i] == ValUndef || - this.values[i].equals(val)) { - this.values[i] = val; - return true; - } - return false; - } - } - Assert.fail("Attempted to assign to nonexistent record field " + name + "."); - return false; // make compiler happy - } - - public final boolean isNormalized() { return this.isNorm; } - - public final void normalize() { - if (!this.isNorm) { - int len = this.names.length; - for (int i = 1; i < len; i++) { - int cmp = this.names[0].compareTo(this.names[i]); - if (cmp == 0) { - Assert.fail("Field name " + this.names[i] + " occurs multiple times in record."); - } - else if (cmp > 0) { - UniqueString ts = this.names[0]; - this.names[0] = this.names[i]; - this.names[i] = ts; - Value tv = this.values[0]; - this.values[0] = this.values[i]; - this.values[i] = tv; - } - } - for (int i = 2; i < len; i++) { - int j = i; - UniqueString st = this.names[i]; - Value val = this.values[i]; - int cmp; - while ((cmp = st.compareTo(this.names[j-1])) < 0) { - this.names[j] = this.names[j-1]; - this.values[j] = this.values[j-1]; - j--; - } - if (cmp == 0) { - Assert.fail("Field name " + this.names[i] + " occurs multiple times in record."); - } - this.names[j] = st; - this.values[j] = val; - } - this.isNorm = true; - } - } - - public final boolean isDefined() { - boolean defined = true; - for (int i = 0; i < this.values.length; i++) { - defined = defined && this.values[i].isDefined(); - } - return defined; - } - - public final Value deepCopy() { - Value[] vals = new Value[this.values.length]; - for (int i = 0; i < this.values.length; i++) { - vals[i] = this.values[i].deepCopy(); - } - return new RecordValue(this.names, vals, this.isNorm); - } - - public final boolean assignable(Value val) { - boolean canAssign = ((val instanceof RecordValue) && - this.names.length == ((RecordValue)val).names.length); - if (!canAssign) return false; - for (int i = 0; i < this.values.length; i++) { - canAssign = (canAssign && - this.names[i].equals(((RecordValue)val).names[i]) && - this.values[i].assignable(((RecordValue)val).values[i])); - } - return canAssign; - } - - /* The fingerprint methods. */ - public final long fingerPrint(long fp) { - this.normalize(); - int rlen = this.names.length; - fp = FP64.Extend(fp, FCNRCDVALUE); - fp = FP64.Extend(fp, rlen); - for (int i = 0; i < rlen; i++) { - String str = this.names[i].toString(); - fp = FP64.Extend(fp, STRINGVALUE); - fp = FP64.Extend(fp, str.length()); - fp = FP64.Extend(fp, str); - fp = this.values[i].fingerPrint(fp); - } - return fp; - } - - public final Value permute(MVPerm perm) { - this.normalize(); - int rlen = this.names.length; - Value[] vals = new Value[rlen]; - boolean changed = false; - for (int i = 0; i < rlen; i++) { - vals[i] = this.values[i].permute(perm); - changed = changed || (vals[i] != this.values[i]); - } - if (changed) { - return new RecordValue(this.names, vals, true); - } - return this; - } - - /* The string representation */ - public final StringBuffer toString(StringBuffer sb, int offset) { - int len = this.names.length; - - sb.append("["); - if (len > 0) { - sb.append(this.names[0] + " |-> "); - sb = this.values[0].toString(sb, offset); - } - for (int i = 1; i < len; i++) { - sb.append(", "); - sb.append(this.names[i] + " |-> "); - sb = this.values[i].toString(sb, offset); - } - return sb.append("]"); - } - -} diff --git a/tlatools/src/tlc2/value/SetCapValue.java b/tlatools/src/tlc2/value/SetCapValue.java deleted file mode 100644 index aa6a1411ba164cf8276e99341a6c19ce5dda58a5..0000000000000000000000000000000000000000 --- a/tlatools/src/tlc2/value/SetCapValue.java +++ /dev/null @@ -1,173 +0,0 @@ -// Copyright (c) 2003 Compaq Corporation. All rights reserved. -// Portions Copyright (c) 2003 Microsoft Corporation. All rights reserved. -// Last modified on Mon 30 Apr 2007 at 13:21:03 PST by lamport -// modified on Fri Aug 10 15:09:21 PDT 2001 by yuanyu - -package tlc2.value; - -import util.Assert; - -public class SetCapValue extends Value implements Enumerable { - public Value set1; - public Value set2; - protected SetEnumValue capSet; - - /* Constructor */ - public SetCapValue(Value set1, Value set2) { - this.set1 = set1; - this.set2 = set2; - this.capSet = null; - } - - public final byte getKind() { return SETCAPVALUE; } - - public final int compareTo(Object obj) { - this.convertAndCache(); - return this.capSet.compareTo(obj); - } - - public final boolean equals(Object obj) { - this.convertAndCache(); - return this.capSet.equals(obj); - } - - public final boolean member(Value elem) { - return (this.set1.member(elem) && this.set2.member(elem)); - } - - public final boolean isFinite() { - if (!this.set1.isFinite() && !this.set2.isFinite()) { - Assert.fail("Attempted to check if the set " + ppr(this.toString()) + "is finite."); - } - return true; - } - - public final Value takeExcept(ValueExcept ex) { - if (ex.idx < ex.path.length) { - Assert.fail("Attempted to apply EXCEPT to the set " + ppr(this.toString()) + "."); - } - return ex.value; - } - - public final Value takeExcept(ValueExcept[] exs) { - if (exs.length != 0) { - Assert.fail("Attempted to apply EXCEPT to the set " + ppr(this.toString()) + "."); - } - return this; - } - - public final int size() { - this.convertAndCache(); - return this.capSet.size(); - } - - public final boolean isNormalized() { - if (this.capSet == null || this.capSet == DummyEnum) { - return (this.set1.isNormalized() && this.set2.isNormalized()); - } - return this.capSet.isNormalized(); - } - - public final void normalize() { - if (this.capSet == null || this.capSet == DummyEnum) { - this.set1.normalize(); - this.set2.normalize(); - } - else { - this.capSet.normalize(); - } - } - - public final boolean isDefined() { - return this.set1.isDefined() && this.set2.isDefined(); - } - - public final Value deepCopy() { return this; } - - public final boolean assignable(Value val) { return this.equals(val); } - - /* The fingerprint methods */ - public final long fingerPrint(long fp) { - this.convertAndCache(); - return this.capSet.fingerPrint(fp); - } - - public final Value permute(MVPerm perm) { - this.convertAndCache(); - return this.capSet.permute(perm); - } - - private final void convertAndCache() { - if (this.capSet == null) { - this.capSet = SetEnumValue.convert(this); - } - else if (this.capSet == DummyEnum) { - SetEnumValue val = null; - synchronized(this) { - if (this.capSet == DummyEnum) { - val = SetEnumValue.convert(this); - val.deepNormalize(); - } - } - synchronized(this) { - if (this.capSet == DummyEnum) { this.capSet = val; } - } - } - } - - /* String representation of this value. */ - public final StringBuffer toString(StringBuffer sb, int offset) { - try { - if (expand) { - Value val = SetEnumValue.convert(this); - return val.toString(sb, offset); - } - } - catch (Throwable e) { /*SKIP*/ } - - sb = this.set1.toString(sb, offset); - sb = sb.append(" \\cap "); - sb = this.set2.toString(sb, offset); - return sb; - } - - public final ValueEnumeration elements() { - if (this.capSet == null || this.capSet == DummyEnum) { - return new Enumerator(); - } - return this.capSet.elements(); - } - - final class Enumerator implements ValueEnumeration { - ValueEnumeration enum1; - Value set; - - public Enumerator() { - if (set1 instanceof Enumerable) { - this.enum1 = ((Enumerable)set1).elements(); - this.set = set2; - } - else if (set2 instanceof Enumerable) { - this.enum1 = ((Enumerable)set2).elements(); - this.set = set1; - } - else { - Assert.fail("Attempted to enumerate S \\cap T when neither S:\n" + - ppr(set1.toString()) + "\nnor T:\n" + ppr(set2.toString()) + - "\nis enumerable"); - } - } - - public final void reset() { this.enum1.reset(); } - - public final Value nextElement() { - Value elem = this.enum1.nextElement(); - while (elem != null) { - if (this.set.member(elem)) return elem; - elem = this.enum1.nextElement(); - } - return null; - } - } - -} diff --git a/tlatools/src/tlc2/value/SetCupValue.java b/tlatools/src/tlc2/value/SetCupValue.java deleted file mode 100644 index 5ae729e2943d1d8fe4cf8219e517b77ac4c57072..0000000000000000000000000000000000000000 --- a/tlatools/src/tlc2/value/SetCupValue.java +++ /dev/null @@ -1,165 +0,0 @@ -// Copyright (c) 2003 Compaq Corporation. All rights reserved. -// Portions Copyright (c) 2003 Microsoft Corporation. All rights reserved. -// Last modified on Mon 30 Apr 2007 at 13:21:03 PST by lamport -// modified on Fri Aug 10 15:09:28 PDT 2001 by yuanyu - -package tlc2.value; - -import util.Assert; - -public class SetCupValue extends Value implements Enumerable { - public Value set1; - public Value set2; - protected SetEnumValue cupSet; - - /* Constructor */ - public SetCupValue(Value set1, Value set2) { - this.set1 = set1; - this.set2 = set2; - this.cupSet = null; - } - - public final byte getKind() { return SETCUPVALUE; } - - public final int compareTo(Object obj) { - this.convertAndCache(); - return this.cupSet.compareTo(obj); - } - - public final boolean equals(Object obj) { - this.convertAndCache(); - return this.cupSet.equals(obj); - } - - public final boolean member(Value elem) { - return this.set1.member(elem) || this.set2.member(elem); - } - - public final boolean isFinite() { - return this.set1.isFinite() && this.set2.isFinite(); - } - - public final Value takeExcept(ValueExcept ex) { - if (ex.idx < ex.path.length) { - Assert.fail("Attempted to apply EXCEPT to the set " + ppr(this.toString()) + "."); - } - return ex.value; - } - - public final Value takeExcept(ValueExcept[] exs) { - if (exs.length != 0) { - Assert.fail("Attempted to apply EXCEPT to the set " + ppr(this.toString()) + "."); - } - return this; - } - - public final int size() { - this.convertAndCache(); - return this.cupSet.size(); - } - - public final boolean isNormalized() { - return (this.cupSet != null && - this.cupSet != DummyEnum && - this.cupSet.isNormalized()); - } - - public final void normalize() { - if (this.cupSet != null && this.cupSet != DummyEnum) { - this.cupSet.normalize(); - } - } - - public final boolean isDefined() { - return this.set1.isDefined() && this.set2.isDefined(); - } - - public final Value deepCopy() { return this; } - - public final boolean assignable(Value val) { - return this.equals(val); - } - - /* The fingerprint methods */ - public final long fingerPrint(long fp) { - this.convertAndCache(); - return this.cupSet.fingerPrint(fp); - } - - public final Value permute(MVPerm perm) { - this.convertAndCache(); - return this.cupSet.permute(perm); - } - - private final void convertAndCache() { - if (this.cupSet == null) { - this.cupSet = SetEnumValue.convert(this); - } - else if (this.cupSet == DummyEnum) { - SetEnumValue val = null; - synchronized(this) { - if (this.cupSet == DummyEnum) { - val = SetEnumValue.convert(this); - val.deepNormalize(); - } - } - synchronized(this) { - if (this.cupSet == DummyEnum) { this.cupSet = val; } - } - } - } - - /* String representation of the value. */ - public final StringBuffer toString(StringBuffer sb, int offset) { - try { - if (expand) { - Value val = SetEnumValue.convert(this); - return val.toString(sb, offset); - } - } - catch (Throwable e) { /*SKIP*/ } - - sb = this.set1.toString(sb, offset); - sb = sb.append(" \\cup "); - sb = this.set2.toString(sb, offset); - return sb; - } - - public final ValueEnumeration elements() { - if (this.cupSet == null || this.cupSet == DummyEnum) { - return new Enumerator(); - } - return this.cupSet.elements(); - } - - final class Enumerator implements ValueEnumeration { - ValueEnumeration enum1; - ValueEnumeration enum2; - - public Enumerator() { - if ((set1 instanceof Enumerable) && - (set2 instanceof Enumerable)) { - this.enum1 = ((Enumerable)set1).elements(); - this.enum2 = ((Enumerable)set2).elements(); - } - else { - Assert.fail("Attempted to enumerate S \\cup T when S:\n" + - ppr(set1.toString()) + "\nand T:\n" + ppr(set2.toString()) + - "\nare not both enumerable"); - } - } - - public final void reset() { - this.enum1.reset(); - this.enum2.reset(); - } - - public final Value nextElement() { - Value elem = this.enum1.nextElement(); - if (elem != null) return elem; - elem = this.enum2.nextElement(); - return elem; - } - } - -} diff --git a/tlatools/src/tlc2/value/SetDiffValue.java b/tlatools/src/tlc2/value/SetDiffValue.java deleted file mode 100644 index d14230d38d11ec5a9afca099b80e720f6e29699d..0000000000000000000000000000000000000000 --- a/tlatools/src/tlc2/value/SetDiffValue.java +++ /dev/null @@ -1,171 +0,0 @@ -// Copyright (c) 2003 Compaq Corporation. All rights reserved. -// Portions Copyright (c) 2003 Microsoft Corporation. All rights reserved. -// Last modified on Mon 30 Apr 2007 at 13:21:03 PST by lamport -// modified on Fri Aug 10 15:09:34 PDT 2001 by yuanyu - -package tlc2.value; - -import util.Assert; - -public class SetDiffValue extends Value implements Enumerable { - public Value set1; - public Value set2; - protected SetEnumValue diffSet; - - /* Constructor */ - public SetDiffValue(Value set1, Value set2) { - this.set1 = set1; - this.set2 = set2; - this.diffSet = null; - } - - public final byte getKind() { return SETDIFFVALUE; } - - public final int compareTo(Object obj) { - this.convertAndCache(); - return this.diffSet.compareTo(obj); - } - - public final boolean equals(Object obj) { - this.convertAndCache(); - return this.diffSet.equals(obj); - } - - public final boolean member(Value elem) { - return (this.set1.member(elem) && !this.set2.member(elem)); - } - - public final boolean isFinite() { - if (this.set1.isFinite()) { - return true; - } - if (!this.set2.isFinite()) { - Assert.fail("Attempted to check if the set " + ppr(this.toString()) + "is finite."); - } - return false; - } - - public final Value takeExcept(ValueExcept ex) { - if (ex.idx < ex.path.length) { - Assert.fail("Attempted to apply EXCEPT to the set " + ppr(this.toString()) + "."); - } - return ex.value; - } - - public final Value takeExcept(ValueExcept[] exs) { - if (exs.length != 0) { - Assert.fail("Attempted to apply EXCEPT to the set " + ppr(this.toString()) + "."); - } - return this; - } - - public final int size() { - this.convertAndCache(); - return this.diffSet.size(); - } - - public final boolean isNormalized() { - if (this.diffSet == null || this.diffSet == DummyEnum) { - return this.set1.isNormalized(); - } - return this.diffSet.isNormalized(); - } - - public final void normalize() { - if (this.diffSet == null || this.diffSet == DummyEnum) { - this.set1.normalize(); - this.set2.normalize(); - } - else { - this.diffSet.normalize(); - } - } - - public final boolean isDefined() { - return this.set1.isDefined() && this.set2.isDefined(); - } - - public final Value deepCopy() { return this; } - - public final boolean assignable(Value val) { - return this.equals(val); - } - - /* The fingerprint methods */ - public final long fingerPrint(long fp) { - this.convertAndCache(); - return this.diffSet.fingerPrint(fp); - } - - public final Value permute(MVPerm perm) { - this.convertAndCache(); - return this.diffSet.permute(perm); - } - - private final void convertAndCache() { - if (this.diffSet == null) { - this.diffSet = SetEnumValue.convert(this); - } - else if (this.diffSet == DummyEnum) { - SetEnumValue val = null; - synchronized(this) { - if (this.diffSet == DummyEnum) { - val = SetEnumValue.convert(this); - val.deepNormalize(); - } - } - synchronized(this) { - if (this.diffSet == DummyEnum) { this.diffSet = val; } - } - } - } - - /* The string representation */ - public final StringBuffer toString(StringBuffer sb, int offset) { - try { - if (expand) { - Value val = SetEnumValue.convert(this); - return val.toString(sb, offset); - } - } - catch (Throwable e) { /*SKIP*/ } - - sb = this.set1.toString(sb, offset); - sb = sb.append(" \\ "); - sb = this.set2.toString(sb, offset); - return sb; - } - - public final ValueEnumeration elements() { - if (this.diffSet == null || this.diffSet == DummyEnum) { - return new Enumerator(); - } - return this.diffSet.elements(); - } - - final class Enumerator implements ValueEnumeration { - ValueEnumeration enum1; - - public Enumerator() { - if (set1 instanceof Enumerable) { - this.enum1 = ((Enumerable)set1).elements(); - } - else { - Assert.fail("Attempted to enumerate S \\ T when S:\n" + - ppr(set1.toString()) + "\nis not enumerable."); - } - } - - public final void reset() { this.enum1.reset(); } - - public final Value nextElement() { - Value elem = this.enum1.nextElement(); - while (elem != null) { - if (!set2.member(elem)) return elem; - elem = this.enum1.nextElement(); - } - return null; - } - } - -} diff --git a/tlatools/src/tlc2/value/SetEnumValue.java b/tlatools/src/tlc2/value/SetEnumValue.java deleted file mode 100644 index 85eecb938558efe9f2aff11998ca339bd9e4d72d..0000000000000000000000000000000000000000 --- a/tlatools/src/tlc2/value/SetEnumValue.java +++ /dev/null @@ -1,378 +0,0 @@ -// Copyright (c) 2003 Compaq Corporation. All rights reserved. -// Portions Copyright (c) 2003 Microsoft Corporation. All rights reserved. -// Last modified on Sat 23 February 2008 at 10:16:24 PST by lamport -// modified on Mon Aug 20 10:54:08 PDT 2001 by yuanyu - -package tlc2.value; - -import tlc2.util.FP64; -import util.Assert; - -public class SetEnumValue extends Value -implements Enumerable, Reducible { - public ValueVec elems; // the elements of the set - private boolean isNorm; // normalized? - - /* Constructor */ - public SetEnumValue(Value[] elems, boolean isNorm) { - this.elems = new ValueVec(elems); - this.isNorm = isNorm; - } - - public SetEnumValue(ValueVec elems, boolean isNorm) { - this.elems = elems; - this.isNorm = isNorm; - } - - public final byte getKind() { return SETENUMVALUE; } - - public final int compareTo(Object obj) { - SetEnumValue set = convert(obj); - if (set == null) { - if (obj instanceof ModelValue) return 1; - Assert.fail("Attempted to compare the set " + ppr(this.toString()) + - " with the value:\n" + ppr(obj.toString())); - } - this.normalize(); - set.normalize(); - int sz = this.elems.size(); - int cmp = sz - set.elems.size(); - if (cmp != 0) return cmp; - for (int i = 0; i < sz; i++) { - cmp = this.elems.elementAt(i).compareTo(set.elems.elementAt(i)); - if (cmp != 0) return cmp; - } - return 0; - } - - public final boolean equals(Object obj) { - SetEnumValue set = convert(obj); - if (set == null) { - if (obj instanceof ModelValue) - return ((ModelValue) obj).modelValueEquals(this) ; - Assert.fail("Attempted to check equality of the set " + ppr(this.toString()) + - " with the value:\n" + ppr(obj.toString())); - } - this.normalize(); - set.normalize(); - int sz = this.elems.size(); - if (sz != set.elems.size()) { - return false; - } - for (int i = 0; i < sz; i++) { - if (!this.elems.elementAt(i).equals(set.elems.elementAt(i))) { - return false; - } - } - return true; - } - - public final boolean member(Value elem) { - return this.elems.search(elem, this.isNorm); - } - - public final boolean isFinite() { return true; } - - public final Value diff(Value val) { - int sz = this.elems.size(); - ValueVec diffElems = new ValueVec(); - for (int i = 0; i < sz; i++) { - Value elem = this.elems.elementAt(i); - if (!val.member(elem)) { - diffElems.addElement(elem); - } - } - return new SetEnumValue(diffElems, this.isNormalized()); - } - - public final Value cap(Value val) { - int sz = this.elems.size(); - ValueVec capElems = new ValueVec(); - for (int i = 0; i < sz; i++) { - Value elem = this.elems.elementAt(i); - if (val.member(elem)) { - capElems.addElement(elem); - } - } - return new SetEnumValue(capElems, this.isNormalized()); - } - - public final Value cup(Value set) { - int sz = this.elems.size(); - if (sz == 0) return set; - - if (set instanceof Reducible) { - ValueVec cupElems = new ValueVec(); - for (int i = 0; i < sz; i++) { - Value elem = this.elems.elementAt(i); - cupElems.addElement(elem); - } - ValueEnumeration Enum = ((Enumerable)set).elements(); - Value elem; - while ((elem = Enum.nextElement()) != null) { - if (!this.member(elem)) cupElems.addElement(elem); - } - return new SetEnumValue(cupElems, false); - } - return new SetCupValue(this, set); - } - - public final Value takeExcept(ValueExcept ex) { - if (ex.idx < ex.path.length) { - Assert.fail("Attempted to apply EXCEPT to the set " + ppr(this.toString()) + "."); - } - return ex.value; - } - - public final Value takeExcept(ValueExcept[] exs) { - if (exs.length != 0) { - Assert.fail("Attempted to apply EXCEPT to the set " + ppr(this.toString()) + "."); - } - return this; - } - - public final int size() { - this.normalize(); - return this.elems.size(); - } - - /* This method normalizes (destructively) this set. */ - public final boolean isNormalized() { return this.isNorm; } - - public final void normalize() { - if (!this.isNorm) { - this.elems.sort(true); // duplicates eliminated - this.isNorm = true; - } - } - - /* Convert val into a SetEnumValue. Returns null if not possible. */ - public static final SetEnumValue convert(Object val) { - switch (((Value)val).getKind()) { - case SETENUMVALUE: - return (SetEnumValue)val; - case INTERVALVALUE: - { - IntervalValue intv = (IntervalValue)val; - Value[] vals = new Value[intv.size()]; - for (int i = 0; i < vals.length; i++) { - vals[i] = IntValue.gen(i + intv.low); - } - return new SetEnumValue(vals, true); - } - case SETCAPVALUE: - { - SetCapValue cap = (SetCapValue)val; - if (cap.capSet != null && cap.capSet != DummyEnum) { - return cap.capSet; - } - ValueVec vals = new ValueVec(); - ValueEnumeration Enum = cap.elements(); - Value elem; - while ((elem = Enum.nextElement()) != null) { - vals.addElement(elem); - } - return new SetEnumValue(vals, cap.isNormalized()); - } - case SETCUPVALUE: - { - SetCupValue cup = (SetCupValue)val; - if (cup.cupSet != null && cup.cupSet != DummyEnum) { - return cup.cupSet; - } - ValueVec vals = new ValueVec(); - ValueEnumeration Enum = cup.elements(); - Value elem; - while ((elem = Enum.nextElement()) != null) { - vals.addElement(elem); - } - return new SetEnumValue(vals, false); - } - case SETDIFFVALUE: - { - SetDiffValue diff = (SetDiffValue)val; - if (diff.diffSet != null && diff.diffSet != DummyEnum) { - return diff.diffSet; - } - ValueVec vals = new ValueVec(); - ValueEnumeration Enum = diff.elements(); - Value elem; - while ((elem = Enum.nextElement()) != null) { - vals.addElement(elem); - } - return new SetEnumValue(vals, diff.set1.isNormalized()); - } - case UNIONVALUE: - { - UnionValue uv = (UnionValue)val; - if (uv.realSet != null && uv.realSet != DummyEnum) { - return uv.realSet; - } - ValueVec vals = new ValueVec(); - ValueEnumeration Enum = uv.elements(); - Value elem; - while ((elem = Enum.nextElement()) != null) { - vals.addElement(elem); - } - return new SetEnumValue(vals, false); - } - case SUBSETVALUE: - { - SubsetValue pset = (SubsetValue)val; - if (pset.pset != null && pset.pset != DummyEnum) { - return pset.pset; - } - ValueVec vals = new ValueVec(pset.size()); - ValueEnumeration Enum = pset.elements(); - Value elem; - while ((elem = Enum.nextElement()) != null) { - vals.addElement(elem); - } - return new SetEnumValue(vals, false); - } - case SETOFRCDSVALUE: - { - SetOfRcdsValue rcds = (SetOfRcdsValue)val; - if (rcds.rcdSet != null && rcds.rcdSet != DummyEnum) { - return rcds.rcdSet; - } - ValueVec vals = new ValueVec(); - ValueEnumeration Enum = rcds.elements(); - Value elem; - while ((elem = Enum.nextElement()) != null) { - vals.addElement(elem); - } - return new SetEnumValue(vals, rcds.isNormalized()); - } - case SETOFFCNSVALUE: - { - SetOfFcnsValue fcns = (SetOfFcnsValue)val; - if (fcns.fcnSet != null && fcns.fcnSet != DummyEnum) { - return fcns.fcnSet; - } - ValueVec vals = new ValueVec(); - ValueEnumeration Enum = fcns.elements(); - Value elem; - while ((elem = Enum.nextElement()) != null) { - vals.addElement(elem); - } - return new SetEnumValue(vals, fcns.isNormalized()); - } - case SETOFTUPLESVALUE: - { - SetOfTuplesValue tvs = (SetOfTuplesValue)val; - if (tvs.tupleSet != null && tvs.tupleSet != DummyEnum) { - return tvs.tupleSet; - } - ValueVec vals = new ValueVec(); - ValueEnumeration Enum = tvs.elements(); - Value elem; - while ((elem = Enum.nextElement()) != null) { - vals.addElement(elem); - } - return new SetEnumValue(vals, tvs.isNormalized()); - } - case SETPREDVALUE: - { - SetPredValue sPred = (SetPredValue)val; - if (sPred.tool == null) return (SetEnumValue)sPred.inVal; - ValueVec vals = new ValueVec(); - ValueEnumeration Enum = sPred.elements(); - Value elem; - while ((elem = Enum.nextElement()) != null) { - vals.addElement(elem); - } - return new SetEnumValue(vals, sPred.isNormalized()); - } - default: - // null if val can not be converted. - return null; - } - } - - public final boolean isDefined() { - boolean defined = true; - int sz = this.elems.size(); - for (int i = 0; i < sz; i++) { - defined = defined && this.elems.elementAt(i).isDefined(); - } - return defined; - } - - public final Value deepCopy() { return this; } - - public final boolean assignable(Value val) { return this.equals(val); } - - /* The fingerprint methods */ - public final long fingerPrint(long fp) { - this.normalize(); - int sz = this.elems.size(); - fp = FP64.Extend(fp, SETENUMVALUE); - fp = FP64.Extend(fp, sz); - for (int i = 0; i < sz; i++) { - Value elem = this.elems.elementAt(i); - fp = elem.fingerPrint(fp); - } - return fp; - } - - public final Value permute(MVPerm perm) { - int sz = this.elems.size(); - Value[] vals = new Value[sz]; - boolean changed = false; - for (int i = 0; i < sz; i++) { - vals[i] = this.elems.elementAt(i).permute(perm); - changed = (changed || vals[i] != this.elems.elementAt(i)); - } - if (changed) { - return new SetEnumValue(vals, false); - } - return this; - } - - /* The string representation */ - public final StringBuffer toString(StringBuffer sb, int offset) { - // If this SetEnumValue object is created by a union, at least one of - // whose elements is a Cartesian product, then this can be an unnormalized - // set with repeated elements. It would therefore seem like a good idea to - // normalize this object here. Since this toString method is probably - // used only for printing the value, it seems that correcting this should - // not do any harm. Therefore, LL added the following if statement - // on 5 Mar 2012. - if (!this.isNormalized()) { - this.normalize(); - } - - int len = this.elems.size(); - sb = sb.append("{"); - if (len > 0) { - this.elems.elementAt(0).toString(sb, offset); - } - for (int i = 1; i < len; i++) { - sb.append(", "); - this.elems.elementAt(i).toString(sb, offset); - } - sb.append("}"); - return sb; - } - - public final ValueEnumeration elements() { return new Enumerator(); } - - final class Enumerator implements ValueEnumeration { - int index = 0; - - public Enumerator() { - normalize(); - } - - public final void reset() { this.index = 0; } - - public final Value nextElement() { - if (this.index < elems.size()) { - return elems.elementAt(this.index++); - } - return null; - } - } - -} diff --git a/tlatools/src/tlc2/value/SetOfFcnsValue.java b/tlatools/src/tlc2/value/SetOfFcnsValue.java deleted file mode 100644 index 751a59f16aa7a957cf1bca8c3b8212eaad424494..0000000000000000000000000000000000000000 --- a/tlatools/src/tlc2/value/SetOfFcnsValue.java +++ /dev/null @@ -1,278 +0,0 @@ -// Copyright (c) 2003 Compaq Corporation. All rights reserved. -// Portions Copyright (c) 2003 Microsoft Corporation. All rights reserved. -// Last modified on Sat 23 February 2008 at 10:17:11 PST by lamport -// modified on Fri Aug 10 15:09:46 PDT 2001 by yuanyu - -package tlc2.value; - -import tlc2.TLCGlobals; -import util.Assert; - -public class SetOfFcnsValue extends Value implements Enumerable { - public Value domain; /* Function domain */ - public Value range; /* Function range */ - protected SetEnumValue fcnSet; - - /* Constructor */ - public SetOfFcnsValue(Value domain, Value range) { - this.domain = domain; - this.range = range; - this.fcnSet = null; - } - - public final byte getKind() { return SETOFFCNSVALUE; } - - public final int compareTo(Object obj) { - this.convertAndCache(); - return this.fcnSet.compareTo(obj); - } - - public final boolean equals(Object obj) { - if (obj instanceof SetOfFcnsValue) { - SetOfFcnsValue fcns = (SetOfFcnsValue)obj; - return (this.domain.equals(fcns.domain) && - this.range.equals(fcns.range)); - } - this.convertAndCache(); - return this.fcnSet.equals(obj); - } - - public final boolean member(Value elem) { - FcnRcdValue fcn = FcnRcdValue.convert(elem); - if (fcn == null) { - if (elem instanceof ModelValue) - return ((ModelValue) elem).modelValueMember(this) ; - Assert.fail("Attempted to check if \n" + elem + "\nwhich is not a TLC function" + - " value, is in the set of functions:\n" + ppr(this.toString())); - } - if (fcn.intv == null) { - fcn.normalize(); - Value fdom = new SetEnumValue(fcn.domain, true); - if (this.domain.equals(fdom)) { - for (int i = 0; i < fcn.values.length; i++) { - if (!this.range.member(fcn.values[i])) { - return false; - } - } - return true; - } - } - else { - if (fcn.intv.equals(this.domain)) { - for (int i = 0; i < fcn.values.length; i++) { - if (!this.range.member(fcn.values[i])) return false; - } - return true; - } - } - return false; - } - - public final boolean isFinite() { - return this.domain.isFinite() && this.range.isFinite(); - } - - public final Value takeExcept(ValueExcept ex) { - if (ex.idx < ex.path.length) { - Assert.fail("Attempted to apply EXCEPT to the set of functions:\n" + - ppr(this.toString())); - } - return ex.value; - } - - public final Value takeExcept(ValueExcept[] exs) { - if (exs.length != 0) { - Assert.fail("Attempted to apply EXCEPT to the set of functions:\n" + - ppr(this.toString())); - } - return this; - } - - public final int size() { - int dsz = this.domain.size(); - int rsz = this.range.size(); - long sz = 1; - for (int i = 0; i < dsz; i++) { - sz *= rsz; - if (sz < -2147483648 || sz > 2147483647) { - Assert.fail("Overflow when computing the number of elements in:\n" + - ppr(toString())); - } - } - return (int)sz; - } - - public final boolean isNormalized() { - if (this.fcnSet == null || this.fcnSet == DummyEnum) { - return this.domain.isNormalized() && this.range.isNormalized(); - } - return this.fcnSet.isNormalized(); - } - - public final void normalize() { - if (this.fcnSet == null || this.fcnSet == DummyEnum) { - this.domain.normalize(); - this.range.normalize(); - } - else { - this.fcnSet.normalize(); - } - } - - public final boolean isDefined() { - return this.domain.isDefined() && this.range.isDefined(); - } - - public final Value deepCopy() { return this; } - - public final boolean assignable(Value val) { - return this.equals(val); - } - - /* The fingerprint */ - public final long fingerPrint(long fp) { - this.convertAndCache(); - return this.fcnSet.fingerPrint(fp); - } - - public final Value permute(MVPerm perm) { - this.convertAndCache(); - return this.fcnSet.permute(perm); - } - - private final void convertAndCache() { - if (this.fcnSet == null) { - this.fcnSet = SetEnumValue.convert(this); - } - else if (this.fcnSet == DummyEnum) { - SetEnumValue val = null; - synchronized(this) { - if (this.fcnSet == DummyEnum) { - val = SetEnumValue.convert(this); - val.deepNormalize(); - } - } - synchronized(this) { - if (this.fcnSet == DummyEnum) { this.fcnSet = val; } - } - } - } - - /* The string representation of the value. */ - public final StringBuffer toString(StringBuffer sb, int offset) { - boolean unlazy = expand; - try { - if (unlazy) { - int dsz = this.domain.size(); - int rsz = this.range.size(); - long sz = 1; - for (int i = 0; i < dsz; i++) { - sz *= rsz; - if (sz < -2147483648 || sz > 2147483647) { - unlazy = false; - break; - } - } - unlazy = sz < TLCGlobals.enumBound; - } - } - catch (Throwable e) { unlazy = false; } - - if (unlazy) { - Value val = SetEnumValue.convert(this); - return val.toString(sb, offset); - } - else { - sb.append("["); - this.domain.toString(sb, offset); - sb.append(" -> "); - this.range.toString(sb, offset); - sb.append("]"); - return sb; - } - } - - public final ValueEnumeration elements() { - if (this.fcnSet == null || this.fcnSet == DummyEnum) { - return new Enumerator(); - } - return this.fcnSet.elements(); - } - - final class Enumerator implements ValueEnumeration { - private Value[] dom; - private ValueEnumeration[] enums; - private Value[] currentElems; - private boolean isDone; - - public Enumerator() { - this.isDone = false; - SetEnumValue domSet = SetEnumValue.convert(domain); - if (domSet == null) - Assert.fail("Attempted to enumerate a set of the form [D -> R]," + - "but the domain D:\n" + ppr(domain.toString()) + - "\ncannot be enumerated."); - domSet.normalize(); - ValueVec elems = domSet.elems; - int sz = elems.size(); - if (range instanceof Enumerable) { - this.dom = new Value[sz]; - this.enums = new ValueEnumeration[sz]; - this.currentElems = new Value[sz]; - // SZ Feb 24, 2009: never read locally - // ValueEnumeration enumeration = ((Enumerable)domSet).elements(); - for (int i = 0; i < sz; i++) { - this.dom[i] = elems.elementAt(i); - this.enums[i] = ((Enumerable)range).elements(); - this.currentElems[i] = this.enums[i].nextElement(); - if (this.currentElems[i] == null) { - this.enums = null; - this.isDone = true; - break; - } - } - } - else { - Assert.fail("Attempted to enumerate a set of the form [D -> R]," + - "but the range R:\n" + ppr(range.toString()) + - "\ncannot be enumerated."); - } - } - - public final void reset() { - if (this.enums != null) { - for (int i = 0; i < this.enums.length; i++) { - this.enums[i].reset(); - this.currentElems[i] = this.enums[i].nextElement(); - } - this.isDone = false; - } - } - - public final Value nextElement() { - if (this.isDone) return null; - Value[] elems = new Value[this.currentElems.length]; - if (elems.length == 0) { - this.isDone = true; - } - else { - for (int i = 0; i < elems.length; i++) { - elems[i] = this.currentElems[i]; - } - for (int i = elems.length-1; i >= 0; i--) { - this.currentElems[i] = this.enums[i].nextElement(); - if (this.currentElems[i] != null) break; - if (i == 0) { - this.isDone = true; - break; - } - this.enums[i].reset(); - this.currentElems[i] = this.enums[i].nextElement(); - } - } - return new FcnRcdValue(this.dom, elems, true); - } - - } - -} diff --git a/tlatools/src/tlc2/value/SetOfRcdsValue.java b/tlatools/src/tlc2/value/SetOfRcdsValue.java deleted file mode 100644 index eddd82c9491bf3b3c0183a55a0feb29ea7725b9a..0000000000000000000000000000000000000000 --- a/tlatools/src/tlc2/value/SetOfRcdsValue.java +++ /dev/null @@ -1,319 +0,0 @@ -// Copyright (c) 2003 Compaq Corporation. All rights reserved. -// Portions Copyright (c) 2003 Microsoft Corporation. All rights reserved. -// Last modified on Sat 23 February 2008 at 10:18:01 PST by lamport -// modified on Fri Aug 10 15:09:53 PDT 2001 by yuanyu - -package tlc2.value; - -import tlc2.TLCGlobals; -import tlc2.output.EC; -import util.Assert; -import util.UniqueString; - -public class SetOfRcdsValue extends Value implements Enumerable { - public UniqueString[] names; // The names of the fields. - public Value[] values; // The values of the fields. - protected SetEnumValue rcdSet; - - /* Constructor */ - public SetOfRcdsValue(UniqueString[] names, Value[] values, boolean isNorm) { - this.names = names; - this.values = values; - this.rcdSet = null; - if (!isNorm) { - this.sortByNames(); - } - } - - public final byte getKind() { return SETOFRCDSVALUE; } - - public final int compareTo(Object obj) { - this.convertAndCache(); - return this.rcdSet.compareTo(obj); - } - - public final boolean equals(Object obj) { - if (obj instanceof SetOfRcdsValue) { - SetOfRcdsValue rcds = (SetOfRcdsValue)obj; - - boolean isEmpty1 = this.isEmpty(); - if (isEmpty1) return rcds.isEmpty(); - if (rcds.isEmpty()) return isEmpty1; - - if (this.names.length != rcds.names.length) { - return false; - } - for (int i = 0; i < this.names.length; i++) { - if (!this.names[i].equals(rcds.names[i]) || - !this.values[i].equals(rcds.values[i])) { - return false; - } - } - return true; - } - this.convertAndCache(); - return this.rcdSet.equals(obj); - } - - public final boolean member(Value elem) { - RecordValue rcd = RecordValue.convert(elem); - if (rcd == null) { - if (elem instanceof ModelValue) - return ((ModelValue) elem).modelValueMember(this) ; - Assert.fail("Attempted to check if non-record\n" + elem + "\nis in the" + - " set of records:\n" + ppr(this.toString())); - } - rcd.normalize(); - if (this.names.length != rcd.names.length) { - return false; - } - for (int i = 0; i < this.names.length; i++) { - if ((!this.names[i].equals(rcd.names[i])) || - (!this.values[i].member(rcd.values[i]))) { - return false; - } - } - return true; - } - - public final boolean isFinite() { - for (int i = 0; i < this.values.length; i++) { - if (!this.values[i].isFinite()) return false; - } - return true; - } - - public final Value takeExcept(ValueExcept ex) { - if (ex.idx < ex.path.length) { - Assert.fail("Attempted to apply EXCEPT to the set of records:\n" + - ppr(this.toString())); - } - return ex.value; - } - - public final Value takeExcept(ValueExcept[] exs) { - if (exs.length != 0) { - Assert.fail("Attempted to apply EXCEPT to the set of records:\n" + - ppr(this.toString())); - } - return this; - } - - public final int size() { - long sz = 1; - for (int i = 0; i < this.values.length; i++) { - sz *= this.values[i].size(); - if (sz < -2147483648 || sz > 2147483647) { - Assert.fail(EC.TLC_MODULE_OVERFLOW, "the number of elements in:\n" + - ppr(this.toString())); - } - } - return (int)sz; - } - - public final boolean isNormalized() { - if (this.rcdSet == null || this.rcdSet == DummyEnum) { - for (int i = 0; i < this.names.length; i++) { - if (!this.values[i].isNormalized()) { - return false; - } - } - return true; - } - return this.rcdSet.isNormalized(); - } - - public final void normalize() { - if (this.rcdSet == null || this.rcdSet == DummyEnum) { - for (int i = 0; i < this.names.length; i++) { - this.values[i].normalize(); - } - } - else { - this.rcdSet.normalize(); - } - } - - private final void sortByNames() { - for (int i = 1; i < this.names.length; i++) { - int cmp = this.names[0].compareTo(this.names[i]); - if (cmp == 0) { - Assert.fail("Field name " + this.names[0] + " occurs multiple times" + - " in set of records."); - } - else if (cmp > 0) { - UniqueString ts = this.names[0]; - this.names[0] = this.names[i]; - this.names[i] = ts; - Value tv = this.values[0]; - this.values[0] = this.values[i]; - this.values[i] = tv; - } - } - for (int i = 2; i < this.names.length; i++) { - int j = i; - UniqueString st = this.names[i]; - Value val = this.values[i]; - int cmp; - while ((cmp = st.compareTo(this.names[j-1])) < 0) { - this.names[j] = this.names[j-1]; - this.values[j] = this.values[j-1]; - j--; - } - if (cmp == 0) { - Assert.fail("Field name " + this.names[i] + " occurs multiple times" + - " in set of records."); - } - this.names[j] = st; - this.values[j] = val; - } - } - - public final boolean isDefined() { - boolean isDefined = true; - for (int i = 0; i < this.values.length; i++) { - isDefined = isDefined && this.values[i].isDefined(); - } - return isDefined; - } - - public final Value deepCopy() { return this; } - - public final boolean assignable(Value val) { - return this.equals(val); - } - - /* The fingerprint */ - public final long fingerPrint(long fp) { - this.convertAndCache(); - return this.rcdSet.fingerPrint(fp); - } - - public final Value permute(MVPerm perm) { - this.convertAndCache(); - return this.rcdSet.permute(perm); - } - - private final void convertAndCache() { - if (this.rcdSet == null) { - this.rcdSet = SetEnumValue.convert(this); - } - else if (this.rcdSet == DummyEnum) { - SetEnumValue val = null; - synchronized(this) { - if (this.rcdSet == DummyEnum) { - val = SetEnumValue.convert(this); - val.deepNormalize(); - } - } - synchronized(this) { - if (this.rcdSet == DummyEnum) { this.rcdSet = val; } - } - } - } - - /* The string representation of the value. */ - public final StringBuffer toString(StringBuffer sb, int offset) { - boolean unlazy = expand; - try { - if (unlazy) { - long sz = 1; - for (int i = 0; i < this.values.length; i++) { - sz *= this.values[i].size(); - if (sz < -2147483648 || sz > 2147483647) { - unlazy = false; - break; - } - } - unlazy = sz < TLCGlobals.enumBound; - } - } - catch (Throwable e) { unlazy = false; } - - if (unlazy) { - Value val = SetEnumValue.convert(this); - return val.toString(sb, offset); - } - else { - sb.append("["); - int len = this.names.length; - if (len != 0) { - sb.append(names[0] + ": "); - this.values[0].toString(sb, offset); - } - for (int i = 1; i < len; i++) { - sb.append(", "); - sb.append(names[i] + ": "); - this.values[i].toString(sb, offset); - } - sb.append("]"); - return sb; - } - } - - public final ValueEnumeration elements() { - if (this.rcdSet == null || this.rcdSet == DummyEnum) { - return new Enumerator(); - } - return this.rcdSet.elements(); - } - - final class Enumerator implements ValueEnumeration { - private ValueEnumeration[] enums; - private Value[] currentElems; - private boolean isDone; - - public Enumerator() { - this.enums = new ValueEnumeration[values.length]; - this.currentElems = new Value[values.length]; - this.isDone = false; - for (int i = 0; i < values.length; i++) { - if (values[i] instanceof Enumerable) { - this.enums[i] = ((Enumerable)values[i]).elements(); - this.currentElems[i] = this.enums[i].nextElement(); - if (this.currentElems[i] == null) { - this.enums = null; - this.isDone = true; - break; - } - } - else { - Assert.fail("Attempted to enumerate a set of the form [l1 : v1, ..., ln : vn]," + - "\nbut can't enumerate the value of the `" + names[i] + "' field:\n" + - ppr(values[i].toString())); - } - } - } - - public final void reset() { - if (this.enums != null) { - for (int i = 0; i < this.enums.length; i++) { - this.enums[i].reset(); - this.currentElems[i] = this.enums[i].nextElement(); - } - this.isDone = false; - } - } - - public final Value nextElement() { - if (this.isDone) return null; - Value[] elems = new Value[this.currentElems.length]; - for (int i = 0; i < elems.length; i++) { - elems[i] = this.currentElems[i]; - } - for (int i = elems.length-1; i >= 0; i--) { - this.currentElems[i] = this.enums[i].nextElement(); - if (this.currentElems[i] != null) break; - if (i == 0) { - this.isDone = true; - break; - } - this.enums[i].reset(); - this.currentElems[i] = this.enums[i].nextElement(); - } - return new RecordValue(names, elems, true); - } - - } - -} diff --git a/tlatools/src/tlc2/value/SetOfTuplesValue.java b/tlatools/src/tlc2/value/SetOfTuplesValue.java deleted file mode 100644 index 3fa97011418a4ad2a7d5d1f72d63b1dfac3f0a7f..0000000000000000000000000000000000000000 --- a/tlatools/src/tlc2/value/SetOfTuplesValue.java +++ /dev/null @@ -1,296 +0,0 @@ -// Copyright (c) 2003 Compaq Corporation. All rights reserved. -// Portions Copyright (c) 2003 Microsoft Corporation. All rights reserved. -// Last modified on Sat 23 February 2008 at 10:18:39 PST by lamport -// modified on Fri Aug 10 15:09:59 PDT 2001 by yuanyu - -package tlc2.value; - -import tlc2.TLCGlobals; -import util.Assert; - -public class SetOfTuplesValue extends Value implements Enumerable { - public Value[] sets; - protected SetEnumValue tupleSet; - - /* Constructor */ - public SetOfTuplesValue(Value[] sets) { - this.sets = sets; - this.tupleSet = null; - } - - public SetOfTuplesValue(Value val) { - this.sets = new Value[1]; - this.sets[0] = val; - this.tupleSet = null; - } - - public SetOfTuplesValue(Value v1, Value v2) { - this.sets = new Value[2]; - this.sets[0] = v1; - this.sets[1] = v2; - this.tupleSet = null; - } - - public final byte getKind() { return SETOFTUPLESVALUE; } - - public final int compareTo(Object obj) { - this.convertAndCache(); - return this.tupleSet.compareTo(obj); - } - - public final boolean equals(Object obj) { - if (obj instanceof SetOfTuplesValue) { - SetOfTuplesValue tvs = (SetOfTuplesValue)obj; - - boolean isEmpty1 = this.isEmpty(); - if (isEmpty1) return tvs.isEmpty(); - if (tvs.isEmpty()) return isEmpty1; - - if (this.sets.length != tvs.sets.length) { - return false; - } - for (int i = 0; i < this.sets.length; i++) { - if (!this.sets[i].equals(tvs.sets[i])) { - return false; - } - } - return true; - } - this.convertAndCache(); - return this.tupleSet.equals(obj); - } - - public final boolean member(Value elem) { - TupleValue tv = TupleValue.convert(elem); - if (tv == null) { - FcnRcdValue fcn = FcnRcdValue.convert(elem); - if (fcn == null) { - if (elem instanceof ModelValue) - return ((ModelValue) elem).modelValueMember(this) ; - Assert.fail("Attempted to check if non-tuple\n" + ppr(elem.toString()) + - "\nis in the set of tuples:\n" + ppr(this.toString())); - } - if (fcn.intv != null) return false; - for (int i = 0; i < fcn.domain.length; i++) { - if (!(fcn.domain[i] instanceof IntValue)) { - Assert.fail("Attempted to check if non-tuple\n" + ppr(elem.toString()) + - "\nis in the set of tuples:\n" + ppr(this.toString())); - } - } - return false; - } - if (tv.elems.length == this.sets.length) { - for (int i = 0; i < this.sets.length; i++) { - if (!this.sets[i].member(tv.elems[i])) - return false; - } - return true; - } - return false; - } - - public final boolean isFinite() { - for (int i = 0; i < this.sets.length; i++) { - if (!this.sets[i].isFinite()) { - return false; - } - } - return true; - } - - public final Value takeExcept(ValueExcept ex) { - if (ex.idx < ex.path.length) { - Assert.fail("Attempted to apply EXCEPT construct to the set of tuples:\n" + - ppr(this.toString())); - } - return ex.value; - } - - public final Value takeExcept(ValueExcept[] exs) { - if (exs.length != 0) { - Assert.fail("Attempted to apply EXCEPT construct to the set of tuples:\n" + - ppr(this.toString())); - } - return this; - } - - public final int size() { - long sz = 1; - for (int i = 0; i < this.sets.length; i++) { - sz *= this.sets[i].size(); - if (sz < -2147483648 || sz > 2147483647) { - Assert.fail("Overflow when computing the number of elements in " + - ppr(this.toString())); - } - } - return (int)sz; - } - - public final boolean isNormalized() { - if (this.tupleSet == null || this.tupleSet == DummyEnum) { - for (int i = 0; i < this.sets.length; i++) { - if (!this.sets[i].isNormalized()) { - return false; - } - } - return true; - } - return this.tupleSet.isNormalized(); - } - - public final void normalize() { - if (this.tupleSet == null || this.tupleSet == DummyEnum) { - for (int i = 0; i < this.sets.length; i++) { - this.sets[i].normalize(); - } - } - else { - this.tupleSet.normalize(); - } - } - - public final boolean isDefined() { - boolean defined = true; - for (int i = 0; i < this.sets.length; i++) { - defined = defined && this.sets[i].isDefined(); - } - return defined; - } - - public final Value deepCopy() { return this; } - - public final boolean assignable(Value val) { - return this.equals(val); - } - - /* The fingerprint */ - public final long fingerPrint(long fp) { - this.convertAndCache(); - return this.tupleSet.fingerPrint(fp); - } - - public final Value permute(MVPerm perm) { - this.convertAndCache(); - return this.tupleSet.permute(perm); - } - - private final void convertAndCache() { - if (this.tupleSet == null) { - this.tupleSet = SetEnumValue.convert(this); - } - else if (this.tupleSet == DummyEnum) { - SetEnumValue val = null; - synchronized(this) { - if (this.tupleSet == DummyEnum) { - val = SetEnumValue.convert(this); - val.deepNormalize(); - } - } - synchronized(this) { - if (this.tupleSet == DummyEnum) { this.tupleSet = val; } - } - } - } - - /* The string representation of the value. */ - public final StringBuffer toString(StringBuffer sb, int offset) { - boolean unlazy = expand; - try { - if (unlazy) { - long sz = 1; - for (int i = 0; i < this.sets.length; i++) { - sz *= this.sets[i].size(); - if (sz < -2147483648 || sz > 2147483647) { - unlazy = false; - break; - } - } - unlazy = sz < TLCGlobals.enumBound; - } - } - catch (Throwable e) { unlazy = false; } - - if (unlazy) { - Value val = SetEnumValue.convert(this); - return val.toString(sb, offset); - } - else { - if (this.sets.length > 0) { - sb.append("("); - this.sets[0].toString(sb, offset); - } - for (int i = 1; i < this.sets.length; i++) { - sb.append(" X "); - this.sets[i].toString(sb, offset); - } - if (this.sets.length > 0) { - sb.append(")"); - } - return sb; - } - } - - public final ValueEnumeration elements() { - if (this.tupleSet == null || this.tupleSet == DummyEnum) { - return new Enumerator(); - } - return this.tupleSet.elements(); - } - - final class Enumerator implements ValueEnumeration { - private ValueEnumeration[] enums; - private Value[] currentElems; - private boolean isDone; - - public Enumerator() { - this.enums = new ValueEnumeration[sets.length]; - this.currentElems = new Value[sets.length]; - this.isDone = false; - for (int i = 0; i < sets.length; i++) { - if (sets[i] instanceof Enumerable) { - this.enums[i] = ((Enumerable)sets[i]).elements(); - this.currentElems[i] = this.enums[i].nextElement(); - if (this.currentElems[i] == null) { - this.enums = null; - this.isDone = true; - break; - } - } - else { - Assert.fail("Attempted to enumerate a set of the form s1 \\X s2 ... \\X sn," + - "\nbut can't enumerate s" + i + ":\n" + ppr(sets[i].toString())); - } - } - } - - public final void reset() { - if (this.enums != null) { - for (int i = 0; i < this.enums.length; i++) { - this.enums[i].reset(); - this.currentElems[i] = this.enums[i].nextElement(); - } - this.isDone = false; - } - } - - public final Value nextElement() { - if (this.isDone) return null; - Value[] elems = new Value[this.currentElems.length]; - for (int i = 0; i < elems.length; i++) { - elems[i] = this.currentElems[i]; - } - for (int i = elems.length-1; i >= 0; i--) { - this.currentElems[i] = this.enums[i].nextElement(); - if (this.currentElems[i] != null) break; - if (i == 0) { - this.isDone = true; - break; - } - this.enums[i].reset(); - this.currentElems[i] = this.enums[i].nextElement(); - } - return new TupleValue(elems); - } - } - -} diff --git a/tlatools/src/tlc2/value/SetPredValue.java b/tlatools/src/tlc2/value/SetPredValue.java deleted file mode 100644 index 3b1915c0ea302ca0a73edb772e11b7e571756905..0000000000000000000000000000000000000000 --- a/tlatools/src/tlc2/value/SetPredValue.java +++ /dev/null @@ -1,261 +0,0 @@ -// Copyright (c) 2003 Compaq Corporation. All rights reserved. -// Portions Copyright (c) 2003 Microsoft Corporation. All rights reserved. -// Last modified on Thu 5 Jul 2007 at 4:44:23 PST by lamport -// modified on Fri Aug 10 15:10:04 PDT 2001 by yuanyu - -package tlc2.value; - -import java.io.IOException; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; - -import tla2sany.semantic.FormalParamNode; -import tla2sany.semantic.SemanticNode; -import tlc2.tool.EvalException; -import tlc2.tool.TLCState; -import tlc2.tool.Tool; -import tlc2.util.Context; -import util.Assert; - -public class SetPredValue extends Value implements Enumerable { - public Object vars; // FormalParamNode or FormalParamNode[] - /*********************************************************************** - * Was OpDeclNode or OpDeclNode[]. * - ***********************************************************************/ - public Value inVal; // the in value or the real set - public SemanticNode pred; // the predicate - public Tool tool; // null iff inVal is the real set - public Context con; - public TLCState state; - public TLCState pstate; - public int control; - - /* Constructor */ - public SetPredValue(Object vars, Value inVal, SemanticNode pred, Tool tool, - Context con, TLCState s0, TLCState s1, int control) { - this.vars = vars; - this.inVal = inVal; - this.pred = pred; - this.tool = tool; - this.con = con; - this.state = s0.copy(); - if (s1 != null) { - this.pstate = s1.copy(); - } else { - this.pstate = null; - } - /** - * The two copy()s above were added by YY on 12 Mar 2010 to fix the - * following bug: When a lazily evaluated expression is saved, the - * state under which it should be evaluated must be saved. The - * s0 and s1 objects with which this method is called can be modified - * after the call, so copies must be made. - */ - this.control = control; - } - - public final byte getKind() { return SETPREDVALUE; } - - public final int compareTo(Object obj) { - this.inVal = SetEnumValue.convert(this); - this.tool = null; - return this.inVal.compareTo(obj); - } - - public final boolean equals(Object obj) { - this.inVal = SetEnumValue.convert(this); - this.tool = null; - return this.inVal.equals(obj); - } - - public final boolean member(Value elem) { - if (this.tool == null) { - return this.inVal.member(elem); - } - try { - if (this.inVal.member(elem)) { - Context con1 = this.con; - if (this.vars instanceof FormalParamNode) { - con1 = con1.cons((FormalParamNode)this.vars, elem); - } - else { - FormalParamNode[] ids = (FormalParamNode[])this.vars; - TupleValue tv = TupleValue.convert(elem); - if ((tv != null) && (tv.elems.length == ids.length)) { - Value[] vals = ((TupleValue)tv).elems; - for (int i = 0; i < ids.length; i++) { - con1 = con1.cons(ids[i], vals[i]); - } - } - else { - Assert.fail("Attempted to check if the value:\n" + ppr(elem.toString()) + - "\nis an element of a set of " + ids.length + "-tuples."); - } - } - Value res = this.tool.eval(this.pred, con1, this.state, this.pstate, this.control); - if (!(res instanceof BoolValue)) { - Assert.fail("The evaluation of predicate " + this.pred + - " yielded non-Boolean value."); - } - return ((BoolValue)res).val; - } - } - catch (EvalException e) { - Assert.fail("Cannot decide if element:\n" + ppr(elem.toString()) + - "\n is element of:\n" + ppr(this.inVal.toString()) + - "\nand satisfies the predicate " + this.pred); - } - return false; - } - - public final boolean isFinite() { - if (!(this.inVal.isFinite())) { - Assert.fail("Attempted to check if expression of form {x \\in S : p(x)} is a " + - "finite set, but cannot check if S:\n" + ppr(this.inVal.toString()) + - "\nis finite."); - } - return true; - } - - public final Value takeExcept(ValueExcept ex) { - if (ex.idx < ex.path.length) { - Assert.fail("Attempted to apply EXCEPT to the set " + ppr(this.toString()) + "."); - } - return ex.value; - } - - public final Value takeExcept(ValueExcept[] exs) { - if (exs.length != 0) { - Assert.fail("Attempted to apply EXCEPT to the set " + ppr(this.toString()) + "."); - } - return this; - } - - public final int size() { - this.inVal = SetEnumValue.convert(this); - this.tool = null; - return this.inVal.size(); - } - - private final void readObject(ObjectInputStream ois) - throws IOException, ClassNotFoundException { - this.inVal = (Value)ois.readObject(); - this.tool = null; - } - - private final void writeObject(ObjectOutputStream oos) throws IOException { - if (this.tool != null) { - this.inVal = SetEnumValue.convert(this); - this.tool = null; - } - oos.writeObject(this.inVal); - } - - /* This method normalizes (destructively) this set. */ - public final boolean isNormalized() { - return this.inVal.isNormalized(); - } - - public final void normalize() { this.inVal.normalize(); } - - public final boolean isDefined() { return true; } - - public final Value deepCopy() { return this; } - - public final boolean assignable(Value val) { - return this.equals(val); - } - - /* The fingerprint method */ - public final long fingerPrint(long fp) { - this.inVal = SetEnumValue.convert(this); - this.tool = null; - return this.inVal.fingerPrint(fp); - } - - public final Value permute(MVPerm perm) { - this.inVal = SetEnumValue.convert(this); - this.tool = null; - return this.inVal.permute(perm); - } - - /* The string representation of the value. */ - public final StringBuffer toString(StringBuffer sb, int offset) { - try { - if (expand) { - Value val = SetEnumValue.convert(this); - return val.toString(sb, offset); - } - } - catch (Throwable e) { /*SKIP*/ } - - sb.append("{"); - if (this.vars instanceof FormalParamNode) { - sb.append(((FormalParamNode)this.vars).getName()); - } - else { - FormalParamNode[] ids = (FormalParamNode[])this.vars; - if (ids.length != 0) sb.append(ids[0].getName()); - for (int i = 1; i < ids.length; i++) { - sb.append(", " + ids[i].getName()); - } - } - sb.append(" \\in " + this.inVal + " : <expression "); - sb.append(this.pred + "> }"); - return sb; - } - - public final ValueEnumeration elements() { - if (this.tool == null) { - return ((SetEnumValue)this.inVal).elements(); - } - return new Enumerator(); - } - - final class Enumerator implements ValueEnumeration { - ValueEnumeration Enum; - - public Enumerator() { - if (!(inVal instanceof Enumerable)) { - Assert.fail("Attempted to enumerate { x \\in S : p(x) } when S:\n" + - ppr(inVal.toString()) + "\nis not enumerable"); - } - this.Enum = ((Enumerable)inVal).elements(); - } - - public final void reset() { this.Enum.reset(); } - - public final Value nextElement() { - Value elem; - while ((elem = this.Enum.nextElement()) != null) { - Context con1 = con; - if (vars instanceof FormalParamNode) { - con1 = con1.cons((FormalParamNode)vars, elem); - } - else { - FormalParamNode[] ids = (FormalParamNode[])vars; - TupleValue tv = TupleValue.convert(elem); - if ((tv != null) && - (((TupleValue)tv).elems.length == ids.length)) { - Value[] vals = ((TupleValue)tv).elems; - for (int i = 0; i < ids.length; i++) { - con1 = con1.cons(ids[i], vals[i]); - } - } - else { - Assert.fail("Attempted to check if the value:\n" + ppr(elem.toString()) + - "\nis an element of a set of " + ids.length + "-tuples."); - } - } - Value res = tool.eval(pred, con1, state, pstate, control); - if (!(res instanceof BoolValue)) { - Assert.fail("Evaluating predicate " + pred + " yielded non-Boolean value."); - } - if (((BoolValue)res).val) return elem; - } - return null; - } - - } - -} diff --git a/tlatools/src/tlc2/value/StringValue.java b/tlatools/src/tlc2/value/StringValue.java deleted file mode 100644 index 3e5507a7c20d31b14a14ef4179fb7fcb9ef6b63c..0000000000000000000000000000000000000000 --- a/tlatools/src/tlc2/value/StringValue.java +++ /dev/null @@ -1,150 +0,0 @@ -// Copyright (c) 2003 Compaq Corporation. All rights reserved. -// Portions Copyright (c) 2003 Microsoft Corporation. All rights reserved. -// Last modified on Sat 23 February 2008 at 10:19:41 PST by lamport -// modified on Fri Aug 10 15:06:37 PDT 2001 by yuanyu - -package tlc2.value; - -import tlc2.util.FP64; -import util.Assert; -import util.UniqueString; - -public class StringValue extends Value { - public UniqueString val; - - /* Constructor */ - public StringValue(String str) { - // SZ 11.04.2009: changed the access method to equivalent - this.val = UniqueString.uniqueStringOf(str); - } - - public StringValue(UniqueString var) { - this.val = var; - } - - public final byte getKind() { return STRINGVALUE; } - - public final UniqueString getVal() { return this.val; } - - public final int compareTo(Object obj) { - if (obj instanceof StringValue) { - return this.val.compareTo(((StringValue)obj).val); - } - if (!(obj instanceof ModelValue)) { - Assert.fail("Attempted to compare string " + ppr(this.toString()) + - " with non-string:\n" + ppr(obj.toString())); - } - return 1; - } - - public final boolean equals(Object obj) { - if (obj instanceof StringValue) { - return this.val.equals(((StringValue)obj).getVal()); - } - if (!(obj instanceof ModelValue)) { - Assert.fail("Attempted to check equality of string " + ppr(this.toString()) + - " with non-string:\n" + ppr(obj.toString())); - } - return ((ModelValue) obj).modelValueEquals(this) ; - } - - public final boolean member(Value elem) { - Assert.fail("Attempted to check if the value:\n" + ppr(elem.toString()) + - "\nis an element of the string " + ppr(this.toString())); - return false; // make compiler happy - } - - public final boolean isFinite() { - Assert.fail("Attempted to check if the string " + ppr(this.toString()) + - " is a finite set."); - return false; // make compiler happy - } - - public final Value takeExcept(ValueExcept ex) { - if (ex.idx < ex.path.length) { - Assert.fail("Attempted to apply EXCEPT construct to the string " + - ppr(this.toString()) + "."); - } - return ex.value; - } - - public final Value takeExcept(ValueExcept[] exs) { - if (exs.length != 0) { - Assert.fail("Attempted to apply EXCEPT construct to the string " + - ppr(this.toString()) + "."); - } - return this; - } - - public final int size() { - Assert.fail("Attempted to compute the number of elements in the string " + - ppr(this.toString()) + "."); - return 0; // make compiler happy - } - - public final boolean isNormalized() { return true; } - - public final void normalize() { /*SKIP*/ } - - public final boolean isDefined() { return true; } - - public final Value deepCopy() { return this; } - - public final boolean assignable(Value val) { - return ((val instanceof StringValue) && - this.equals(val)); - } - - public final int length() { return this.val.length(); } - - /* The fingerprint method */ - public final long fingerPrint(long fp) { - fp = FP64.Extend(fp, STRINGVALUE) ; - fp = FP64.Extend(fp, this.val.length()) ; - fp = FP64.Extend(fp, this.val.toString()); - return fp; - } - - public final Value permute(MVPerm perm) { return this; } - - /************************************************************************* - * toString() modified 23 Aug 2007 by LL to call PrintVersion so strings * - * with special characters are printed properly. * - *************************************************************************/ - final String PrintVersion(String str) { - StringBuffer buf = new StringBuffer(str.length()) ; - for (int i = 0 ; i < str.length() ; i++) { - switch (str.charAt(i)) { - case '\"' : - buf.append("\\\"") ; - break ; - case '\\' : - buf.append("\\\\") ; - break ; - case '\t' : - buf.append("\\t") ; - break ; - case '\n' : - buf.append("\\n") ; - break ; - case '\f' : - buf.append("\\f") ; - break ; - case '\r' : - buf.append("\\r") ; - break ; - default : - buf.append(str.charAt(i)) ; - break ; - } // switch - }; // for - return buf.toString(); - } - - - /* The string representation of the value. */ - public final StringBuffer toString(StringBuffer sb, int offset) { - return sb.append("\"" + PrintVersion(this.val.toString()) + "\""); - } - -} diff --git a/tlatools/src/tlc2/value/SubsetValue.java b/tlatools/src/tlc2/value/SubsetValue.java deleted file mode 100644 index be76f6626238423862fca1e2f2bbd9ff1d56024f..0000000000000000000000000000000000000000 --- a/tlatools/src/tlc2/value/SubsetValue.java +++ /dev/null @@ -1,206 +0,0 @@ -// Copyright (c) 2003 Compaq Corporation. All rights reserved. -// Portions Copyright (c) 2003 Microsoft Corporation. All rights reserved. -// Last modified on Mon 30 Apr 2007 at 13:46:07 PST by lamport -// modified on Fri Aug 10 15:10:16 PDT 2001 by yuanyu - -package tlc2.value; - -import java.util.BitSet; - -import tlc2.output.EC; -import util.Assert; - -public class SubsetValue extends Value implements Enumerable { - public Value set; // SUBSET set - protected SetEnumValue pset; - - /* Constructor */ - public SubsetValue(Value set) { - this.set = set; - this.pset = null; - } - - public final byte getKind() { return SUBSETVALUE; } - - public final int compareTo(Object obj) { - if (obj instanceof SubsetValue) { - return this.set.compareTo(((SubsetValue)obj).set); - } - this.convertAndCache(); - return this.pset.compareTo(obj); - } - - public final boolean equals(Object obj) { - if (obj instanceof SubsetValue) { - return this.set.equals(((SubsetValue)obj).set); - } - this.convertAndCache(); - return this.pset.equals(obj); - } - - public final boolean member(Value val) { - if (val instanceof Enumerable) { - ValueEnumeration Enum = ((Enumerable)val).elements(); - Value elem; - while ((elem = Enum.nextElement()) != null) { - if (!this.set.member(elem)) return false; - } - } - else { - Assert.fail("Attempted to check if the non-enumerable value\n" + - ppr(val.toString()) + "\nis element of\n" + ppr(this.toString())); - } - return true; - } - - public final boolean isFinite() { return this.set.isFinite(); } - - public final Value takeExcept(ValueExcept ex) { - if (ex.idx < ex.path.length) { - Assert.fail("Attempted to apply EXCEPT to the set " + ppr(this.toString()) + "."); - } - return ex.value; - } - - public final Value takeExcept(ValueExcept[] exs) { - if (exs.length != 0) { - Assert.fail("Attempted to apply EXCEPT to the set " + ppr(this.toString()) + "."); - } - return this; - } - - public final int size() { - int sz = this.set.size(); - if (sz >= 31) { - Assert.fail(EC.TLC_MODULE_OVERFLOW, "the number of elements in:\n" + - ppr(this.toString())); - } - return (1 << sz); - } - - public final boolean isNormalized() { - return (this.pset != null && - this.pset != DummyEnum && - this.pset.isNormalized()); - } - - public final void normalize() { - if (this.pset == null || this.pset == DummyEnum) { - this.set.normalize(); - } - else { - this.pset.normalize(); - } - } - - public final boolean isDefined() { return this.set.isDefined(); } - - public final Value deepCopy() { return this; } - - public final boolean assignable(Value val) { return this.equals(val); } - - /* The fingerprint */ - public final long fingerPrint(long fp) { - this.convertAndCache(); - return this.pset.fingerPrint(fp); - } - - public final Value permute(MVPerm perm) { - this.convertAndCache(); - return this.pset.permute(perm); - } - - private final void convertAndCache() { - if (this.pset == null) { - this.pset = SetEnumValue.convert(this); - } - else if (this.pset == DummyEnum) { - SetEnumValue val = null; - synchronized(this) { - if (this.pset == DummyEnum) { - val = SetEnumValue.convert(this); - val.deepNormalize(); - } - } - synchronized(this) { - if (this.pset == DummyEnum) { this.pset = val; } - } - } - } - - /* The string representation */ - public final StringBuffer toString(StringBuffer sb, int offset) { - boolean unlazy = expand; - try { - if (unlazy) { - unlazy = this.set.size() < 7; - } - } - catch (Throwable e) { unlazy = false; } - - if (unlazy) { - Value val = SetEnumValue.convert(this); - return val.toString(sb, offset); - } - else { - sb = sb.append("SUBSET "); - sb = this.set.toString(sb, offset); - return sb; - } - } - - public final ValueEnumeration elements() { - if (this.pset == null || this.pset == DummyEnum) { - return new Enumerator(); - } - return this.pset.elements(); - } - - final class Enumerator implements ValueEnumeration { - ValueVec elems; - private BitSet descriptor; - - public Enumerator() { - set = SetEnumValue.convert(set); - set.normalize(); - this.elems = ((SetEnumValue)set).elems; - this.descriptor = new BitSet(this.elems.size()); - } - - public final void reset() { - this.descriptor = new BitSet(this.elems.size()); - } - - public final Value nextElement() { - if (this.descriptor == null) return null; - ValueVec vals = new ValueVec(); - int sz = this.elems.size(); - if (sz == 0) { - this.descriptor = null; - } - else { - for (int i = 0; i < sz; i++) { - if (this.descriptor.get(i)) { - vals.addElement(this.elems.elementAt(i)); - } - } - for (int i = 0; i < sz; i++) { - if (this.descriptor.get(i)) { - this.descriptor.clear(i); - if (i >= sz - 1) { - this.descriptor = null; - break; - } - } - else { - this.descriptor.set(i); - break; - } - } - } - return new SetEnumValue(vals, true); - } - - } - -} diff --git a/tlatools/src/tlc2/value/TupleValue.java b/tlatools/src/tlc2/value/TupleValue.java deleted file mode 100644 index d385eeaad817079385d1c9f80c6a730d395eb91a..0000000000000000000000000000000000000000 --- a/tlatools/src/tlc2/value/TupleValue.java +++ /dev/null @@ -1,282 +0,0 @@ -// Copyright (c) 2003 Compaq Corporation. All rights reserved. -// Portions Copyright (c) 2003 Microsoft Corporation. All rights reserved. -// Last modified on Mon 30 Apr 2007 at 15:30:09 PST by lamport -// modified on Fri Aug 10 15:10:22 PDT 2001 by yuanyu - -package tlc2.value; - -import tla2sany.semantic.SymbolNode; -import tlc2.output.EC; -import tlc2.output.MP; -import tlc2.tool.EvalControl; -import tlc2.util.Context; -import tlc2.util.FP64; -import util.Assert; - -public class TupleValue extends Value implements Applicable { - public Value[] elems; // the elements of this tuple. - - /* Constructor */ - public TupleValue(Value[] elems) { this.elems = elems; } - - public TupleValue(Value v) { - this.elems = new Value[1]; - this.elems[0] = v; - } - - public TupleValue(Value v1, Value v2) { - this.elems = new Value[2]; - this.elems[0] = v1; - this.elems[1] = v2; - } - - public final byte getKind() { return TUPLEVALUE; } - - public final int compareTo(Object obj) { - TupleValue tv = convert(obj); - if (tv == null) { - // Well, we have to convert this to function and compare. - return FcnRcdValue.convert(this).compareTo(obj); - } - int len = this.elems.length; - int cmp = len - tv.elems.length; - if (cmp == 0) { - for (int i = 0; i < len; i++) { - cmp = this.elems[i].compareTo(tv.elems[i]); - if (cmp != 0) break; - } - } - return cmp; - } - - public final boolean equals(Object obj) { - TupleValue tv = convert(obj); - if (tv == null) { - // Well, we have to convert this to function and compare. - return FcnRcdValue.convert(this).equals(obj); - } - int len = this.elems.length; - if (len != tv.elems.length) - return false; - for (int i = 0; i < len; i++) { - if (!this.elems[i].equals(tv.elems[i])) - return false; - } - return true; - } - - public final boolean member(Value elem) { - Assert.fail("Attempted to check set membership in a tuple value."); - return false; // make compiler happy - } - - public final boolean isFinite() { return true; } - - public final Value apply(Value arg, int control) { - if (!(arg instanceof IntValue)) { - Assert.fail("Attempted to apply tuple to a non-integer argument."); - } - int idx = ((IntValue)arg).val; - if (idx <= 0 || idx > this.elems.length) { - Assert.fail("Attempted to apply tuple\n" + ppr(this.toString()) + - "\nto integer " + idx + " which is out of domain."); - } - return this.elems[idx-1]; - } - - public final Value apply(Value[] args, int control) { - if (args.length != 1) { - Assert.fail("Attetmpted to apply tuple with wrong number of arguments."); - } - return this.apply(args[0], EvalControl.Clear); - } - - public final Value select(Value arg) { - if (!(arg instanceof IntValue)) { - Assert.fail("Attempted to apply tuple to a non-integer argument " + - ppr(arg.toString()) + "."); - } - int idx = ((IntValue)arg).val; - if (idx > 0 && idx <= this.elems.length) { - return this.elems[idx-1]; - } - return null; - } - - public final Value takeExcept(ValueExcept ex) { - if (ex.idx < ex.path.length) { - int tlen = this.elems.length; - Value[] newElems = new Value[tlen]; - Value arcVal = ex.path[ex.idx]; - if (arcVal instanceof IntValue) { - int idx = ((IntValue)arcVal).val - 1; - if (0 <= idx && idx < tlen) { - for (int i = 0; i < tlen; i++) { - newElems[i] = this.elems[i]; - } - ex.idx++; - newElems[idx] = this.elems[idx].takeExcept(ex); - } - return new TupleValue(newElems); - } - MP.printWarning(EC.TLC_WRONG_TUPLE_FIELD_NAME, new String[]{ppr(arcVal.toString())}); - } - return ex.value; - } - - public final Value takeExcept(ValueExcept[] exs) { - Value val = this; - for (int i = 0; i < exs.length; i++) { - val = val.takeExcept(exs[i]); - } - return val; - } - - public final Value getDomain() { - return new IntervalValue(1, this.size()); - } - - public final int size() { return this.elems.length; } - - /* - * This method converts a value to a tuple value. It returns - * null if the conversion fails. - */ - public static TupleValue convert(Object val) { - if (val instanceof TupleValue) { - return (TupleValue)val; - } - else if (val instanceof FcnRcdValue) { - FcnRcdValue fcn = (FcnRcdValue)val; - if (fcn.intv != null) { - if (fcn.intv.low != 1) return null; - return new TupleValue(fcn.values); - } - int len = fcn.values.length; - Value[] elems = new Value[len]; - for (int i = 0; i < len; i++) { - if (!(fcn.domain[i] instanceof IntValue)) return null; - int idx = ((IntValue)fcn.domain[i]).val; - if (0 < idx && idx <= len) { - if (elems[idx-1] != null) return null; - elems[idx-1] = fcn.values[i]; - } - else { - return null; - } - } - return new TupleValue(elems); - } - else if (val instanceof FcnLambdaValue) { - FcnLambdaValue fcn = (FcnLambdaValue)val; - if (fcn.params.length() != 1) return null; - Value dom = fcn.params.domains[0]; - SymbolNode var = fcn.params.formals[0][0]; - if (dom instanceof IntervalValue) { - IntervalValue intv = (IntervalValue)dom; - if (intv.low != 1) return null; - Value[] elems = new Value[intv.high]; - for (int i = 1; i <= intv.high; i++) { - Context c1 = fcn.con.cons(var, IntValue.gen(i)); - elems[i-1] = fcn.tool.eval(fcn.body, c1, fcn.state, fcn.pstate, fcn.control); - } - return new TupleValue(elems); - } - else { - SetEnumValue eSet = SetEnumValue.convert(dom); - if (eSet == null) - Assert.fail("To convert a function of form [x \\in S |-> f(x)] " + - "to a tuple, the set S must be enumerable."); - eSet.normalize(); - int len = eSet.size(); - Value[] elems = new Value[len]; - for (int i = 0; i < len; i++) { - Value argVal = eSet.elems.elementAt(i); - if (!(argVal instanceof IntValue)) return null; - if (((IntValue)argVal).val != i + 1) return null; - Context c1 = fcn.con.cons(var, argVal); - elems[i] = fcn.tool.eval(fcn.body, c1, fcn.state, fcn.pstate, fcn.control); - } - return new TupleValue(elems); - } - } - else if ((val instanceof RecordValue) && - ((RecordValue)val).size() == 0) { - return EmptyTuple; - } - return null; - } - - /* The normalization of the value. */ - public final boolean isNormalized() { return true; } - - public final void normalize() { /*nop*/ } - - public final boolean isDefined() { - boolean defined = true; - for (int i = 0; i < this.elems.length; i++) { - defined = defined && this.elems[i].isDefined(); - } - return defined; - } - - public final Value deepCopy() { - Value[] vals = new Value[this.elems.length]; - for (int i = 0; i < this.elems.length; i++) { - vals[i] = this.elems[i].deepCopy(); - } - return new TupleValue(vals); - } - - public final boolean assignable(Value val) { - boolean canAssign = ((val instanceof TupleValue) && - (this.elems.length == ((TupleValue)val).elems.length)); - if (!canAssign) return false; - for (int i = 0; i < this.elems.length; i++) { - canAssign = canAssign && this.elems[i].assignable(((TupleValue)val).elems[i]); - } - return canAssign; - } - - /* The fingerprint method: tuples are functions. */ - public final long fingerPrint(long fp) { - int len = this.elems.length; - fp = FP64.Extend(fp, FCNRCDVALUE); - fp = FP64.Extend(fp, len); - for (int i = 0; i < len; i++) { - fp = FP64.Extend(fp, INTVALUE); - fp = FP64.Extend(fp, i+1); - fp = this.elems[i].fingerPrint(fp); - } - return fp; - } - - public final Value permute(MVPerm perm) { - Value[] vals = new Value[this.elems.length]; - boolean changed = false; - for (int i = 0; i < vals.length; i++) { - vals[i] = this.elems[i].permute(perm); - changed = changed || (vals[i] != this.elems[i]); - } - if (changed) { - return new TupleValue(vals); - } - return this; - } - - /* The string representation of this value. */ - public final StringBuffer toString(StringBuffer sb, int offset) { - sb.append("<<"); - int len = this.elems.length; - if (len > 0) { - sb = this.elems[0].toString(sb, offset); - } - for (int i = 1; i < len; i++) { - sb = sb.append(", "); - sb = this.elems[i].toString(sb, offset); - } - sb.append(">>"); - return sb; - } - -} diff --git a/tlatools/src/tlc2/value/UndefValue.java b/tlatools/src/tlc2/value/UndefValue.java deleted file mode 100644 index 34765bdb0019eb63e66fdc19e18e9e1b5cad9aba..0000000000000000000000000000000000000000 --- a/tlatools/src/tlc2/value/UndefValue.java +++ /dev/null @@ -1,73 +0,0 @@ -// Copyright (c) 2003 Compaq Corporation. All rights reserved. -// Portions Copyright (c) 2003 Microsoft Corporation. All rights reserved. -// Last modified on Mon 30 Apr 2007 at 13:21:06 PST by lamport -// modified on Tue Aug 15 23:08:23 PDT 2000 by yuanyu - -package tlc2.value; - -import util.Assert; - -public class UndefValue extends Value { - - public UndefValue() { /*SKIP*/ } - - public byte getKind() { return UNDEFVALUE; } - - public final int compareTo(Object obj) { - return (obj instanceof UndefValue) ? 0 : 1; - } - - public final boolean equals(Object obj) { - return (obj instanceof UndefValue); - } - - public final boolean member(Value elem) { - Assert.fail("Attempted to check if the value:\n" + ppr(elem.toString()) + - "\nis an element " + ppr(this.toString())); - return false; // make compiler happy - } - - public final boolean isFinite() { - Assert.fail("Attempted to check if the value " + ppr(this.toString()) + - " is a finite set."); - return false; // make compiler happy - } - - public final Value takeExcept(ValueExcept ex) { - if (ex.idx < ex.path.length) { - Assert.fail("Attempted to apply EXCEPT construct to the value " + - ppr(this.toString()) + "."); - } - return ex.value; - } - - public final Value takeExcept(ValueExcept[] exs) { - if (exs.length != 0) { - Assert.fail("Attempted to apply EXCEPT construct to the value " + - ppr(this.toString()) + "."); - } - return this; - } - - public final int size() { - Assert.fail("Attempted to compute the number of elements in the value " + - ppr(this.toString()) + "."); - return 0; // make compiler happy - } - - public final boolean isNormalized() { return true; } - - public final void normalize() { /*nop*/ } - - public final boolean isDefined() { return false; } - - public final Value deepCopy() { return this; } - - public final boolean assignable(Value val) { return true; } - - /* The string representation. */ - public final StringBuffer toString(StringBuffer sb, int offset) { - return sb.append("UNDEF"); - } - -} diff --git a/tlatools/src/tlc2/value/UnionValue.java b/tlatools/src/tlc2/value/UnionValue.java deleted file mode 100644 index 32e58d10ff5bf2a60845f61c017891ab11f7de91..0000000000000000000000000000000000000000 --- a/tlatools/src/tlc2/value/UnionValue.java +++ /dev/null @@ -1,217 +0,0 @@ -// Copyright (c) 2003 Compaq Corporation. All rights reserved. -// Portions Copyright (c) 2003 Microsoft Corporation. All rights reserved. -// Last modified on Mon 30 Apr 2007 at 13:46:50 PST by lamport -// modified on Fri Aug 10 15:10:39 PDT 2001 by yuanyu - -package tlc2.value; - -import util.Assert; - -public class UnionValue extends Value implements Enumerable { - public Value set; - protected SetEnumValue realSet; - - /* Constructor */ - public UnionValue(Value set) { - this.set = set; - this.realSet = null; - } - - public byte getKind() { return UNIONVALUE; } - - public final int compareTo(Object obj) { - this.convertAndCache(); - return this.realSet.compareTo(obj); - } - - public final boolean equals(Object obj) { - this.convertAndCache(); - return this.realSet.equals(obj); - } - - public final boolean member(Value elem) { - if (!(this.set instanceof Enumerable)) { - Assert.fail("Attempted to check if:\n " + ppr(elem.toString()) + - "\nis an element of the non-enumerable set:\n " + - ppr(this.toString())); - } - ValueEnumeration Enum = ((Enumerable)this.set).elements(); - Value val; - while ((val = Enum.nextElement()) != null) { - if (val.member(elem)) return true; - } - return false; - } - - public final boolean isFinite() { - if (!(this.set instanceof Enumerable)) { - Assert.fail("Attempted to check if the nonenumerable set:\n" + ppr(this.toString()) + - "\nis a finite set."); - } - ValueEnumeration Enum = ((Enumerable)this.set).elements(); - Value val; - while ((val = Enum.nextElement()) != null) { - if (!val.isFinite()) return false; - } - return true; - } - - public final Value takeExcept(ValueExcept ex) { - if (ex.idx < ex.path.length) { - Assert.fail("Attempted to apply EXCEPT to the set:\n" + ppr(this.toString())); - } - return ex.value; - } - - public final Value takeExcept(ValueExcept[] exs) { - if (exs.length != 0) { - Assert.fail("Attempted to apply EXCEPT to the set:\n " + ppr(this.toString()) + "."); - } - return this; - } - - public final int size() { - this.convertAndCache(); - return this.realSet.size(); - } - - public final boolean isNormalized() { - return (this.realSet != null && - this.realSet != DummyEnum && - this.realSet.isNormalized()); - } - - public final void normalize() { - if (this.realSet != null && this.realSet != DummyEnum) { - this.realSet.normalize(); - } - } - - public final boolean isDefined() { return this.set.isDefined(); } - - public final Value deepCopy() { return this; } - - public final boolean assignable(Value val) { - return this.equals(val); - } - - public static Value union(Value val) { - boolean canCombine = (val instanceof SetEnumValue); - if (canCombine) { - ValueVec elems = ((SetEnumValue)val).elems; - for (int i = 0; i < elems.size(); i++) { - canCombine = (canCombine && - (elems.elementAt(i) instanceof SetEnumValue)); - } - if (canCombine) { - ValueVec resElems = new ValueVec(); - Value result = new SetEnumValue(resElems, false); - for (int i = 0; i < elems.size(); i++) { - ValueVec elems1 = ((SetEnumValue)elems.elementAt(i)).elems; - for (int j = 0; j < elems1.size(); j++) { - Value elem = elems1.elementAt(j); - if (!result.member(elem)) resElems.addElement(elem); - } - } - return result; - } - } - return new UnionValue(val); - } - - /* The fingerprint */ - public final long fingerPrint(long fp) { - this.convertAndCache(); - return this.realSet.fingerPrint(fp); - } - - public final Value permute(MVPerm perm) { - this.convertAndCache(); - return this.realSet.permute(perm); - } - - private final void convertAndCache() { - if (this.realSet == null) { - this.realSet = SetEnumValue.convert(this); - } - else if (this.realSet == DummyEnum) { - SetEnumValue val = null; - synchronized(this) { - if (this.realSet == DummyEnum) { - val = SetEnumValue.convert(this); - val.deepNormalize(); - } - } - synchronized(this) { - if (this.realSet == DummyEnum) { this.realSet = val; } - } - } - } - - /* String representation of this value. */ - public final StringBuffer toString(StringBuffer sb, int offset) { - if (expand) { - Value val = SetEnumValue.convert(this); - return val.toString(sb, offset); - } - else { - sb = sb.append("UNION("); - sb = this.set.toString(sb, offset); - sb.append(")"); - return sb; - } - } - - public final ValueEnumeration elements() { - if (this.realSet == null || this.realSet == DummyEnum) { - return new Enumerator(); - } - return this.realSet.elements(); - } - - final class Enumerator implements ValueEnumeration { - ValueEnumeration Enum; - Value elemSet; - ValueEnumeration elemSetEnum; - - public Enumerator() { - if (!(set instanceof Enumerable)) { - Assert.fail("Attempted to enumerate the nonenumerable set:\n"+ - ppr(this.toString())); - } - this.Enum = ((Enumerable)set).elements(); - this.elemSet = this.Enum.nextElement(); - if (this.elemSet != null) { - if (!(this.elemSet instanceof Enumerable)) { - Assert.fail("Attempted to enumerate UNION(s), but some element of s is nonenumerable."); - } - this.elemSetEnum = ((Enumerable)this.elemSet).elements(); - } - } - - public final void reset() { - this.Enum.reset(); - this.elemSet = this.Enum.nextElement(); - this.elemSetEnum = ((Enumerable)this.elemSet).elements(); - } - - public final Value nextElement() { - if (this.elemSet == null) return null; - Value val = this.elemSetEnum.nextElement(); - if (val == null) { - this.elemSet = this.Enum.nextElement(); - if (this.elemSet == null) return null; - if (!(this.elemSet instanceof Enumerable)) { - Assert.fail("Attempted to enumerate the nonenumerable set:\n" + - ppr(this.elemSet.toString()) + - "\nwhen enumerating:\n" + ppr(this.toString())); - } - this.elemSetEnum = ((Enumerable)this.elemSet).elements(); - val = this.nextElement(); - } - return val; - } - - } - -} diff --git a/tlatools/src/tlc2/value/UserValue.java b/tlatools/src/tlc2/value/UserValue.java deleted file mode 100644 index 0fb75732f5b151e2c22c2b5f168bf6fc00634b7f..0000000000000000000000000000000000000000 --- a/tlatools/src/tlc2/value/UserValue.java +++ /dev/null @@ -1,77 +0,0 @@ -// Copyright (c) 2003 Compaq Corporation. All rights reserved. -// Portions Copyright (c) 2003 Microsoft Corporation. All rights reserved. -// Last modified on Mon 30 Apr 2007 at 13:21:09 PST by lamport -// modified on Fri May 11 15:14:04 PDT 2001 by yuanyu - -package tlc2.value; - -import util.Assert; - -public class UserValue extends Value { - public UserObj userObj; - - public UserValue(UserObj obj) { this.userObj = obj; } - - public final byte getKind() { return USERVALUE; } - - public final int compareTo(Object obj) { - if (obj instanceof UserValue) { - return this.userObj.compareTo((Value)obj); - } - if (!(obj instanceof ModelValue)) - Assert.fail("Attempted to compare overridden value " + ppr(this.toString()) + - " with non-overridden value:\n" + ppr(obj.toString())); - return 1; - } - - public final boolean equals(Object obj) { - return (this.compareTo(obj) == 0); - } - - public final boolean member(Value val) { - return this.userObj.member(val); - } - - public final boolean isFinite() { return this.userObj.isFinite(); } - - public final Value takeExcept(ValueExcept ex) { - if (ex.idx < ex.path.length) { - Assert.fail("Attempted to apply EXCEPT to the overridden value " + - ppr(this.toString()) + "."); - } - return ex.value; - } - - public final Value takeExcept(ValueExcept[] exs) { - if (exs.length != 0) { - Assert.fail("Attempted to apply EXCEPT to the overridden value " + - ppr(this.toString()) + "."); - } - return this; - } - - public final int size() { - Assert.fail("Attempted to compute the number of elements in the overridden value " + - ppr(this.toString()) + "."); - return 0; // make compiler happy - } - - /* Nothing to normalize. */ - public final boolean isNormalized() { return true; } - - public final void normalize() { /*SKIP*/ } - - public final boolean isDefined() { return true; } - - public final Value deepCopy() { return this; } - - public final boolean assignable(Value val) { - return this.equals(val); - } - - /* The string representation. */ - public final StringBuffer toString(StringBuffer sb, int offset) { - return this.userObj.toString(sb, offset); - } - -} diff --git a/tlatools/src/tlc2/value/Value.java b/tlatools/src/tlc2/value/Value.java deleted file mode 100644 index 17d48ad4d1b51229f232fff7933eccaa0a12fbd1..0000000000000000000000000000000000000000 --- a/tlatools/src/tlc2/value/Value.java +++ /dev/null @@ -1,366 +0,0 @@ -// Copyright (c) 2003 Compaq Corporation. All rights reserved. -// Portions Copyright (c) 2003 Microsoft Corporation. All rights reserved. -// Last modified on Mon 30 Apr 2007 at 15:30:13 PST by lamport -// modified on Wed Dec 5 23:18:07 PST 2001 by yuanyu - -package tlc2.value; - -import java.io.Serializable; - -import tla2sany.semantic.SemanticNode; -import tlc2.TLCGlobals; -import tlc2.pprint.PrettyPrint; -import tlc2.util.FP64; -import util.Assert; - -public abstract class Value implements ValueConstants, Serializable { - /** - * For each kind of value, we introduce a subclass of Value. - * All the subclasses are given in this value package. - */ - - /** - * This method returns the value kind: an integer that represents - * the kind of this value. See the interface ValueConstants.java. - */ - public abstract byte getKind(); - - public String getKindString() { return ValueImage[this.getKind()]; } - - /* This method compares this with val. */ - public abstract int compareTo(Object val); - - /* This method returns true iff elem is a member of this. */ - public abstract boolean member(Value elem); - - /* This method returns a new value after taking the except. */ - public abstract Value takeExcept(ValueExcept ex); - - /* This method returns a new value after taking the excepts. */ - public abstract Value takeExcept(ValueExcept[] exs); - - /** - * This method normalizes (destructively) the representation of - * the value. It is essential for equality comparison. - */ - public abstract boolean isNormalized(); - public abstract void normalize(); - - public final boolean isEmpty() - { - switch (this.getKind()) { - case SETENUMVALUE: - { - SetEnumValue set = (SetEnumValue)this; - return set.elems.size() == 0; - } - case INTERVALVALUE: - { - IntervalValue intv = (IntervalValue)this; - return intv.size() == 0; - } - case SETCAPVALUE: - { - SetCapValue cap = (SetCapValue)this; - return cap.elements().nextElement() == null; - } - case SETCUPVALUE: - { - SetCupValue cup = (SetCupValue)this; - return cup.elements().nextElement() == null; - } - case SETDIFFVALUE: - { - SetDiffValue diff = (SetDiffValue)this; - return diff.elements().nextElement() == null; - } - case SETOFFCNSVALUE: - { - SetOfFcnsValue fcns = (SetOfFcnsValue)this; - return fcns.elements().nextElement() == null; - } - case SETOFRCDSVALUE: - { - SetOfRcdsValue srv = (SetOfRcdsValue)this; - return srv.elements().nextElement() == null; - } - case SETOFTUPLESVALUE: - { - SetOfTuplesValue stv = (SetOfTuplesValue)this; - return stv.elements().nextElement() == null; - } - case SUBSETVALUE: - { - // SUBSET S is never empty. (It always contains {}.) - return false; - } - case UNIONVALUE: - { - UnionValue uv = (UnionValue)this; - return uv.elements().nextElement() == null; - } - case SETPREDVALUE: - { - SetPredValue spv = (SetPredValue)this; - return spv.elements().nextElement() == null; - } - default: - Assert.fail("Shouldn't call isEmpty() on value " + Value.ppr(this.toString())); - return false; - } - } - - /* Fully normalize this (composite) value. */ - public final void deepNormalize() { - switch (this.getKind()) { - case RECORDVALUE: - { - RecordValue rcd = (RecordValue)this; - for (int i = 0; i < rcd.values.length; i++) { - rcd.values[i].deepNormalize(); - } - rcd.normalize(); - break; - } - case FCNRCDVALUE: - { - FcnRcdValue fcn = (FcnRcdValue)this; - for (int i = 0; i < fcn.values.length; i++) { - fcn.values[i].deepNormalize(); - } - fcn.normalize(); - break; - } - case SETENUMVALUE: - { - SetEnumValue set = (SetEnumValue)this; - for (int i = 0; i < set.elems.size(); i++) { - set.elems.elementAt(i).deepNormalize(); - } - set.normalize(); - break; - } - case TUPLEVALUE: - { - TupleValue tv = (TupleValue)this; - for (int i = 0; i < tv.elems.length; i++) { - tv.elems[i].deepNormalize(); - } - break; - } - case SETCAPVALUE: - { - SetCapValue cap = (SetCapValue)this; - cap.set1.deepNormalize(); - cap.set2.deepNormalize(); - if (cap.capSet == null) { - cap.capSet = DummyEnum; - } - else if (cap.capSet != DummyEnum) { - cap.capSet.deepNormalize(); - } - break; - } - case SETCUPVALUE: - { - SetCupValue cup = (SetCupValue)this; - cup.set1.deepNormalize(); - cup.set2.deepNormalize(); - if (cup.cupSet == null) { - cup.cupSet = DummyEnum; - } - else if (cup.cupSet != DummyEnum) { - cup.cupSet.deepNormalize(); - } - break; - } - case SETDIFFVALUE: - { - SetDiffValue diff = (SetDiffValue)this; - diff.set1.deepNormalize(); - diff.set2.deepNormalize(); - if (diff.diffSet == null) { - diff.diffSet = DummyEnum; - } - else if (diff.diffSet != DummyEnum) { - diff.diffSet.deepNormalize(); - } - break; - } - case SETOFFCNSVALUE: - { - SetOfFcnsValue fcns = (SetOfFcnsValue)this; - fcns.domain.deepNormalize(); - fcns.range.deepNormalize(); - if (fcns.fcnSet == null) { - fcns.fcnSet = DummyEnum; - } - else if (fcns.fcnSet != DummyEnum) { - fcns.fcnSet.deepNormalize(); - } - break; - } - case SETOFRCDSVALUE: - { - SetOfRcdsValue srv = (SetOfRcdsValue)this; - for (int i = 0; i < srv.values.length; i++) { - srv.values[i].deepNormalize(); - } - if (srv.rcdSet == null) { - srv.rcdSet = DummyEnum; - } - else if (srv.rcdSet != DummyEnum) { - srv.rcdSet.deepNormalize(); - } - break; - } - case SETOFTUPLESVALUE: - { - SetOfTuplesValue stv = (SetOfTuplesValue)this; - for (int i = 0; i < stv.sets.length; i++) { - stv.sets[i].deepNormalize(); - } - if (stv.tupleSet == null) { - stv.tupleSet = DummyEnum; - } - else if (stv.tupleSet != DummyEnum) { - stv.tupleSet.deepNormalize(); - } - break; - } - case SUBSETVALUE: - { - SubsetValue pset = (SubsetValue)this; - pset.set.deepNormalize(); - if (pset.pset == null) { - pset.pset = DummyEnum; - } - else if (pset.pset != DummyEnum) { - pset.pset.deepNormalize(); - } - break; - } - case UNIONVALUE: - { - UnionValue uv = (UnionValue)this; - if (uv.realSet == null) { - uv.realSet = DummyEnum; - } - else if (uv.realSet != DummyEnum) { - uv.realSet.deepNormalize(); - } - break; - } - case SETPREDVALUE: - { - SetPredValue spv = (SetPredValue)this; - spv.inVal.deepNormalize(); - break; - } - case FCNLAMBDAVALUE: - { - FcnLambdaValue flv = (FcnLambdaValue)this; - if (flv.fcnRcd == null) { - if (flv.excepts != null) { - for (int i = 0; i < flv.excepts.length; i++) { - flv.excepts[i].value.deepNormalize(); - for (int j = 0; j < flv.excepts[i].path.length; j++) { - flv.excepts[i].path[j].deepNormalize(); - } - } - } - Value[] paramDoms = flv.params.domains; - for (int i = 0; i < paramDoms.length; i++) { - paramDoms[i].deepNormalize(); - } - } - else { - flv.fcnRcd.deepNormalize(); - } - } - default: - break; - } - } - - /* This method returns the fingerprint of this value. */ - public long fingerPrint(long fp) { - Assert.fail("TLC has found a state in which the value of a variable contains " + - Value.ppr(this.toString())); // SZ Feb 24, 2009: changed to static access - return 0; // make compiler happy - } - - /** - * This method returns the value permuted by the permutation. It - * returns this if nothing is permuted. - */ - public Value permute(MVPerm perm) { - Assert.fail("TLC has found a state in which the value of a variable contains " + - Value.ppr(this.toString())); // SZ Feb 24, 2009: changed to static access - return null; // make compiler happy - } - - /* This method returns true iff the value is finite. */ - public abstract boolean isFinite(); - - /* This method returns the size of the value. */ - public abstract int size(); - - /* This method returns true iff the value is fully defined. */ - public abstract boolean isDefined(); - - /* This method makes a real deep copy of this. */ - public abstract Value deepCopy(); - - /* This method returns true iff val can be assigned to this. */ - public abstract boolean assignable(Value val); - - /* This method returns the hash code of this value. */ - public final int hashCode() { - long fp = this.fingerPrint(FP64.New()); - int high = (int)(fp >> 32); - int low = (int)fp; - return high ^ low; - } - - public static boolean expand = true; - - /** - * This method selects the component of this value. The component is - * specified by path. - */ - public final Value select(Value[] path) { - Value result = this; - for (int i = 0; i < path.length; i++) { - if (!(result instanceof Applicable)) { - Assert.fail("Attempted to apply EXCEPT construct to the value " + - ppr(result.toString()) + "."); - } - Value elem = path[i]; - result = ((Applicable)result).select(elem); - if (result == null) return null; - } - return result; - } - - - public static Value getValue(SemanticNode expr) { - return (Value)expr.getToolObject(TLCGlobals.ToolId); - } - - /** - * This abstract method returns a string representation of this - * value. Each subclass must provide its own implementation. - */ - public abstract StringBuffer toString(StringBuffer sb, int offset); - - /* The string representation of this value */ - public final String toString() { - StringBuffer sb = new StringBuffer(); - return this.toString(sb, 0).toString(); - } - - public static String ppr(String s) { - return PrettyPrint.mypp(s, 80) ; - } -} diff --git a/tlatools/src/tlc2/value/ValueConstants.java b/tlatools/src/tlc2/value/ValueConstants.java index e67c6f926b8946e04c27e27239e62094209faec1..108c7b08049db3e49bd9afa493ee8b16a9f2ecc6 100644 --- a/tlatools/src/tlc2/value/ValueConstants.java +++ b/tlatools/src/tlc2/value/ValueConstants.java @@ -5,80 +5,35 @@ package tlc2.value; -import util.UniqueString; - public interface ValueConstants { /* Type code for values. */ - public final byte BOOLVALUE = 0; - public final byte INTVALUE = BOOLVALUE + 1; - public final byte REALVALUE = INTVALUE + 1; - public final byte STRINGVALUE = REALVALUE + 1; - public final byte RECORDVALUE = STRINGVALUE + 1; - public final byte SETENUMVALUE = RECORDVALUE + 1; - public final byte SETPREDVALUE = SETENUMVALUE + 1; - public final byte TUPLEVALUE = SETPREDVALUE + 1; - public final byte FCNLAMBDAVALUE = TUPLEVALUE + 1; - public final byte FCNRCDVALUE = FCNLAMBDAVALUE + 1; - public final byte OPLAMBDAVALUE = FCNRCDVALUE + 1; - public final byte OPRCDVALUE = OPLAMBDAVALUE + 1; - public final byte METHODVALUE = OPRCDVALUE + 1; - public final byte SETOFFCNSVALUE = METHODVALUE + 1; - public final byte SETOFRCDSVALUE = SETOFFCNSVALUE + 1; - public final byte SETOFTUPLESVALUE = SETOFRCDSVALUE + 1; - public final byte SUBSETVALUE = SETOFTUPLESVALUE + 1; - public final byte SETDIFFVALUE = SUBSETVALUE + 1; - public final byte SETCAPVALUE = SETDIFFVALUE + 1; - public final byte SETCUPVALUE = SETCAPVALUE + 1; - public final byte UNIONVALUE = SETCUPVALUE + 1; - public final byte MODELVALUE = UNIONVALUE + 1; - public final byte USERVALUE = MODELVALUE + 1; - public final byte INTERVALVALUE = USERVALUE + 1; - public final byte UNDEFVALUE = INTERVALVALUE + 1; - public final byte LAZYVALUE = UNDEFVALUE + 1; - public final byte DUMMYVALUE = LAZYVALUE + 1; - - public final String[] ValueImage = { - "a Boolean value", // "BoolValue", - "an integer", // "IntValue", - "a real", // "RealValue", - "a string", // "StringValue", - "a record", // "RecordValue", - "a set of the form {e1, ... ,eN}", // "SetEnumValue", - "a set of the form {x \\in S : expr}", // "SetPredValue", - "a tuple", // "TupleValue", - "a function of the form [x \\in S |-> expr]", // "FcnLambdaValue", - "a function of the form (d1 :> e1 @@ ... @@ dN :> eN)", // "FcnRcdValue", - "an operator", // "OpLambdaValue", - "a constant operator", // "OpRcdValue", - "a java method", // "MethodValue", - "a set of the form [S -> T]", // "SetOfFcnsValue", - "a set of the form [d1 : S1, ... , dN : SN]", // "SetOfRcdsValue", - "a cartesian product", // "SetOfTuplesValue", - "a set of the form SUBSET S", // "SubsetValue", - "a set of the form S \\ T", // "SetDiffValue", - "a set of the form S \\cap T", // "SetCapValue", - "a set of the form S \\cup T", // "SetCupValue", - "a set of the form UNION S", // "UnionValue", - "a model value", // "ModelValue", - "a special set constant", // "UserValue", - "a set of the form i..j", // "IntervalValue", - "an undefined value", // "UndefValue", - "a value represented in lazy form", // "LazyValue", - "a dummy for not-a-value", // "DummyValue", - }; - - /* Value constants. */ - public final BoolValue ValTrue = new BoolValue(true); - public final BoolValue ValFalse = new BoolValue(false); - public final IntValue ValZero = IntValue.gen(0); - public final IntValue ValOne = IntValue.gen(1); - public final IntValue ValNegOne = IntValue.gen(-1); - public final Value EmptySet = new SetEnumValue(new ValueVec(0), true); - public final Value EmptyFcn = new FcnRcdValue(new Value[0], new Value[0], true); - public final TupleValue EmptyTuple = new TupleValue(new Value[0]); - public final RecordValue EmptyRcd = new RecordValue(new UniqueString[0], new Value[0], true); - public final UndefValue ValUndef = new UndefValue(); - public final SetEnumValue DummyEnum = new SetEnumValue((ValueVec)null, true); + byte BOOLVALUE = 0; + byte INTVALUE = BOOLVALUE + 1; + byte REALVALUE = INTVALUE + 1; + byte STRINGVALUE = REALVALUE + 1; + byte RECORDVALUE = STRINGVALUE + 1; + byte SETENUMVALUE = RECORDVALUE + 1; + byte SETPREDVALUE = SETENUMVALUE + 1; + byte TUPLEVALUE = SETPREDVALUE + 1; + byte FCNLAMBDAVALUE = TUPLEVALUE + 1; + byte FCNRCDVALUE = FCNLAMBDAVALUE + 1; + byte OPLAMBDAVALUE = FCNRCDVALUE + 1; + byte OPRCDVALUE = OPLAMBDAVALUE + 1; + byte METHODVALUE = OPRCDVALUE + 1; + byte SETOFFCNSVALUE = METHODVALUE + 1; + byte SETOFRCDSVALUE = SETOFFCNSVALUE + 1; + byte SETOFTUPLESVALUE = SETOFRCDSVALUE + 1; + byte SUBSETVALUE = SETOFTUPLESVALUE + 1; + byte SETDIFFVALUE = SUBSETVALUE + 1; + byte SETCAPVALUE = SETDIFFVALUE + 1; + byte SETCUPVALUE = SETCAPVALUE + 1; + byte UNIONVALUE = SETCUPVALUE + 1; + byte MODELVALUE = UNIONVALUE + 1; + byte USERVALUE = MODELVALUE + 1; + byte INTERVALVALUE = USERVALUE + 1; + byte UNDEFVALUE = INTERVALVALUE + 1; + byte LAZYVALUE = UNDEFVALUE + 1; + byte DUMMYVALUE = LAZYVALUE + 1; } diff --git a/tlatools/src/tlc2/value/ValueEnumeration.java b/tlatools/src/tlc2/value/ValueEnumeration.java deleted file mode 100644 index efd6dc27e05ccb6f6d3a8df7364553ca69a97bb2..0000000000000000000000000000000000000000 --- a/tlatools/src/tlc2/value/ValueEnumeration.java +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright (c) 2003 Compaq Corporation. All rights reserved. -// Portions Copyright (c) 2003 Microsoft Corporation. All rights reserved. -// Last modified on Mon 30 Apr 2007 at 13:21:10 PST by lamport -// modified on Tue Aug 22 11:56:52 PDT 2000 by yuanyu - -package tlc2.value; - -public interface ValueEnumeration { - /* Reset allows repeated use of this enumerator. */ - public void reset(); - - /* Return the next element if there is one. Otherwise return null. */ - public Value nextElement(); - -} diff --git a/tlatools/src/tlc2/value/ValueInputStream.java b/tlatools/src/tlc2/value/ValueInputStream.java index 40f00fb926ce10ce7c547e13e607cd1d28e9f844..a2a80d4624be158c50e6b641a3beebb908f02326 100644 --- a/tlatools/src/tlc2/value/ValueInputStream.java +++ b/tlatools/src/tlc2/value/ValueInputStream.java @@ -2,166 +2,150 @@ package tlc2.value; +import java.io.EOFException; import java.io.File; import java.io.IOException; +import java.util.Map; import tlc2.TLCGlobals; +import tlc2.value.impl.BoolValue; +import tlc2.value.impl.FcnRcdValue; +import tlc2.value.impl.IntValue; +import tlc2.value.impl.IntervalValue; +import tlc2.value.impl.ModelValue; +import tlc2.value.impl.RecordValue; +import tlc2.value.impl.SetEnumValue; +import tlc2.value.impl.StringValue; +import tlc2.value.impl.TupleValue; import util.BufferedDataInputStream; import util.FileUtil; +import util.IDataInputStream; import util.UniqueString; import util.WrongInvocationException; -public final class ValueInputStream implements ValueConstants { +public final class ValueInputStream implements ValueConstants, IValueInputStream { - private BufferedDataInputStream dis; - private HandleTable handles; + private final BufferedDataInputStream dis; + private final HandleTable handles; - public ValueInputStream(File file) throws IOException + public ValueInputStream(File file, final boolean compressed) throws IOException { // SZ Feb 24, 2009: FileUtil refactoring - this.dis = FileUtil.newBdFIS(TLCGlobals.useGZIP, file); + this.dis = FileUtil.newBdFIS(compressed, file); this.handles = new HandleTable(); } + + public ValueInputStream(File file) throws IOException + { + this(file, TLCGlobals.useGZIP); + } public ValueInputStream(String fname) throws IOException { this(new File(fname)); } - public final Value read() throws IOException { - byte kind = this.dis.readByte(); - - switch (kind) { - case BOOLVALUE: - { - boolean b = this.dis.readBoolean(); - return (b) ? ValTrue : ValFalse; - } - case INTVALUE: - { - int x = this.dis.readInt(); - return IntValue.gen(x); - } - case STRINGVALUE: - { - UniqueString str = UniqueString.read(this.dis); - Value res = new StringValue(str); - int index = this.handles.getIndex(); - this.handles.assign(res, index); - return res; - } - case MODELVALUE: - { - int index = this.dis.readShort(); - return ModelValue.mvs[index]; - } - case INTERVALVALUE: - { - int low = this.dis.readInt(); - int hi = this.dis.readInt(); - return new IntervalValue(low, hi); - } - case RECORDVALUE: - { - int index = this.handles.getIndex(); - boolean isNorm = true; - int len = this.dis.readInt(); - if (len < 0) { len = -len; isNorm = false; } - UniqueString[] names = new UniqueString[len]; - Value[] vals = new Value[len]; - for (int i = 0; i < len; i++) { - byte kind1 = this.dis.readByte(); - if (kind1 == DUMMYVALUE) { - int index1 = this.readNat(); - names[i] = (UniqueString)this.handles.getValue(index1); - } - else { - int index1 = this.handles.getIndex(); - names[i] = UniqueString.read(this.dis); - this.handles.assign(names[i], index1); - } - vals[i] = this.read(); - } - Value res = new RecordValue(names, vals, isNorm); - this.handles.assign(res, index); - return res; - } - case FCNRCDVALUE: - { - int index = this.handles.getIndex(); - int len = this.readNat(); - int info = this.dis.readByte(); - Value res; - Value[] rvals = new Value[len]; - if (info == 0) { - int low = this.dis.readInt(); - int high = this.dis.readInt(); - for (int i = 0; i < len; i++) { - rvals[i] = this.read(); - } - IntervalValue intv = new IntervalValue(low, high); - res = new FcnRcdValue(intv, rvals); + @Override + public final IValue read() throws IOException { + final byte kind = this.dis.readByte(); + + switch (kind) { + case BOOLVALUE: { + return (this.dis.readBoolean()) ? BoolValue.ValTrue : BoolValue.ValFalse; + } + case INTVALUE: { + return IntValue.gen(this.dis.readInt()); + } + case STRINGVALUE: { + return StringValue.createFrom(this); + } + case MODELVALUE: { + return ModelValue.mvs[this.dis.readShort()]; + } + case INTERVALVALUE: { + return new IntervalValue(this.dis.readInt(), this.dis.readInt()); + } + case RECORDVALUE: { + return RecordValue.createFrom(this); + } + case FCNRCDVALUE: { + return FcnRcdValue.createFrom(this); + } + case SETENUMVALUE: { + return SetEnumValue.createFrom(this); + } + case TUPLEVALUE: { + return TupleValue.createFrom(this); + } + case DUMMYVALUE: { + return (IValue) this.handles.getValue(this.readNat()); + } + default: { + throw new WrongInvocationException("ValueInputStream: Can not unpickle a value of kind " + kind); + } + } } - else { - Value[] dvals = new Value[len]; - for (int i = 0; i < len; i++) { - dvals[i] = this.read(); - rvals[i] = this.read(); - } - res = new FcnRcdValue(dvals, rvals, (info == 1)); - } - this.handles.assign(res, index); - return res; - } - case SETENUMVALUE: - { - int index = this.handles.getIndex(); - boolean isNorm = true; - int len = this.dis.readInt(); - if (len < 0) { len = -len; isNorm = false; } - Value[] elems = new Value[len]; - for (int i = 0; i < len; i++) { - elems[i] = this.read(); - } - Value res = new SetEnumValue(elems, isNorm); - this.handles.assign(res, index); - return res; - } - case TUPLEVALUE: - { - int index = this.handles.getIndex(); - int len = this.readNat(); - Value[] elems = new Value[len]; - for (int i = 0; i < len; i++) { - elems[i] = this.read(); + + public final IValue read(final Map<String, UniqueString> tbl) throws IOException { + final byte kind = this.dis.readByte(); + + switch (kind) { + case BOOLVALUE: { + return (this.dis.readBoolean()) ? BoolValue.ValTrue : BoolValue.ValFalse; + } + case INTVALUE: { + return IntValue.gen(this.dis.readInt()); + } + case STRINGVALUE: { + return StringValue.createFrom(this, tbl); + } + case MODELVALUE: { + return ModelValue.mvs[this.dis.readShort()]; + } + case INTERVALVALUE: { + return new IntervalValue(this.dis.readInt(), this.dis.readInt()); + } + case RECORDVALUE: { + return RecordValue.createFrom(this, tbl); + } + case FCNRCDVALUE: { + return FcnRcdValue.createFrom(this, tbl); + } + case SETENUMVALUE: { + return SetEnumValue.createFrom(this, tbl); + } + case TUPLEVALUE: { + return TupleValue.createFrom(this, tbl); + } + case DUMMYVALUE: { + return (IValue) this.handles.getValue(this.readNat()); + } + default: { + throw new WrongInvocationException("ValueInputStream: Can not unpickle a value of kind " + kind); + } + } } - Value res = new TupleValue(elems); - this.handles.assign(res, index); - return res; - } - case DUMMYVALUE: - { - int index = this.readNat(); - return (Value)this.handles.getValue(index); - } - default: - { - throw new WrongInvocationException("ValueInputStream: Can not unpickle a value of kind " + kind); - } - } + + @Override + public final int readShort() throws IOException { + return this.dis.readShort(); } + @Override public final int readInt() throws IOException { return this.dis.readInt(); } + @Override public final long readLong() throws IOException { return this.dis.readLong(); } + @Override public final void close() throws IOException { this.dis.close(); } + @Override public final int readNat() throws IOException { int res = this.dis.readShort(); if (res >= 0) return res; @@ -169,6 +153,14 @@ public final class ValueInputStream implements ValueConstants { return -res; } + @Override + public final short readShortNat() throws IOException { + short res = this.dis.readByte(); + if (res >= 0) return res; + return (short) -((res << 8) | (this.dis.readByte() & 0xFF)); + } + + @Override public final long readLongNat() throws IOException { long res = this.dis.readInt(); if (res >= 0) return res; @@ -176,6 +168,32 @@ public final class ValueInputStream implements ValueConstants { return -res; } + @Override + public final byte readByte() throws EOFException, IOException { + return this.dis.readByte(); + } + + @Override + public final void assign(final Object obj, final int idx) { + this.handles.assign(obj, idx); + } + + @Override + public final int getIndex() { + return handles.getIndex(); + } + + @Override + public final IDataInputStream getInputStream() { + return dis; + } + + @Override + public final UniqueString getValue(int idx) { + return (UniqueString) this.handles.getValue(idx); + } + + // @see ValueOutputStream#put private static class HandleTable { private Object[] values; private int index; @@ -201,5 +219,4 @@ public final class ValueInputStream implements ValueConstants { final Object getValue(int idx) { return this.values[idx]; } } - } diff --git a/tlatools/src/tlc2/value/ValueOutputStream.java b/tlatools/src/tlc2/value/ValueOutputStream.java index 0b2bdbaa9378f25c2c58004cc29559d96c5113b5..f4b56b4da9fbfa2f3bc350de0f72d64a9cd74295 100644 --- a/tlatools/src/tlc2/value/ValueOutputStream.java +++ b/tlatools/src/tlc2/value/ValueOutputStream.java @@ -11,15 +11,18 @@ import java.util.zip.GZIPOutputStream; import tlc2.TLCGlobals; import util.BufferedDataOutputStream; -import util.WrongInvocationException; -public final class ValueOutputStream implements ValueConstants { +public final class ValueOutputStream implements IValueOutputStream { - private BufferedDataOutputStream dos; - private HandleTable handles; + private final BufferedDataOutputStream dos; + private final HandleTable handles; public ValueOutputStream(File file) throws IOException { - if (TLCGlobals.useGZIP) { + this(file, TLCGlobals.useGZIP); + } + + public ValueOutputStream(File file, final boolean compress) throws IOException { + if (compress) { OutputStream os = new GZIPOutputStream(new FileOutputStream(file)); this.dos = new BufferedDataOutputStream(os); } @@ -30,240 +33,53 @@ public final class ValueOutputStream implements ValueConstants { } public ValueOutputStream(String fname) throws IOException { - if (TLCGlobals.useGZIP) { - OutputStream os = new GZIPOutputStream(new FileOutputStream(fname)); - this.dos = new BufferedDataOutputStream(os); - } - else { - this.dos = new BufferedDataOutputStream(fname); - } - this.handles = new HandleTable(); + this(fname, TLCGlobals.useGZIP); } - - public final void write(Value val) throws IOException { - switch (val.getKind()) { - case BOOLVALUE: - { - this.dos.writeByte(BOOLVALUE); - this.dos.writeBoolean(((BoolValue)val).val); - break; - } - case INTVALUE: - { - this.dos.writeByte(INTVALUE); - dos.writeInt(((IntValue)val).val); - break; - } - case STRINGVALUE: - { - int index = this.handles.put(val); - if (index == -1) { - this.dos.writeByte(STRINGVALUE); - ((StringValue)val).val.write(this.dos); - } - else { - this.dos.writeByte(DUMMYVALUE); - this.writeNat(index); - } - break; - } - case MODELVALUE: - { - this.dos.writeByte(MODELVALUE); - this.dos.writeShort((short)((ModelValue)val).index); - break; - } - case INTERVALVALUE: - { - this.dos.writeByte(INTERVALVALUE); - this.dos.writeInt(((IntervalValue)val).low); - this.dos.writeInt(((IntervalValue)val).high); - break; - } - case RECORDVALUE: - { - int index = this.handles.put(val); - if (index == -1) { - this.dos.writeByte(RECORDVALUE); - RecordValue rval = (RecordValue)val; - int len = rval.names.length; - this.dos.writeInt((rval.isNormalized()) ? len : -len); - for (int i = 0; i < len; i++) { - int index1 = this.handles.put(rval.names[i]); - if (index1 == -1) { - this.dos.writeByte(STRINGVALUE); - rval.names[i].write(this.dos); + + public ValueOutputStream(String fname, boolean zip) throws IOException { + if (zip) { + OutputStream os = new GZIPOutputStream(new FileOutputStream(fname)); + this.dos = new BufferedDataOutputStream(os); } else { - this.dos.writeByte(DUMMYVALUE); - this.writeNat(index1); - } - this.write(rval.values[i]); - } - } - else { - this.dos.writeByte(DUMMYVALUE); - this.writeNat(index); - } - break; - } - case FCNRCDVALUE: - { - int index = this.handles.put(val); - if (index == -1) { - this.dos.writeByte(FCNRCDVALUE); - FcnRcdValue fval = (FcnRcdValue)val; - int len = fval.values.length; - this.writeNat(len); - if (fval.intv != null) { - this.dos.writeByte((byte)0); - this.dos.writeInt(fval.intv.low); - this.dos.writeInt(fval.intv.high); - for (int i = 0; i < len; i++) { - this.write(fval.values[i]); - } - } - else { - this.dos.writeByte((fval.isNormalized()) ? (byte)1 : (byte)2); - for (int i = 0; i < len; i++) { - this.write(fval.domain[i]); - this.write(fval.values[i]); + this.dos = new BufferedDataOutputStream(fname); } + this.handles = new HandleTable(); } - } - else { - this.dos.writeByte(DUMMYVALUE); - this.writeNat(index); - } - break; - } - case SETENUMVALUE: - { - int index = this.handles.put(val); - if (index == -1) { - this.dos.writeByte(SETENUMVALUE); - SetEnumValue sval = (SetEnumValue)val; - int len = sval.elems.size(); - this.dos.writeInt((sval.isNormalized()) ? len : -len); - for (int i = 0; i < len; i++) { - this.write(sval.elems.elementAt(i)); - } - } - else { - this.dos.writeByte(DUMMYVALUE); - this.writeNat(index); - } - break; - } - case TUPLEVALUE: - { - int index = this.handles.put(val); - if (index == -1) { - this.dos.writeByte(TUPLEVALUE); - TupleValue tval = (TupleValue)val; - int len = tval.elems.length; - this.writeNat(len); - for (int i = 0; i < len; i++) { - this.write(tval.elems[i]); - } - } - else { - this.dos.writeByte(DUMMYVALUE); - this.writeNat(index); - } - break; - } - case SETCAPVALUE: - { - SetCapValue cap = (SetCapValue)val; - // Assert.check(cap.capSet != null); - this.write(cap.capSet); - break; - } - case SETCUPVALUE: - { - SetCupValue cup = (SetCupValue)val; - // Assert.check(cup.cupSet != null); - this.write(cup.cupSet); - break; - } - case SETDIFFVALUE: - { - SetDiffValue diff = (SetDiffValue)val; - // Assert.check(diff.diffSet != null); - this.write(diff.diffSet); - break; - } - case SUBSETVALUE: - { - SubsetValue pset = (SubsetValue)val; - // Assert.check(pset.pset != null); - this.write(pset.pset); - break; - } - case UNIONVALUE: - { - UnionValue uv = (UnionValue)val; - // Assert.check(uv.realSet != null); - this.write(uv.realSet); - break; - } - case SETOFRCDSVALUE: - { - SetOfRcdsValue rcds = (SetOfRcdsValue)val; - // Assert.check(rcds.rcdSet != null); - this.write(rcds.rcdSet); - break; - } - case SETOFFCNSVALUE: - { - SetOfFcnsValue fcns = (SetOfFcnsValue)val; - // Assert.check(fcns.fcnSet != null); - this.write(fcns.fcnSet); - break; - } - case SETOFTUPLESVALUE: - { - SetOfTuplesValue tuples = (SetOfTuplesValue)val; - // Assert.check(tuples.tupleSet != null); - this.write(tuples.tupleSet); - break; - } - case SETPREDVALUE: - { - SetPredValue spred = (SetPredValue)val; - // Assert.check(spred.tool == null); - this.write(spred.inVal); - break; - } - case FCNLAMBDAVALUE: - { - FcnLambdaValue flambda = (FcnLambdaValue)val; - // Assert.check(flambda.fcnRcd != null); - this.write(flambda.fcnRcd); - break; - } - default: - { - throw new WrongInvocationException("ValueOutputStream: Can not pickle the value\n" + - Value.ppr(val.toString())); - } - } - } + @Override + public final void writeShort(short x) throws IOException { + this.dos.writeShort(x); + } + + @Override public final void writeInt(int x) throws IOException { this.dos.writeInt(x); } + @Override public final void writeLong(long x) throws IOException { this.dos.writeLong(x); } + @Override public final void close() throws IOException { this.dos.close(); } + + /* Precondition: x is a non-negative short. */ + @Override + public final void writeShortNat(short x) throws IOException { + if (x > 0x7f) { + this.dos.writeShort((short) -x); + } + else { + this.dos.writeByte((byte)x); + } + } /* Precondition: x is a non-negative int. */ + @Override public final void writeNat(int x) throws IOException { if (x > 0x7fff) { this.dos.writeInt(-x); @@ -274,6 +90,7 @@ public final class ValueOutputStream implements ValueConstants { } /* Precondition: x is a non-negative long. */ + @Override public final void writeLongNat(long x) throws IOException { if (x <= 0x7fffffff) { this.dos.writeInt((int)x); @@ -282,6 +99,53 @@ public final class ValueOutputStream implements ValueConstants { this.dos.writeLong(-x); } } + + @Override + public final void writeByte(final byte b) throws IOException { + this.dos.writeByte(b); + } + + @Override + public final void writeBoolean(final boolean b) throws IOException { + this.dos.writeBoolean(b); + } + + @Override + public final BufferedDataOutputStream getOutputStream() { + return dos; + } + + /** + * Check if another TLCState - which is currently also being serialized to the + * same storage (i.e. disk file) - has/contains an identical Value. If yes, do + * not serialize the Value instance again but make this TLCState point to the + * Value instance previously serialized for the other TLCState. In other words, + * this is a custom-tailored compression/de-duplication mechanism for Value + * instances. + * <p> + * This approach only works because both TLCStates are serialized to the same + * storage and thus de-serialized as part of the same operation (same + * Value*Stream instance). + * <p> + * The purpose of this approach appears to be: + * <ul> + * <li>Reduce serialization efforts and storage size</li> + * <li>Reduce the number of Value instances created during de-serialization</li> + * <li>Allow identity comparison on Value instances (AFAICT not used by Value + * explicitly, just UniqueString) to speed up check. Value#equals internally + * likely uses identity comparison as first check.</li> + * </ul> + * <p> + * A disadvantage is the cost of maintaining the internal HandleTable which can + * grow to thousands of elements during serialization/de-serialization (in + * ValueInputStream). Since serialization suspends the DiskStateQueue and thus + * blocks tlc2.tool.Workers from exploring the state space, this might has + * adverse effects. + */ + @Override + public final int put(final Object obj) { + return this.handles.put(obj); + } private static class HandleTable { private int[] spine; @@ -347,62 +211,4 @@ public final class ValueOutputStream implements ValueConstants { } } } - - public static void main(String[] args) { - if (args.length != 1) { - System.err.println("Usage: java tlc2.value.ValueOutputStream filename."); - System.exit(1); - } - - IntValue[] aa = new IntValue[100]; - StringValue[] bb = new StringValue[100]; - - for (int i = 0; i < aa.length; i++) { - aa[i] = IntValue.gen(88); - } - - StringValue sval = new StringValue("ssssssssss"); - for (int i = 0; i < bb.length; i++) { - bb[i] = sval; - } - - try { - /** - BufferedDataOutputStream dos = new BufferedDataOutputStream(args[0]+"_1"); - for (int i = 0; i < aa.length; i++) { - dos.writeInt(88); - } - dos.close(); - - ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(args[0]+"_2")); - for (int i = 0; i < bb.length; i++) { - oos.writeObject(bb[i]); - } - oos.close(); - - ValueOutputStream vos = new ValueOutputStream(new File(args[0]+"_3")); - for (int i = 0; i < bb.length; i++) { - vos.write(bb[i]); - } - vos.close(); - **/ - - ValueOutputStream vos = new ValueOutputStream(new File(args[0])); - long x = 1; - for (int i = 0; i < 63; i++) { - System.err.println("write " + x); - vos.writeLongNat(x); - x = x * 2; - } - vos.close(); - - ValueInputStream vis = new ValueInputStream(new File(args[0])); - for (int i = 0; i < 63; i++) { - System.err.println("read " + vis.readLongNat()); - } - vis.close(); - } - catch (Exception e) { } - } - } diff --git a/tlatools/src/tlc2/value/Values.java b/tlatools/src/tlc2/value/Values.java new file mode 100644 index 0000000000000000000000000000000000000000..22c5ba3af1085710be5e5432fa8ba682c14858ec --- /dev/null +++ b/tlatools/src/tlc2/value/Values.java @@ -0,0 +1,42 @@ +/******************************************************************************* + * Copyright (c) 2019 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.value; + +import tlc2.pprint.PrettyPrint; + +public abstract class Values { + + public static String ppr(String s) { + return PrettyPrint.mypp(s, 80) ; + } + + public static String ppr(IValue v) { + if (v == null) { + return "null"; + } + return PrettyPrint.mypp(v.toString(), 80) ; + } +} diff --git a/tlatools/src/tlc2/value/Applicable.java b/tlatools/src/tlc2/value/impl/Applicable.java similarity index 56% rename from tlatools/src/tlc2/value/Applicable.java rename to tlatools/src/tlc2/value/impl/Applicable.java index 2f784f73a99f03b63de9e593e327e27934db3501..3822610bebdcf9b0e245320b9c09d7c5cefef5ed 100644 --- a/tlatools/src/tlc2/value/Applicable.java +++ b/tlatools/src/tlc2/value/impl/Applicable.java @@ -3,15 +3,15 @@ // Last modified on Mon 30 Apr 2007 at 13:20:42 PST by lamport // modified on Wed Jun 2 00:10:22 PDT 1999 by yuanyu -package tlc2.value; +package tlc2.value.impl; import tlc2.tool.EvalException; public interface Applicable { - public Value apply(Value[] args, int control) throws EvalException; - public Value apply(Value arg, int control) throws EvalException; - public Value getDomain() throws EvalException; - public Value select(Value arg) throws EvalException; + Value apply(Value[] args, int control) throws EvalException; + Value apply(Value arg, int control) throws EvalException; + Value getDomain() throws EvalException; + Value select(Value arg) throws EvalException; } diff --git a/tlatools/src/tlc2/value/impl/BoolValue.java b/tlatools/src/tlc2/value/impl/BoolValue.java new file mode 100644 index 0000000000000000000000000000000000000000..a6b40dc3bb7d40b15c3e27f4e3a98aee00d8462c --- /dev/null +++ b/tlatools/src/tlc2/value/impl/BoolValue.java @@ -0,0 +1,212 @@ +// Copyright (c) 2003 Compaq Corporation. All rights reserved. +// Portions Copyright (c) 2003 Microsoft Corporation. All rights reserved. +// Last modified on Wed 12 Jul 2017 at 16:10:00 PST by ian morris nieves +// modified on Sat 23 February 2008 at 10:01:16 PST by lamport +// modified on Fri Aug 10 15:07:07 PDT 2001 by yuanyu + +package tlc2.value.impl; + +import java.io.IOException; + +import tlc2.tool.FingerprintException; +import tlc2.util.FP64; +import tlc2.value.IBoolValue; +import tlc2.value.IMVPerm; +import tlc2.value.IValue; +import tlc2.value.IValueOutputStream; +import tlc2.value.Values; +import util.Assert; + +public class BoolValue extends Value implements IBoolValue { + public boolean val; // the boolean + public static final BoolValue ValFalse = new BoolValue(false); + /* Value constants. */ + public static final BoolValue ValTrue = new BoolValue(true); + + /* Constructor */ + public BoolValue(boolean b) { this.val = b; } + + @Override + public final boolean getVal() { + return val; + } + + @Override + public final byte getKind() { return BOOLVALUE; } + + @Override + public final int compareTo(Object obj) { + try { + if (obj instanceof BoolValue) { + int x = this.val ? 1 : 0; + int y = ((BoolValue)obj).val ? 1 : 0; + return x - y; + } + if (!(obj instanceof ModelValue)) { + Assert.fail("Attempted to compare boolean " + Values.ppr(this.toString()) + + " with non-boolean:\n" + Values.ppr(obj.toString())); + } + return 1; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + public final boolean equals(Object obj) { + try { + if (obj instanceof BoolValue) { + return this.val == ((BoolValue)obj).val; + } + if (!(obj instanceof ModelValue)) { + Assert.fail("Attempted to compare equality of boolean " + Values.ppr(this.toString()) + + " with non-boolean:\n" + Values.ppr(obj.toString())); + } + return ((ModelValue) obj).modelValueEquals(this) ; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final boolean member(Value elem) { + try { + Assert.fail("Attempted to check if the value:\n" + Values.ppr(elem.toString()) + + "\nis an element of the boolean " + Values.ppr(this.toString())); + return false; // make compiler happy + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final boolean isFinite() { + try { + Assert.fail("Attempted to check if the boolean " + Values.ppr(this.toString()) + + " is a finite set."); + return false; // make compiler happy + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final Value takeExcept(ValueExcept ex) { + try { + if (ex.idx < ex.path.length) { + Assert.fail("Attempted to apply EXCEPT construct to the boolean " + + Values.ppr(this.toString()) + "."); + } + return ex.value; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final Value takeExcept(ValueExcept[] exs) { + try { + if (exs.length != 0) { + Assert.fail("Attempted to apply EXCEPT construct to the boolean " + + Values.ppr(this.toString()) + "."); + } + return this; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final int size() { + try { + Assert.fail("Attempted to compute the number of elements in the boolean " + + Values.ppr(this.toString()) + "."); + return 0; // make compiler happy + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public boolean mutates() { + return false; + } + + @Override + public final boolean isNormalized() { return true; } + + @Override + public final Value normalize() { /*nop*/ return this; } + + @Override + public final boolean isDefined() { return true; } + + @Override + public final IValue deepCopy() { return this; } + + @Override + public final boolean assignable(Value val) { + try { + return ((val instanceof BoolValue) && + this.val == ((BoolValue)val).val); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public void write(IValueOutputStream vos) throws IOException { + vos.writeByte(BOOLVALUE); + vos.writeBoolean(val); + } + + /* The fingerprint method */ + @Override + public final long fingerPrint(long fp) { + try { + fp = FP64.Extend(fp, BOOLVALUE) ; + fp = FP64.Extend(fp, (this.val) ? 't' : 'f') ; + return fp; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final IValue permute(IMVPerm perm) { return this; } + + /* The string representation */ + public final StringBuffer toString(StringBuffer sb, int offset) { + return toString(sb, offset, true); +} + +/* The string representation */ + @Override + public final StringBuffer toString(StringBuffer sb, int offset, boolean swallow) { + try { + return sb.append((this.val) ? "TRUE" : "FALSE"); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + +} diff --git a/tlatools/src/tlc2/value/impl/CallableValue.java b/tlatools/src/tlc2/value/impl/CallableValue.java new file mode 100644 index 0000000000000000000000000000000000000000..f047ac80e450fd1d8402f90766a6159e2c59c138 --- /dev/null +++ b/tlatools/src/tlc2/value/impl/CallableValue.java @@ -0,0 +1,63 @@ +/******************************************************************************* + * Copyright (c) 2020 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.value.impl; + +import java.lang.invoke.MethodHandles; +import java.lang.reflect.Method; +import java.util.concurrent.Callable; + +import tla2sany.semantic.ExprOrOpArgNode; +import tlc2.output.EC; +import tlc2.tool.TLCState; +import tlc2.tool.coverage.CostModel; +import tlc2.tool.impl.Tool; +import tlc2.util.Context; +import tlc2.value.IValue; +import util.Assert; + +public class CallableValue extends EvaluatingValue { + + public CallableValue(Method md, int minLevel) throws IllegalAccessException { + super(MethodHandles.publicLookup().unreflect(md).asSpreader(IValue[].class, md.getParameterCount()), md, minLevel); + } + + @Override + public Value eval(final Tool tool, final ExprOrOpArgNode[] args, final Context c, final TLCState s0, + final TLCState s1, final int control, final CostModel cm) { + final Value[] argVals = new Value[args.length]; + // evaluate the operator's arguments: + for (int i = 0; i < args.length; i++) { + argVals[i] = tool.eval(args[i], c, s0, s1, control, cm); + } + try { + final Callable<?> cl = (Callable<?>) this.mh.invoke(argVals); + s1.setCallable(cl); + } catch (Throwable e) { + Assert.fail(EC.TLC_MODULE_VALUE_JAVA_METHOD_OVERRIDE, new String[] { this.md.toString(), e.getMessage() }); + } + return BoolValue.ValTrue; + } +} diff --git a/tlatools/src/tlc2/value/impl/Enumerable.java b/tlatools/src/tlc2/value/impl/Enumerable.java new file mode 100644 index 0000000000000000000000000000000000000000..2622b6a8f93c05bee0189c7ab298c99932edee0b --- /dev/null +++ b/tlatools/src/tlc2/value/impl/Enumerable.java @@ -0,0 +1,40 @@ +// Copyright (c) 2003 Compaq Corporation. All rights reserved. +// Portions Copyright (c) 2003 Microsoft Corporation. All rights reserved. +// Last modified on Mon 30 Apr 2007 at 13:20:54 PST by lamport +// modified on Thu Mar 11 21:25:20 PST 1999 by yuanyu + +package tlc2.value.impl; + +import tlc2.value.IValue; + +public interface Enumerable extends IValue { + + enum Ordering { + UNDEFINED, + /** + * The normalized order is the order of elements when a Value gets + * fingerprinted (@see {@link Value#fingerPrint(long)}. + */ + NORMALIZED + } + + @Override + int size(); + boolean member(Value elem); + /** + * Semantics or Enumerable#elements(Ordering#UNDEFINED) + */ + ValueEnumeration elements(); + ValueEnumeration elements(final Ordering ordering); + /** + * Returns a {@link ValueEnumeration} which returns k + * {@link IValue}s of all {@link IValue}s returned by + * {@link Enumerable#elements()}. In other words, it returns + * a randomly chosen subset of all {@link IValue} members of + * this {@link Enumerable}. + */ + ValueEnumeration elements(final int k); + EnumerableValue getRandomSubset(final int k); + Value isSubsetEq(Value other); +} + diff --git a/tlatools/src/tlc2/value/impl/EnumerableValue.java b/tlatools/src/tlc2/value/impl/EnumerableValue.java new file mode 100644 index 0000000000000000000000000000000000000000..86586301a1e8549df487709e53bd3c1d62ed2e42 --- /dev/null +++ b/tlatools/src/tlc2/value/impl/EnumerableValue.java @@ -0,0 +1,171 @@ +/******************************************************************************* + * Copyright (c) 2017 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + * Ian Morris Nieves - added support for fingerprint stack trace + ******************************************************************************/ + +package tlc2.value.impl; + +import java.util.List; + +import tlc2.tool.FingerprintException; +import tlc2.value.RandomEnumerableValues; + +public abstract class EnumerableValue extends Value implements Enumerable { + + @Override + public Value isSubsetEq(Value other) { + try { + final ValueEnumeration Enum = this.elements(); + Value elem; + while ((elem = Enum.nextElement()) != null) { + if (!other.member(elem)) { + return BoolValue.ValFalse; + } + } + return BoolValue.ValTrue; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public EnumerableValue getRandomSubset(final int kOutOfN) { + // By default, convert all EVs into SetEnumValue and delegate to its + // getRandomSubset. + return ((SetEnumValue) this.toSetEnum()).getRandomSubset(kOutOfN); + } + + @Override + public ValueEnumeration elements(final Ordering ordering) { + if (ordering == Ordering.NORMALIZED) { + // By default, none of the EnumerableValues supports a ValueEnumeration that + // provides normalized ordering. Thus, to traverse the elements in normalized + // ordering, any EV type gets converted into a SetEnumValue - this effectively + // enumerates the EV and normalizes the new SEV. Traversing the normalized + // SEV with a ValueEnumeration returned by Enumerable#elements is guaranteed + // to be in normalized order (@see Ordering.NORMALIZED for what this means). + // In case a subclass provides a more efficient ValueEnumeration that guarantees + // normalized order, the subclass may override this default method. This is + // so far done by SubsetValue. + final Value enumerated = this.toSetEnum(); + if (enumerated != null) { + return ((Enumerable) enumerated.normalize()).elements(); + } + } + return elements(); + } + + @Override + public ValueEnumeration elements(final int k) { + // The generic implementation collects all n elements of the actual Enumerable + // into the temporary variable values. The SubSetEnumerator then randomly + // returns up to k elements. + // The underlying assuming here is that the size of Enumerable (n) is + // very small and this method is not called as part of the next-state relation. + // If it gets called on larger sets or as part of the next-state relation, + // subclasses can provide a more efficient implementation (e.g. see + // IntervalValue and SetEnumValue which return a more efficient subclass + // of SubsetEnumerator). + final List<Value> values = elements().all(); + return new SubsetEnumerator(k) { + @Override + public Value nextElement() { + if (!hasNext()) { + return null; + } + return values.get(nextIndex()); + } + }; + } + + abstract class SubsetEnumerator implements ValueEnumeration { + + protected final long x; + protected final int a; + protected final int n; + protected final int k; + protected int i; + + public SubsetEnumerator(final int k) { + this(k, size()); + } + + public SubsetEnumerator(final int k, final int n) { + this.n = n; + + // https://en.wikipedia.org/wiki/Linear_congruential_generator + // + // x has to be co-prime to n. Since n might or might not be a prime number + // - it depends on the actual size of the set - we simply set x to + // be a prime number. The prime x has to be larger than n though, since n is + // bound by Integer.MAX_VALUE, we simply choose the Mersenne prime + // Integer.MAX_VALUE + // for x and accept that we fail if n = Integer.MAX_VALUE. To minimize + // intermediate results and the rounds of mod in nextIndex, we choose 191 if n + // happens to be smaller (very likely). + assert n < Integer.MAX_VALUE; + this.x = n < 191 ? 191L : 1L * Integer.MAX_VALUE; + + // k out of n elements in the range 0 <= k <= n. + if (n > 0) { + this.k = k; + this.a = RandomEnumerableValues.get().nextInt(n); + } else { + this.k = 0; + this.a = 0; // RANDOM.nextInt(0) causes IllegalArgumentException. + } + } + + @Override + public void reset() { + i = 0; + } + + public boolean hasNext() { + return i < k; + } + + /** + * @return A random (uniformly distributed) index in the range [0,n) where n is + * {@link Enumerable#size()}. + */ + public int nextIndex() { + if (n <= 0) { + i++; + return 0; + } + // long x avoids intermediate overflow, final cast to int safe though because of + // mod n. + final int index = (int) (((x * i++) + a) % n); + assert 0 <= index && index < n; + return index; + } + + @Override + public abstract Value nextElement(); + } +} diff --git a/tlatools/src/tlc2/value/impl/EvaluatingValue.java b/tlatools/src/tlc2/value/impl/EvaluatingValue.java new file mode 100644 index 0000000000000000000000000000000000000000..6847405e172d4664f4dca3352536bb49dfdc5d3a --- /dev/null +++ b/tlatools/src/tlc2/value/impl/EvaluatingValue.java @@ -0,0 +1,256 @@ +/******************************************************************************* + * Copyright (c) 2019 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.value.impl; + +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.reflect.Method; + +import tla2sany.semantic.ExprOrOpArgNode; +import tlc2.output.EC; +import tlc2.output.MP; +import tlc2.tool.FingerprintException; +import tlc2.tool.TLCState; +import tlc2.tool.coverage.CostModel; +import tlc2.tool.impl.Tool; +import tlc2.util.Context; +import tlc2.value.IValue; +import tlc2.value.Values; +import util.Assert; +import util.Assert.TLCRuntimeException; +import util.WrongInvocationException; + +public class EvaluatingValue extends OpValue implements Applicable { + protected final MethodHandle mh; + protected final Method md; + protected final int minLevel; + + protected EvaluatingValue(final MethodHandle mh, final Method md, final int minLevel) { + this.mh = mh; + this.md = md; + this.minLevel = minLevel; + } + + /* Constructor */ + public EvaluatingValue(final Method md, final int minLevel) { + this.md = md; + this.minLevel = minLevel; + try { + this.mh = MethodHandles.publicLookup().unreflect(md).asFixedArity(); + } catch (IllegalAccessException e) { + throw new TLCRuntimeException(EC.TLC_MODULE_VALUE_JAVA_METHOD_OVERRIDE, MP.getMessage( + EC.TLC_MODULE_VALUE_JAVA_METHOD_OVERRIDE, new String[] { md.toString(), e.getMessage() })); + } + } + + public Value eval(final Tool tool, final ExprOrOpArgNode[] args, final Context c, final TLCState s0, + final TLCState s1, final int control, final CostModel cm) { + try { + return (Value) this.mh.invoke(tool, args, c, s0, s1, control, cm); + } catch (Throwable e) { + Assert.fail(EC.TLC_MODULE_VALUE_JAVA_METHOD_OVERRIDE, new String[]{this.md.toString(), e.getMessage()}); + return null; // make compiler happy + } + } + + public final byte getKind() { return METHODVALUE; } + + public final int compareTo(Object obj) { + try { + Assert.fail("Attempted to compare operator " + this.toString() + + " with value:\n" + obj == null ? "null" : Values.ppr(obj.toString())); + return 0; // make compiler happy + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + public final boolean equals(Object obj) { + try { + Assert.fail("Attempted to check equality of operator " + this.toString() + + " with value:\n" + obj == null ? "null" : Values.ppr(obj.toString())); + return false; // make compiler happy + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + public final boolean member(Value elem) { + try { + Assert.fail("Attempted to check if the value:\n" + elem == null ? "null" : Values.ppr(elem.toString()) + + "\nis an element of operator " + this.toString()); + return false; // make compiler happy + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + public final boolean isFinite() { + try { + Assert.fail("Attempted to check if the operator " + this.toString() + + " is a finite set."); + return false; // make compiler happy + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + public final Value apply(Value arg, int control) { + try { + throw new WrongInvocationException("It is a TLC bug: Should use the other apply method."); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + public final Value apply(Value[] args, int control) { + try { + throw new WrongInvocationException("It is a TLC bug: Should use the other apply method."); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + public final Value select(Value arg) { + try { + throw new WrongInvocationException("It is a TLC bug: Attempted to call MethodValue.select()."); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + public final Value takeExcept(ValueExcept ex) { + try { + Assert.fail("Attempted to appy EXCEPT construct to the operator " + + this.toString() + "."); + return null; // make compiler happy + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + public final Value takeExcept(ValueExcept[] exs) { + try { + Assert.fail("Attempted to apply EXCEPT construct to the operator " + + this.toString() + "."); + return null; // make compiler happy + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + public final Value getDomain() { + try { + Assert.fail("Attempted to compute the domain of the operator " + + this.toString() + "."); + return SetEnumValue.EmptySet; // make compiler happy + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + public final int size() { + try { + Assert.fail("Attempted to compute the number of elements in the operator " + + this.toString() + "."); + return 0; // make compiler happy + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + /* Should never normalize an operator. */ + public final boolean isNormalized() { + try { + throw new WrongInvocationException("It is a TLC bug: Attempted to normalize an operator."); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + public final Value normalize() { + try { + throw new WrongInvocationException("It is a TLC bug: Attempted to normalize an operator."); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + public final boolean isDefined() { return true; } + + public final IValue deepCopy() { return this; } + + public final boolean assignable(Value val) { + try { + throw new WrongInvocationException("It is a TLC bug: Attempted to initialize an operator."); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + /* String representation of the value. */ + public final StringBuffer toString(StringBuffer sb, int offset, boolean ignored) { + try { + return sb.append("<Java Method: " + this.md + ">"); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + public int getMinLevel() { + return minLevel; + } +} diff --git a/tlatools/src/tlc2/value/impl/FcnLambdaValue.java b/tlatools/src/tlc2/value/impl/FcnLambdaValue.java new file mode 100644 index 0000000000000000000000000000000000000000..364c0e89130796b6250c7ae7505a23cc5ca72d1e --- /dev/null +++ b/tlatools/src/tlc2/value/impl/FcnLambdaValue.java @@ -0,0 +1,811 @@ +// Copyright (c) 2003 Compaq Corporation. All rights reserved. +// Portions Copyright (c) 2003 Microsoft Corporation. All rights reserved. +// Last modified on Wed 12 Jul 2017 at 16:10:00 PST by ian morris nieves +// modified on Wed 4 Jul 2007 at 17:31:23 PST by lamport +// modified on Thu Dec 6 21:46:34 PST 2001 by yuanyu + +package tlc2.value.impl; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; + +import tla2sany.semantic.FormalParamNode; +import tla2sany.semantic.SemanticNode; +import tla2sany.semantic.SymbolNode; +import tlc2.TLCGlobals; +import tlc2.tool.EvalControl; +import tlc2.tool.EvalException; +import tlc2.tool.FingerprintException; +import tlc2.tool.ITool; +import tlc2.tool.TLCState; +import tlc2.tool.coverage.CostModel; +import tlc2.util.Context; +import tlc2.value.IFcnLambdaValue; +import tlc2.value.IMVPerm; +import tlc2.value.IValue; +import tlc2.value.IValueOutputStream; +import tlc2.value.Values; +import util.Assert; +import util.UniqueString; + +public class FcnLambdaValue extends Value implements Applicable, IFcnLambdaValue { + public final FcnParams params; // the function formals + public final SemanticNode body; // the function body + public ValueExcept[] excepts; // the EXCEPTs + public final ITool tool; + public Context con; + public final TLCState state; + public final TLCState pstate; + public int control; + public FcnRcdValue fcnRcd; + + /* + * Constructor: E.g. [ s \in {"A", "B", "C"} |-> "foo" ] where s \in {"A", "B", + * "C"} is FcnLambdaValue and body is the expression "foo". + */ + public FcnLambdaValue(FcnParams params, SemanticNode body, ITool tool, + Context c, TLCState s0, TLCState s1, int control) { + this.params = params; + this.body = body; + this.excepts = null; + this.tool = tool; + this.con = c; + this.state = s0.copy(); // copy() added 12 Mar 2010 by Yuan Yu. + if (s1 != null) { // see SetPredValue constructor. + this.pstate = s1.copy(); + } else { + this.pstate = null; + } + + this.control = control; + this.fcnRcd = null; + } + + public FcnLambdaValue(FcnParams params, SemanticNode body, ITool tool, + Context c, TLCState s0, TLCState s1, int control, CostModel cm) { + this(params, body, tool, c, s0, s1, control); + this.cm = cm; + } + + public FcnLambdaValue(FcnLambdaValue fcn, ITool tool) { + this.params = fcn.params; + this.body = fcn.body; + this.excepts = fcn.excepts; + this.tool = tool; + this.con = fcn.con; + this.state = fcn.state; + this.pstate = fcn.pstate; + this.control = fcn.control; + this.fcnRcd = fcn.fcnRcd; + } + + public FcnLambdaValue(FcnLambdaValue fcn) { + this(fcn, fcn.tool); + } + + @Override + public final byte getKind() { return FCNLAMBDAVALUE; } + + public final void makeRecursive(SymbolNode fname) { + try { + this.con = this.con.cons(fname, this); + this.control = EvalControl.setKeepLazy(this.control); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final int compareTo(Object obj) { + try { + FcnRcdValue fcn = (FcnRcdValue) this.toFcnRcd(); + return fcn.compareTo(obj); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + public final boolean equals(Object obj) { + try { + FcnRcdValue fcn = (FcnRcdValue) this.toFcnRcd(); + return fcn.equals(obj); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final boolean member(Value elem) { + try { + Assert.fail("Attempted to check if the value:\n" + Values.ppr(elem.toString()) + + "\nis an element of the function " + Values.ppr(this.toString())); + return false; // make compiler happy + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final boolean isFinite() { + try { + Assert.fail("Attempted to check if the function:\n" + Values.ppr(this.toString()) + + "\nis a finite set."); + return false; // make compiler happy + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + /* Apply this function to the arguments given by args. */ + @Override + public final Value apply(Value args, int control) throws EvalException { + try { + + if (this.fcnRcd != null) { + return this.fcnRcd.apply(args, control); + } + + // First, find all the excepts that match args. + Value res = null; + int num = 0; + ValueExcept[] excepts1 = null; + if (this.excepts != null) { + int exlen = this.excepts.length; + for (int i = exlen-1; i >= 0; i--) { + ValueExcept ex = this.excepts[i]; + Value arg = ex.current(); + boolean inExcept = true; + inExcept = arg.equals(args); + if (inExcept) { + if (ex.isLast()) { res = ex.value; break; } + if (excepts1 == null) excepts1 = new ValueExcept[exlen]; + excepts1[num++] = new ValueExcept(ex, ex.idx+1); + } + } + } + + // Second, evaluate the function application. + if (res == null) { + Context c1 = this.con; + FormalParamNode[][] formals = this.params.formals; + Value [] domains = this.params.domains; + boolean[] isTuples = this.params.isTuples; + int plen = this.params.length(); + + if (plen == 1) { + if (!domains[0].member(args)) { + Assert.fail("In applying the function\n" + Values.ppr(this.toString()) + + ",\nthe first argument is:\n" + Values.ppr(args.toString()) + + "\nwhich is not in its domain.\n"); + } + if (isTuples[0]) { + FormalParamNode[] ids = formals[0]; + TupleValue argVal = (TupleValue) args.toTuple(); + if (argVal == null) { + Assert.fail("In applying the function\n" + Values.ppr(this.toString()) + + ",\nthe first argument is:\n" + Values.ppr(args.toString()) + + "\nwhich does not match its formal parameter.\n"); + } + if (argVal.size() != ids.length) return null; + Value [] elems = argVal.elems; + for (int i = 0; i < ids.length; i++) { + c1 = c1.cons(ids[i], elems[i]); + } + } + else { + c1 = c1.cons(formals[0][0], args); + } + } + else { + TupleValue tv = (TupleValue) args.toTuple(); + if (tv == null) { + Assert.fail("In applying the function\n" + Values.ppr(this.toString()) + + ",\nthe argument list is:\n" + Values.ppr(args.toString()) + + "\nwhich does not match its formal parameter.\n"); + } + Value[] elems = tv.elems; + int argn = 0; + for (int i = 0; i < formals.length; i++) { + FormalParamNode[] ids = formals[i]; + Value domain = domains[i]; + if (isTuples[i]) { + if (!domain.member(elems[argn])) { + Assert.fail("In applying the function\n" + Values.ppr(this.toString()) + + ",\nthe argument number " + (argn+1) + " is:\n" + + Values.ppr(elems[argn].toString()) + + "\nwhich is not in its domain.\n"); + } + TupleValue tv1 = (TupleValue) elems[argn++].toTuple(); + if (tv1 == null || tv1.size() != ids.length) { + Assert.fail("In applying the function\n" + Values.ppr(this.toString()) + + ",\nthe argument number " + argn + " is:\n" + + Values.ppr(elems[argn-1].toString()) + + "\nwhich does not match its formal parameter.\n"); + } + Value [] avals = tv1.elems; + for (int j = 0; j < ids.length; j++) { + c1 = c1.cons(ids[j], avals[j]); + } + } + else { + for (int j = 0; j < ids.length; j++) { + if (!domain.member(elems[argn])) { + Assert.fail("In applying the function\n" + Values.ppr(this.toString()) + + ",\nthe argument number " + (argn+1) + " is:\n" + + Values.ppr(elems[argn].toString()) + "\nwhich is not in its domain.\n"); + } + c1 = c1.cons(ids[j], elems[argn++]); + } + } + } + } + res = (Value) this.tool.eval(this.body, c1, this.state, this.pstate, control); + } + + // Finally, apply the matching excepts on the result. + if (num == 0) return res; + ValueExcept[] excepts2 = new ValueExcept[num]; + for (int i = 0; i < num; i++) { + excepts2[num-1-i] = excepts1[i]; + } + return res.takeExcept(excepts2); + + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + /* This one does not seem to be needed anymore. */ + @Override + public final Value apply(Value[] args, int control) throws EvalException { + try { + return this.apply(new TupleValue(args), control); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final Value select(Value arg) { + try { + + if (this.fcnRcd != null) { + return this.fcnRcd.select(arg); + } + + // First, find all the excepts that match arg. + Value res = null; + int num = 0; + ValueExcept[] excepts1 = null; + if (this.excepts != null) { + int exlen = this.excepts.length; + for (int i = exlen-1; i >= 0; i--) { + ValueExcept ex = this.excepts[i]; + Value exArg = ex.current(); + boolean inExcept = exArg.equals(arg); + if (inExcept) { + if (ex.isLast()) { res = ex.value; break; } + if (excepts1 == null) excepts1 = new ValueExcept[exlen]; + excepts1[num++] = new ValueExcept(ex, ex.idx+1); + } + } + } + + // Second, evaluate the function application. + if (res == null) { + Context c1 = this.con; + FormalParamNode[][] formals = this.params.formals; + Value[] domains = this.params.domains; + boolean[] isTuples = this.params.isTuples; + int plen = this.params.length(); + + if (plen == 1) { + if (!domains[0].member(arg)) return null; + if (isTuples[0]) { + FormalParamNode[] ids = formals[0]; + TupleValue argVal = (TupleValue) arg.toTuple(); + /* + * SZA: Changed from argVal.toString() to arg.toString() to prevent a NullPointerException + */ + if (argVal == null) { + Assert.fail("In applying the function\n" + Values.ppr(this.toString()) + + ",\nthe first argument is:\n" + Values.ppr(arg.toString()) + + "\nwhich does not match its formal parameter.\n"); + } + if (argVal.size() != ids.length) return null; + Value [] elems = argVal.elems; + for (int i = 0; i < ids.length; i++) { + c1 = c1.cons(ids[i], elems[i]); + } + } + else { + c1 = c1.cons(formals[0][0], arg); + } + } + else { + TupleValue tv = (TupleValue) arg.toTuple(); + if (tv == null) { + Assert.fail("In applying the function\n" + Values.ppr(this.toString()) + + ",\nthe argument list is:\n" + Values.ppr(arg.toString()) + + "\nwhich does not match its formal parameter.\n"); + } + Value[] elems = tv.elems; + int argn = 0; + for (int i = 0; i < formals.length; i++) { + FormalParamNode[] ids = formals[i]; + Value domain = domains[i]; + if (isTuples[i]) { + if (!domain.member(elems[argn])) return null; + TupleValue tv1 = (TupleValue) elems[argn++].toTuple(); + if (tv1 == null) { + Assert.fail("In applying the function\n" + Values.ppr(this.toString()) + + ",\nthe argument number " + argn + " is:\n" + + Values.ppr(elems[argn-1].toString()) + + "\nwhich does not match its formal parameter.\n"); + } + if (tv1.size() != ids.length) return null; + Value [] avals = tv1.elems; + for (int j = 0; j < ids.length; j++) { + c1 = c1.cons(ids[j], avals[j]); + } + } + else { + for (int j = 0; j < ids.length; j++) { + if (!domain.member(elems[argn])) return null; + c1 = c1.cons(ids[j], elems[argn++]); + } + } + } + } + res = (Value) this.tool.eval(this.body, c1, this.state, this.pstate, this.control); + } + + // Finally, apply the matching excepts on the result. + if (num == 0) return res; + ValueExcept[] excepts2 = new ValueExcept[num]; + for (int i = 0; i < num; i++) { + excepts2[num-1-i] = excepts1[i]; + } + return res.takeExcept(excepts2); + + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + /* This method returns a new function value by taking except. */ + @Override + public final Value takeExcept(ValueExcept ex) { + try { + + if (ex.idx >= ex.path.length) return ex.value; + + if (this.fcnRcd != null) { + return this.fcnRcd.takeExcept(ex); + } + FcnLambdaValue fcn = new FcnLambdaValue(this); + if (this.excepts == null) { + fcn.excepts = new ValueExcept[1]; + fcn.excepts[0] = ex; + } + else { + int exlen = this.excepts.length; + fcn.excepts = new ValueExcept[exlen+1]; + for (int i = 0; i < exlen; i++) { + fcn.excepts[i] = this.excepts[i]; + } + fcn.excepts[exlen] = ex; + } + return fcn; + + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + /* This method returns a new function value by taking excepts. */ + @Override + public final Value takeExcept(ValueExcept[] exs) { + try { + + if (this.fcnRcd != null) { + return this.fcnRcd.takeExcept(exs); + } + FcnLambdaValue fcn = new FcnLambdaValue(this); + int exslen = exs.length; + if (exslen != 0) { + int i = 0; + for (i = exs.length-1; i >= 0; i--) { + if (exs[i].idx >= exs[i].path.length) break; + } + if (i >= 0) { + int xlen = exslen-i-1; + fcn.excepts = new ValueExcept[xlen]; + System.arraycopy(exs, i+1, fcn.excepts, 0, xlen); + } + else if (this.excepts == null) { + fcn.excepts = new ValueExcept[exslen]; + System.arraycopy(exs, 0, fcn.excepts, 0, exslen); + } + else { + int len = this.excepts.length; + fcn.excepts = new ValueExcept[len + exslen]; + System.arraycopy(this.excepts, 0, fcn.excepts, 0, len); + System.arraycopy(exs, 0, fcn.excepts, len, exslen); + } + } + return fcn; + + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final Value getDomain() { + try { + + if (this.fcnRcd != null) { + return this.fcnRcd.getDomain(); + } + int len = this.params.length(); + if (len == 1) { + return this.params.domains[0]; + } + Value [] sets = new Value [len]; + int dlen = this.params.domains.length; + boolean[] isTuples = this.params.isTuples; + int idx = 0; + for (int i = 0; i < dlen; i++) { + FormalParamNode[] formal = this.params.formals[i]; + Value domain = this.params.domains[i]; + if (isTuples[i]) { + sets[idx++] = domain; + } + else { + for (int j = 0; j < formal.length; j++) { + sets[idx++] = domain; + } + } + } + return new SetOfTuplesValue(sets); + + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final int size() { + try { + if (this.fcnRcd == null) { + return this.params.size(); + } + return this.fcnRcd.size(); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final boolean isDefined() { return true; } + + @Override + public final IValue deepCopy() { + try { + FcnLambdaValue fcn = new FcnLambdaValue(this); + // A bug occured when printing a function whose domain is a Cartesian product because this.fcnRcd + // is null at this point. On 5 Mar 2012, LL wrapped the following null test around the assignment. + if (this.fcnRcd != null) { + fcn.fcnRcd = (FcnRcdValue)this.fcnRcd.deepCopy(); + } + return fcn; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final boolean assignable(Value val) { + try { + return (val instanceof FcnLambdaValue); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException { + this.fcnRcd = (FcnRcdValue)ois.readObject(); + } + + private void writeObject(ObjectOutputStream oos) throws IOException { + FcnRcdValue res = (FcnRcdValue) this.toFcnRcd(); + oos.writeObject(res); + } + + @Override + public final boolean isNormalized() { + try { + if (this.fcnRcd == null) { + return false; + } + return this.fcnRcd.isNormalized(); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final Value normalize() { + try { + if (this.fcnRcd != null) { + this.fcnRcd.normalize(); + } + return this; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final void deepNormalize() { + try { + if (fcnRcd == null) { + if (excepts != null) { + for (int i = 0; i < excepts.length; i++) { + excepts[i].value.deepNormalize(); + for (int j = 0; j < excepts[i].path.length; j++) { + excepts[i].path[j].deepNormalize(); + } + } + } + IValue[] paramDoms = params.domains; + for (int i = 0; i < paramDoms.length; i++) { + paramDoms[i].deepNormalize(); + } + } + else { + fcnRcd.deepNormalize(); + } + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final Value toTuple() { + if (this.params.length() != 1) return null; + Value dom = this.params.domains[0]; + SymbolNode var = this.params.formals[0][0]; + if (dom instanceof IntervalValue) { + IntervalValue intv = (IntervalValue)dom; + if (intv.low != 1) return null; + Value [] elems = new Value [intv.high]; + for (int i = 1; i <= intv.high; i++) { + Context c1 = this.con.cons(var, IntValue.gen(i)); + elems[i-1] = (Value) this.tool.eval(this.body, c1, this.state, this.pstate, this.control); + } + if (coverage) {cm.incSecondary(elems.length);} + return new TupleValue(elems, cm); + } + else { + SetEnumValue eSet = (SetEnumValue) dom.toSetEnum(); + if (eSet == null) + Assert.fail("To convert a function of form [x \\in S |-> f(x)] " + + "to a tuple, the set S must be enumerable."); + eSet.normalize(); + int len = eSet.size(); + Value [] elems = new Value [len]; + for (int i = 0; i < len; i++) { + Value argVal = eSet.elems.elementAt(i); + if (!(argVal instanceof IntValue)) return null; + if (((IntValue)argVal).val != i + 1) return null; + Context c1 = this.con.cons(var, argVal); + elems[i] = (Value) this.tool.eval(this.body, c1, this.state, this.pstate, this.control); + } + cm.incSecondary(elems.length); + return new TupleValue(elems, cm); + } + } + + @Override + public final Value toRcd() { + FcnRcdValue fcn = (FcnRcdValue) this.toFcnRcd(); + if (fcn == null || fcn.domain == null) { return null; } + fcn.normalize(); + UniqueString[] vars = new UniqueString[fcn.domain.length]; + for (int i = 0; i < fcn.domain.length; i++) { + if (!(fcn.domain[i] instanceof StringValue)) { + return null; + } + vars[i] = ((StringValue)fcn.domain[i]).getVal(); + } + if (coverage) {cm.incSecondary(vars.length);} + return new RecordValue(vars, fcn.values, fcn.isNormalized(), cm); + } + + @Override + public final Value toFcnRcd() { + try { + + if (this.fcnRcd == null) { + int sz = this.params.size(); + FormalParamNode[][] formals = this.params.formals; + boolean[] isTuples = this.params.isTuples; + + Value [] domain = new Value [sz]; + Value [] values = new Value [sz]; + int idx = 0; + ValueEnumeration Enum = this.params.elements(); + Value arg; + if (this.params.length() == 1) { + while ((arg = Enum.nextElement()) != null) { + domain[idx] = arg; + Context c1 = this.con; + if (isTuples[0]) { + FormalParamNode[] ids = formals[0]; + Value [] avals = ((TupleValue)arg).elems; + for (int j = 0; j < ids.length; j++) { + c1 = c1.cons(ids[j], avals[j]); + } + } + else { + c1 = c1.cons(formals[0][0], arg); + } + values[idx++] = (Value) this.tool.eval(this.body, c1, this.state, this.pstate, this.control); + } + } + else { + while ((arg = Enum.nextElement()) != null) { + domain[idx] = arg; + Value [] argList = ((TupleValue)arg).elems; + int argn = 0; + Context c1 = this.con; + for (int i = 0; i < formals.length; i++) { + FormalParamNode[] ids = formals[i]; + if (isTuples[i]) { + Value [] avals = ((TupleValue)argList[argn++]).elems; + for (int j = 0; j < ids.length; j++) { + c1 = c1.cons(ids[j], avals[j]); + } + } + else { + for (int j = 0; j < ids.length; j++) { + c1 = c1.cons(ids[j], argList[argn++]); + } + } + } + values[idx++] = (Value) this.tool.eval(this.body, c1, this.state, this.pstate, this.control); + } + } + if (coverage) {cm.incSecondary(sz);} + this.fcnRcd = new FcnRcdValue(domain, values, false, cm); + if (this.excepts != null) { + this.fcnRcd = (FcnRcdValue)fcnRcd.takeExcept(this.excepts); + } + } + return this.fcnRcd; + + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final void write(final IValueOutputStream vos) throws IOException { + fcnRcd.write(vos); + } + + /* The fingerprint methods. */ + @Override + public final long fingerPrint(long fp) { + try { + Value fcn = this.toFcnRcd(); + return fcn.fingerPrint(fp); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final IValue permute(IMVPerm perm) { + try { + Value fcn = this.toFcnRcd(); + return fcn.permute(perm); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + /* The string representation of this function. */ + public final StringBuffer toString(StringBuffer sb, int offset) { + return toString(sb, offset, true); +} + +/* The string representation of this function. */ + @Override + public final StringBuffer toString(StringBuffer sb, int offset, boolean swallow) { + try { + if (TLCGlobals.expand || this.params == null) { + try { + Value val = this.toFcnRcd(); + return val.toString(sb, offset, true); + } + catch (Throwable e) { /*SKIP*/ } + } + sb.append("[" + this.params.toString()); + sb.append(" |-> <expression " + this.body + ">]"); + return sb; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final SemanticNode getBody() { + return body; + } + + @Override + public final FcnRcdValue getRcd() { + return fcnRcd; + } + + @Override + public FcnParams getParams() { + return params; + } + + @Override + public Context getCon() { + return con; + } + + @Override + public boolean hasRcd() { + return fcnRcd != null; + } +} diff --git a/tlatools/src/tlc2/value/FcnParams.java b/tlatools/src/tlc2/value/impl/FcnParams.java similarity index 92% rename from tlatools/src/tlc2/value/FcnParams.java rename to tlatools/src/tlc2/value/impl/FcnParams.java index ab089e771116fd7164dc4ca53afdbeb68d27293f..d91b0d953573e5d7c51243742dfc0f11d7262488 100644 --- a/tlatools/src/tlc2/value/FcnParams.java +++ b/tlatools/src/tlc2/value/impl/FcnParams.java @@ -3,13 +3,15 @@ // Last modified on Wed 4 Jul 2007 at 17:26:45 PST by lamport // modified on Sat Nov 13 11:14:54 PST 1999 by yuanyu -package tlc2.value; +package tlc2.value.impl; import tla2sany.semantic.FormalParamNode; import tlc2.output.EC; +import tlc2.value.IFcnParams; +import tlc2.value.IValue; import util.Assert; -public class FcnParams { +public class FcnParams implements IFcnParams { public FormalParamNode[][] formals; // array of formal params public boolean[] isTuples; // true iff tuple param public Value[] domains; // the bounds of the formals @@ -25,6 +27,7 @@ public class FcnParams { } } + @Override public final int length() { return this.argLen; } public final int size() { @@ -134,6 +137,7 @@ public class FcnParams { } } + @Override public final void reset() { if (this.enums != null) { for (int i = 0; i < this.enums.length; i++) { @@ -143,6 +147,7 @@ public class FcnParams { } } + @Override public final Value nextElement() { if (this.enums == null) return null; Value[] elems = new Value[argLen]; @@ -164,5 +169,19 @@ public class FcnParams { } } - + + @Override + public FormalParamNode[][] getFormals() { + return formals; + } + + @Override + public IValue[] getDomains() { + return domains; + } + + @Override + public boolean[] isTuples() { + return isTuples; + } } diff --git a/tlatools/src/tlc2/value/impl/FcnRcdValue.java b/tlatools/src/tlc2/value/impl/FcnRcdValue.java new file mode 100644 index 0000000000000000000000000000000000000000..95e8619d9018f70ada980df15339c30ddfe1fe54 --- /dev/null +++ b/tlatools/src/tlc2/value/impl/FcnRcdValue.java @@ -0,0 +1,900 @@ +// Copyright (c) 2003 Compaq Corporation. All rights reserved. +// Portions Copyright (c) 2003 Microsoft Corporation. All rights reserved. +// Last modified on Wed 12 Jul 2017 at 16:10:00 PST by ian morris nieves +// modified on Sat 23 February 2008 at 10:07:06 PST by lamport +// modified on Fri Aug 10 15:07:25 PDT 2001 by yuanyu + +package tlc2.value.impl; + +import java.io.IOException; +import java.util.Map; + +import tlc2.tool.EvalControl; +import tlc2.tool.FingerprintException; +import tlc2.tool.coverage.CostModel; +import tlc2.util.FP64; +import tlc2.value.IFcnRcdValue; +import tlc2.value.IMVPerm; +import tlc2.value.IValue; +import tlc2.value.IValueInputStream; +import tlc2.value.IValueOutputStream; +import tlc2.value.ValueInputStream; +import tlc2.value.Values; +import util.Assert; +import util.TLAConstants; +import util.UniqueString; + +public class FcnRcdValue extends Value implements Applicable, IFcnRcdValue { + public final Value[] domain; + public final IntervalValue intv; + public final Value[] values; + private boolean isNorm; + public static final Value EmptyFcn = new FcnRcdValue(new Value[0], new Value[0], true); + + /* Constructor */ + public FcnRcdValue(Value[] domain, Value[] values, boolean isNorm) { + this.domain = domain; + this.values = values; + this.intv = null; + this.isNorm = isNorm; + } + + public FcnRcdValue(IntervalValue intv, Value[] values) { + this.intv = intv; + this.values = values; + this.domain = null; + this.isNorm = true; + } + + public FcnRcdValue(IntervalValue intv, Value[] values, CostModel cm) { + this(intv, values); + this.cm = cm; + } + + private FcnRcdValue(FcnRcdValue fcn, Value[] values) { + this.domain = fcn.domain; + this.intv = fcn.intv; + this.values = values; + this.isNorm = fcn.isNorm; + } + + public FcnRcdValue(ValueVec elems, Value[] values, boolean isNorm) { + this(elems.toArray(), values, isNorm); + } + + public FcnRcdValue(ValueVec elems, Value[] values, boolean isNorm, CostModel cm) { + this(elems, values, isNorm); + this.cm = cm; + } + + public FcnRcdValue(Value[] domain, Value[] values, boolean isNorm, CostModel cm) { + this(domain, values, isNorm); + this.cm = cm; + } + + @Override + public final byte getKind() { return FCNRCDVALUE; } + + @Override + public final int compareTo(Object obj) { + try { + + final FcnRcdValue fcn = obj instanceof Value ? (FcnRcdValue) ((Value) obj).toFcnRcd() : null; + if (fcn == null) { + if (obj instanceof ModelValue) + return 1; + Assert.fail("Attempted to compare the function " + Values.ppr(this.toString()) + " with the value:\n" + + Values.ppr(obj.toString())); + } + this.normalize(); + fcn.normalize(); + + final int result = this.values.length - fcn.values.length; + if (result != 0) { + return result; + } + + if (this.intv != null) { + return compareToInterval(fcn); + } else { + return compareOtherInterval(fcn); + } + + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + private final int compareOtherInterval(final FcnRcdValue fcn) { + int result; + if (fcn.intv != null) { + for (int i = 0; i < this.domain.length; i++) { + final Value dElem = this.domain[i]; + if (!(dElem instanceof IntValue)) { + Assert.fail( + "Attempted to compare integer with non-integer\n" + Values.ppr(dElem.toString()) + "."); + } + result = ((IntValue) dElem).val - (fcn.intv.low + i); + if (result != 0) { + return result; + } + result = this.values[i].compareTo(fcn.values[i]); + if (result != 0) { + return result; + } + } + } else { + for (int i = 0; i < this.domain.length; i++) { + result = this.domain[i].compareTo(fcn.domain[i]); + if (result != 0) { + return result; + } + result = this.values[i].compareTo(fcn.values[i]); + if (result != 0) { + return result; + } + } + } + return 0; + } + + private final int compareToInterval(final FcnRcdValue fcn) { + int result; + if (fcn.intv != null) { + result = this.intv.low - fcn.intv.low; + if (result != 0) { + return result; + } + for (int i = 0; i < this.values.length; i++) { + result = this.values[i].compareTo(fcn.values[i]); + if (result != 0) { + return result; + } + } + } else { + for (int i = 0; i < fcn.domain.length; i++) { + final Value dElem = fcn.domain[i]; + if (!(dElem instanceof IntValue)) { + Assert.fail( + "Attempted to compare integer with non-integer:\n" + Values.ppr(dElem.toString()) + "."); + } + result = this.intv.low + i - ((IntValue) dElem).val; + if (result != 0) { + return result; + } + result = this.values[i].compareTo(fcn.values[i]); + if (result != 0) { + return result; + } + } + } + return 0; + } + + public final boolean equals(Object obj) { + try { + + FcnRcdValue fcn = obj instanceof Value ? (FcnRcdValue) ((Value)obj).toFcnRcd() : null; + if (fcn == null) { + if (obj instanceof ModelValue) + return ((ModelValue) obj).modelValueEquals(this) ; + Assert.fail("Attempted to check equality of the function " + Values.ppr(this.toString()) + + " with the value:\n" + Values.ppr(obj.toString())); + } + this.normalize(); + fcn.normalize(); + + if (this.intv != null) { + if (fcn.intv != null) { + if (!this.intv.equals(fcn.intv)) return false; + for (int i = 0; i < this.values.length; i++) { + if (!this.values[i].equals(fcn.values[i])) + return false; + } + } + else { + if (fcn.domain.length != this.intv.size()) return false; + for (int i = 0; i < fcn.domain.length; i++) { + Value dElem = fcn.domain[i]; + if (!(dElem instanceof IntValue)) { + Assert.fail("Attempted to compare an integer with non-integer:\n" + + Values.ppr(dElem.toString()) + "."); + } + if (((IntValue)dElem).val != (this.intv.low + i) || + !this.values[i].equals(fcn.values[i])) { + return false; + } + } + } + } + else { + if (this.values.length != fcn.values.length) return false; + if (fcn.intv != null) { + for (int i = 0; i < this.domain.length; i++) { + Value dElem = this.domain[i]; + if (!(dElem instanceof IntValue)) { + Assert.fail("Attempted to compare an integer with non-integer:\n" + + Values.ppr(dElem.toString()) + "."); + } + if (((IntValue)dElem).val != (fcn.intv.low + i) || + !this.values[i].equals(fcn.values[i])) { + return false; + } + } + } + else { + for (int i = 0; i < this.domain.length; i++) { + if (!this.domain[i].equals(fcn.domain[i]) || + !this.values[i].equals(fcn.values[i])) { + return false; + } + } + } + } + return true; + + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final boolean member(Value elem) { + try { + Assert.fail("Attempted to check if the value:\n" + Values.ppr(elem.toString()) + + "\nis an element of the function " + Values.ppr(this.toString())); + return false; // make compiler happy + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final boolean isFinite() { return true; } + + @Override + public final Value apply(Value arg, int control) { + try { + Value result = this.select(arg); + if (result == null) { + Assert.fail("Attempted to apply function:\n" + Values.ppr(this.toString()) + + "\nto argument " + Values.ppr(arg.toString()) + ", which is" + + " not in the domain of the function."); + } + return result; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + /* This one does not seem to be needed anymore. */ + @Override + public final Value apply(Value[] args, int control) { + try { + return this.apply(new TupleValue(args), EvalControl.Clear); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final Value select(Value arg) { + try { + + if (this.intv != null) { + // domain is represented as an integer interval: + if (!(arg instanceof IntValue)) { + Assert.fail("Attempted to apply function with integer domain to" + + " the non-integer argument " + Values.ppr(arg.toString())); + } + int idx = ((IntValue)arg).val; + if ((idx >= this.intv.low) && (idx <= this.intv.high)) { + return this.values[idx - this.intv.low]; + } + } + else { + // domain is represented as an array of values: + int len = this.domain.length; + for (int i = 0; i < len; i++) { + if (this.domain[i].equals(arg)) { + return this.values[i]; + } + } + } + return null; + + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + public final boolean assign(Value[] args, Value val) { + try { + + if (this.intv != null) { + // domain is represented as an integer interval: + if (args.length != 1) { + Assert.fail("Wrong number of function arguments."); + } + if (args[0] instanceof IntValue) { + int idx = ((IntValue)args[0]).val; + if ((idx >= this.intv.low) && (idx <= this.intv.high)) { + int vIdx = idx - this.intv.low; + if (this.values[vIdx] == UndefValue.ValUndef || + this.values[vIdx].equals(val)) { + this.values[vIdx] = val; + return true; + } + return false; + } + } + } + else { + // domain is represented as an array of values: + Value argv = new TupleValue(args); + int len = this.domain.length; + for (int i = 0; i < len; i++) { + if (this.domain[i].equals(argv)) { + if (this.values[i] == UndefValue.ValUndef || + this.values[i].equals(val)) { + this.values[i] = val; + return true; + } + return false; + } + } + } + Assert.fail("Function initialization out of domain."); + return false; // make compiler happy + + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final Value takeExcept(ValueExcept ex) { + try { + + if (ex.idx >= ex.path.length) return ex.value; + + int flen = this.values.length; + Value[] newValues = new Value[flen]; + for (int i = 0; i < flen; i++) { + newValues[i] = this.values[i]; + } + Value arg = ex.path[ex.idx]; + + if (this.intv != null) { + // domain is represented as an integer interval: + if (arg instanceof IntValue) { + int idx = ((IntValue)arg).val; + if ((idx >= this.intv.low) && (idx <= this.intv.high)) { + int vidx = idx - this.intv.low; + ex.idx++; + newValues[idx] = this.values[vidx].takeExcept(ex); + } + return new FcnRcdValue(this.intv, newValues); + } + } + else { + // domain is represented as an array of values: + for (int i = 0; i < flen; i++) { + if (arg.equals(this.domain[i])) { + ex.idx++; + newValues[i] = newValues[i].takeExcept(ex); + Value[] newDomain = this.domain; + if (!this.isNorm) { + newDomain = new Value[flen]; + for (int j = 0; j < flen; j++) { + newDomain[j] = this.domain[j]; + } + } + return new FcnRcdValue(newDomain, newValues, this.isNorm); + } + } + } + return this; + + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final Value takeExcept(ValueExcept[] exs) { + try { + Value res = this; + for (int i = 0; i < exs.length; i++) { + res = res.takeExcept(exs[i]); + } + return res; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final Value getDomain() { + try { + if (this.intv != null) { + return this.intv; + } + this.normalize(); + return new SetEnumValue(this.domain, true); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + /** + * Returns the domain of this FunctionRecordValue regardless of its internal + * representation as either Value[] or IntervalValue as Value[]. + */ + public final Value[] getDomainAsValues() { + if (this.intv != null) { + return this.intv.asValues(); + } else { + return this.domain; + } + } + + @Override + public final int size() { + try { + this.normalize(); + return this.values.length; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + /** + * {@link #size()} first normalizes, destructively, this instance; for inspections on the size without normalization + * use this method. + * + * @return + */ + public int nonNormalizedSize() { + return values.length; + } + + @Override + public final Value toTuple() { + if (this.intv != null) { + if (this.intv.low != 1) return null; + return new TupleValue(this.values); + } + int len = this.values.length; + Value[] elems = new Value[len]; + for (int i = 0; i < len; i++) { + if (!(this.domain[i] instanceof IntValue)) return null; + int idx = ((IntValue)this.domain[i]).val; + if (0 < idx && idx <= len) { + if (elems[idx-1] != null) return null; + elems[idx-1] = this.values[i]; + } + else { + return null; + } + } + if (coverage) {cm.incSecondary(elems.length);} + return new TupleValue(elems, cm); + } + + @Override + public final Value toRcd() { + if (this.domain == null) return null; + this.normalize(); + UniqueString[] vars = new UniqueString[this.domain.length]; + for (int i = 0; i < this.domain.length; i++) { + if (!(this.domain[i] instanceof StringValue)) { + return null; + } + vars[i] = ((StringValue)this.domain[i]).getVal(); + } + if (coverage) {cm.incSecondary(this.values.length);} + return new RecordValue(vars, this.values, this.isNormalized(), cm); + } + + @Override + public final Value toFcnRcd() { + return this; + } + + /* Return true iff this function is in normal form. */ + @Override + public final boolean isNormalized() { return this.isNorm; } + + /* This method normalizes (destructively) this function. */ + @Override + public final Value normalize() { + try { + + if (!this.isNorm) { + // Assert.check(this.domain != null) + int dlen = this.domain.length; + for (int i = 1; i < dlen; i++) { + int cmp = this.domain[0].compareTo(this.domain[i]); + if (cmp == 0) { + Assert.fail("The value\n" + this.domain[i] + + "\noccurs multiple times in the function domain."); + } + else if (cmp > 0) { + Value tv = this.domain[0]; + this.domain[0] = this.domain[i]; + this.domain[i] = tv; + tv = this.values[0]; + this.values[0] = this.values[i]; + this.values[i] = tv; + } + } + for (int i = 2; i < dlen; i++) { + Value d = this.domain[i]; + Value v = this.values[i]; + int j = i; + int cmp; + while ((cmp = d.compareTo(this.domain[j-1])) < 0) { + this.domain[j] = this.domain[j-1]; + this.values[j] = this.values[j-1]; + j--; + } + if (cmp == 0) { + Assert.fail("The value\n" + this.domain[i] + + "\noccurs multiple times in the function domain."); + } + this.domain[j] = d; + this.values[j] = v; + } + this.isNorm = true; + } + return this; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final void deepNormalize() { + try { + for (int i = 0; i < values.length; i++) { + values[i].deepNormalize(); + } + normalize(); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final boolean isDefined() { + try { + + boolean defined = true; + if (this.intv == null) { + for (int i = 0; i < this.values.length; i++) { + defined = defined && this.domain[i].isDefined(); + } + } + for (int i = 0; i < this.values.length; i++) { + defined = defined && this.values[i].isDefined(); + } + return defined; + + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final IValue deepCopy() { + try { + Value[] vals = new Value[this.values.length]; + for (int i = 0; i < vals.length; i++) { + vals[i] = (Value) this.values[i].deepCopy(); + } + return new FcnRcdValue(this, vals); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final boolean assignable(Value val) { + try { + boolean canAssign = ((val instanceof FcnRcdValue) && + this.values.length == ((FcnRcdValue)val).values.length); + if (!canAssign) return false; + FcnRcdValue fcn = (FcnRcdValue)val; + for (int i = 0; i < this.values.length; i++) { + canAssign = (canAssign && + this.domain[i].equals(fcn.domain[i]) && + this.values[i].assignable(fcn.values[i])); + } + return canAssign; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final void write(final IValueOutputStream vos) throws IOException { + final int index = vos.put(this); + if (index == -1) { + vos.writeByte(FCNRCDVALUE); + int len = values.length; + vos.writeNat(len); + if (intv != null) { + vos.writeByte((byte) 0); + vos.writeInt(intv.low); + vos.writeInt(intv.high); + for (int i = 0; i < len; i++) { + values[i].write(vos); + } + } else { + vos.writeByte((isNormalized()) ? (byte) 1 : (byte) 2); + for (int i = 0; i < len; i++) { + domain[i].write(vos); + values[i].write(vos); + } + } + } else { + vos.writeByte(DUMMYVALUE); + vos.writeNat(index); + } + } + + /* The fingerprint method. */ + @Override + public final long fingerPrint(long fp) { + try { + this.normalize(); + int flen = this.values.length; + fp = FP64.Extend(fp, FCNRCDVALUE); + fp = FP64.Extend(fp, flen); + if (this.intv == null) { + for (int i = 0; i < flen; i++) { + fp = this.domain[i].fingerPrint(fp); + fp = this.values[i].fingerPrint(fp); + } + } + else { + for (int i = 0; i < flen; i++) { + fp = FP64.Extend(fp, INTVALUE); + fp = FP64.Extend(fp, i + this.intv.low); + fp = this.values[i].fingerPrint(fp); + } + } + return fp; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final IValue permute(IMVPerm perm) { + try { + + this.normalize(); + int flen = this.domain.length; + Value[] vals = new Value[flen]; + + boolean vchanged = false; + for (int i = 0; i < flen; i++) { + vals[i] = (Value) this.values[i].permute(perm); + vchanged = vchanged || (vals[i] != this.values[i]); + } + + if (this.intv == null) { + Value[] dom = new Value[flen]; + boolean dchanged = false; + for (int i = 0; i < flen; i++) { + dom[i] = (Value) this.domain[i].permute(perm); + dchanged = dchanged || (dom[i] != this.domain[i]); + } + + if (dchanged) { + return new FcnRcdValue(dom, vals, false); + } + else if (vchanged) { + return new FcnRcdValue(this.domain, vals, true); + } + } + else { + if (vchanged) { + return new FcnRcdValue(this.intv, vals); + } + } + return this; + + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + private static final boolean isName(String name) { + int len = name.length(); + boolean hasLetter = false; + + for (int idx = 0; idx < len; idx++) { + char ch = name.charAt(idx); + if (ch == '_') continue; + if (!Character.isLetterOrDigit(ch)) return false; + hasLetter = hasLetter || Character.isLetter(ch); + } + + return hasLetter && (len < 4 || (!name.startsWith("WF_") && !name.startsWith("SF_"))); + } + + private final boolean isRcd() { + if (this.intv != null) return false; + for (int i = 0; i < this.domain.length; i++) { + Value dval = this.domain[i]; + boolean isName = ((dval instanceof StringValue) && + isName(((StringValue)dval).val.toString())); + if (!isName) return false; + } + return true; + } + + private final boolean isTuple() { + if (this.intv != null) { + return (this.intv.low == 1); + } + for (int i = 0; i < this.domain.length; i++) { + if (!(this.domain[i] instanceof IntValue)) { + return false; + } + } + this.normalize(); + for (int i = 0; i < this.domain.length; i++) { + if (((IntValue)this.domain[i]).val != (i+1)) { + return false; + } + } + return true; + } + + /* The string representation of the value. */ + @Override + public final StringBuffer toString(StringBuffer sb, int offset, boolean swallow) { + try { + + int len = this.values.length; + if (len == 0) { + sb.append("<< >>"); + } + else if (this.isRcd()) { + sb.append("["); + sb.append(((StringValue)this.domain[0]).val + TLAConstants.RECORD_ARROW); + sb = this.values[0].toString(sb, offset, swallow); + + for (int i = 1; i < len; i++) { + sb.append(", "); + sb.append(((StringValue)this.domain[i]).val + TLAConstants.RECORD_ARROW); + sb = this.values[i].toString(sb, offset, swallow); + } + sb.append("]"); + } + else if (this.isTuple()) { + // It is actually a sequence: + sb = sb.append("<<"); + sb = this.values[0].toString(sb, offset, swallow); + + for (int i = 1; i < len; i++) { + sb.append(", "); + sb = this.values[i].toString(sb, offset, swallow); + } + sb.append(">>"); + } + else { + sb = sb.append("("); + sb = this.domain[0].toString(sb, offset, swallow); + sb.append(" :> "); + sb = this.values[0].toString(sb, offset, swallow); + + for (int i = 1; i < len; i++) { + sb.append(" @@ "); + sb = this.domain[i].toString(sb, offset, swallow); + sb.append(" :> "); + sb = this.values[i].toString(sb, offset, swallow); + } + sb.append(")"); + } + return sb; + + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + public static IValue createFrom(final IValueInputStream vos) throws IOException { + final int index = vos.getIndex(); + final int len = vos.readNat(); + final int info = vos.readByte(); + Value res; + final Value[] rvals = new Value[len]; + if (info == 0) { + final int low = vos.readInt(); + final int high = vos.readInt(); + for (int i = 0; i < len; i++) { + rvals[i] = (Value) vos.read(); + } + final IntervalValue intv = new IntervalValue(low, high); + res = new FcnRcdValue(intv, rvals); + } else { + final Value[] dvals = new Value[len]; + for (int i = 0; i < len; i++) { + dvals[i] = (Value) vos.read(); + rvals[i] = (Value) vos.read(); + } + res = new FcnRcdValue(dvals, rvals, (info == 1)); + } + vos.assign(res, index); + return res; + } + + public static IValue createFrom(final ValueInputStream vos, final Map<String, UniqueString> tbl) throws IOException { + final int index = vos.getIndex(); + final int len = vos.readNat(); + final int info = vos.readByte(); + Value res; + final Value[] rvals = new Value[len]; + if (info == 0) { + final int low = vos.readInt(); + final int high = vos.readInt(); + for (int i = 0; i < len; i++) { + rvals[i] = (Value) vos.read(tbl); + } + final IntervalValue intv = new IntervalValue(low, high); + res = new FcnRcdValue(intv, rvals); + } else { + final Value[] dvals = new Value[len]; + for (int i = 0; i < len; i++) { + dvals[i] = (Value) vos.read(tbl); + rvals[i] = (Value) vos.read(tbl); + } + res = new FcnRcdValue(dvals, rvals, (info == 1)); + } + vos.assign(res, index); + return res; + } +} diff --git a/tlatools/src/tlc2/value/impl/IntValue.java b/tlatools/src/tlc2/value/impl/IntValue.java new file mode 100644 index 0000000000000000000000000000000000000000..5f6fc3212df40c69f3d1d2e5c0a2434ccca48a37 --- /dev/null +++ b/tlatools/src/tlc2/value/impl/IntValue.java @@ -0,0 +1,236 @@ +// Copyright (c) 2003 Compaq Corporation. All rights reserved. +// Portions Copyright (c) 2003 Microsoft Corporation. All rights reserved. +// Last modified on Wed 12 Jul 2017 at 16:10:00 PST by ian morris nieves +// modified on Sat 23 February 2008 at 10:08:05 PST by lamport +// modified on Fri Aug 10 15:07:30 PDT 2001 by yuanyu + +package tlc2.value.impl; + +import java.io.IOException; + +import tlc2.tool.FingerprintException; +import tlc2.util.FP64; +import tlc2.value.IMVPerm; +import tlc2.value.IValue; +import tlc2.value.IValueOutputStream; +import tlc2.value.Values; +import util.Assert; + +public class IntValue extends Value { + private static final IntValue[] cache; + + static { + cache = new IntValue[10]; + for (int i = 0; i < cache.length; i++) { + cache[i] = new IntValue(i); + } + } + + public static final IntValue ValNegOne = gen(-1); + + public static final IntValue ValOne = gen(1); + + public static final IntValue ValZero = gen(0); + + public static final int nbits(int tmp) { + int nb = 0; + while(tmp != 0 && tmp != -1) { + nb++; + tmp >>= 1; + } + return nb + 1; + } + + public final int val; + + private IntValue(int i) { this.val = i; } + + @Override + public final byte getKind() { return INTVALUE; } + + // the number of bits needed to encode the value of this int + public final int nbits() { + try { + return nbits(this.val); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + public static IntValue gen(int i) { + if (i >= 0 && i < cache.length) { + return cache[i]; + } + return new IntValue(i); + } + + @Override + public final int compareTo(Object obj) { + try { + if (obj instanceof IntValue) { + return Integer.compare(this.val, ((IntValue)obj).val); + } + if (!(obj instanceof ModelValue)) { + Assert.fail("Attempted to compare integer " + Values.ppr(this.toString()) + + " with non-integer:\n" + Values.ppr(obj.toString())); + } + return 1; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + public final boolean equals(Object obj) { + try { + if (obj instanceof IntValue) { + return this.val == ((IntValue)obj).val; + } + if (!(obj instanceof ModelValue)) { + Assert.fail("Attempted to check equality of integer " + Values.ppr(this.toString()) + + " with non-integer:\n" + Values.ppr(obj.toString())); + } + return ((ModelValue) obj).modelValueEquals(this); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final boolean member(Value elem) { + try { + Assert.fail("Attempted to check if the value:\n" + Values.ppr(elem.toString()) + + "\nis an element of the integer " + Values.ppr(this.toString())); + return false; // make compiler happy + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final boolean isFinite() { + try { + Assert.fail("Attempted to check if the integer " + Values.ppr(this.toString()) + + " is a finite set."); + return false; // make compiler happy + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final Value takeExcept(ValueExcept ex) { + try { + if (ex.idx < ex.path.length) { + Assert.fail("Attempted to appy EXCEPT construct to the integer " + + Values.ppr(this.toString()) + "."); + } + return ex.value; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final Value takeExcept(ValueExcept[] exs) { + try { + if (exs.length != 0) { + Assert.fail("Attempted to apply EXCEPT construct to the integer " + + Values.ppr(this.toString()) + "."); + } + return this; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final int size() { + try { + Assert.fail("Attempted to compute the number of elements in the integer " + + Values.ppr(this.toString()) + "."); + return 0; // make compiler happy + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public boolean mutates() { + // finalized after construction. + return true; + } + + @Override + public final boolean isNormalized() { return true; } + + @Override + public final Value normalize() { /*nop*/return this; } + + @Override + public final boolean isDefined() { return true; } + + @Override + public final IValue deepCopy() { return this; } + + @Override + public final boolean assignable(Value val) { + try { + return ((val instanceof IntValue) && + this.val == ((IntValue)val).val); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public void write(IValueOutputStream vos) throws IOException { + vos.writeByte(INTVALUE); + vos.writeInt(val); + } + + /* The fingerprint methods */ + @Override + public final long fingerPrint(long fp) { + try { + return FP64.Extend(FP64.Extend(fp, INTVALUE), this.val); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final IValue permute(IMVPerm perm) { return this; } + + /* The string representation. */ + @Override + public final StringBuffer toString(StringBuffer sb, int offset, boolean ignored) { + try { + return sb.append(this.val); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + +} diff --git a/tlatools/src/tlc2/value/impl/IntervalValue.java b/tlatools/src/tlc2/value/impl/IntervalValue.java new file mode 100644 index 0000000000000000000000000000000000000000..f6b47041fa28d3baa84b87fd5633c9cbeb083f34 --- /dev/null +++ b/tlatools/src/tlc2/value/impl/IntervalValue.java @@ -0,0 +1,383 @@ +// Copyright (c) 2003 Compaq Corporation. All rights reserved. +// Portions Copyright (c) 2003 Microsoft Corporation. All rights reserved. +// Last modified on Wed 12 Jul 2017 at 16:10:00 PST by ian morris nieves +// modified on Sat 23 February 2008 at 10:12:59 PST by lamport +// modified on Fri Aug 10 15:07:36 PDT 2001 by yuanyu + +package tlc2.value.impl; + +import java.io.IOException; + +import tlc2.tool.FingerprintException; +import tlc2.util.FP64; +import tlc2.value.IMVPerm; +import tlc2.value.IValue; +import tlc2.value.IValueOutputStream; +import tlc2.value.Values; +import util.Assert; + +public class IntervalValue extends EnumerableValue +implements Enumerable, Reducible { + public int low, high; // the integer interval [low, high] + + /* Constructor */ + public IntervalValue(int low, int high) { + this.low = low; + this.high = high; + } + + @Override + public final byte getKind() { return INTERVALVALUE; } + + @Override + public final int compareTo(Object obj) { + try { + if (obj instanceof IntervalValue) { + IntervalValue intv = (IntervalValue) obj; + int cmp = this.size() - intv.size(); + if (cmp != 0) { + return cmp; + } + if (this.size() == 0) { + // empty intervals are equal, regardless of the low value + return 0; + } + return Integer.compare(this.low, intv.low); + } + // Well, we have to convert them to sets and compare. + return this.toSetEnum().compareTo(obj); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + public final boolean equals(Object obj) { + try { + if (obj instanceof IntervalValue) { + IntervalValue intv = (IntervalValue)obj; + if (this.size() == 0) return intv.size() == 0; + return (this.low == intv.low) && (this.high == intv.high); + } + // Well, we have to convert them to sets and compare. + return this.toSetEnum().equals(obj); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final boolean member(Value elem) { + try { + if (elem instanceof IntValue) { + int x = ((IntValue)elem).val; + return (x >= low) && (x <= high); + } + if ( (this.low <= this.high) + && ( !(elem instanceof ModelValue) + || (((ModelValue) elem).type != 0)) ) { + Assert.fail("Attempted to check if the value:\n" + Values.ppr(elem.toString()) + + "\nis in the integer interval " + Values.ppr(this.toString())); + } + return false; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public Value isSubsetEq(Value other) { + try { + if (other instanceof IntervalValue) { + final IntervalValue iv = (IntervalValue) other; + if (iv.low <= low && iv.high >= high) { + return BoolValue.ValTrue; + } + } + return super.isSubsetEq(other); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final boolean isFinite() { return true; } + + @Override + public final int size() { + try { + if (this.high < this.low) { + return 0; + } + try { + return Math.addExact(Math.subtractExact(this.high, this.low), 1); + } catch (ArithmeticException e) { + Assert.fail("Size of interval value exceeds the maximum representable size (32bits): " + + Values.ppr(this.toString()) + "."); + return 0; // unreachable, but it satisfies the compiler + } + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + /** + * @return Converts this IntervalValue instance into a Value[]. This can be seen + * as the inverse to the performance optimization that the IntervalValue + * actually is. + */ + final Value[] asValues() { + final Value[] values = new Value[size()]; + for (int i = 0; i < size(); i++) { + values[i] = IntValue.gen(this.low + i); + } + return values; + } + + /* Return this - val. */ + @Override + public final Value diff(Value val) { + try { + ValueVec diffElems = new ValueVec(); + for (int i = this.low; i <= this.high; i++) { + Value elem = IntValue.gen(i); + if (!val.member(elem)) diffElems.addElement(elem); + } + return new SetEnumValue(diffElems, true, cm); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + /* Return this \cap val. */ + @Override + public final Value cap(Value val) { + try { + ValueVec capElems = new ValueVec(); + for (int i = this.low; i <= this.high; i++) { + Value elem = IntValue.gen(i); + if (val.member(elem)) capElems.addElement(elem); + } + return new SetEnumValue(capElems, true, cm); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + /* Return this \cup val. */ + @Override + public final Value cup(Value set) { + try { + if (this.size() == 0) return set; + + if (set instanceof Reducible) { + ValueVec cupElems = new ValueVec(); + for (int i = this.low; i <= this.high; i++) { + cupElems.addElement(IntValue.gen(i)); + } + ValueEnumeration Enum = ((Enumerable)set).elements(); + Value elem; + while ((elem = Enum.nextElement()) != null) { + if (!this.member(elem)) cupElems.addElement(elem); + } + return new SetEnumValue(cupElems, false, cm); + } + return new SetCupValue(this, set, cm); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final Value takeExcept(ValueExcept ex) { + try { + if (ex.idx < ex.path.length) { + Assert.fail("Attempted to apply EXCEPT construct to the interval value " + + Values.ppr(this.toString()) + "."); + } + return ex.value; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final Value takeExcept(ValueExcept[] exs) { + try { + if (exs.length != 0) { + Assert.fail("Attempted to apply EXCEPT construct to the interval value " + + Values.ppr(this.toString()) + "."); + } + return this; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final boolean isNormalized() { return true; } + + @Override + public final Value normalize() { /*nop*/return this; } + + @Override + public final boolean isDefined() { return true; } + + @Override + public final IValue deepCopy() { return this; } + + @Override + public final boolean assignable(Value val) { + try { + return ((val instanceof IntervalValue) && + this.high == ((IntervalValue)val).high && + this.low == ((IntervalValue)val).low); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public void write(final IValueOutputStream vos) throws IOException { + vos.writeByte(INTERVALVALUE); + vos.writeInt(low); + vos.writeInt(high); + } + + /* The fingerprint method */ + @Override + public final long fingerPrint(long fp) { + try { + fp = FP64.Extend(fp, SETENUMVALUE); + fp = FP64.Extend(fp, this.size()) ; + for (int i = this.low; i <= this.high; i++) { + fp = FP64.Extend(fp, INTVALUE); + fp = FP64.Extend(fp, i); + } + return fp; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public boolean mutates() { + return false; + } + + @Override + public final IValue permute(IMVPerm perm) { + return this; + } + + @Override + public Value toSetEnum() { + Value[] vals = new Value[size()]; + for (int i = 0; i < vals.length; i++) { + vals[i] = IntValue.gen(i + this.low); + } + if (coverage) {cm.incSecondary(vals.length);} + return new SetEnumValue(vals, true, cm); + } + + /* The string representation */ + @Override + public final StringBuffer toString(StringBuffer sb, int offset, final boolean ignored) { + try { + if (this.low <= this.high) { + return sb.append(this.low).append("..").append(this.high); + } + return sb.append("{").append("}"); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public EnumerableValue getRandomSubset(final int kOutOfN) { + final ValueVec vec = new ValueVec(kOutOfN); + + final ValueEnumeration ve = elements(kOutOfN); + + Value v = null; + while ((v = ve.nextElement()) != null) { + vec.addElement(v); + } + return new SetEnumValue(vec, false, cm); + } + + public Value elementAt(final int idx) { + if (0 <= idx && idx < size()) { + return IntValue.gen(low + idx); + } + Assert.fail( + "Attempted to retrieve out-of-bounds element from the interval value " + Values.ppr(this.toString()) + "."); + return null; // make compiler happy + } + + @Override + public final ValueEnumeration elements() { + try { + return new Enumerator(); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + final class Enumerator implements ValueEnumeration { + int index = low; + + @Override + public final void reset() { this.index = low; } + + @Override + public final Value nextElement() { + if (this.index <= high) { + if (coverage) { cm.incSecondary(); } + return IntValue.gen(this.index++); + } + return null; + } + + } + + @Override + public ValueEnumeration elements(final int kOutOfN) { + return new EnumerableValue.SubsetEnumerator(kOutOfN) { + @Override + public Value nextElement() { + if (!hasNext()) { + return null; + } + return IntValue.gen(low + nextIndex()); + } + }; + } +} diff --git a/tlatools/src/tlc2/value/impl/LazyValue.java b/tlatools/src/tlc2/value/impl/LazyValue.java new file mode 100644 index 0000000000000000000000000000000000000000..fe5b1eba3978c9fecdc5ca4e5c9b064a343762c3 --- /dev/null +++ b/tlatools/src/tlc2/value/impl/LazyValue.java @@ -0,0 +1,295 @@ +// Copyright (c) 2003 Compaq Corporation. All rights reserved. +// Portions Copyright (c) 2003 Microsoft Corporation. All rights reserved. +// Last modified on Wed 12 Jul 2017 at 16:10:00 PST by ian morris nieves +// modified on Mon 30 Apr 2007 at 15:30:08 PST by lamport +// modified on Thu Feb 8 21:23:55 PST 2001 by yuanyu + +package tlc2.value.impl; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; + +import tla2sany.semantic.SemanticNode; +import tlc2.tool.FingerprintException; +import tlc2.tool.coverage.CostModel; +import tlc2.util.Context; +import tlc2.value.IMVPerm; +import tlc2.value.IValue; +import util.Assert; +import util.ToolIO; + +public class LazyValue extends Value { + /** + * Allow to completely disable LazyValue by passing a VM property/system + * property to the Java VM with "-Dtlc2.value.LazyValue.off=true". This is meant + * for debug purposes only and can be removed at any time. This is not API. + * + * This property was added 01/12/2018 by Markus Kuppe in the process of fixing a + * bug where TLC generates and incorrect set of states with certain statements. + * More details can be found at https://github.com/tlaplus/tlaplus/issues/113. + */ + public static final boolean LAZYEVAL_OFF = Boolean.getBoolean(tlc2.value.impl.LazyValue.class.getName() + ".off"); + + static { + // Indicate if LazyValue will be disabled in this TLC run. + if (LAZYEVAL_OFF) { + ToolIO.out.println("LazyValue is disabled."); + } + } + /** + * The field val is the result of evaluating expr in context con and + * a pair of states. If val is null, then the value has not been + * computed, but when computed, the value can be cached in the field + * val. If val is ValUndef, then the value has not been computed, + * and when computed, it can not be cached in the field val. + */ + + public SemanticNode expr; + public Context con; + private Value val; + + public LazyValue(SemanticNode expr, Context con, final CostModel cm) { + this(expr, con, true, coverage ? cm.get(expr) : cm); + } + + public LazyValue(SemanticNode expr, Context con, final boolean cachable, final CostModel cm) { + this.expr = expr; + this.con = con; + this.cm = coverage ? cm.get(expr) : cm; + this.val = null; + // See comment on cachable's meager performance in Tool.java on line 1408. + // See other note about a bug that surfaced with LazyValue in Tool.java on line ~1385. + if (LAZYEVAL_OFF || !cachable) { + this.val = UndefValue.ValUndef; + } + } + + public final boolean isUncachable() { return this.val == UndefValue.ValUndef; } + + public final void setValue(final Value aValue) { + assert !isUncachable(); + this.val = aValue; + } + + public final Value getValue() { + // cache hit on (this.val != null && !isUncachable) + // cache miss on (this.val == null) + return this.val; + } + + @Override + public final byte getKind() { return LAZYVALUE; } + + @Override + public final int compareTo(Object obj) { + try { + if (this.val == null || this.val == UndefValue.ValUndef) { + Assert.fail("Error(TLC): Attempted to compare lazy values."); + } + return this.val.compareTo(obj); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + public final boolean equals(Object obj) { + try { + if (this.val == null || this.val == UndefValue.ValUndef) { + Assert.fail("Error(TLC): Attempted to check equality of lazy values."); + } + return this.val.equals(obj); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final boolean member(Value elem) { + try { + if (this.val == null || this.val == UndefValue.ValUndef) { + Assert.fail("Error(TLC): Attempted to check set membership of lazy values."); + } + return this.val.member(elem); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final boolean isFinite() { + try { + if (this.val == null || this.val == UndefValue.ValUndef) { + Assert.fail("Error(TLC): Attempted to check if a lazy value is a finite set."); + } + return this.val.isFinite(); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final Value takeExcept(ValueExcept ex) { + try { + if (this.val == null || this.val == UndefValue.ValUndef) { + Assert.fail("Error(TLC): Attempted to apply EXCEPT construct to lazy value."); + } + return this.val.takeExcept(ex); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final Value takeExcept(ValueExcept[] exs) { + try { + if (this.val == null || this.val == UndefValue.ValUndef) { + Assert.fail("Error(TLC): Attempted to apply EXCEPT construct to lazy value."); + } + return this.val.takeExcept(exs); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final int size() { + try { + if (this.val == null || this.val == UndefValue.ValUndef) { + Assert.fail("Error(TLC): Attempted to compute size of lazy value."); + } + return this.val.size(); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException { + this.val = (Value)ois.readObject(); + } + + private void writeObject(ObjectOutputStream oos) throws IOException { + if (this.val == null || this.val == UndefValue.ValUndef) { + Assert.fail("Error(TLC): Attempted to serialize lazy value."); + } + oos.writeObject(this.val); + } + + /* Nothing to normalize. */ + @Override + public final boolean isNormalized() { + try { + if (this.val == null || this.val == UndefValue.ValUndef) { + Assert.fail("Error(TLC): Attempted to normalize lazy value."); + } + return this.val.isNormalized(); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final Value normalize() { + try { + if (this.val == null || this.val == UndefValue.ValUndef) { + Assert.fail("Error(TLC): Attempted to normalize lazy value."); + } + this.val.normalize(); + return this; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final boolean isDefined() { return true; } + + @Override + public final IValue deepCopy() { + try { + if (this.val == null || this.val == UndefValue.ValUndef) return this; + return this.val.deepCopy(); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final boolean assignable(Value val) { + try { + if (this.val == null || this.val == UndefValue.ValUndef) { + Assert.fail("Error(TLC): Attempted to call assignable on lazy value."); + } + return this.val.assignable(val); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + /* The fingerprint method */ + @Override + public final long fingerPrint(long fp) { + try { + if (this.val == null || this.val == UndefValue.ValUndef) { + Assert.fail("Error(TLC): Attempted to fingerprint a lazy value."); + } + return this.val.fingerPrint(fp); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final IValue permute(IMVPerm perm) { + try { + if (this.val == null || this.val == UndefValue.ValUndef) { + Assert.fail("Error(TLC): Attempted to apply permutation to lazy value."); + } + return this.val.permute(perm); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + /* The string representation of the value. */ + @Override + public final StringBuffer toString(StringBuffer sb, int offset, boolean swallow) { + try { + if (this.val == null || this.val == UndefValue.ValUndef) { + return sb.append("<LAZY " + this.expr + ">"); + } + return this.val.toString(sb, offset, swallow); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + +} diff --git a/tlatools/src/tlc2/value/impl/MVPerm.java b/tlatools/src/tlc2/value/impl/MVPerm.java new file mode 100644 index 0000000000000000000000000000000000000000..ac04e48c0ce83dac470fd0b5ab802b78e1bb2bee --- /dev/null +++ b/tlatools/src/tlc2/value/impl/MVPerm.java @@ -0,0 +1,140 @@ +// Copyright (c) 2003 Compaq Corporation. All rights reserved. +// Portions Copyright (c) 2003 Microsoft Corporation. All rights reserved. +// Last modified on Mon 30 Apr 2007 at 13:42:27 PST by lamport +// modified on Thu Nov 16 15:53:30 PST 2000 by yuanyu + +package tlc2.value.impl; + +import java.util.ArrayList; +import java.util.List; + +import tlc2.value.IMVPerm; +import tlc2.value.IModelValue; +import tlc2.value.IValue; + +public final class MVPerm implements IMVPerm { + private final ModelValue[] elems; + private int count; + + MVPerm() { + this.elems = new ModelValue[ModelValue.mvs.length]; + this.count = 0; + } + + public final boolean equals(Object obj) { + if (obj instanceof MVPerm) { + MVPerm perm = (MVPerm)obj; + for (int i = 0; i < this.elems.length; i++) { + if (this.elems[i] == null) { + if (perm.elems[i] != null) { + return false; + } + } + else if (!this.elems[i].equals(perm.elems[i])) { + return false; + } + } + return true; + } + return false; + } + + public final int hashCode() { + int res = 0; + for (int i = 0; i < this.elems.length; i++) { + ModelValue mv = this.elems[i]; + if (mv != null) { + res = 31*res + mv.val.hashCode(); + } + } + return res; + } + + @Override + public final int size() { return this.count; } + + @Override + public final IValue get(IValue k) { + return this.elems[((ModelValue) k).index]; + } + + @Override + public final void put(IModelValue m1, IModelValue m2) { + ModelValue k = (ModelValue) m1; + ModelValue elem = (ModelValue) m2; + if (!k.equals(elem) && this.elems[k.index] == null) { + this.elems[k.index] = elem; + this.count++; + } + } + + private final void put(int i, ModelValue elem) { + if (this.elems[i] == null && elem != null) { + this.elems[i] = elem; + this.count++; + } + } + + @Override + public final IMVPerm compose(IMVPerm perm) { + MVPerm res = new MVPerm(); + for (int i = 0; i < this.elems.length; i++) { + ModelValue mv = this.elems[i]; + if (mv == null) { + res.put(i, ((MVPerm) perm).elems[i]); + } + else { + ModelValue mv1 = ((MVPerm) perm).elems[mv.index]; + if (mv1 == null) { + res.put(i, mv); + } + else if (!ModelValue.mvs[i].equals(mv1)) { + res.put(i, mv1); + } + } + } + return res; + } + + /** + * Consider caching if this method is used frequently; currently it is used once per instance per + * execution of TLC, during initial state setup / expansion. + * + * @return a {@code List} of all {@link ModelValue} instances held by this permutation. + */ + public List<ModelValue> getAllModelValues() { + final List<ModelValue> values = new ArrayList<>(); + + for (int i = 0; i < elems.length; i++) { + if (elems[i] != null) { + values.add(elems[i]); + } + } + + return values; + } + + public final String toString() { + StringBuffer sb = new StringBuffer("["); + int i = 0; + for (i = 0; i < this.elems.length; i++) { + if (this.elems[i] != null) { + sb.append(ModelValue.mvs[i].toString()); + sb.append(" -> "); + sb.append(this.elems[i].toString()); + break; + } + } + for (int j = i+1; j < this.elems.length; j++) { + if (this.elems[j] != null) { + sb.append(", "); + sb.append(ModelValue.mvs[j].toString()); + sb.append(" -> "); + sb.append(this.elems[j].toString()); + } + } + sb.append("]"); + return sb.toString(); + } + +} diff --git a/tlatools/src/tlc2/value/impl/MVPerms.java b/tlatools/src/tlc2/value/impl/MVPerms.java new file mode 100644 index 0000000000000000000000000000000000000000..52b8fa7d30d93aa02f8e52288bea8526e78a0192 --- /dev/null +++ b/tlatools/src/tlc2/value/impl/MVPerms.java @@ -0,0 +1,70 @@ +// Copyright (c) 2003 Compaq Corporation. All rights reserved. +// Portions Copyright (c) 2003 Microsoft Corporation. All rights reserved. +// Last modified on Mon 30 Apr 2007 at 13:42:27 PST by lamport +// modified on Thu Nov 16 15:53:30 PST 2000 by yuanyu + +package tlc2.value.impl; + +import java.util.Enumeration; + +import tlc2.util.Vect; +import tlc2.value.IMVPerm; +import tlc2.value.IValue; +import util.Assert; +import util.Set; + +public abstract class MVPerms { + + public static final IMVPerm[] permutationSubgroup(final Enumerable enumerable) { + final ValueEnumeration Enum = enumerable.elements(); + final int sz = enumerable.size() - 1; + final Set perms = new Set(sz); + final Vect<IMVPerm> permVec = new Vect<>(sz); + // Compute the group generators: + Value elem; + while ((elem = Enum.nextElement()) != null) { + final FcnRcdValue fcn = (FcnRcdValue) elem.toFcnRcd(); + if (fcn == null) { + Assert.fail("The symmetry operator must specify a set of functions."); + } + final IMVPerm perm = new MVPerm(); + for (int i = 0; i < fcn.domain.length; i++) { + final IValue dval = fcn.domain[i]; + final IValue rval = fcn.values[i]; + if ((dval instanceof ModelValue) && (rval instanceof ModelValue)) { + perm.put((ModelValue)dval, (ModelValue)rval); + } + else { + Assert.fail("Symmetry function must have model values as domain and range."); + } + } + if (perm.size() > 0 && perms.put(perm) == null) { + permVec.addElement(perm); + } + } + // Compute the group generated by the generators: + final int gsz = permVec.size(); + int sz0 = 0; + while (true) { + final int sz1 = permVec.size(); + for (int i = 0; i < gsz; i++) { + final IMVPerm perm1 = (IMVPerm)permVec.elementAt(i); + for (int j = sz0; j < sz1; j++) { + IMVPerm perm = perm1.compose((IMVPerm)permVec.elementAt(j)); + if (perm.size() > 0 && perms.put(perm) == null) { + permVec.addElement(perm); + } + } + } + if (sz1 == permVec.size()) { break; } + sz0 = sz1; + } + // Finally, put all the elements in an array ready for use: + final IMVPerm[] res = new IMVPerm[permVec.size()]; + final Enumeration<IMVPerm> permEnum = permVec.elements(); + for (int i = 0; i < res.length; i++) { + res[i] = permEnum.nextElement(); + } + return res; + } +} diff --git a/tlatools/src/tlc2/value/impl/MethodValue.java b/tlatools/src/tlc2/value/impl/MethodValue.java new file mode 100644 index 0000000000000000000000000000000000000000..2674782629d468ae96332aa9dca3fe7baaf8e2ee --- /dev/null +++ b/tlatools/src/tlc2/value/impl/MethodValue.java @@ -0,0 +1,305 @@ +// Copyright (c) 2003 Compaq Corporation. All rights reserved. +// Portions Copyright (c) 2003 Microsoft Corporation. All rights reserved. +// Last modified on Wed 12 Jul 2017 at 16:10:00 PST by ian morris nieves +// modified on Mon 30 Apr 2007 at 13:21:00 PST by lamport +// modified on Fri Sep 22 13:18:45 PDT 2000 by yuanyu + +package tlc2.value.impl; + +import java.io.PrintWriter; +import java.io.StringWriter; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; + +import tlc2.output.EC; +import tlc2.output.MP; +import tlc2.tool.EvalControl; +import tlc2.tool.EvalException; +import tlc2.tool.FingerprintException; +import tlc2.tool.impl.Tool; +import tlc2.value.IValue; +import tlc2.value.Values; +import util.Assert; +import util.Assert.TLCRuntimeException; +import util.WrongInvocationException; + +public class MethodValue extends OpValue implements Applicable { + + public static Value get(final Method md) { + return get(md, 0); + } + + public static Value get(final Method md, int minLevel) { + final MethodValue mv = new MethodValue(md, minLevel); + // Eagerly evaluate the constant operator if possible (zero arity) to only + // evaluate once at startup and not during state exploration. + final int acnt = md.getParameterTypes().length; + final boolean isConstant = (acnt == 0) && Modifier.isFinal(md.getModifiers()); + return isConstant ? mv.apply(Tool.EmptyArgs, EvalControl.Clear) : mv; + } + + private final MethodHandle mh; + private final Method md; + private final int minLevel; + + /* Constructor */ + private MethodValue(final Method md, final int minLevel) { + this.md = md; + this.minLevel = minLevel; + try { + final int parameterCount = this.md.getParameterCount(); + if (parameterCount > 0) { + // With more than one argument, we want to setup the method handle to use a + // spreader which essentially converts the Value[] into something that is + // accepted by the method handle. Without a spreader, passing a Value[] to + // MethodHandle#invoke does not work. Instead one has to use MethodHandle#invokeWithArguments. + // MethodHandle#invokeWithArguments internally creates a spreader on the fly + // which turns out to be costly (for the spec MongoRepl of the performance + // tests it resulted in a 20% performance drop). + this.mh = MethodHandles.publicLookup().unreflect(md).asSpreader(IValue[].class, parameterCount); + } else { + this.mh = MethodHandles.publicLookup().unreflect(md); + } + } catch (IllegalAccessException e) { + throw new TLCRuntimeException(EC.TLC_MODULE_VALUE_JAVA_METHOD_OVERRIDE, MP.getMessage( + EC.TLC_MODULE_VALUE_JAVA_METHOD_OVERRIDE, new String[] { md.toString(), e.getMessage() })); + } + } + + @Override + public final byte getKind() { return METHODVALUE; } + + @Override + public final IValue initialize() { + this.deepNormalize(); + // Do not call fingerprint as a MethodValue has no fingerprint. + return this; + } + + @Override + public final int compareTo(Object obj) { + try { + Assert.fail("Attempted to compare operator " + this.toString() + + " with value:\n" + obj == null ? "null" : Values.ppr(obj.toString())); + return 0; // make compiler happy + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + public final boolean equals(Object obj) { + try { + Assert.fail("Attempted to check equality of operator " + this.toString() + + " with value:\n" + obj == null ? "null" : Values.ppr(obj.toString())); + return false; // make compiler happy + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final boolean member(Value elem) { + try { + Assert.fail("Attempted to check if the value:\n" + elem == null ? "null" : Values.ppr(elem.toString()) + + "\nis an element of operator " + this.toString()); + return false; // make compiler happy + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final boolean isFinite() { + try { + Assert.fail("Attempted to check if the operator " + this.toString() + + " is a finite set."); + return false; // make compiler happy + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final Value apply(Value arg, int control) { + try { + throw new WrongInvocationException("It is a TLC bug: Should use the other apply method."); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final Value apply(Value[] args, int control) { + try { + Value res = null; + try + { + if (args.length == 0) { + res = (Value) this.mh.invokeExact(); + } else { + res = (Value) this.mh.invoke(args); + } + } catch (Throwable e) + { + if (e instanceof InvocationTargetException) + { + Throwable targetException = ((InvocationTargetException)e).getTargetException(); + throw new EvalException(EC.TLC_MODULE_VALUE_JAVA_METHOD_OVERRIDE, new String[]{this.md.toString(), targetException.getMessage()}); + } else if (e instanceof NullPointerException) { + throw new EvalException(EC.TLC_MODULE_VALUE_JAVA_METHOD_OVERRIDE, new String[]{this.md.toString(), e.getMessage()}); + } else if (e instanceof EvalException) { + // Do not wrap an EvalException below. + throw (EvalException) e; + } else + { + String message = e.getMessage(); + if (message == null) { + // Try to pass some information along (i.e. the full stack-trace) in cases where + // message is null. + final StringWriter sw = new StringWriter(); + e.printStackTrace(new PrintWriter(sw)); + message = sw.toString(); + } + Assert.fail(EC.TLC_MODULE_VALUE_JAVA_METHOD_OVERRIDE, new String[]{this.md.toString(), message}); + } + } + return res; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final Value select(Value arg) { + try { + throw new WrongInvocationException("It is a TLC bug: Attempted to call MethodValue.select()."); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final Value takeExcept(ValueExcept ex) { + try { + Assert.fail("Attempted to appy EXCEPT construct to the operator " + + this.toString() + "."); + return null; // make compiler happy + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final Value takeExcept(ValueExcept[] exs) { + try { + Assert.fail("Attempted to apply EXCEPT construct to the operator " + + this.toString() + "."); + return null; // make compiler happy + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final Value getDomain() { + try { + Assert.fail("Attempted to compute the domain of the operator " + + this.toString() + "."); + return SetEnumValue.EmptySet; // make compiler happy + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final int size() { + try { + Assert.fail("Attempted to compute the number of elements in the operator " + + this.toString() + "."); + return 0; // make compiler happy + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + /* Should never normalize an operator. */ + @Override + public final boolean isNormalized() { + try { + throw new WrongInvocationException("It is a TLC bug: Attempted to normalize an operator."); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final Value normalize() { + try { + throw new WrongInvocationException("It is a TLC bug: Attempted to normalize an operator."); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final boolean isDefined() { return true; } + + @Override + public final IValue deepCopy() { return this; } + + @Override + public final boolean assignable(Value val) { + try { + throw new WrongInvocationException("It is a TLC bug: Attempted to initialize an operator."); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + /* String representation of the value. */ + @Override + public final StringBuffer toString(StringBuffer sb, int offset, boolean ignored) { + try { + return sb.append("<Java Method: " + this.md + ">"); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + public final int getMinLevel() { + return minLevel; + } +} diff --git a/tlatools/src/tlc2/value/impl/ModelValue.java b/tlatools/src/tlc2/value/impl/ModelValue.java new file mode 100644 index 0000000000000000000000000000000000000000..1651468acdf8356f15e6d19aa6aecaece64b3277 --- /dev/null +++ b/tlatools/src/tlc2/value/impl/ModelValue.java @@ -0,0 +1,359 @@ +// Copyright (c) 2003 Compaq Corporation. All rights reserved. +// Portions Copyright (c) 2003 Microsoft Corporation. All rights reserved. +// Last modified on Wed 12 Jul 2017 at 16:10:00 PST by ian morris nieves +// modified on Sat 23 February 2008 at 10:32:25 PST by lamport +// modified on Fri Aug 10 15:07:47 PDT 2001 by yuanyu + +/*************************************************************************** +* Change to model values made by LL on 23 Feb 2008: * +* ------ * +* A model value whose name is "x_str" for any character x and string str * +* is said to have TYPE 'x'. Otherwise, it is said to be untyped. It is * +* an error to test if a typed model value is equal to any value except a * +* model value that is either untyped or has the same type. (As before, * +* an untyped model value is unequal to any value except itself.) * +* * +* This was implemented by adding a `type' field to a ModelValue object * +* and making changes to the following classes: * +* * +* changed member method * +* module/Integers.java * +* module/Naturals.java * +* module/Sequences.java * +* module/Strings.java * +* value/IntervalValue.java CHECK THIS with mv \notin 1..2 * +* value/SetOfFcnsValue.java * +* value/SetOfRcdsValue.java * +* value/SetOfTuplesValue.java * +* value/StringValue.java * +* changed equals method * +* value/BoolValue.java * +* value/FcnRcdValue.java * +* value/IntValue.java * +* value/RecordValue.java * +* value/SetEnumValue.java * +***************************************************************************/ + +package tlc2.value.impl; + +import java.io.IOException; +import java.util.Enumeration; +import java.util.Hashtable; + +import tlc2.tool.FingerprintException; +import tlc2.util.FP64; +import tlc2.value.IMVPerm; +import tlc2.value.IModelValue; +import tlc2.value.IValue; +import tlc2.value.IValueOutputStream; +import tlc2.value.Values; +import util.Assert; +import util.UniqueString; + +public class ModelValue extends Value implements IModelValue { + + /** + * A method to reset the model values + * All callers should make sure that the model value class has been initialized + */ + public static void init() + { + count = 0; + mvTable = new Hashtable(); + mvs = null; + } + + /** + * Workround to the static usage + */ + static + { + init(); + } + + private static int count; + private static Hashtable mvTable; + // SZ Mar 9, 2009: public accessed field, this will cause troubles + public static ModelValue[] mvs; + + public UniqueString val; + public int index; + public char type; // type = 0 means untyped. + + /* Constructor */ + private ModelValue(String val) { + // SZ 11.04.2009: changed access method + this.val = UniqueString.uniqueStringOf(val); + this.index = count++; + if ( (val.length() > 2) + && (val.charAt(1) == '_')) { + this.type = val.charAt(0) ; + } + else { this.type = 0 ; } + } + + /* Make str a new model value, if it is not one yet. */ + public static Value make(String str) { + ModelValue mv = (ModelValue)mvTable.get(str); + if (mv != null) return mv; + mv = new ModelValue(str); + mvTable.put(str, mv); + return mv; + } + + /* Collect all the model values defined thus far. */ + public static void setValues() { + mvs = new ModelValue[mvTable.size()]; + Enumeration Enum = mvTable.elements(); + while (Enum.hasMoreElements()) { + ModelValue mv = (ModelValue)Enum.nextElement(); + mvs[mv.index] = mv; + } + } + + @Override + public final byte getKind() { return MODELVALUE; } + + @Override + public final int compareTo(Object obj) { + try { + if (obj instanceof ModelValue) { + return this.val.compareTo(((ModelValue)obj).val); + } + return -1; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + public final boolean equals(Object obj) { + try { + if (this.type == 0) { + return (obj instanceof ModelValue && + this.val.equals(((ModelValue)obj).val)); + } + if (obj instanceof ModelValue) { + ModelValue mobj = (ModelValue) obj ; + if ( (mobj.type == this.type) + || (mobj.type == 0) ) { + return mobj.val == this.val ; + } + else { + Assert.fail("Attempted to check equality " + + "of the differently-typed model values " + + Values.ppr(this.toString()) + " and " + + Values.ppr(mobj.toString())); + } + } + Assert.fail("Attempted to check equality of typed model value " + + Values.ppr(this.toString()) + " and non-model value\n" + + Values.ppr(obj.toString())) ; + return false; // make compiler happy + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + /************************************************************************* + * The following two methods are used used to check if this model value * + * equal to or a member of non-model value obj. They return false if * + * this model value is untyped and raise an exception if it is typed. * + *************************************************************************/ + public final boolean modelValueEquals(Object obj){ + try { + if (this.type != 0) { + Assert.fail("Attempted to check equality of the typed model value " + + Values.ppr(this.toString()) + " and the non-model value\n" + + Values.ppr(obj.toString())) ; + + } + return false ; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + public final boolean modelValueMember(Object obj){ + try { + if (this.type != 0) { + Assert.fail("Attempted to check if the typed model value " + + Values.ppr(this.toString()) + + " is an element of\n" + + Values.ppr(obj.toString())) ; + + } + return false ; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final boolean member(Value elem) { + try { + Assert.fail("Attempted to check if the value:\n" + Values.ppr(elem.toString()) + + "\nis an element of the model value " + Values.ppr(this.toString())); + return false; // make compiler happy + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final boolean isFinite() { + try { + Assert.fail("Attempted to check if the model value " + Values.ppr(this.toString()) + + " is a finite set."); + return false; // make compiler happy + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final Value takeExcept(ValueExcept ex) { + try { + if (ex.idx < ex.path.length) { + Assert.fail("Attempted to apply EXCEPT construct to the model value " + + Values.ppr(this.toString()) + "."); + } + return ex.value; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final Value takeExcept(ValueExcept[] exs) { + try { + if (exs.length != 0) { + Assert.fail("Attempted to apply EXCEPT construct to the model value " + + Values.ppr(this.toString()) + "."); + } + return this; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final int size() { + try { + Assert.fail("Attempted to compute the number of elements in the model value " + + Values.ppr(this.toString()) + "."); + return 0; // make compiler happy + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public boolean mutates() { + return false; + } + + @Override + public final boolean isNormalized() { return true; } + + @Override + public final Value normalize() { /*nop*/return this; } + + @Override + public final boolean isDefined() { return true; } + + @Override + public final IValue deepCopy() { return this; } + + @Override + public final boolean assignable(Value val) { + try { + return ((val instanceof ModelValue) && + this.val.equals(((ModelValue)val).val)); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public void write(IValueOutputStream vos) throws IOException { + vos.writeByte(MODELVALUE); + vos.writeShort((short) index); + } + + /* The fingerprint methods */ + @Override + public final long fingerPrint(long fp) { + try { + return this.val.fingerPrint(FP64.Extend(fp, MODELVALUE)); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final IValue permute(IMVPerm perm) { + try { + IValue res = perm.get(this); + if (res == null) return this; + return res; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + /* The string representation. */ + @Override + public final StringBuffer toString(StringBuffer sb, int offset, boolean ignored) { + try { + return sb.append(this.val); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + // The presence of this field is justified by a small number of ModelValue + // instances being created overall. + private Object data; + + @Override + public boolean hasData() { + return data != null; + } + + @Override + public Object getData() { + return data; + } + + @Override + public Object setData(final Object obj) { + this.data = obj; + return obj; + } +} diff --git a/tlatools/src/tlc2/value/impl/OpLambdaValue.java b/tlatools/src/tlc2/value/impl/OpLambdaValue.java new file mode 100644 index 0000000000000000000000000000000000000000..24c4d13bf63fadc8845da1ae0e3660894cbb7a53 --- /dev/null +++ b/tlatools/src/tlc2/value/impl/OpLambdaValue.java @@ -0,0 +1,251 @@ +// Copyright (c) 2003 Compaq Corporation. All rights reserved. +// Portions Copyright (c) 2003 Microsoft Corporation. All rights reserved. +// Last modified on Wed 12 Jul 2017 at 16:10:00 PST by ian morris nieves +// modified on Mon 30 Apr 2007 at 15:30:09 PST by lamport +// modified on Fri Sep 22 13:18:45 PDT 2000 by yuanyu + +package tlc2.value.impl; + +import tla2sany.semantic.FormalParamNode; +import tla2sany.semantic.OpDefNode; +import tlc2.tool.FingerprintException; +import tlc2.tool.ITool; +import tlc2.tool.TLCState; +import tlc2.tool.coverage.CostModel; +import tlc2.util.Context; +import tlc2.value.IValue; +import tlc2.value.Values; +import util.Assert; +import util.WrongInvocationException; + +public class OpLambdaValue extends OpValue implements Applicable { + public final OpDefNode opDef; // the operator definition. + public final ITool tool; + public final Context con; + public final TLCState state; + public final TLCState pstate; + + /* Constructor */ + public OpLambdaValue(OpDefNode op, ITool tool, Context con, + TLCState state, TLCState pstate) { + this.opDef = op; + this.tool = tool; + this.state = state; + this.con = con; + this.pstate = pstate; + } + + public OpLambdaValue(OpDefNode op, ITool tool, Context con, + TLCState state, TLCState pstate, CostModel cm) { + this(op, tool, con, state, pstate); + this.cm = cm; + } + + public OpLambdaValue(OpLambdaValue other, ITool tool) { + this(other.opDef, tool, other.con, other.state, other.pstate); + } + + @Override + public final byte getKind() { return OPLAMBDAVALUE; } + + @Override + public final int compareTo(Object obj) { + try { + Assert.fail("Attempted to compare operator " + Values.ppr(this.toString()) + + " with value:\n" + Values.ppr(obj.toString())); + return 0; // make compiler happy + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + public final boolean equals(Object obj) { + try { + Assert.fail("Attempted to check equality of operator " + Values.ppr(this.toString()) + + " with value:\n" + Values.ppr(obj.toString())); + return false; // make compiler happy + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final boolean member(Value elem) { + try { + Assert.fail("Attempted to check if the value:\n" + Values.ppr(elem.toString()) + + "\nis an element of operator " + Values.ppr(this.toString())); + return false; // make compiler happy + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final boolean isFinite() { + try { + Assert.fail("Attempted to check if the operator " + Values.ppr(this.toString()) + + " is a finite set."); + return false; // make compiler happy + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final Value apply(Value arg, int control) { + try { + throw new WrongInvocationException("Should use the other apply method."); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final Value apply(Value[] args, int control) { + try { + int alen = this.opDef.getArity(); + if (alen != args.length) { + Assert.fail("Applying the operator " + Values.ppr(this.toString()) + + " with wrong number of arguments."); + } + Context c1 = this.con; + FormalParamNode[] formals = this.opDef.getParams(); + for (int i = 0; i < alen; i++) { + c1 = c1.cons(formals[i], args[i]); + } + return (Value) this.tool.eval(this.opDef.getBody(), c1, this.state, this.pstate, + control); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final Value select(Value arg) { + try { + throw new WrongInvocationException("Error(TLC): attempted to call OpLambdaValue.select()."); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final Value takeExcept(ValueExcept ex) { + try { + Assert.fail("Attempted to appy EXCEPT construct to the operator " + + Values.ppr(this.toString()) + "."); + return null; // make compiler happy + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final Value takeExcept(ValueExcept[] exs) { + try { + Assert.fail("Attempted to apply EXCEPT construct to the operator " + + Values.ppr(this.toString()) + "."); + return null; // make compiler happy + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final Value getDomain() { + try { + Assert.fail("Attempted to compute the domain of the operator " + + Values.ppr(this.toString()) + "."); + return SetEnumValue.EmptySet; // make compiler happy + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final int size() { + try { + Assert.fail("Attempted to compute the number of elements in the operator " + + Values.ppr(this.toString()) + "."); + return 0; // make compiler happy + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + /* Should never normalize an operator. */ + @Override + public final boolean isNormalized() { + try { + throw new WrongInvocationException("Should not normalize an operator."); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final Value normalize() { + try { + throw new WrongInvocationException("Should not normalize an operator."); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final boolean isDefined() { return true; } + + @Override + public final IValue deepCopy() { return this; } + + @Override + public final boolean assignable(Value val) { + try { + throw new WrongInvocationException("Should not initialize an operator."); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + /* String representation of the value. */ + @Override + public final StringBuffer toString(StringBuffer sb, int offset, boolean ignored) { + try { + String opName = this.opDef.getName().toString(); + return sb.append("<Operator ").append(opName).append(">"); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + +} diff --git a/tlatools/src/tlc2/value/impl/OpRcdValue.java b/tlatools/src/tlc2/value/impl/OpRcdValue.java new file mode 100644 index 0000000000000000000000000000000000000000..132de719487848f99a2735e1428aaf048f945958 --- /dev/null +++ b/tlatools/src/tlc2/value/impl/OpRcdValue.java @@ -0,0 +1,296 @@ +// Copyright (c) 2003 Compaq Corporation. All rights reserved. +// Portions Copyright (c) 2003 Microsoft Corporation. All rights reserved. +// Last modified on Wed 12 Jul 2017 at 16:10:00 PST by ian morris nieves +// modified on Mon 30 Apr 2007 at 13:21:01 PST by lamport +// modified on Sat Nov 13 12:43:44 PST 1999 by yuanyu + +package tlc2.value.impl; + +import tlc2.tool.FingerprintException; +import tlc2.util.Vect; +import tlc2.value.IValue; +import tlc2.value.Values; +import util.Assert; +import util.WrongInvocationException; + +public class OpRcdValue extends OpValue implements Applicable { + public Vect domain; + public Vect values; + + /* Constructor */ + public OpRcdValue() { + this.domain = new Vect(); + this.values = new Vect(); + } + + public OpRcdValue(Vect domain, Vect values) { + this.domain = domain; + this.values = values; + } + + @Override + public final byte getKind() { return OPRCDVALUE; } + + @Override + public final int compareTo(Object obj) { + try { + Assert.fail("Attempted to compare operator " + Values.ppr(this.toString()) + + " with value:\n" + Values.ppr(obj.toString())); + return 0; // make compiler happy + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + public final boolean equals(Object obj) { + try { + Assert.fail("Attempted to check equality of operator " + Values.ppr(this.toString()) + + " with value:\n" + Values.ppr(obj.toString())); + return false; // make compiler happy + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final boolean member(Value elem) { + try { + Assert.fail("Attempted to check if the value:\n" + Values.ppr(elem.toString()) + + "\nis an element of operator " + Values.ppr(this.toString())); + return false; // make compiler happy + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final boolean isFinite() { + try { + Assert.fail("Attempted to check if the operator " + Values.ppr(this.toString()) + + " is a finite set."); + return false; // make compiler happy + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + public final void addLine(Vect vs) { + try { + int len = vs.size(); + Value[] args = new Value[len-2]; + for (int i = 0; i < len-2; i++) { + args[i] = (Value)vs.elementAt(i+1); + } + this.domain.addElement(args); + this.values.addElement(vs.elementAt(len-1)); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final Value apply(Value arg, int control) { + try { + throw new WrongInvocationException("Should use the other apply method."); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final Value apply(Value[] args, int control) { + try { + int sz = this.domain.size(); + for (int i = 0; i < sz; i++) { + Value[] vals = (Value[])this.domain.elementAt(i); + if (args.length != vals.length) { + Assert.fail("Attempted to apply the operator " + Values.ppr(this.toString()) + + "\nwith wrong number of arguments."); + } + boolean matched = true; + for (int j = 0; j < vals.length; j++) { + matched = vals[j].equals(args[j]); + if (!matched) break; + } + if (matched) { + return (Value)this.values.elementAt(i); + } + } + // Generate the error message: + String msg = "Attempted to apply operator:\n" + Values.ppr(this.toString()) + + "\nto arguments ("; + if (args.length > 0) msg += args[0]; + for (int i = 1; i < args.length; i++) { + msg += ", " + args[i]; + } + Assert.fail(msg + "), which is undefined."); + return null; // make compiler happy + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final Value select(Value arg) { + try { + Assert.fail("Attempted to call OpRcdValue.select(). This is a TLC bug."); + return null; // make compiler happy + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final Value takeExcept(ValueExcept ex) { + try { + Assert.fail("Attempted to appy EXCEPT construct to the operator " + + Values.ppr(this.toString()) + "."); + return null; // make compiler happy + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final Value takeExcept(ValueExcept[] exs) { + try { + Assert.fail("Attempted to apply EXCEPT construct to the operator " + + Values.ppr(this.toString()) + "."); + return null; // make compiler happy + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final Value getDomain() { + try { + Assert.fail("Attempted to compute the domain of the operator " + + Values.ppr(this.toString()) + "."); + return SetEnumValue.EmptySet; // make compiler happy + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final int size() { + try { + Assert.fail("Attempted to compute the number of elements in the operator " + + Values.ppr(this.toString()) + "."); + return 0; // make compiler happy + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + /* Should never normalize an operator. */ + @Override + public final boolean isNormalized() { + try { + throw new WrongInvocationException("Should not normalize an operator."); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final Value normalize() { + try { + throw new WrongInvocationException("Should not normalize an operator."); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final boolean isDefined() { + try { + boolean defined = true; + for (int i = 0; i < this.values.size(); i++) { + defined = defined && ((Value)this.values.elementAt(i)).isDefined(); + } + return defined; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final IValue deepCopy() { return this; } + + @Override + public final boolean assignable(Value val) { + try { + throw new WrongInvocationException("Should not initialize an operator."); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + /* Pretty-printing */ + @Override + public final StringBuffer toString(StringBuffer sb, int offset, boolean swallow) { + try { + sb.append("{ "); + if (this.values.size() != 0) { + sb.append("<"); + Value[] args = (Value[])this.domain.elementAt(0); + for (int j = 0; j < args.length; j++) { + sb = args[j].toString(sb, offset, swallow); + sb.append(", "); + } + sb = ((Value)this.values.elementAt(0)).toString(sb, offset, swallow); + sb.append(">"); + } + for (int i = 1; i < this.values.size(); i++) { + sb.append(", <"); + Value[] args = (Value[])this.domain.elementAt(i); + for (int j = 0; j < args.length; j++) { + sb = args[j].toString(sb, offset, swallow); + sb.append(", "); + } + sb = ((Value)this.values.elementAt(i)).toString(sb, offset, swallow); + sb.append(">"); + } + return sb.append("}"); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + +} diff --git a/tlatools/src/tlc2/value/impl/OpValue.java b/tlatools/src/tlc2/value/impl/OpValue.java new file mode 100644 index 0000000000000000000000000000000000000000..1281b1ae02dfcf0710e500da1fc483871d735e74 --- /dev/null +++ b/tlatools/src/tlc2/value/impl/OpValue.java @@ -0,0 +1,27 @@ +// Copyright (c) 2003 Compaq Corporation. All rights reserved. +// Portions Copyright (c) 2003 Microsoft Corporation. All rights reserved. +// Last modified on Mon 30 Apr 2007 at 13:21:02 PST by lamport +// modified on Fri Sep 22 13:18:45 PDT 2000 by yuanyu + +package tlc2.value.impl; + +import tla2sany.semantic.ExprOrOpArgNode; +import tlc2.tool.TLCState; +import tlc2.tool.coverage.CostModel; +import tlc2.tool.impl.Tool; +import tlc2.util.Context; + +public abstract class OpValue extends Value implements Applicable { + + // Allow sub-classes to override. + public Value eval(final Tool tool, final ExprOrOpArgNode[] args, final Context c, final TLCState s0, + final TLCState s1, final int control, final CostModel cm) { + final Value[] argVals = new Value[args.length]; + // evaluate the operator's arguments: + for (int i = 0; i < args.length; i++) { + argVals[i] = tool.eval(args[i], c, s0, s1, control, cm); + } + // evaluate the operator: + return this.apply(argVals, control); + } +} diff --git a/tlatools/src/tlc2/value/impl/RecordValue.java b/tlatools/src/tlc2/value/impl/RecordValue.java new file mode 100644 index 0000000000000000000000000000000000000000..18279c7558fc97045cee77a0a37f0691ade1ff92 --- /dev/null +++ b/tlatools/src/tlc2/value/impl/RecordValue.java @@ -0,0 +1,607 @@ +// Copyright (c) 2003 Compaq Corporation. All rights reserved. +// Portions Copyright (c) 2003 Microsoft Corporation. All rights reserved. +// Last modified on Wed 12 Jul 2017 at 16:10:00 PST by ian morris nieves +// modified on Sat 23 February 2008 at 10:15:47 PST by lamport +// modified on Fri Aug 10 15:09:07 PDT 2001 by yuanyu + +package tlc2.value.impl; + +import java.io.EOFException; +import java.io.IOException; +import java.util.Arrays; +import java.util.Map; + +import tla2sany.semantic.OpDeclNode; +import tlc2.output.EC; +import tlc2.output.MP; +import tlc2.tool.FingerprintException; +import tlc2.tool.TLCState; +import tlc2.tool.coverage.CostModel; +import tlc2.util.FP64; +import tlc2.value.IMVPerm; +import tlc2.value.IValue; +import tlc2.value.IValueInputStream; +import tlc2.value.IValueOutputStream; +import tlc2.value.ValueInputStream; +import tlc2.value.Values; +import util.Assert; +import util.TLAConstants; +import util.UniqueString; + +public class RecordValue extends Value implements Applicable { + public final UniqueString[] names; // the field names + public final Value[] values; // the field values + private boolean isNorm; +public static final RecordValue EmptyRcd = new RecordValue(new UniqueString[0], new Value[0], true); + + /* Constructor */ + public RecordValue(UniqueString[] names, Value[] values, boolean isNorm) { + this.names = names; + this.values = values; + this.isNorm = isNorm; + } + + public RecordValue(UniqueString[] names, Value[] values, boolean isNorm, CostModel cm) { + this(names, values, isNorm); + this.cm = cm; + } + + public RecordValue(UniqueString name, Value v, boolean isNorm) { + this(new UniqueString[] {name}, new Value[] {v}, isNorm); + } + + public RecordValue(UniqueString name, Value v) { + this(new UniqueString[] {name}, new Value[] {v}, false); + } + + public RecordValue(final TLCState state) { + final OpDeclNode[] vars = state.getVars(); + + this.names = new UniqueString[vars.length]; + this.values = new Value[vars.length]; + + for (int i = 0; i < vars.length; i++) { + this.names[i] = vars[i].getName(); + this.values[i] = (Value) state.lookup(this.names[i]); + } + + this.isNorm = false; + } + + @Override + public final byte getKind() { return RECORDVALUE; } + + @Override + public final int compareTo(Object obj) { + try { + RecordValue rcd = obj instanceof Value ? (RecordValue) ((Value)obj).toRcd() : null; + if (rcd == null) { + if (obj instanceof ModelValue) return 1; + Assert.fail("Attempted to compare record:\n" + Values.ppr(this.toString()) + + "\nwith non-record\n" + Values.ppr(obj.toString())); + } + this.normalize(); + rcd.normalize(); + int len = this.names.length; + int cmp = len - rcd.names.length; + if (cmp == 0) { + for (int i = 0; i < len; i++) { + cmp = this.names[i].compareTo(rcd.names[i]); + if (cmp != 0) break; + cmp = this.values[i].compareTo(rcd.values[i]); + if (cmp != 0) break; + } + } + return cmp; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + public final boolean equals(Object obj) { + try { + RecordValue rcd = obj instanceof Value ? (RecordValue) ((Value)obj).toRcd() : null; + if (rcd == null) { + if (obj instanceof ModelValue) + return ((ModelValue) obj).modelValueEquals(this) ; + Assert.fail("Attempted to check equality of record:\n" + Values.ppr(this.toString()) + + "\nwith non-record\n" + Values.ppr(obj.toString())); + } + this.normalize(); + rcd.normalize(); + int len = this.names.length; + if (len != rcd.names.length) return false; + for (int i = 0; i < len; i++) { + if ((!(this.names[i].equals(rcd.names[i]))) || + (!(this.values[i].equals(rcd.values[i])))) + return false; + } + return true; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final boolean member(Value elem) { + try { + Assert.fail("Attempted to check if element:\n" + Values.ppr(elem.toString()) + + "\nis in the record:\n" + Values.ppr(this.toString())); + return false; // make compiler happy + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final boolean isFinite() { return true; } + + @Override + public final Value takeExcept(ValueExcept ex) { + try { + if (ex.idx < ex.path.length) { + int rlen = this.names.length; + Value[] newValues = new Value[rlen]; + Value arcVal = ex.path[ex.idx]; + if (arcVal instanceof StringValue) { + UniqueString arc = ((StringValue)arcVal).val; + for (int i = 0; i < rlen; i++) { + if (this.names[i].equals(arc)) { + ex.idx++; + newValues[i] = this.values[i].takeExcept(ex); + } + else { + newValues[i] = this.values[i]; + } + } + UniqueString[] newNames = this.names; + if (!this.isNorm) { + newNames = new UniqueString[rlen]; + for (int i = 0; i < rlen; i++) { + newNames[i] = this.names[i]; + } + } + return new RecordValue(newNames, newValues, this.isNorm); + } + else { + MP.printWarning(EC.TLC_WRONG_RECORD_FIELD_NAME, new String[]{Values.ppr(arcVal.toString())}); + } + } + return ex.value; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final Value takeExcept(ValueExcept[] exs) { + try { + Value res = this; + for (int i = 0; i < exs.length; i++) { + res = res.takeExcept(exs[i]); + } + return res; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final Value toRcd() { + return this; + } + + @Override + public final Value toTuple() { + return size() == 0 ? TupleValue.EmptyTuple : super.toTuple(); + } + + @Override + public final Value toFcnRcd() { + this.normalize(); + Value[] dom = new Value[this.names.length]; + for (int i = 0; i < this.names.length; i++) { + dom[i] = new StringValue(this.names[i], cm); + } + if (coverage) {cm.incSecondary(dom.length);} + return new FcnRcdValue(dom, this.values, this.isNormalized(), cm); + } + + @Override + public final int size() { + try { + return this.names.length; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final Value apply(Value arg, int control) { + try { + if (!(arg instanceof StringValue)) { + Assert.fail("Attempted to apply record to a non-string value " + + Values.ppr(arg.toString()) + "."); + } + UniqueString name = ((StringValue)arg).getVal(); + int rlen = this.names.length; + for (int i = 0; i < rlen; i++) { + if (name.equals(this.names[i])) { + return this.values[i]; + } + } + Assert.fail("Attempted to apply the record\n" + Values.ppr(this.toString()) + + "\nto nonexistent record field " + name + "."); + return null; // make compiler happy + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final Value apply(Value[] args, int control) { + try { + if (args.length != 1) { + Assert.fail("Attempted to apply record to more than one arguments."); + } + return this.apply(args[0], control); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + /* This method returns the named component of the record. */ + @Override + public final Value select(Value arg) { + try { + if (!(arg instanceof StringValue)) { + Assert.fail("Attempted to apply record to a non-string argument " + + Values.ppr(arg.toString()) + "."); + } + UniqueString name = ((StringValue)arg).getVal(); + int rlen = this.names.length; + for (int i = 0; i < rlen; i++) { + if (name.equals(this.names[i])) { + return this.values[i]; + } + } + return null; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final Value getDomain() { + try { + Value[] dElems = new Value[this.names.length]; + for (int i = 0; i < this.names.length; i++) { + dElems[i] = new StringValue(this.names[i]); + } + return new SetEnumValue(dElems, this.isNormalized()); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + public final boolean assign(UniqueString name, Value val) { + try { + for (int i = 0; i < this.names.length; i++) { + if (name.equals(this.names[i])) { + if (this.values[i] == UndefValue.ValUndef || + this.values[i].equals(val)) { + this.values[i] = val; + return true; + } + return false; + } + } + Assert.fail("Attempted to assign to nonexistent record field " + name + "."); + return false; // make compiler happy + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final boolean isNormalized() { return this.isNorm; } + + @Override + public final Value normalize() { + try { + if (!this.isNorm) { + int len = this.names.length; + for (int i = 1; i < len; i++) { + int cmp = this.names[0].compareTo(this.names[i]); + if (cmp == 0) { + Assert.fail("Field name " + this.names[i] + " occurs multiple times in record."); + } + else if (cmp > 0) { + UniqueString ts = this.names[0]; + this.names[0] = this.names[i]; + this.names[i] = ts; + Value tv = this.values[0]; + this.values[0] = this.values[i]; + this.values[i] = tv; + } + } + for (int i = 2; i < len; i++) { + int j = i; + UniqueString st = this.names[i]; + Value val = this.values[i]; + int cmp; + while ((cmp = st.compareTo(this.names[j-1])) < 0) { + this.names[j] = this.names[j-1]; + this.values[j] = this.values[j-1]; + j--; + } + if (cmp == 0) { + Assert.fail("Field name " + this.names[i] + " occurs multiple times in record."); + } + this.names[j] = st; + this.values[j] = val; + } + this.isNorm = true; + } + return this; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final void deepNormalize() { + try { + for (int i = 0; i < values.length; i++) { + values[i].deepNormalize(); + } + normalize(); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final boolean isDefined() { + try { + boolean defined = true; + for (int i = 0; i < this.values.length; i++) { + defined = defined && this.values[i].isDefined(); + } + return defined; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final IValue deepCopy() { + try { + Value[] vals = new Value[this.values.length]; + for (int i = 0; i < this.values.length; i++) { + vals[i] = (Value) this.values[i].deepCopy(); + } + // Following code modified 16 June 2015 by adding Arrays.copyOf to fix + // the following bug that seems to have manifested itself only in TLC.Print and + // TLC.PrintT: Calling normalize on the original modifies the + // order of the names array in the deepCopy (and vice-versa) without doing the + // corresponding modification on the values array. Thus, the names are + // copied too to prevent any modification/normalization done to the + // original to appear in the deepCopy. + return new RecordValue(Arrays.copyOf(this.names, this.names.length), vals, this.isNorm); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final boolean assignable(Value val) { + try { + boolean canAssign = ((val instanceof RecordValue) && + this.names.length == ((RecordValue)val).names.length); + if (!canAssign) return false; + for (int i = 0; i < this.values.length; i++) { + canAssign = (canAssign && + this.names[i].equals(((RecordValue)val).names[i]) && + this.values[i].assignable(((RecordValue)val).values[i])); + } + return canAssign; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final void write(final IValueOutputStream vos) throws IOException { + final int index = vos.put(this); + if (index == -1) { + vos.writeByte(RECORDVALUE); + final int len = names.length; + vos.writeInt((isNormalized()) ? len : -len); + for (int i = 0; i < len; i++) { + final int index1 = vos.put(names[i]); + if (index1 == -1) { + vos.writeByte(STRINGVALUE); + names[i].write(vos.getOutputStream()); + } else { + vos.writeByte(DUMMYVALUE); + vos.writeNat(index1); + } + values[i].write(vos); + } + } else { + vos.writeByte(DUMMYVALUE); + vos.writeNat(index); + } + } + + /* The fingerprint methods. */ + @Override + public final long fingerPrint(long fp) { + try { + this.normalize(); + int rlen = this.names.length; + fp = FP64.Extend(fp, FCNRCDVALUE); + fp = FP64.Extend(fp, rlen); + for (int i = 0; i < rlen; i++) { + String str = this.names[i].toString(); + fp = FP64.Extend(fp, STRINGVALUE); + fp = FP64.Extend(fp, str.length()); + fp = FP64.Extend(fp, str); + fp = this.values[i].fingerPrint(fp); + } + return fp; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final IValue permute(IMVPerm perm) { + try { + this.normalize(); + int rlen = this.names.length; + Value[] vals = new Value[rlen]; + boolean changed = false; + for (int i = 0; i < rlen; i++) { + vals[i] = (Value) this.values[i].permute(perm); + changed = changed || (vals[i] != this.values[i]); + } + if (changed) { + return new RecordValue(this.names, vals, true); + } + return this; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + /* The string representation */ + @Override + public final StringBuffer toString(StringBuffer sb, int offset, boolean swallow) { + try { + int len = this.names.length; + + sb.append("["); + if (len > 0) { + sb.append(this.names[0] + TLAConstants.RECORD_ARROW); + sb = this.values[0].toString(sb, offset, swallow); + } + for (int i = 1; i < len; i++) { + sb.append(", "); + sb.append(this.names[i] + TLAConstants.RECORD_ARROW); + sb = this.values[i].toString(sb, offset, swallow); + } + return sb.append("]"); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + public static IValue createFrom(final IValueInputStream vos) throws EOFException, IOException { + final int index = vos.getIndex(); + boolean isNorm = true; + int len = vos.readInt(); + if (len < 0) { + len = -len; + isNorm = false; + } + final UniqueString[] names = new UniqueString[len]; + final Value[] vals = new Value[len]; + for (int i = 0; i < len; i++) { + final byte kind1 = vos.readByte(); + if (kind1 == DUMMYVALUE) { + final int index1 = vos.readNat(); + names[i] = vos.getValue(index1); + } else { + final int index1 = vos.getIndex(); + names[i] = UniqueString.read(vos.getInputStream()); + vos.assign(names[i], index1); + } + vals[i] = (Value) vos.read(); + } + final Value res = new RecordValue(names, vals, isNorm); + vos.assign(res, index); + return res; + } + + public static IValue createFrom(final ValueInputStream vos, final Map<String, UniqueString> tbl) throws EOFException, IOException { + final int index = vos.getIndex(); + boolean isNorm = true; + int len = vos.readInt(); + if (len < 0) { + len = -len; + isNorm = false; + } + final UniqueString[] names = new UniqueString[len]; + final Value[] vals = new Value[len]; + for (int i = 0; i < len; i++) { + final byte kind1 = vos.readByte(); + if (kind1 == DUMMYVALUE) { + final int index1 = vos.readNat(); + names[i] = vos.getValue(index1); + } else { + final int index1 = vos.getIndex(); + names[i] = UniqueString.read(vos.getInputStream(), tbl); + vos.assign(names[i], index1); + } + vals[i] = (Value) vos.read(tbl); + } + final Value res = new RecordValue(names, vals, isNorm); + vos.assign(res, index); + return res; + } + + public TLCState toState() { + final TLCState state = TLCState.Empty.createEmpty(); + final OpDeclNode[] vars = state.getVars(); + for (int i = 0; i < vars.length; i++) { + final UniqueString name = vars[i].getName(); + int rlen = this.names.length; + for (int j = 0; j < rlen; j++) { + if (name.equals(this.names[j])) { + state.bind(name, this.values[j]); + } + } + } + return state; + } +} diff --git a/tlatools/src/tlc2/value/Reducible.java b/tlatools/src/tlc2/value/impl/Reducible.java similarity index 58% rename from tlatools/src/tlc2/value/Reducible.java rename to tlatools/src/tlc2/value/impl/Reducible.java index 492894ca289fa4b5fdde0204c8a5a0eda936a2be..d9183d8d90e642e6c951d0fc2742a97a1a538f7a 100644 --- a/tlatools/src/tlc2/value/Reducible.java +++ b/tlatools/src/tlc2/value/impl/Reducible.java @@ -3,16 +3,16 @@ // Last modified on Mon 30 Apr 2007 at 13:21:02 PST by lamport // modified on Tue Mar 23 00:37:35 PST 1999 by yuanyu -package tlc2.value; +package tlc2.value.impl; public interface Reducible { - public int size(); - public boolean member(Value elem); - public Value diff(Value val); - public Value cap(Value val); - public Value cup(Value val); + int size(); + boolean member(Value elem); + Value diff(Value val); + Value cap(Value val); + Value cup(Value val); - public ValueEnumeration elements(); + ValueEnumeration elements(); } diff --git a/tlatools/src/tlc2/value/impl/SetCapValue.java b/tlatools/src/tlc2/value/impl/SetCapValue.java new file mode 100644 index 0000000000000000000000000000000000000000..ed59231e3c0031b5d1b989dda90d1abe5d0c3756 --- /dev/null +++ b/tlatools/src/tlc2/value/impl/SetCapValue.java @@ -0,0 +1,332 @@ +// Copyright (c) 2003 Compaq Corporation. All rights reserved. +// Portions Copyright (c) 2003 Microsoft Corporation. All rights reserved. +// Last modified on Wed 12 Jul 2017 at 16:10:00 PST by ian morris nieves +// modified on Mon 30 Apr 2007 at 13:21:03 PST by lamport +// modified on Fri Aug 10 15:09:21 PDT 2001 by yuanyu + +package tlc2.value.impl; + +import java.io.IOException; + +import tlc2.TLCGlobals; +import tlc2.tool.FingerprintException; +import tlc2.value.IMVPerm; +import tlc2.value.IValue; +import tlc2.value.IValueOutputStream; +import tlc2.value.Values; +import util.Assert; + +public class SetCapValue extends EnumerableValue implements Enumerable { + public final Value set1; + public final Value set2; + protected SetEnumValue capSet; + + /* Constructor */ + public SetCapValue(Value set1, Value set2) { + this.set1 = set1; + this.set2 = set2; + this.capSet = null; + } + + @Override + public final byte getKind() { return SETCAPVALUE; } + + @Override + public final int compareTo(Object obj) { + try { + this.convertAndCache(); + return this.capSet.compareTo(obj); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + public final boolean equals(Object obj) { + try { + this.convertAndCache(); + return this.capSet.equals(obj); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final boolean member(Value elem) { + try { + return (this.set1.member(elem) && this.set2.member(elem)); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final boolean isFinite() { + try { + if (!this.set1.isFinite() && !this.set2.isFinite()) { + Assert.fail("Attempted to check if the set " + Values.ppr(this.toString()) + "is finite."); + } + return true; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final Value takeExcept(ValueExcept ex) { + try { + if (ex.idx < ex.path.length) { + Assert.fail("Attempted to apply EXCEPT to the set " + Values.ppr(this.toString()) + "."); + } + return ex.value; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final Value takeExcept(ValueExcept[] exs) { + try { + if (exs.length != 0) { + Assert.fail("Attempted to apply EXCEPT to the set " + Values.ppr(this.toString()) + "."); + } + return this; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final int size() { + try { + this.convertAndCache(); + return this.capSet.size(); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final boolean isNormalized() { + try { + if (this.capSet == null || this.capSet == SetEnumValue.DummyEnum) { + return (this.set1.isNormalized() && this.set2.isNormalized()); + } + return this.capSet.isNormalized(); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final Value normalize() { + try { + if (this.capSet == null || this.capSet == SetEnumValue.DummyEnum) { + this.set1.normalize(); + this.set2.normalize(); + } + else { + this.capSet.normalize(); + } + return this; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final boolean isDefined() { + try { + return this.set1.isDefined() && this.set2.isDefined(); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final IValue deepCopy() { return this; } + + @Override + public final boolean assignable(Value val) { + try { + return this.equals(val); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public void write(final IValueOutputStream vos) throws IOException { + capSet.write(vos); + } + + /* The fingerprint methods */ + @Override + public final long fingerPrint(long fp) { + try { + this.convertAndCache(); + return this.capSet.fingerPrint(fp); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final IValue permute(IMVPerm perm) { + try { + this.convertAndCache(); + return this.capSet.permute(perm); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + private final void convertAndCache() { + if (this.capSet == null) { + this.capSet = (SetEnumValue) this.toSetEnum(); + } + else if (this.capSet == SetEnumValue.DummyEnum) { + SetEnumValue val = null; + synchronized(this) { + if (this.capSet == SetEnumValue.DummyEnum) { + val = (SetEnumValue) this.toSetEnum(); + val.deepNormalize(); + } + } + synchronized(this) { + if (this.capSet == SetEnumValue.DummyEnum) { this.capSet = val; } + } + } + } + + @Override + public final void deepNormalize() { + try { + set1.deepNormalize(); + set2.deepNormalize(); + if (capSet == null) { + capSet = SetEnumValue.DummyEnum; + } + else if (capSet != SetEnumValue.DummyEnum) { + capSet.deepNormalize(); + } + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final Value toSetEnum() { + if (this.capSet != null && this.capSet != SetEnumValue.DummyEnum) { + return this.capSet; + } + ValueVec vals = new ValueVec(); + ValueEnumeration Enum = this.elements(); + Value elem; + while ((elem = Enum.nextElement()) != null) { + vals.addElement(elem); + } + if (coverage) {cm.incSecondary(vals.size());} + return new SetEnumValue(vals, this.isNormalized(), cm); + } + + /* String representation of this value. */ + @Override + public final StringBuffer toString(StringBuffer sb, int offset, boolean swallow) { + try { + try { + if (TLCGlobals.expand) { + Value val = this.toSetEnum(); + return val.toString(sb, offset, swallow); + } + } + catch (Throwable e) { if (!swallow) throw e; } + + sb = this.set1.toString(sb, offset, swallow); + sb = sb.append(" \\cap "); + sb = this.set2.toString(sb, offset, swallow); + return sb; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final ValueEnumeration elements() { + try { + if (this.capSet == null || this.capSet == SetEnumValue.DummyEnum) { + return new Enumerator(); + } + return this.capSet.elements(); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + final class Enumerator implements ValueEnumeration { + ValueEnumeration enum1; + Value set; + + public Enumerator() { + if (set1 instanceof Enumerable) { + this.enum1 = ((Enumerable)set1).elements(); + this.set = set2; + } + else if (set2 instanceof Enumerable) { + this.enum1 = ((Enumerable)set2).elements(); + this.set = set1; + } + else { + Assert.fail("Attempted to enumerate S \\cap T when neither S:\n" + + Values.ppr(set1.toString()) + "\nnor T:\n" + Values.ppr(set2.toString()) + + "\nis enumerable"); + } + } + + @Override + public final void reset() { this.enum1.reset(); } + + @Override + public final Value nextElement() { + Value elem = this.enum1.nextElement(); + while (elem != null) { + if (coverage) { cm.incSecondary(); } + if (this.set.member(elem)) return elem; + elem = this.enum1.nextElement(); + } + return null; + } + } + +} diff --git a/tlatools/src/tlc2/value/impl/SetCupValue.java b/tlatools/src/tlc2/value/impl/SetCupValue.java new file mode 100644 index 0000000000000000000000000000000000000000..15a183ef9d68651291d2dc8161380c4018f75e0e --- /dev/null +++ b/tlatools/src/tlc2/value/impl/SetCupValue.java @@ -0,0 +1,329 @@ +// Copyright (c) 2003 Compaq Corporation. All rights reserved. +// Portions Copyright (c) 2003 Microsoft Corporation. All rights reserved. +// Last modified on Wed 12 Jul 2017 at 16:10:00 PST by ian morris nieves +// modified on Mon 30 Apr 2007 at 13:21:03 PST by lamport +// modified on Fri Aug 10 15:09:28 PDT 2001 by yuanyu + +package tlc2.value.impl; + +import java.io.IOException; + +import tlc2.TLCGlobals; +import tlc2.tool.FingerprintException; +import tlc2.tool.coverage.CostModel; +import tlc2.value.IMVPerm; +import tlc2.value.IValue; +import tlc2.value.IValueOutputStream; +import tlc2.value.Values; +import util.Assert; + +public class SetCupValue extends EnumerableValue implements Enumerable { + public final Value set1; + public final Value set2; + protected SetEnumValue cupSet; + + /* Constructor */ + public SetCupValue(Value set1, Value set2) { + this.set1 = set1; + this.set2 = set2; + this.cupSet = null; + } + + public SetCupValue(Value set1, Value set2, CostModel cm) { + this(set1, set2); + this.cm = cm; + } + + @Override + public final byte getKind() { return SETCUPVALUE; } + + @Override + public final int compareTo(Object obj) { + try { + this.convertAndCache(); + return this.cupSet.compareTo(obj); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + public final boolean equals(Object obj) { + try { + this.convertAndCache(); + return this.cupSet.equals(obj); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final boolean member(Value elem) { + try { + return this.set1.member(elem) || this.set2.member(elem); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final boolean isFinite() { + try { + return this.set1.isFinite() && this.set2.isFinite(); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final Value takeExcept(ValueExcept ex) { + try { + if (ex.idx < ex.path.length) { + Assert.fail("Attempted to apply EXCEPT to the set " + Values.ppr(this.toString()) + "."); + } + return ex.value; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final Value takeExcept(ValueExcept[] exs) { + try { + if (exs.length != 0) { + Assert.fail("Attempted to apply EXCEPT to the set " + Values.ppr(this.toString()) + "."); + } + return this; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final int size() { + try { + this.convertAndCache(); + return this.cupSet.size(); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final boolean isNormalized() { + try { + return (this.cupSet != null && + this.cupSet != SetEnumValue.DummyEnum && + this.cupSet.isNormalized()); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final Value normalize() { + try { + if (this.cupSet != null && this.cupSet != SetEnumValue.DummyEnum) { + this.cupSet.normalize(); + } + return this; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final void deepNormalize() { + try { + set1.deepNormalize(); + set2.deepNormalize(); + if (cupSet == null) { + cupSet = SetEnumValue.DummyEnum; + } + else if (cupSet != SetEnumValue.DummyEnum) { + cupSet.deepNormalize(); + } + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final boolean isDefined() { + try { + return this.set1.isDefined() && this.set2.isDefined(); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final IValue deepCopy() { return this; } + + @Override + public final boolean assignable(Value val) { + try { + return this.equals(val); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final void write(final IValueOutputStream vos) throws IOException { + cupSet.write(vos); + } + + /* The fingerprint methods */ + @Override + public final long fingerPrint(long fp) { + try { + this.convertAndCache(); + return this.cupSet.fingerPrint(fp); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final IValue permute(IMVPerm perm) { + try { + this.convertAndCache(); + return this.cupSet.permute(perm); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + private final void convertAndCache() { + if (this.cupSet == null) { + this.cupSet = (SetEnumValue) this.toSetEnum(); + } + else if (this.cupSet == SetEnumValue.DummyEnum) { + SetEnumValue val = null; + synchronized(this) { + if (this.cupSet == SetEnumValue.DummyEnum) { + val = (SetEnumValue) this.toSetEnum(); + val.deepNormalize(); + } + } + synchronized(this) { + if (this.cupSet == SetEnumValue.DummyEnum) { this.cupSet = val; } + } + } + } + + @Override + public final Value toSetEnum() { + if (this.cupSet != null && this.cupSet != SetEnumValue.DummyEnum) { + return this.cupSet; + } + ValueVec vals = new ValueVec(); + ValueEnumeration Enum = this.elements(); + Value elem; + while ((elem = Enum.nextElement()) != null) { + vals.addElement(elem); + } + if (coverage) {cm.incSecondary(vals.size());} + return new SetEnumValue(vals, false, cm); + } + + /* String representation of the value. */ + @Override + public final StringBuffer toString(StringBuffer sb, int offset, boolean swallow) { + try { + try { + if (TLCGlobals.expand) { + Value val = this.toSetEnum(); + return val.toString(sb, offset, swallow); + } + } + catch (Throwable e) { if (!swallow) throw e; } + + sb = this.set1.toString(sb, offset, swallow); + sb = sb.append(" \\cup "); + sb = this.set2.toString(sb, offset, swallow); + return sb; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final ValueEnumeration elements() { + try { + if (this.cupSet == null || this.cupSet == SetEnumValue.DummyEnum) { + return new Enumerator(); + } + return this.cupSet.elements(); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + + } + + final class Enumerator implements ValueEnumeration { + ValueEnumeration enum1; + ValueEnumeration enum2; + + public Enumerator() { + if ((set1 instanceof Enumerable) && + (set2 instanceof Enumerable)) { + this.enum1 = ((Enumerable)set1).elements(); + this.enum2 = ((Enumerable)set2).elements(); + } + else { + Assert.fail("Attempted to enumerate S \\cup T when S:\n" + + Values.ppr(set1.toString()) + "\nand T:\n" + Values.ppr(set2.toString()) + + "\nare not both enumerable"); + } + } + + @Override + public final void reset() { + this.enum1.reset(); + this.enum2.reset(); + } + + @Override + public final Value nextElement() { + if (coverage) { cm.incSecondary(); } + Value elem = this.enum1.nextElement(); + if (elem != null) return elem; + elem = this.enum2.nextElement(); + return elem; + } + } + +} diff --git a/tlatools/src/tlc2/value/impl/SetDiffValue.java b/tlatools/src/tlc2/value/impl/SetDiffValue.java new file mode 100644 index 0000000000000000000000000000000000000000..0778ce08b88f5f6b4af985386b8294955a606211 --- /dev/null +++ b/tlatools/src/tlc2/value/impl/SetDiffValue.java @@ -0,0 +1,329 @@ +// Copyright (c) 2003 Compaq Corporation. All rights reserved. +// Portions Copyright (c) 2003 Microsoft Corporation. All rights reserved. +// Last modified on Wed 12 Jul 2017 at 16:10:00 PST by ian morris nieves +// modified on Mon 30 Apr 2007 at 13:21:03 PST by lamport +// modified on Fri Aug 10 15:09:34 PDT 2001 by yuanyu + +package tlc2.value.impl; + +import java.io.IOException; + +import tlc2.TLCGlobals; +import tlc2.tool.FingerprintException; +import tlc2.value.IMVPerm; +import tlc2.value.IValue; +import tlc2.value.IValueOutputStream; +import tlc2.value.Values; +import util.Assert; + +public class SetDiffValue extends EnumerableValue implements Enumerable { + public final Value set1; + public final Value set2; + protected SetEnumValue diffSet; + + /* Constructor */ + public SetDiffValue(Value set1, Value set2) { + this.set1 = set1; + this.set2 = set2; + this.diffSet = null; + } + + @Override + public final byte getKind() { return SETDIFFVALUE; } + + @Override + public final int compareTo(Object obj) { + try { + this.convertAndCache(); + return this.diffSet.compareTo(obj); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + public final boolean equals(Object obj) { + try { + this.convertAndCache(); + return this.diffSet.equals(obj); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final boolean member(Value elem) { + try { + return (this.set1.member(elem) && !this.set2.member(elem)); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final boolean isFinite() { + try { + if (this.set1.isFinite()) { + return true; + } + if (!this.set2.isFinite()) { + Assert.fail("Attempted to check if the set " + Values.ppr(this.toString()) + "is finite."); + } + return false; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final Value takeExcept(ValueExcept ex) { + try { + if (ex.idx < ex.path.length) { + Assert.fail("Attempted to apply EXCEPT to the set " + Values.ppr(this.toString()) + "."); + } + return ex.value; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final Value takeExcept(ValueExcept[] exs) { + try { + if (exs.length != 0) { + Assert.fail("Attempted to apply EXCEPT to the set " + Values.ppr(this.toString()) + "."); + } + return this; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final int size() { + try { + this.convertAndCache(); + return this.diffSet.size(); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final boolean isNormalized() { + try { + if (this.diffSet == null || this.diffSet == SetEnumValue.DummyEnum) { + return this.set1.isNormalized(); + } + return this.diffSet.isNormalized(); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final Value normalize() { + try { + if (this.diffSet == null || this.diffSet == SetEnumValue.DummyEnum) { + this.set1.normalize(); + this.set2.normalize(); + } + else { + this.diffSet.normalize(); + } + return this; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final void deepNormalize() { + try { + set1.deepNormalize(); + set2.deepNormalize(); + if (diffSet == null) { + diffSet = SetEnumValue.DummyEnum; + } + else if (diffSet != SetEnumValue.DummyEnum) { + diffSet.deepNormalize(); + } + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final boolean isDefined() { + try { + return this.set1.isDefined() && this.set2.isDefined(); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final IValue deepCopy() { return this; } + + @Override + public final boolean assignable(Value val) { + try { + return this.equals(val); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final void write(final IValueOutputStream vos) throws IOException { + diffSet.write(vos); + } + + /* The fingerprint methods */ + @Override + public final long fingerPrint(long fp) { + try { + this.convertAndCache(); + return this.diffSet.fingerPrint(fp); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final IValue permute(IMVPerm perm) { + try { + this.convertAndCache(); + return this.diffSet.permute(perm); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + private final void convertAndCache() { + if (this.diffSet == null) { + this.diffSet = (SetEnumValue) this.toSetEnum(); + } + else if (this.diffSet == SetEnumValue.DummyEnum) { + SetEnumValue val = null; + synchronized(this) { + if (this.diffSet == SetEnumValue.DummyEnum) { + val = (SetEnumValue) this.toSetEnum(); + val.deepNormalize(); + } + } + synchronized(this) { + if (this.diffSet == SetEnumValue.DummyEnum) { this.diffSet = val; } + } + } + } + + @Override + public final Value toSetEnum() { + if (this.diffSet != null && this.diffSet != SetEnumValue.DummyEnum) { + return this.diffSet; + } + ValueVec vals = new ValueVec(); + ValueEnumeration Enum = this.elements(); + Value elem; + while ((elem = Enum.nextElement()) != null) { + vals.addElement(elem); + } + if (coverage) {cm.incSecondary(vals.size());} + return new SetEnumValue(vals, this.set1.isNormalized(), cm); + } + + /* The string representation */ + @Override + public final StringBuffer toString(StringBuffer sb, int offset, boolean swallow) { + try { + try { + if (TLCGlobals.expand) { + Value val = this.toSetEnum(); + return val.toString(sb, offset, swallow); + } + } + catch (Throwable e) { if (!swallow) throw e; } + + sb = this.set1.toString(sb, offset, swallow); + sb = sb.append(" \\ "); + sb = this.set2.toString(sb, offset, swallow); + return sb; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final ValueEnumeration elements() { + try { + if (this.diffSet == null || this.diffSet == SetEnumValue.DummyEnum) { + return new Enumerator(); + } + return this.diffSet.elements(); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + + } + + final class Enumerator implements ValueEnumeration { + ValueEnumeration enum1; + + public Enumerator() { + if (set1 instanceof Enumerable) { + this.enum1 = ((Enumerable)set1).elements(); + } + else { + Assert.fail("Attempted to enumerate S \\ T when S:\n" + + Values.ppr(set1.toString()) + "\nis not enumerable."); + } + } + + @Override + public final void reset() { this.enum1.reset(); } + + @Override + public final Value nextElement() { + Value elem = this.enum1.nextElement(); + while (elem != null) { + if (coverage) { cm.incSecondary(); } + if (!set2.member(elem)) return elem; + elem = this.enum1.nextElement(); + } + return null; + } + } + +} diff --git a/tlatools/src/tlc2/value/impl/SetEnumValue.java b/tlatools/src/tlc2/value/impl/SetEnumValue.java new file mode 100644 index 0000000000000000000000000000000000000000..0d0419ddeb2969bbcb1d88799a1de6d58f775c71 --- /dev/null +++ b/tlatools/src/tlc2/value/impl/SetEnumValue.java @@ -0,0 +1,522 @@ +// Copyright (c) 2003 Compaq Corporation. All rights reserved. +// Portions Copyright (c) 2003 Microsoft Corporation. All rights reserved. +// Last modified on Wed 12 Jul 2017 at 16:10:00 PST by ian morris nieves +// modified on Sat 23 February 2008 at 10:16:24 PST by lamport +// modified on Mon Aug 20 10:54:08 PDT 2001 by yuanyu + +package tlc2.value.impl; + +import java.io.IOException; +import java.util.Map; + +import tlc2.tool.FingerprintException; +import tlc2.tool.coverage.CostModel; +import tlc2.util.FP64; +import tlc2.value.IMVPerm; +import tlc2.value.IValue; +import tlc2.value.IValueInputStream; +import tlc2.value.IValueOutputStream; +import tlc2.value.RandomEnumerableValues; +import tlc2.value.ValueInputStream; +import tlc2.value.Values; +import util.Assert; +import util.UniqueString; + +@SuppressWarnings("serial") +public class SetEnumValue extends EnumerableValue +implements Enumerable, Reducible { + public ValueVec elems; // the elements of the set + private boolean isNorm; // normalized? +public static final SetEnumValue EmptySet = new SetEnumValue(new ValueVec(0), true); +public static final SetEnumValue DummyEnum = new SetEnumValue((ValueVec)null, true); + + /* Constructor */ + public SetEnumValue(Value[] elems, boolean isNorm) { + this(new ValueVec(elems), isNorm); + } + + public SetEnumValue(Value[] vals, boolean isNorm, CostModel cm) { + this(vals, isNorm); + this.cm = cm; + } + + public SetEnumValue(ValueVec elems, boolean isNorm) { + this.elems = elems; + this.isNorm = isNorm; + } + + public SetEnumValue(ValueVec elems, boolean isNorm, final CostModel cm) { + this(elems, isNorm); + this.cm = cm; + } + + public SetEnumValue() { + this(new ValueVec(0), true); + } + + public SetEnumValue(final Value elem) { + this(new Value[] {elem}, true); // single element is normalized by definition. + } + + public SetEnumValue(CostModel cm) { + this(); + this.cm = cm; + } + + // See IValue#isAtom except that this is for sets of atoms. + public final boolean isSetOfAtoms() { + final int len = this.elems.size(); + for (int i = 0; i < len; i++) { + final Value v = this.elems.elementAt(i); + if (v instanceof SetEnumValue) { + // Sets of sets of sets... of atoms. + final SetEnumValue sev = (SetEnumValue) v; + if (!sev.isSetOfAtoms()) { + return false; + } + } else if (!v.isAtom()) { + return false; + } + } + return true; + } + + @Override + public final byte getKind() { return SETENUMVALUE; } + + @Override + public final int compareTo(Object obj) { + try { + SetEnumValue set = obj instanceof Value ? (SetEnumValue) ((Value)obj).toSetEnum() : null; + if (set == null) { + if (obj instanceof ModelValue) return 1; + Assert.fail("Attempted to compare the set " + Values.ppr(this.toString()) + + " with the value:\n" + Values.ppr(obj.toString())); + } + this.normalize(); + set.normalize(); + int sz = this.elems.size(); + int cmp = sz - set.elems.size(); + if (cmp != 0) return cmp; + for (int i = 0; i < sz; i++) { + cmp = this.elems.elementAt(i).compareTo(set.elems.elementAt(i)); + if (cmp != 0) return cmp; + } + return 0; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + public final boolean equals(Object obj) { + try { + SetEnumValue set = obj instanceof Value ? (SetEnumValue) ((Value)obj).toSetEnum() : null; + if (set == null) { + if (obj instanceof ModelValue) + return ((ModelValue) obj).modelValueEquals(this) ; + Assert.fail("Attempted to check equality of the set " + Values.ppr(this.toString()) + + " with the value:\n" + Values.ppr(obj.toString())); + } + this.normalize(); + set.normalize(); + int sz = this.elems.size(); + if (sz != set.elems.size()) { + return false; + } + for (int i = 0; i < sz; i++) { + if (!this.elems.elementAt(i).equals(set.elems.elementAt(i))) { + return false; + } + } + return true; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final boolean member(Value elem) { + try { + return this.elems.search(elem, this.isNorm); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final boolean isFinite() { return true; } + + @Override + public final Value diff(Value val) { + try { + int sz = this.elems.size(); + ValueVec diffElems = new ValueVec(); + for (int i = 0; i < sz; i++) { + Value elem = this.elems.elementAt(i); + if (!val.member(elem)) { + diffElems.addElement(elem); + } + } + return new SetEnumValue(diffElems, this.isNormalized(), cm); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final Value cap(Value val) { + try { + int sz = this.elems.size(); + ValueVec capElems = new ValueVec(); + for (int i = 0; i < sz; i++) { + Value elem = this.elems.elementAt(i); + if (val.member(elem)) { + capElems.addElement(elem); + } + } + return new SetEnumValue(capElems, this.isNormalized(), cm); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final Value cup(Value set) { + try { + int sz = this.elems.size(); + if (sz == 0) return set; + + if (set instanceof Reducible) { + ValueVec cupElems = new ValueVec(); + for (int i = 0; i < sz; i++) { + Value elem = this.elems.elementAt(i); + cupElems.addElement(elem); + } + ValueEnumeration Enum = ((Enumerable)set).elements(); + Value elem; + while ((elem = Enum.nextElement()) != null) { + if (!this.member(elem)) cupElems.addElement(elem); + } + return new SetEnumValue(cupElems, false); + } + return new SetCupValue(this, set, cm); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final Value takeExcept(ValueExcept ex) { + try { + if (ex.idx < ex.path.length) { + Assert.fail("Attempted to apply EXCEPT to the set " + Values.ppr(this.toString()) + "."); + } + return ex.value; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final Value takeExcept(ValueExcept[] exs) { + try { + if (exs.length != 0) { + Assert.fail("Attempted to apply EXCEPT to the set " + Values.ppr(this.toString()) + "."); + } + return this; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final int size() { + try { + this.normalize(); + return this.elems.size(); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + /* This method normalizes (destructively) this set. */ + @Override + public final boolean isNormalized() { return this.isNorm; } + + @Override + public final Value normalize() { + try { + if (!this.isNorm) { + this.elems.sort(true); // duplicates eliminated + this.isNorm = true; + } + return this; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final void deepNormalize() { + try { + for (int i = 0; i < elems.size(); i++) { + elems.elementAt(i).deepNormalize(); + } + normalize(); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final Value toSetEnum() { + return this; + } + + @Override + public final boolean isDefined() { + try { + boolean defined = true; + int sz = this.elems.size(); + for (int i = 0; i < sz; i++) { + defined = defined && this.elems.elementAt(i).isDefined(); + } + return defined; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final IValue deepCopy() { return this; } + + @Override + public final boolean assignable(Value val) { + try { + return this.equals(val); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final void write(IValueOutputStream vos) throws IOException { + final int index = vos.put(this); + if (index == -1) { + vos.writeByte(SETENUMVALUE); + final int len = elems.size(); + vos.writeInt((isNormalized()) ? len : -len); + for (int i = 0; i < len; i++) { + elems.elementAt(i).write(vos); + } + } else { + vos.writeByte(DUMMYVALUE); + vos.writeNat(index); + } + } + + /* The fingerprint methods */ + @Override + public final long fingerPrint(long fp) { + try { + this.normalize(); + int sz = this.elems.size(); + fp = FP64.Extend(fp, SETENUMVALUE); + fp = FP64.Extend(fp, sz); + for (int i = 0; i < sz; i++) { + Value elem = this.elems.elementAt(i); + fp = elem.fingerPrint(fp); + } + return fp; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final IValue permute(IMVPerm perm) { + try { + int sz = this.elems.size(); + Value[] vals = new Value[sz]; + boolean changed = false; + for (int i = 0; i < sz; i++) { + vals[i] = (Value) this.elems.elementAt(i).permute(perm); + changed = (changed || vals[i] != this.elems.elementAt(i)); + } + if (changed) { + return new SetEnumValue(vals, false); + } + return this; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + /* The string representation */ + @Override + public final StringBuffer toString(StringBuffer sb, int offset, boolean swallow) { + try { + // If this SetEnumValue object is created by a union, at least one of + // whose elements is a Cartesian product, then this can be an unnormalized + // set with repeated elements. It would therefore seem like a good idea to + // normalize this object here. Since this toString method is probably + // used only for printing the value, it seems that correcting this should + // not do any harm. Therefore, LL added the following if statement + // on 5 Mar 2012. + // Beware: + // normalize() mutates a SetEnumValue's state. Thus calling toString() + // on a SetEnumValue mutates its state. By convention, toString methods + // generally do not mutate an instance's state (side-effect free) and + // and are thus safe to be called. Failing to adhere to this convention + // can lead to subtle bugs. E.g. think of a programmer who inspects an + // instance with a debugger unconsciously mutating the instance's state. + if (!this.isNormalized()) { + this.normalize(); + } + + int len = this.elems.size(); + sb = sb.append("{"); + if (len > 0) { + this.elems.elementAt(0).toString(sb, offset, swallow); + } + for (int i = 1; i < len; i++) { + sb.append(", "); + this.elems.elementAt(i).toString(sb, offset, swallow); + } + sb.append("}"); + return sb; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + public final Value randomElement() { + int sz = size(); + int index = (int) Math.floor(RandomEnumerableValues.get().nextDouble() * sz); + return this.elems.elementAt(index); + } + + @Override + public final ValueEnumeration elements() { + try { + return new Enumerator(); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + final class Enumerator implements ValueEnumeration { + int index = 0; + + public Enumerator() { + normalize(); + } + + @Override + public final void reset() { this.index = 0; } + + @Override + public final Value nextElement() { + if (coverage) { cm.incSecondary(); } + if (this.index < elems.size()) { + return elems.elementAt(this.index++); + } + return null; + } + } + + @Override + public EnumerableValue getRandomSubset(final int kOutOfN) { + final ValueVec vec = new ValueVec(kOutOfN); + + final ValueEnumeration ve = elements(kOutOfN); + + Value v = null; + while ((v = ve.nextElement()) != null) { + vec.addElement(v); + } + return new SetEnumValue(vec, false, cm); + } + + @Override + public ValueEnumeration elements(final int k) { + normalize(); + return new EnumerableValue.SubsetEnumerator(k) { + @Override + public Value nextElement() { + if (!hasNext()) { + return null; + } + return elems.elementAt(nextIndex()); + } + }; + } + + public static IValue createFrom(final IValueInputStream vos) throws IOException { + final int index = vos.getIndex(); + boolean isNorm = true; + int len = vos.readInt(); + if (len < 0) { + len = -len; + isNorm = false; + } + final Value[] elems = new Value[len]; + for (int i = 0; i < len; i++) { + elems[i] = (Value) vos.read(); + } + final Value res = new SetEnumValue(elems, isNorm); + vos.assign(res, index); + return res; + } + + public static IValue createFrom(final ValueInputStream vos, final Map<String, UniqueString> tbl) throws IOException { + final int index = vos.getIndex(); + boolean isNorm = true; + int len = vos.readInt(); + if (len < 0) { + len = -len; + isNorm = false; + } + final Value[] elems = new Value[len]; + for (int i = 0; i < len; i++) { + elems[i] = (Value) vos.read(tbl); + } + final Value res = new SetEnumValue(elems, isNorm); + vos.assign(res, index); + return res; + } +} diff --git a/tlatools/src/tlc2/value/impl/SetOfFcnsOrRcdsValue.java b/tlatools/src/tlc2/value/impl/SetOfFcnsOrRcdsValue.java new file mode 100644 index 0000000000000000000000000000000000000000..3a04b08e989a43bf17988f3ab4e42b1b7cf7d7f0 --- /dev/null +++ b/tlatools/src/tlc2/value/impl/SetOfFcnsOrRcdsValue.java @@ -0,0 +1,132 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.value.impl; + +import java.math.BigInteger; + +import tlc2.value.RandomEnumerableValues; + +public abstract class SetOfFcnsOrRcdsValue extends EnumerableValue { + + @Override + public EnumerableValue getRandomSubset(final int kOutOfN) { + final ValueVec vec = new ValueVec(kOutOfN); + + final ValueEnumeration ve = elements(kOutOfN); + + Value v = null; + while ((v = ve.nextElement()) != null) { + vec.addElement(v); + } + + // Assert no duplicates. For large sets we assume kOutOfN < size() to avoid + // calling size() which then throws an assertion exception anyway. + assert (needBigInteger() ? vec.sort(true).size() == kOutOfN + : vec.sort(true).size() == Math.min(kOutOfN, size())); + + if (coverage) {cm.incSecondary(vec.size());} + return new SetEnumValue(vec, false, cm); + } + + @Override + public ValueEnumeration elements(final int k) { + if (needBigInteger()) { + return getBigSubsetEnumerator(k); + } else { + return getSubsetEnumerator(k, size()); + } + } + + protected abstract BigIntegerSubsetEnumerator getBigSubsetEnumerator(int k); + + protected abstract SubsetEnumerator getSubsetEnumerator(int k, int n); + + protected abstract boolean needBigInteger(); + + abstract class SubsetEnumerator extends EnumerableValue.SubsetEnumerator { + public SubsetEnumerator(final int k, final int n) { + super(k, n); + } + + @Override + public Value nextElement() { + if (!hasNext()) { + return null; + } + return elementAt(nextIndex()); + } + + protected abstract Value elementAt(int nextIndex); + } + + abstract class BigIntegerSubsetEnumerator implements ValueEnumeration { + + protected final BigInteger x; + protected final BigInteger a; + + protected final int k; + + protected BigInteger sz; + protected int i; + + public BigIntegerSubsetEnumerator(final int k) { + this.k = k; + this.i = 0; + + this.a = BigInteger.valueOf(Math.abs(RandomEnumerableValues.get().nextLong())); + + // http://primes.utm.edu/lists/2small/0bit.html + // (2^63 - 25) + this.x = BigInteger.valueOf(Long.MAX_VALUE - 24L); + } + + private BigInteger nextIndex() { + // ((x * i) + a) % sz + final BigInteger bi = BigInteger.valueOf(this.i++); + final BigInteger multiply = this.x.multiply(bi); + return multiply.add(this.a).mod(this.sz); + } + + @Override + public void reset() { + this.i = 0; + } + + private boolean hasNext() { + return this.i < this.k; + } + + @Override + public Value nextElement() { + if (!hasNext()) { + return null; + } + return elementAt(nextIndex()); + } + + protected abstract Value elementAt(BigInteger nextIndex); + } +} diff --git a/tlatools/src/tlc2/value/impl/SetOfFcnsValue.java b/tlatools/src/tlc2/value/impl/SetOfFcnsValue.java new file mode 100644 index 0000000000000000000000000000000000000000..4498dd72263af7438cc1c2e10e25d38bb64d46fb --- /dev/null +++ b/tlatools/src/tlc2/value/impl/SetOfFcnsValue.java @@ -0,0 +1,539 @@ +// Copyright (c) 2003 Compaq Corporation. All rights reserved. +// Portions Copyright (c) 2003 Microsoft Corporation. All rights reserved. +// Last modified on Wed 12 Jul 2017 at 16:10:00 PST by ian morris nieves +// modified on Sat 23 February 2008 at 10:17:11 PST by lamport +// modified on Fri Aug 10 15:09:46 PDT 2001 by yuanyu + +package tlc2.value.impl; + +import java.io.IOException; +import java.math.BigInteger; + +import tlc2.TLCGlobals; +import tlc2.tool.FingerprintException; +import tlc2.tool.coverage.CostModel; +import tlc2.value.IMVPerm; +import tlc2.value.IValue; +import tlc2.value.IValueOutputStream; +import tlc2.value.Values; +import util.Assert; + +public class SetOfFcnsValue extends SetOfFcnsOrRcdsValue implements Enumerable { + public final Value domain; /* Function domain */ + public final Value range; /* Function range */ + protected SetEnumValue fcnSet; + + /* Constructor */ + public SetOfFcnsValue(Value domain, Value range) { + this.domain = domain; + this.range = range; + this.fcnSet = null; + } + + public SetOfFcnsValue(Value domain, Value range, CostModel cm) { + this(domain, range); + this.cm = cm; + } + + @Override + public final byte getKind() { return SETOFFCNSVALUE; } + + @Override + public final int compareTo(Object obj) { + try { + this.convertAndCache(); + return this.fcnSet.compareTo(obj); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + public final boolean equals(Object obj) { + try { + if (obj instanceof SetOfFcnsValue) { + SetOfFcnsValue fcns = (SetOfFcnsValue)obj; + return (this.domain.equals(fcns.domain) && + this.range.equals(fcns.range)); + } + this.convertAndCache(); + return this.fcnSet.equals(obj); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final boolean member(Value elem) { + try { + FcnRcdValue fcn = (FcnRcdValue) elem.toFcnRcd(); + if (fcn == null) { + if (elem instanceof ModelValue) + return ((ModelValue) elem).modelValueMember(this) ; + Assert.fail("Attempted to check if \n" + elem + "\nwhich is not a TLC function" + + " value, is in the set of functions:\n" + Values.ppr(this.toString())); + } + if (fcn.intv == null) { + fcn.normalize(); + Value fdom = new SetEnumValue(fcn.domain, true); + if (this.domain.equals(fdom)) { + for (int i = 0; i < fcn.values.length; i++) { + if (!this.range.member(fcn.values[i])) { + return false; + } + } + return true; + } + } + else { + if (fcn.intv.equals(this.domain)) { + for (int i = 0; i < fcn.values.length; i++) { + if (!this.range.member(fcn.values[i])) return false; + } + return true; + } + } + return false; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final boolean isFinite() { + try { + return this.domain.isFinite() && this.range.isFinite(); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final Value takeExcept(ValueExcept ex) { + try { + if (ex.idx < ex.path.length) { + Assert.fail("Attempted to apply EXCEPT to the set of functions:\n" + + Values.ppr(this.toString())); + } + return ex.value; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final Value takeExcept(ValueExcept[] exs) { + try { + if (exs.length != 0) { + Assert.fail("Attempted to apply EXCEPT to the set of functions:\n" + + Values.ppr(this.toString())); + } + return this; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final int size() { + try { + int dsz = this.domain.size(); + int rsz = this.range.size(); + long sz = 1; + for (int i = 0; i < dsz; i++) { + sz *= rsz; + if (sz < -2147483648 || sz > 2147483647) { + Assert.fail("Overflow when computing the number of elements in:\n" + + Values.ppr(toString())); + } + } + return (int)sz; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + protected boolean needBigInteger() { + final int rsz = this.range.size(); + final int dsz = this.domain.size(); + long sz = 1; + for (int i = 0; i < dsz; i++) { + sz *= rsz; + if (sz < -2147483648 || sz > 2147483647) { + return true; + } + } + return false; + } + + @Override + public final boolean isNormalized() { + try { + if (this.fcnSet == null || this.fcnSet == SetEnumValue.DummyEnum) { + return this.domain.isNormalized() && this.range.isNormalized(); + } + return this.fcnSet.isNormalized(); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final Value normalize() { + try { + if (this.fcnSet == null || this.fcnSet == SetEnumValue.DummyEnum) { + this.domain.normalize(); + this.range.normalize(); + } + else { + this.fcnSet.normalize(); + } + return this; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final void deepNormalize() { + try { + domain.deepNormalize(); + range.deepNormalize(); + if (fcnSet == null) { + fcnSet = SetEnumValue.DummyEnum; + } + else if (fcnSet != SetEnumValue.DummyEnum) { + fcnSet.deepNormalize(); + } + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final boolean isDefined() { + try { + return this.domain.isDefined() && this.range.isDefined(); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final IValue deepCopy() { return this; } + + @Override + public final boolean assignable(Value val) { + try { + return this.equals(val); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + /* The fingerprint */ + @Override + public final long fingerPrint(long fp) { + try { + this.convertAndCache(); + return this.fcnSet.fingerPrint(fp); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final IValue permute(IMVPerm perm) { + try { + this.convertAndCache(); + return this.fcnSet.permute(perm); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + private final void convertAndCache() { + if (this.fcnSet == null) { + this.fcnSet = (SetEnumValue) this.toSetEnum(); + } + else if (this.fcnSet == SetEnumValue.DummyEnum) { + SetEnumValue val = null; + synchronized(this) { + if (this.fcnSet == SetEnumValue.DummyEnum) { + val = (SetEnumValue) this.toSetEnum(); + val.deepNormalize(); + } + } + synchronized(this) { + if (this.fcnSet == SetEnumValue.DummyEnum) { this.fcnSet = val; } + } + } + } + + @Override + public final Value toSetEnum() { + if (this.fcnSet != null && this.fcnSet != SetEnumValue.DummyEnum) { + return this.fcnSet; + } + ValueVec vals = new ValueVec(); + ValueEnumeration Enum = this.elements(); + Value elem; + while ((elem = Enum.nextElement()) != null) { + vals.addElement(elem); + } + if (coverage) {cm.incSecondary(vals.size());} + return new SetEnumValue(vals, this.isNormalized(), cm); + } + + @Override + public final void write(final IValueOutputStream vos) throws IOException { + fcnSet.write(vos); + } + + /* The string representation of the value. */ + @Override + public final StringBuffer toString(StringBuffer sb, int offset, boolean swallow) { + try { + boolean unlazy = TLCGlobals.expand; + try { + if (unlazy) { + int dsz = this.domain.size(); + int rsz = this.range.size(); + long sz = 1; + for (int i = 0; i < dsz; i++) { + sz *= rsz; + if (sz < -2147483648 || sz > 2147483647) { + unlazy = false; + break; + } + } + unlazy = sz < TLCGlobals.enumBound; + } + } + catch (Throwable e) { if (swallow) unlazy = false; else throw e; } + + if (unlazy) { + Value val = this.toSetEnum(); + return val.toString(sb, offset, swallow); + } + else { + sb.append("["); + this.domain.toString(sb, offset, swallow); + sb.append(" -> "); + this.range.toString(sb, offset, swallow); + sb.append("]"); + return sb; + } + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final ValueEnumeration elements() { + try { + if (this.fcnSet == null || this.fcnSet == SetEnumValue.DummyEnum) { + return new Enumerator(); + } + return this.fcnSet.elements(); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + final class Enumerator implements ValueEnumeration { + private Value[] dom; + private ValueEnumeration[] enums; + private Value[] currentElems; + private boolean isDone; + + public Enumerator() { + this.isDone = false; + SetEnumValue domSet = (SetEnumValue) domain.toSetEnum(); + if (domSet == null) + Assert.fail("Attempted to enumerate a set of the form [D -> R]," + + "but the domain D:\n" + Values.ppr(domain.toString()) + + "\ncannot be enumerated."); + domSet.normalize(); + ValueVec elems = domSet.elems; + int sz = elems.size(); + if (range instanceof Enumerable) { + this.dom = new Value[sz]; + this.enums = new ValueEnumeration[sz]; + this.currentElems = new Value[sz]; + // SZ Feb 24, 2009: never read locally + // ValueEnumeration enumeration = ((Enumerable)domSet).elements(); + for (int i = 0; i < sz; i++) { + this.dom[i] = elems.elementAt(i); + this.enums[i] = ((Enumerable)range).elements(); + this.currentElems[i] = this.enums[i].nextElement(); + if (this.currentElems[i] == null) { + this.enums = null; + this.isDone = true; + break; + } + } + } + else { + Assert.fail("Attempted to enumerate a set of the form [D -> R]," + + "but the range R:\n" + Values.ppr(range.toString()) + + "\ncannot be enumerated."); + } + } + + @Override + public final void reset() { + if (this.enums != null) { + for (int i = 0; i < this.enums.length; i++) { + this.enums[i].reset(); + this.currentElems[i] = this.enums[i].nextElement(); + } + this.isDone = false; + } + } + + @Override + public final Value nextElement() { + if (this.isDone) { + return null; + } + if (this.currentElems.length == 0) { + if (coverage) { cm.incSecondary(); } + this.isDone = true; + return new FcnRcdValue(this.dom, new Value[this.currentElems.length], true, cm); + } else { + // Take and store a snapshot of currentElems as the element to return for + // this invocation of nextElement(). + final Value[] elems = new Value[this.currentElems.length]; + System.arraycopy(this.currentElems, 0, elems, 0, this.currentElems.length); + + // Eagerly generate the next element which is going to be returned the upon next + // invocation of nextElement(). + if (coverage) { cm.incSecondary(this.currentElems.length); } + for (int i = this.currentElems.length - 1; i >= 0; i--) { + this.currentElems[i] = this.enums[i].nextElement(); + if (this.currentElems[i] != null) { + break; + } + if (i == 0) { + this.isDone = true; + break; + } + this.enums[i].reset(); + this.currentElems[i] = this.enums[i].nextElement(); + } + + return new FcnRcdValue(this.dom, elems, true, cm); + } + } + + } + + @Override + protected tlc2.value.impl.SetOfFcnsOrRcdsValue.SubsetEnumerator getSubsetEnumerator(int k, int n) { + return new SubsetEnumerator(k, n); + } + + class SubsetEnumerator extends SetOfFcnsOrRcdsValue.SubsetEnumerator { + private final SetEnumValue domSet; + private final SetEnumValue rangeSet; + private final int mod; + + SubsetEnumerator(final int k, final int n) { + super(k, n); + domSet = (SetEnumValue) domain.toSetEnum(); + domSet.normalize(); + + rangeSet = (SetEnumValue) range.toSetEnum(); + + mod = range.size(); + } + + @Override + protected Value elementAt(final int idx) { + assert 0 <= idx && idx < size(); + + final Value[] range = new Value[domSet.size()]; + + for (int i = 0; i < domSet.size(); i++) { + final int elementAt = (int) (Math.floor(idx / Math.pow(mod, i)) % mod); + range[range.length - 1 - i] = rangeSet.elems.elementAt(elementAt); + } + + return new FcnRcdValue(domSet.elems, range, true); + } + } + + @Override + protected tlc2.value.impl.SetOfFcnsOrRcdsValue.BigIntegerSubsetEnumerator getBigSubsetEnumerator(int k) { + return new BigIntegerSubsetEnumerator(k); + } + + class BigIntegerSubsetEnumerator extends SetOfFcnsOrRcdsValue.BigIntegerSubsetEnumerator { + + private final SetEnumValue domSet; + private final SetEnumValue rangeSet; + private final BigInteger bMod; + private final int mod; + + public BigIntegerSubsetEnumerator(final int k) { + super(k); + this.domSet = (SetEnumValue) domain.toSetEnum(); + this.domSet.normalize(); + + this.rangeSet = (SetEnumValue) range.toSetEnum(); + this.mod = range.size(); + this.bMod = BigInteger.valueOf(mod); + + this.sz = bMod.pow(domSet.size()); + } + + @Override + protected Value elementAt(final BigInteger idx) { + final Value[] range = new Value[domSet.size()]; + + for (int i = 0; i < domSet.size(); i++) { + final long scale = (long) Math.pow(mod, i); + final BigInteger bScale = BigInteger.valueOf(scale); + // idx2 is the index in the range (0,range.size^domset.size] + final BigInteger idx2 = idx.divide(bScale); + final int elementAt = idx2.mod(bMod).intValueExact(); + range[range.length - 1 - i] = rangeSet.elems.elementAt(elementAt); + } + + return new FcnRcdValue(domSet.elems, range, true); + } + } +} diff --git a/tlatools/src/tlc2/value/impl/SetOfRcdsValue.java b/tlatools/src/tlc2/value/impl/SetOfRcdsValue.java new file mode 100644 index 0000000000000000000000000000000000000000..8cbff7cbc8b27c210b4b647cd42d92d7684e5c0b --- /dev/null +++ b/tlatools/src/tlc2/value/impl/SetOfRcdsValue.java @@ -0,0 +1,581 @@ +// Copyright (c) 2003 Compaq Corporation. All rights reserved. +// Portions Copyright (c) 2003 Microsoft Corporation. All rights reserved. +// Last modified on Wed 12 Jul 2017 at 16:10:00 PST by ian morris nieves +// modified on Sat 23 February 2008 at 10:18:01 PST by lamport +// modified on Fri Aug 10 15:09:53 PDT 2001 by yuanyu + +package tlc2.value.impl; + +import java.io.IOException; +import java.math.BigInteger; + +import tlc2.TLCGlobals; +import tlc2.output.EC; +import tlc2.tool.FingerprintException; +import tlc2.tool.coverage.CostModel; +import tlc2.value.IMVPerm; +import tlc2.value.IValue; +import tlc2.value.IValueOutputStream; +import tlc2.value.Values; +import util.Assert; +import util.UniqueString; + +public class SetOfRcdsValue extends SetOfFcnsOrRcdsValue implements Enumerable { + public final UniqueString[] names; // The names of the fields. + public final Value[] values; // The values of the fields. + protected SetEnumValue rcdSet; + + /* Constructor */ + public SetOfRcdsValue(UniqueString[] names, Value[] values, boolean isNorm) { + assert names.length == values.length; // see tlc2.tool.Tool.evalAppl(OpApplNode, Context, TLCState, TLCState, int) case for OPCODE_sor + this.names = names; + this.values = values; + this.rcdSet = null; + if (!isNorm) { + this.sortByNames(); + } + } + + public SetOfRcdsValue(UniqueString[] names, Value[] values, boolean isNorm, CostModel cm) { + this(names, values, isNorm); + this.cm = cm; + } + + @Override + public final byte getKind() { return SETOFRCDSVALUE; } + + @Override + public final int compareTo(Object obj) { + try { + this.convertAndCache(); + return this.rcdSet.compareTo(obj); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + public final boolean equals(Object obj) { + try { + if (obj instanceof SetOfRcdsValue) { + SetOfRcdsValue rcds = (SetOfRcdsValue)obj; + + boolean isEmpty1 = this.isEmpty(); + if (isEmpty1) { return rcds.isEmpty(); } + if (rcds.isEmpty()) { return isEmpty1; } + + if (this.names.length != rcds.names.length) { + return false; + } + for (int i = 0; i < this.names.length; i++) { + if (!this.names[i].equals(rcds.names[i]) || + !this.values[i].equals(rcds.values[i])) { + return false; + } + } + return true; + } + this.convertAndCache(); + return this.rcdSet.equals(obj); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final boolean member(Value elem) { + try { + RecordValue rcd = (RecordValue) elem.toRcd(); + if (rcd == null) { + if (elem instanceof ModelValue) + return ((ModelValue) elem).modelValueMember(this) ; + Assert.fail("Attempted to check if non-record\n" + elem + "\nis in the" + + " set of records:\n" + Values.ppr(this.toString())); + } + rcd.normalize(); + if (this.names.length != rcd.names.length) { + return false; + } + for (int i = 0; i < this.names.length; i++) { + if ((!this.names[i].equals(rcd.names[i])) || + (!this.values[i].member(rcd.values[i]))) { + return false; + } + } + return true; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final boolean isFinite() { + try { + for (int i = 0; i < this.values.length; i++) { + if (!this.values[i].isFinite()) return false; + } + return true; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final Value takeExcept(ValueExcept ex) { + try { + if (ex.idx < ex.path.length) { + Assert.fail("Attempted to apply EXCEPT to the set of records:\n" + + Values.ppr(this.toString())); + } + return ex.value; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final Value takeExcept(ValueExcept[] exs) { + try { + if (exs.length != 0) { + Assert.fail("Attempted to apply EXCEPT to the set of records:\n" + + Values.ppr(this.toString())); + } + return this; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final int size() { + try { + long sz = 1; + for (int i = 0; i < this.values.length; i++) { + sz *= this.values[i].size(); + if (sz < -2147483648 || sz > 2147483647) { + Assert.fail(EC.TLC_MODULE_OVERFLOW, "the number of elements in:\n" + + Values.ppr(this.toString())); + } + } + return (int)sz; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + protected boolean needBigInteger() { + long sz = 1; + for (int i = 0; i < values.length; i++) { + sz *= values[i].size(); + if (sz < -2147483648 || sz > 2147483647) { + return true; + } + } + return false; + } + + @Override + public final boolean isNormalized() { + try { + if (this.rcdSet == null || this.rcdSet == SetEnumValue.DummyEnum) { + for (int i = 0; i < this.names.length; i++) { + if (!this.values[i].isNormalized()) { + return false; + } + } + return true; + } + return this.rcdSet.isNormalized(); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final Value normalize() { + try { + if (this.rcdSet == null || this.rcdSet == SetEnumValue.DummyEnum) { + for (int i = 0; i < this.names.length; i++) { + this.values[i].normalize(); + } + } + else { + this.rcdSet.normalize(); + } + return this; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final void deepNormalize() { + try { + for (int i = 0; i < values.length; i++) { + values[i].deepNormalize(); + } + if (rcdSet == null) { + rcdSet = SetEnumValue.DummyEnum; + } + else if (rcdSet != SetEnumValue.DummyEnum) { + rcdSet.deepNormalize(); + } + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + private final void sortByNames() { + for (int i = 1; i < this.names.length; i++) { + int cmp = this.names[0].compareTo(this.names[i]); + if (cmp == 0) { + Assert.fail("Field name " + this.names[0] + " occurs multiple times" + + " in set of records."); + } + else if (cmp > 0) { + UniqueString ts = this.names[0]; + this.names[0] = this.names[i]; + this.names[i] = ts; + Value tv = this.values[0]; + this.values[0] = this.values[i]; + this.values[i] = tv; + } + } + for (int i = 2; i < this.names.length; i++) { + int j = i; + UniqueString st = this.names[i]; + Value val = this.values[i]; + int cmp; + while ((cmp = st.compareTo(this.names[j-1])) < 0) { + this.names[j] = this.names[j-1]; + this.values[j] = this.values[j-1]; + j--; + } + if (cmp == 0) { + Assert.fail("Field name " + this.names[i] + " occurs multiple times" + + " in set of records."); + } + this.names[j] = st; + this.values[j] = val; + } + } + + @Override + public final boolean isDefined() { + try { + boolean isDefined = true; + for (int i = 0; i < this.values.length; i++) { + isDefined = isDefined && this.values[i].isDefined(); + } + return isDefined; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final IValue deepCopy() { return this; } + + @Override + public final boolean assignable(Value val) { + try { + return this.equals(val); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + /* The fingerprint */ + @Override + public final long fingerPrint(long fp) { + try { + this.convertAndCache(); + return this.rcdSet.fingerPrint(fp); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final IValue permute(IMVPerm perm) { + try { + this.convertAndCache(); + return this.rcdSet.permute(perm); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + private final void convertAndCache() { + if (this.rcdSet == null) { + this.rcdSet = (SetEnumValue) this.toSetEnum(); + } + else if (this.rcdSet == SetEnumValue.DummyEnum) { + SetEnumValue val = null; + synchronized(this) { + if (this.rcdSet == SetEnumValue.DummyEnum) { + val = (SetEnumValue) this.toSetEnum(); + val.deepNormalize(); + } + } + synchronized(this) { + if (this.rcdSet == SetEnumValue.DummyEnum) { this.rcdSet = val; } + } + } + } + + @Override + public final Value toSetEnum() { + if (this.rcdSet != null && this.rcdSet != SetEnumValue.DummyEnum) { + return this.rcdSet; + } + ValueVec vals = new ValueVec(); + ValueEnumeration Enum = this.elements(); + Value elem; + while ((elem = Enum.nextElement()) != null) { + vals.addElement(elem); + } + if (coverage) {cm.incSecondary(vals.size());} + return new SetEnumValue(vals, this.isNormalized(), cm); + } + + @Override + public final void write(final IValueOutputStream vos) throws IOException { + rcdSet.write(vos); + } + + /* The string representation of the value. */ + @Override + public final StringBuffer toString(StringBuffer sb, int offset, boolean swallow) { + try { + boolean unlazy = TLCGlobals.expand; + try { + if (unlazy) { + long sz = 1; + for (int i = 0; i < this.values.length; i++) { + sz *= this.values[i].size(); + if (sz < -2147483648 || sz > 2147483647) { + unlazy = false; + break; + } + } + unlazy = sz < TLCGlobals.enumBound; + } + } + catch (Throwable e) { if (swallow) unlazy = false; else throw e; } + + if (unlazy) { + Value val = this.toSetEnum(); + return val.toString(sb, offset, swallow); + } + else { + sb.append("["); + int len = this.names.length; + if (len != 0) { + sb.append(names[0] + ": "); + this.values[0].toString(sb, offset, swallow); + } + for (int i = 1; i < len; i++) { + sb.append(", "); + sb.append(names[i] + ": "); + this.values[i].toString(sb, offset, swallow); + } + sb.append("]"); + return sb; + } + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final ValueEnumeration elements() { + try { + if (this.rcdSet == null || this.rcdSet == SetEnumValue.DummyEnum) { + return new Enumerator(); + } + return this.rcdSet.elements(); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + final class Enumerator implements ValueEnumeration { + private ValueEnumeration[] enums; + private Value[] currentElems; + private boolean isDone; + + public Enumerator() { + this.enums = new ValueEnumeration[values.length]; + this.currentElems = new Value[values.length]; + this.isDone = false; + for (int i = 0; i < values.length; i++) { + if (values[i] instanceof Enumerable) { + this.enums[i] = ((Enumerable)values[i]).elements(); + this.currentElems[i] = this.enums[i].nextElement(); + if (this.currentElems[i] == null) { + this.enums = null; + this.isDone = true; + break; + } + } + else { + Assert.fail("Attempted to enumerate a set of the form [l1 : v1, ..., ln : vn]," + + "\nbut can't enumerate the value of the `" + names[i] + "' field:\n" + + Values.ppr(values[i].toString())); + } + } + } + + @Override + public final void reset() { + if (this.enums != null) { + for (int i = 0; i < this.enums.length; i++) { + this.enums[i].reset(); + this.currentElems[i] = this.enums[i].nextElement(); + } + this.isDone = false; + } + } + + @Override + public final Value nextElement() { + if (this.isDone) return null; + Value[] elems = new Value[this.currentElems.length]; + if (coverage) { cm.incSecondary(elems.length); } + for (int i = 0; i < elems.length; i++) { + elems[i] = this.currentElems[i]; + } + for (int i = elems.length-1; i >= 0; i--) { + this.currentElems[i] = this.enums[i].nextElement(); + if (this.currentElems[i] != null) break; + if (i == 0) { + this.isDone = true; + break; + } + this.enums[i].reset(); + this.currentElems[i] = this.enums[i].nextElement(); + } + return new RecordValue(names, elems, true, cm); + } + + } + + @Override + protected tlc2.value.impl.SetOfFcnsOrRcdsValue.SubsetEnumerator getSubsetEnumerator(int k, int n) { + return new SubsetEnumerator(k, n); + } + + class SubsetEnumerator extends SetOfFcnsOrRcdsValue.SubsetEnumerator { + + private final SetEnumValue[] convert; + private final int[] rescaleBy; + + SubsetEnumerator(final int k, final int n) { + super(k, n); + + convert = new SetEnumValue[values.length]; + rescaleBy = new int[values.length]; + + int numElems = 1; // 1 to avoid div by zero in elementAt + for (int i = values.length - 1; i >= 0; i--) { + convert[i] = (SetEnumValue) values[i].toSetEnum(); + rescaleBy[i] = numElems; + numElems *= convert[i].elems.size(); + } + } + + @Override + protected RecordValue elementAt(final int idx) { + assert 0 <= idx && idx < size(); + + final Value[] val = new Value[names.length]; + for (int i = 0; i < val.length; i++) { + final SetEnumValue sev = convert[i]; + final int mod = sev.elems.size(); + + final int rescaledIdx = (int) Math.floor(idx / rescaleBy[i]); + final int elementAt = rescaledIdx % mod; + + val[i] = sev.elems.elementAt(elementAt); + } + return new RecordValue(names, val, false, cm); + } + } + + @Override + protected BigIntegerSubsetEnumerator getBigSubsetEnumerator(int k) { + return new BigIntegerSubsetEnumerator(k); + } + + class BigIntegerSubsetEnumerator extends SetOfFcnsOrRcdsValue.BigIntegerSubsetEnumerator { + + private final SetEnumValue[] convert; + private final BigInteger[] rescaleBy; + + public BigIntegerSubsetEnumerator(final int k) { + super(k); + + convert = new SetEnumValue[values.length]; + rescaleBy = new BigInteger[values.length]; + + BigInteger numElems = BigInteger.ONE; // 1 to avoid div by zero in elementAt + for (int i = values.length - 1; i >= 0; i--) { + convert[i] = (SetEnumValue) values[i].toSetEnum(); + rescaleBy[i] = numElems; + numElems = numElems.multiply(BigInteger.valueOf(convert[i].elems.size())); + } + + // The size of the (enumerated) SetOfFcnsOrRcdsValue needs BigInteger. + this.sz = numElems; + } + + @Override + protected Value elementAt(final BigInteger idx) { + final Value[] val = new Value[names.length]; + for (int i = 0; i < val.length; i++) { + final SetEnumValue sev = convert[i]; + final int mod = sev.elems.size(); + + final BigInteger d = idx.divide(rescaleBy[i]); + final BigInteger m = d.mod(BigInteger.valueOf(mod)); + final int elementAt = m.intValueExact(); + + val[i] = sev.elems.elementAt(elementAt); + } + return new RecordValue(names, val, false, cm); + } + } +} diff --git a/tlatools/src/tlc2/value/impl/SetOfTuplesValue.java b/tlatools/src/tlc2/value/impl/SetOfTuplesValue.java new file mode 100644 index 0000000000000000000000000000000000000000..7a73ac078f3fe833c7fd228c95fae65fa741aeac --- /dev/null +++ b/tlatools/src/tlc2/value/impl/SetOfTuplesValue.java @@ -0,0 +1,456 @@ +// Copyright (c) 2003 Compaq Corporation. All rights reserved. +// Portions Copyright (c) 2003 Microsoft Corporation. All rights reserved. +// Last modified on Wed 12 Jul 2017 at 16:10:00 PST by ian morris nieves +// modified on Sat 23 February 2008 at 10:18:39 PST by lamport +// modified on Fri Aug 10 15:09:59 PDT 2001 by yuanyu + +package tlc2.value.impl; + +import java.io.IOException; + +import tlc2.TLCGlobals; +import tlc2.tool.FingerprintException; +import tlc2.tool.coverage.CostModel; +import tlc2.value.IMVPerm; +import tlc2.value.IValue; +import tlc2.value.IValueOutputStream; +import tlc2.value.Values; +import util.Assert; + +public class SetOfTuplesValue extends EnumerableValue implements Enumerable { + public final Value[] sets; + protected SetEnumValue tupleSet; + + /* Constructor */ + public SetOfTuplesValue(Value[] sets) { + this.sets = sets; + this.tupleSet = null; + } + public SetOfTuplesValue(Value[] set, CostModel cm) { + this(set); + this.cm = cm; + } + + public SetOfTuplesValue(Value val) { + this(new Value[1]); + this.sets[0] = val; + } + + public SetOfTuplesValue(Value v1, Value v2) { + this(new Value[2]); + this.sets[0] = v1; + this.sets[1] = v2; + } + + @Override + public final byte getKind() { return SETOFTUPLESVALUE; } + + @Override + public final int compareTo(Object obj) { + try { + this.convertAndCache(); + return this.tupleSet.compareTo(obj); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + public final boolean equals(Object obj) { + try { + if (obj instanceof SetOfTuplesValue) { + SetOfTuplesValue tvs = (SetOfTuplesValue)obj; + + boolean isEmpty1 = this.isEmpty(); + if (isEmpty1) return tvs.isEmpty(); + if (tvs.isEmpty()) return isEmpty1; + + if (this.sets.length != tvs.sets.length) { + return false; + } + for (int i = 0; i < this.sets.length; i++) { + if (!this.sets[i].equals(tvs.sets[i])) { + return false; + } + } + return true; + } + this.convertAndCache(); + return this.tupleSet.equals(obj); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final boolean member(Value elem) { + try { + TupleValue tv = (TupleValue) elem.toTuple(); + if (tv == null) { + FcnRcdValue fcn = (FcnRcdValue) elem.toFcnRcd(); + if (fcn == null) { + if (elem instanceof ModelValue) + return ((ModelValue) elem).modelValueMember(this) ; + Assert.fail("Attempted to check if non-tuple\n" + Values.ppr(elem.toString()) + + "\nis in the set of tuples:\n" + Values.ppr(this.toString())); + } + if (fcn.intv != null) return false; + for (int i = 0; i < fcn.domain.length; i++) { + if (!(fcn.domain[i] instanceof IntValue)) { + Assert.fail("Attempted to check if non-tuple\n" + Values.ppr(elem.toString()) + + "\nis in the set of tuples:\n" + Values.ppr(this.toString())); + } + } + return false; + } + if (tv.elems.length == this.sets.length) { + for (int i = 0; i < this.sets.length; i++) { + if (!this.sets[i].member(tv.elems[i])) + return false; + } + return true; + } + return false; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final boolean isFinite() { + try { + for (int i = 0; i < this.sets.length; i++) { + if (!this.sets[i].isFinite()) { + return false; + } + } + return true; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final Value takeExcept(ValueExcept ex) { + try { + if (ex.idx < ex.path.length) { + Assert.fail("Attempted to apply EXCEPT construct to the set of tuples:\n" + + Values.ppr(this.toString())); + } + return ex.value; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final Value takeExcept(ValueExcept[] exs) { + try { + if (exs.length != 0) { + Assert.fail("Attempted to apply EXCEPT construct to the set of tuples:\n" + + Values.ppr(this.toString())); + } + return this; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final int size() { + try { + long sz = 1; + for (int i = 0; i < this.sets.length; i++) { + sz *= this.sets[i].size(); + if (sz < -2147483648 || sz > 2147483647) { + Assert.fail("Overflow when computing the number of elements in " + + Values.ppr(this.toString())); + } + } + return (int)sz; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final boolean isNormalized() { + try { + if (this.tupleSet == null || this.tupleSet == SetEnumValue.DummyEnum) { + for (int i = 0; i < this.sets.length; i++) { + if (!this.sets[i].isNormalized()) { + return false; + } + } + return true; + } + return this.tupleSet.isNormalized(); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final Value normalize() { + try { + if (this.tupleSet == null || this.tupleSet == SetEnumValue.DummyEnum) { + for (int i = 0; i < this.sets.length; i++) { + this.sets[i].normalize(); + } + } + else { + this.tupleSet.normalize(); + } + return this; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final void deepNormalize() { + try { + for (int i = 0; i < sets.length; i++) { + sets[i].deepNormalize(); + } + if (tupleSet == null) { + tupleSet = SetEnumValue.DummyEnum; + } + else if (tupleSet != SetEnumValue.DummyEnum) { + tupleSet.deepNormalize(); + } + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final boolean isDefined() { + try { + boolean defined = true; + for (int i = 0; i < this.sets.length; i++) { + defined = defined && this.sets[i].isDefined(); + } + return defined; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final IValue deepCopy() { return this; } + + @Override + public final boolean assignable(Value val) { + try { + return this.equals(val); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + /* The fingerprint */ + @Override + public final long fingerPrint(long fp) { + try { + this.convertAndCache(); + return this.tupleSet.fingerPrint(fp); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final IValue permute(IMVPerm perm) { + try { + this.convertAndCache(); + return this.tupleSet.permute(perm); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + private final void convertAndCache() { + if (this.tupleSet == null) { + this.tupleSet = (SetEnumValue) this.toSetEnum(); + } + else if (this.tupleSet == SetEnumValue.DummyEnum) { + SetEnumValue val = null; + synchronized(this) { + if (this.tupleSet == SetEnumValue.DummyEnum) { + val = (SetEnumValue) this.toSetEnum(); + val.deepNormalize(); + } + } + synchronized(this) { + if (this.tupleSet == SetEnumValue.DummyEnum) { this.tupleSet = val; } + } + } + } + + @Override + public final Value toSetEnum() { + if (this.tupleSet != null && this.tupleSet != SetEnumValue.DummyEnum) { + return this.tupleSet; + } + ValueVec vals = new ValueVec(); + ValueEnumeration Enum = this.elements(); + Value elem; + while ((elem = Enum.nextElement()) != null) { + vals.addElement(elem); + } + if (coverage) {cm.incSecondary(vals.size());} + return new SetEnumValue(vals, this.isNormalized(), cm); + } + + @Override + public final void write(final IValueOutputStream vos) throws IOException { + tupleSet.write(vos); + } + + /* The string representation of the value. */ + @Override + public final StringBuffer toString(StringBuffer sb, int offset, boolean swallow) { + try { + boolean unlazy = TLCGlobals.expand; + try { + if (unlazy) { + long sz = 1; + for (int i = 0; i < this.sets.length; i++) { + sz *= this.sets[i].size(); + if (sz < -2147483648 || sz > 2147483647) { + unlazy = false; + break; + } + } + unlazy = sz < TLCGlobals.enumBound; + } + } + catch (Throwable e) { if (swallow) unlazy = false; else throw e; } + + if (unlazy) { + Value val = this.toSetEnum(); + return val.toString(sb, offset, swallow); + } + else { + if (this.sets.length > 0) { + sb.append("("); + this.sets[0].toString(sb, offset, swallow); + } + for (int i = 1; i < this.sets.length; i++) { + sb.append(" \\X "); + this.sets[i].toString(sb, offset, swallow); + } + if (this.sets.length > 0) { + sb.append(")"); + } + return sb; + } + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final ValueEnumeration elements() { + try { + if (this.tupleSet == null || this.tupleSet == SetEnumValue.DummyEnum) { + return new Enumerator(); + } + return this.tupleSet.elements(); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + final class Enumerator implements ValueEnumeration { + private ValueEnumeration[] enums; + private Value[] currentElems; + private boolean isDone; + + public Enumerator() { + this.enums = new ValueEnumeration[sets.length]; + this.currentElems = new Value[sets.length]; + this.isDone = false; + for (int i = 0; i < sets.length; i++) { + if (sets[i] instanceof Enumerable) { + this.enums[i] = ((Enumerable)sets[i]).elements(); + this.currentElems[i] = this.enums[i].nextElement(); + if (this.currentElems[i] == null) { + this.enums = null; + this.isDone = true; + break; + } + } + else { + Assert.fail("Attempted to enumerate a set of the form s1 \\X s2 ... \\X sn," + + "\nbut can't enumerate s" + i + ":\n" + Values.ppr(sets[i].toString())); + } + } + } + + @Override + public final void reset() { + if (this.enums != null) { + for (int i = 0; i < this.enums.length; i++) { + this.enums[i].reset(); + this.currentElems[i] = this.enums[i].nextElement(); + } + this.isDone = false; + } + } + + @Override + public final Value nextElement() { + if (this.isDone) return null; + Value[] elems = new Value[this.currentElems.length]; + if (coverage) { cm.incSecondary(elems.length); } + for (int i = 0; i < elems.length; i++) { + elems[i] = this.currentElems[i]; + } + for (int i = elems.length-1; i >= 0; i--) { + this.currentElems[i] = this.enums[i].nextElement(); + if (this.currentElems[i] != null) break; + if (i == 0) { + this.isDone = true; + break; + } + this.enums[i].reset(); + this.currentElems[i] = this.enums[i].nextElement(); + } + return new TupleValue(elems, cm); + } + } + +} diff --git a/tlatools/src/tlc2/value/impl/SetPredValue.java b/tlatools/src/tlc2/value/impl/SetPredValue.java new file mode 100644 index 0000000000000000000000000000000000000000..f3ac2bfa8d01838f753c67e5f419e623259fa1ee --- /dev/null +++ b/tlatools/src/tlc2/value/impl/SetPredValue.java @@ -0,0 +1,423 @@ +// Copyright (c) 2003 Compaq Corporation. All rights reserved. +// Portions Copyright (c) 2003 Microsoft Corporation. All rights reserved. +// Last modified on Wed 12 Jul 2017 at 16:10:00 PST by ian morris nieves +// modified on Thu 5 Jul 2007 at 4:44:23 PST by lamport +// modified on Fri Aug 10 15:10:04 PDT 2001 by yuanyu + +package tlc2.value.impl; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; + +import tla2sany.semantic.FormalParamNode; +import tla2sany.semantic.SemanticNode; +import tlc2.TLCGlobals; +import tlc2.tool.EvalException; +import tlc2.tool.FingerprintException; +import tlc2.tool.ITool; +import tlc2.tool.TLCState; +import tlc2.tool.coverage.CostModel; +import tlc2.util.Context; +import tlc2.value.IBoolValue; +import tlc2.value.IMVPerm; +import tlc2.value.IValue; +import tlc2.value.IValueOutputStream; +import tlc2.value.Values; +import util.Assert; + +public class SetPredValue extends EnumerableValue implements Enumerable { + public final Object vars; // FormalParamNode or FormalParamNode[] + /*********************************************************************** + * Was OpDeclNode or OpDeclNode[]. * + ***********************************************************************/ + public Value inVal; // the in value or the real set + public final SemanticNode pred; // the predicate + public final ITool tool; // null iff inVal is the real set + /** + * true after inVal has been converted to a SetEnumValue. I assume this + * implies (inVal instanceof SetEnumValue) too but the serialization + * might interfere. + * MAK 07/18/2019 + */ + private boolean converted = false; + public final Context con; + public final TLCState state; + public final TLCState pstate; + public final int control; + + /* Constructor */ + public SetPredValue(Object vars, Value inVal, SemanticNode pred, ITool tool, + Context con, TLCState s0, TLCState s1, int control) { + this.vars = vars; + this.inVal = inVal; + this.pred = pred; + this.tool = tool; + this.con = con; + this.state = s0.copy(); + if (s1 != null) { + this.pstate = s1.copy(); + } else { + this.pstate = null; + } + /** + * The two copy()s above were added by YY on 12 Mar 2010 to fix the + * following bug: When a lazily evaluated expression is saved, the + * state under which it should be evaluated must be saved. The + * s0 and s1 objects with which this method is called can be modified + * after the call, so copies must be made. + */ + this.control = control; + } + + public SetPredValue(Object vars, Value inVal, SemanticNode pred, ITool tool, + Context con, TLCState s0, TLCState s1, int control, CostModel cm) { + this(vars, inVal, pred, tool, con, s0, s1, control); + this.cm = cm; + } + + public SetPredValue(SetPredValue other, ITool tool) { + this(other.vars, other.inVal, other.pred, tool, other.con, other.state, other.pstate, other.control, other.cm); + } + + @Override + public final byte getKind() { return SETPREDVALUE; } + + @Override + public final int compareTo(Object obj) { + try { + this.inVal = this.toSetEnum(); + this.converted = true; + return this.inVal.compareTo(obj); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + public final boolean equals(Object obj) { + try { + this.inVal = this.toSetEnum(); + this.converted = true; + return this.inVal.equals(obj); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final boolean member(Value elem) { + try { + if (this.converted) { + return this.inVal.member(elem); + } + try { + if (this.inVal.member(elem)) { + Context con1 = this.con; + if (this.vars instanceof FormalParamNode) { + con1 = con1.cons((FormalParamNode)this.vars, elem); + } + else { + FormalParamNode[] ids = (FormalParamNode[])this.vars; + TupleValue tv = (TupleValue) elem.toTuple(); + if ((tv != null) && (tv.elems.length == ids.length)) { + Value [] vals = ((TupleValue)tv).elems; + for (int i = 0; i < ids.length; i++) { + con1 = con1.cons(ids[i], vals[i]); + } + } + else { + Assert.fail("Attempted to check if the value:\n" + Values.ppr(elem.toString()) + + "\nis an element of a set of " + ids.length + "-tuples."); + } + } + Value res = (Value) this.tool.eval(this.pred, con1, this.state, this.pstate, this.control); + if (!(res instanceof IBoolValue)) { + Assert.fail("The evaluation of predicate " + this.pred + + " yielded non-Boolean value."); + } + return ((BoolValue)res).val; + } + } + catch (EvalException e) { + Assert.fail("Cannot decide if element:\n" + Values.ppr(elem.toString()) + + "\n is element of:\n" + Values.ppr(this.inVal.toString()) + + "\nand satisfies the predicate " + this.pred); + } + return false; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final boolean isFinite() { + try { + if (!(this.inVal.isFinite())) { + Assert.fail("Attempted to check if expression of form {x \\in S : p(x)} is a " + + "finite set, but cannot check if S:\n" + Values.ppr(this.inVal.toString()) + + "\nis finite."); + } + return true; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final Value takeExcept(ValueExcept ex) { + try { + if (ex.idx < ex.path.length) { + Assert.fail("Attempted to apply EXCEPT to the set " + Values.ppr(this.toString()) + "."); + } + return ex.value; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final Value takeExcept(ValueExcept[] exs) { + try { + if (exs.length != 0) { + Assert.fail("Attempted to apply EXCEPT to the set " + Values.ppr(this.toString()) + "."); + } + return this; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final int size() { + try { + this.inVal = this.toSetEnum(); + this.converted = true; + return this.inVal.size(); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + private final void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException { + this.inVal = (Value )ois.readObject(); + this.converted = true; + } + + private final void writeObject(ObjectOutputStream oos) throws IOException { + if (!this.converted) { + this.inVal = this.toSetEnum(); + this.converted = true; + } + oos.writeObject(this.inVal); + } + + /* This method normalizes (destructively) this set. */ + @Override + public final boolean isNormalized() { + try { + return this.inVal.isNormalized(); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final Value normalize() { + try { + this.inVal.normalize(); + return this; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final void deepNormalize() { + try { + inVal.deepNormalize(); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final boolean isDefined() { return true; } + + @Override + public final IValue deepCopy() { return this; } + + @Override + public final boolean assignable(Value val) { + try { + return this.equals(val); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + /* The fingerprint method */ + @Override + public final long fingerPrint(long fp) { + try { + this.inVal = this.toSetEnum(); + this.converted = true; + return this.inVal.fingerPrint(fp); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final IValue permute(IMVPerm perm) { + try { + this.inVal = this.toSetEnum(); + this.converted = true; + return this.inVal.permute(perm); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public Value toSetEnum() { + if (this.converted) { + return (SetEnumValue) this.inVal; + } + ValueVec vals = new ValueVec(); + ValueEnumeration Enum = this.elements(); + Value elem; + while ((elem = Enum.nextElement()) != null) { + vals.addElement(elem); + } + if (coverage) {cm.incSecondary(vals.size());} + return new SetEnumValue(vals, this.isNormalized(), cm); + } + + @Override + public void write(final IValueOutputStream vos) throws IOException { + inVal.write(vos); + } + + /* The string representation of the value. */ + @Override + public final StringBuffer toString(StringBuffer sb, int offset, boolean swallow) { + try { + try { + if (TLCGlobals.expand) { + Value val = this.toSetEnum(); + return val.toString(sb, offset, swallow); + } + } + catch (Throwable e) { if (!swallow) throw e; } + + sb.append("{"); + if (this.vars instanceof FormalParamNode) { + sb.append(((FormalParamNode)this.vars).getName()); + } + else { + FormalParamNode[] ids = (FormalParamNode[])this.vars; + if (ids.length != 0) sb.append(ids[0].getName()); + for (int i = 1; i < ids.length; i++) { + sb.append(", " + ids[i].getName()); + } + } + sb.append(" \\in " + this.inVal + " : <expression "); + sb.append(this.pred + "> }"); + return sb; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final ValueEnumeration elements() { + try { + if (this.converted) { + return ((SetEnumValue)this.inVal).elements(); + } + return new Enumerator(); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + final class Enumerator implements ValueEnumeration { + ValueEnumeration Enum; + + public Enumerator() { + if (!(inVal instanceof Enumerable)) { + Assert.fail("Attempted to enumerate { x \\in S : p(x) } when S:\n" + + Values.ppr(inVal.toString()) + "\nis not enumerable"); + } + this.Enum = ((Enumerable)inVal).elements(); + } + + @Override + public final void reset() { this.Enum.reset(); } + + @Override + public final Value nextElement() { + Value elem; + while ((elem = this.Enum.nextElement()) != null) { + if (coverage) { cm.incSecondary(); } + Context con1 = con; + if (vars instanceof FormalParamNode) { + con1 = con1.cons((FormalParamNode)vars, elem); + } + else { + FormalParamNode[] ids = (FormalParamNode[])vars; + TupleValue tv = (TupleValue) elem.toTuple(); + if ((tv != null) && + (((TupleValue)tv).elems.length == ids.length)) { + Value [] vals = ((TupleValue)tv).elems; + for (int i = 0; i < ids.length; i++) { + con1 = con1.cons(ids[i], vals[i]); + } + } + else { + Assert.fail("Attempted to check if the value:\n" + Values.ppr(elem.toString()) + + "\nis an element of a set of " + ids.length + "-tuples."); + } + } + Value res = (Value) tool.eval(pred, con1, state, pstate, control, cm); + if (!(res instanceof IBoolValue)) { + Assert.fail("Evaluating predicate " + pred + " yielded non-Boolean value."); + } + if (((BoolValue)res).val) return elem; + } + return null; + } + + } + +} diff --git a/tlatools/src/tlc2/value/impl/StringValue.java b/tlatools/src/tlc2/value/impl/StringValue.java new file mode 100644 index 0000000000000000000000000000000000000000..5e3e203210b58749e2dcdd9d2466cdb83c255590 --- /dev/null +++ b/tlatools/src/tlc2/value/impl/StringValue.java @@ -0,0 +1,304 @@ +// Copyright (c) 2003 Compaq Corporation. All rights reserved. +// Portions Copyright (c) 2003 Microsoft Corporation. All rights reserved. +// Last modified on Wed 12 Jul 2017 at 16:10:00 PST by ian morris nieves +// modified on Sat 23 February 2008 at 10:19:41 PST by lamport +// modified on Fri Aug 10 15:06:37 PDT 2001 by yuanyu + +package tlc2.value.impl; + +import java.io.IOException; +import java.util.Map; + +import tlc2.tool.FingerprintException; +import tlc2.tool.coverage.CostModel; +import tlc2.util.FP64; +import tlc2.value.IMVPerm; +import tlc2.value.IValue; +import tlc2.value.IValueInputStream; +import tlc2.value.IValueOutputStream; +import tlc2.value.Values; +import util.Assert; +import util.UniqueString; + +public class StringValue extends Value { + public final UniqueString val; + + /* Constructor */ + public StringValue(String str) { + // SZ 11.04.2009: changed the access method to equivalent + this.val = UniqueString.uniqueStringOf(str); + } + + public StringValue(UniqueString var) { + this.val = var; + } + + public StringValue(UniqueString var, CostModel cm) { + this(var); + this.cm = cm; + } + + @Override + public final byte getKind() { return STRINGVALUE; } + + public final UniqueString getVal() { return this.val; } + + @Override + public final int compareTo(Object obj) { + try { + if (obj instanceof StringValue) { + return this.val.compareTo(((StringValue)obj).val); + } + if (!(obj instanceof ModelValue)) { + Assert.fail("Attempted to compare string " + Values.ppr(this.toString()) + + " with non-string:\n" + Values.ppr(obj.toString())); + } + return 1; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + public final boolean equals(Object obj) { + try { + if (obj instanceof StringValue) { + return this.val.equals(((StringValue)obj).getVal()); + } + if (!(obj instanceof ModelValue)) { + Assert.fail("Attempted to check equality of string " + Values.ppr(this.toString()) + + " with non-string:\n" + Values.ppr(obj.toString())); + } + return ((ModelValue) obj).modelValueEquals(this) ; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final boolean member(Value elem) { + try { + Assert.fail("Attempted to check if the value:\n" + Values.ppr(elem.toString()) + + "\nis an element of the string " + Values.ppr(this.toString())); + return false; // make compiler happy + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final boolean isFinite() { + try { + Assert.fail("Attempted to check if the string " + Values.ppr(this.toString()) + + " is a finite set."); + return false; // make compiler happy + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final Value takeExcept(ValueExcept ex) { + try { + if (ex.idx < ex.path.length) { + Assert.fail("Attempted to apply EXCEPT construct to the string " + + Values.ppr(this.toString()) + "."); + } + return ex.value; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final Value takeExcept(ValueExcept[] exs) { + try { + if (exs.length != 0) { + Assert.fail("Attempted to apply EXCEPT construct to the string " + + Values.ppr(this.toString()) + "."); + } + return this; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final int size() { + try { + Assert.fail("Attempted to compute the number of elements in the string " + + Values.ppr(this.toString()) + "."); + return 0; // make compiler happy + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public boolean mutates() { + // finalized after construction. + return true; + } + + @Override + public final Value toTuple() { + final String s = val.toString(); + Value[] vals = new Value[s.length()]; + for (int i = 0; i < s.length(); i++) { + vals[i] = new StringValue(Character.toString(s.charAt(i))); + } + return new TupleValue(vals); + } + + @Override + public final boolean isNormalized() { return true; } + + @Override + public final Value normalize() { /*SKIP*/return this; } + + @Override + public final boolean isDefined() { return true; } + + @Override + public final IValue deepCopy() { return this; } + + @Override + public final boolean assignable(Value val) { + try { + return ((val instanceof StringValue) && + this.equals(val)); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + public final int length() { + try { + return this.val.length(); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public void write(IValueOutputStream vos) throws IOException { + final int index = vos.put(this); + if (index == -1) { + vos.writeByte(STRINGVALUE); + val.write(vos.getOutputStream()); + } else { + vos.writeByte(DUMMYVALUE); + vos.writeNat(index); + } + } + + /* The fingerprint method */ + @Override + public final long fingerPrint(long fp) { + try { + fp = FP64.Extend(fp, STRINGVALUE) ; + fp = FP64.Extend(fp, this.val.length()) ; + fp = FP64.Extend(fp, this.val.toString()); + return fp; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final IValue permute(IMVPerm perm) { return this; } + + /************************************************************************* + * toString() modified 23 Aug 2007 by LL to call PrintVersion so strings * + * with special characters are printed properly. * + *************************************************************************/ + final String PrintVersion(String str) { + try { + StringBuffer buf = new StringBuffer(str.length()) ; + for (int i = 0 ; i < str.length() ; i++) { + switch (str.charAt(i)) { + case '\"' : + buf.append("\\\"") ; + break ; + case '\\' : + buf.append("\\\\") ; + break ; + case '\t' : + buf.append("\\t") ; + break ; + case '\n' : + buf.append("\\n") ; + break ; + case '\f' : + buf.append("\\f") ; + break ; + case '\r' : + buf.append("\\r") ; + break ; + default : + buf.append(str.charAt(i)) ; + break ; + } // switch + }// for + return buf.toString(); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + + /* The string representation of the value. */ + @Override + public StringBuffer toString(StringBuffer sb, int offset, boolean swallow) { + try { + return sb.append("\"" + PrintVersion(this.val.toString()) + "\""); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + /* Same as toString. */ + @Override + public final String toUnquotedString() { + return PrintVersion(this.val.toString()); + } + + public static IValue createFrom(final IValueInputStream vos) throws IOException { + final UniqueString str = UniqueString.read(vos.getInputStream()); + final IValue res = new StringValue(str); + final int index = vos.getIndex(); + vos.assign(res, index); + return res; + } + + public static IValue createFrom(final IValueInputStream vos, final Map<String, UniqueString> tbl) throws IOException { + final UniqueString str = UniqueString.read(vos.getInputStream(), tbl); + final IValue res = new StringValue(str); + final int index = vos.getIndex(); + vos.assign(res, index); + return res; + } +} diff --git a/tlatools/src/tlc2/value/impl/SubsetValue.java b/tlatools/src/tlc2/value/impl/SubsetValue.java new file mode 100644 index 0000000000000000000000000000000000000000..c3e3d1776a155673ec5feffbca08468520903666 --- /dev/null +++ b/tlatools/src/tlc2/value/impl/SubsetValue.java @@ -0,0 +1,869 @@ +// Copyright (c) 2003 Compaq Corporation. All rights reserved. +// Portions Copyright (c) 2003 Microsoft Corporation. All rights reserved. +// Last modified on Wed 12 Jul 2017 at 16:10:00 PST by ian morris nieves +// modified on Mon 30 Apr 2007 at 13:46:07 PST by lamport +// modified on Fri Aug 10 15:10:16 PDT 2001 by yuanyu + +package tlc2.value.impl; + +import java.io.IOException; +import java.math.BigDecimal; +import java.math.BigInteger; +import java.util.BitSet; +import java.util.Collection; +import java.util.HashSet; +import java.util.Random; +import java.util.TreeMap; + +import tlc2.TLCGlobals; +import tlc2.output.EC; +import tlc2.tool.FingerprintException; +import tlc2.tool.coverage.CostModel; +import tlc2.util.Combinatorics; +import tlc2.value.IMVPerm; +import tlc2.value.IValue; +import tlc2.value.IValueOutputStream; +import tlc2.value.RandomEnumerableValues; +import tlc2.value.Values; +import util.Assert; + +public class SubsetValue extends EnumerableValue implements Enumerable { + public Value set; // SUBSET set + protected SetEnumValue pset; + + /* Constructor */ + public SubsetValue(Value set) { + this.set = set; + this.pset = null; + } + + public SubsetValue(Value set, CostModel cm) { + this(set); + this.cm = cm; + } + + @Override + public final byte getKind() { return SUBSETVALUE; } + + @Override + public final int compareTo(Object obj) { + try { + if (obj instanceof SubsetValue) { + return this.set.compareTo(((SubsetValue)obj).set); + } + this.convertAndCache(); + return this.pset.compareTo(obj); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + public final boolean equals(Object obj) { + try { + if (obj instanceof SubsetValue) { + return this.set.equals(((SubsetValue)obj).set); + } + this.convertAndCache(); + return this.pset.equals(obj); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final boolean member(Value val) { + try { + if (val instanceof Enumerable) { + ValueEnumeration Enum = ((Enumerable)val).elements(); + Value elem; + while ((elem = Enum.nextElement()) != null) { + if (!this.set.member(elem)) { + return false; + } + } + } + else { + Assert.fail("Attempted to check if the non-enumerable value\n" + + Values.ppr(val.toString()) + "\nis element of\n" + Values.ppr(this.toString())); + } + return true; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public Value isSubsetEq(Value other) { + try { + // Reduce (SUBSET A \subseteq SUBSET B) to (A \subseteq B) to avoid + // exponential blowup inherent in generating the power set. + if (other instanceof SubsetValue && this.set instanceof Enumerable) { + final SubsetValue sv = (SubsetValue) other; + return ((Enumerable) this.set).isSubsetEq(sv.set); + } + return super.isSubsetEq(other); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final boolean isFinite() { + try { + return this.set.isFinite(); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final Value takeExcept(ValueExcept ex) { + try { + if (ex.idx < ex.path.length) { + Assert.fail("Attempted to apply EXCEPT to the set " + Values.ppr(this.toString()) + "."); + } + return ex.value; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final Value takeExcept(ValueExcept[] exs) { + try { + if (exs.length != 0) { + Assert.fail("Attempted to apply EXCEPT to the set " + Values.ppr(this.toString()) + "."); + } + return this; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final int size() { + try { + int sz = this.set.size(); + if (sz >= 31) { + Assert.fail(EC.TLC_MODULE_OVERFLOW, "the number of elements in:\n" + + Values.ppr(this.toString())); + } + return (1 << sz); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final boolean isNormalized() { + try { + return (this.pset != null && + this.pset != SetEnumValue.DummyEnum && + this.pset.isNormalized()); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final Value normalize() { + try { + if (this.pset == null || this.pset == SetEnumValue.DummyEnum) { + this.set.normalize(); + } + else { + this.pset.normalize(); + } + return this; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final void deepNormalize() { + try { + set.deepNormalize(); + if (pset == null) { + pset = SetEnumValue.DummyEnum; + } + else if (pset != SetEnumValue.DummyEnum) { + pset.deepNormalize(); + } + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final boolean isDefined() { + try { + return this.set.isDefined(); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final IValue deepCopy() { return this; } + + @Override + public final boolean assignable(Value val) { + try { + return this.equals(val); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final void write(final IValueOutputStream vos) throws IOException { + pset.write(vos); + } + + /* The fingerprint */ + @Override + public final long fingerPrint(long fp) { + try { + this.convertAndCache(); + return this.pset.fingerPrint(fp); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final IValue permute(IMVPerm perm) { + try { + this.convertAndCache(); + return this.pset.permute(perm); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + private final void convertAndCache() { + if (this.pset == null) { + this.pset = (SetEnumValue) this.toSetEnum(); + } + else if (this.pset == SetEnumValue.DummyEnum) { + SetEnumValue val = null; + synchronized(this) { + if (this.pset == SetEnumValue.DummyEnum) { + val = (SetEnumValue) this.toSetEnum(); + val.deepNormalize(); + } + } + synchronized(this) { + if (this.pset == SetEnumValue.DummyEnum) { this.pset = val; } + } + } + } + + @Override + public final Value toSetEnum() { + if (this.pset != null && this.pset != SetEnumValue.DummyEnum) { + return this.pset; + } + ValueVec vals = new ValueVec(this.size()); + ValueEnumeration Enum = this.elements(); + Value elem; + while ((elem = Enum.nextElement()) != null) { + vals.addElement(elem); + } + // For as long as pset.elements() (SubsetValue#elements) + // internally calls SubsetValue#elementsNormalized, the + // result SetEnumValue here is indeed normalized. + if (coverage) {cm.incSecondary(vals.size());} + return new SetEnumValue(vals, true, cm); + } + + /* The string representation */ + @Override + public final StringBuffer toString(StringBuffer sb, int offset, boolean swallow) { + try { + boolean unlazy = TLCGlobals.expand; + try { + if (unlazy) { + unlazy = this.set.size() < 7; + } + } + catch (Throwable e) { if (swallow) unlazy = false; else throw e; } + + if (unlazy) { + Value val = this.toSetEnum(); + return val.toString(sb, offset, swallow); + } + else { + sb = sb.append("SUBSET "); + sb = this.set.toString(sb, offset, swallow); + return sb; + } + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + public Unrank getUnrank(final int kSubset) { + // Convert outer set only once. + final SetEnumValue convert = (SetEnumValue) set.toSetEnum(); + convert.normalize(); + final ValueVec elems = convert.elems; + + return new Unrank(kSubset, Combinatorics.bigSumChoose(elems.size(), kSubset).longValueExact(), + Combinatorics.pascalTableUpTo(elems.size(), kSubset), elems, kSubset); + } + + public Enumerable getRandomSetOfSubsets(final int numOfSubsetsRequested, final int maxLengthOfSubsets) { + // Convert outer set only once. + final SetEnumValue convert = (SetEnumValue) set.toSetEnum(); + convert.normalize(); + final ValueVec elems = convert.elems; + final int size = elems.size(); + + // Calculate the sums of the rows of the Pascal Triangle up to + // maxLengthOfSubsets. + final long[] kss = new long[maxLengthOfSubsets + 1]; + kss[0] = 1L; + // Sum the elems.size()'s row of the Pascal Triangle up to maxLengthOfSubsets. + // This corresponds to Combinatorics.bigSumChoose except that we also keep the + // intermediate results. + BigInteger sum = BigInteger.ONE; // 1 for k=0 + for (int i = 1; i <= maxLengthOfSubsets; i++) { + kss[i] = Combinatorics.bigChoose(size, i).longValueExact(); + sum = sum.add(BigInteger.valueOf(kss[i])); + } + assert sum.equals(Combinatorics.bigSumChoose(size, maxLengthOfSubsets)); + + // Extend existing Pascal Triangle by a table for the k's 2..maxLengthOfSubset + // for all n up to |S| (if needed, otherwise long[0]). + final long[] ppt = Combinatorics.pascalTableUpTo(size, maxLengthOfSubsets); + + final ValueVec vec = new ValueVec(numOfSubsetsRequested); + for (int rank = 0; rank < kss.length; rank++) { + final BigDecimal divide = BigDecimal.valueOf(kss[rank]).divide(new BigDecimal(sum), 32, + BigDecimal.ROUND_HALF_DOWN); + // Small bias towards smaller/shorter k-Subsets because 0 gets rounded up to 1. + final long n = divide.multiply(BigDecimal.valueOf(numOfSubsetsRequested)).max(BigDecimal.ONE).toBigInteger() + .longValueExact(); + + // The last one (kSubsetSizes.length - 1) is generates the outstanding + // number of subsets (will be close to its calculated n anyway). + final RandomUnrank unrank = new RandomUnrank(rank, + rank == kss.length - 1 ? numOfSubsetsRequested - vec.size() : n, ppt, elems, maxLengthOfSubsets, + RandomEnumerableValues.get()); + + Value subset; + while ((subset = unrank.randomSubset()) != null && vec.size() < numOfSubsetsRequested) { + vec.addElement(subset); + } + } + assert vec.size() == numOfSubsetsRequested; + return new SetEnumValue(vec, false, cm); + } + + public class Unrank { + + private final TreeMap<Long, Integer> sums = new TreeMap<>(); + private final long[] partialPascalTable; + private final int maxK; + + private final ValueVec elems; + + private final int k; // rank of k-Subset + + public Unrank(final int k, final long n, final long[] ppt, final ValueVec elems, final int maxK) { + this.k = k; + this.elems = elems; + this.partialPascalTable = ppt; + this.maxK = maxK - 1; + + // Cache the sum of all binomials lt n for 0..elems.size choose k. + int choice = Math.max(k - 1, 0); + sums.put(-1L, choice); // As base for idx = 0 (see lowerEntry below); + long bin = 0L; + while ((bin = memoizedBinomial(choice, k)) < n) { + sums.put(bin, ++choice); + } + } + + public Value subsetAt(long idx) { + // More subsets in this kSubset available. + final ValueVec vec = new ValueVec(k); + + int y = k, choice = sums.lowerEntry(idx).getValue(); + for (; choice >= 0 && k > 0; choice--) { + final long c = memoizedBinomial(choice, y); + if (c <= idx) { + idx -= c; + y--; + vec.addElement(this.elems.elementAt(choice)); + } + } + return new SetEnumValue(vec, false, cm); + } + + protected long memoizedBinomial(final int n, final int k) { + if (k == 0 || k == n) { + return (long) 1; + } else if (k == 1 || k == n - 1) { + return (long) n; + } else if (n == 0 || k > n) { + // Cannot choose from zero elements or more elements than present. + return 0; + } + final int pti = Combinatorics.choosePairToInt(n, k); + if (pti < Combinatorics.CHOOSETABLE.length) { + return Combinatorics.choose(n, k); + } + return partialPascalTable[(n - Combinatorics.MAXCHOOSENUM - 1) * maxK + k - 2]; + } + } + + private class RandomUnrank extends Unrank { + + // Primes taken from: https://primes.utm.edu/lists/2small/0bit.html + // TODO: 9223372036854775783L; // 2^63 - 25 +// private static final long x = 549755813881L; // 2^39 - 7 + private static final long x = 34359738337L; // 2^35 - 31 + + private final long n; + private final long a; + private long i; + + public RandomUnrank(final int k, final long n, final long[] ppt, final ValueVec elems, final int maxK, + final Random random) { + super(k, n, ppt, elems, maxK); + this.n = n; + this.a = Math.abs(random.nextLong()) % n; + } + + public Value randomSubset() { + if (i < n) { + return subsetAt(((x * i++) + a) % n); + } + return null; + } + } + + public EnumerableValue getRandomSetOfSubsets(final int numOfPicks, final double probability) { + final CoinTossingSubsetEnumerator enumerator = new CoinTossingSubsetEnumerator(numOfPicks, probability); + + // Using a set here instead of ValueVec preserves the set invariant (no + // duplicates). The alternative - a ValueVec which gets sorted to remove + // duplicates after the while loops is slower. + final int estimated = (int) (numOfPicks * probability); + final Collection<Value > sets = new HashSet<>(estimated); + Value val; + while ((val = enumerator.nextElement()) != null) { + sets.add(val); + } + + return new SetEnumValue(new ValueVec(sets), false, cm); + } + + private final ValueEnumeration emptyEnumeration = new ValueEnumeration() { + private boolean done = false; + + @Override + public void reset() { + done = false; + } + + @Override + public Value nextElement() { + if (done) { return null; } + done = true; + return new SetEnumValue(cm); + } + }; + + /** + * @see file SubsetValue.tla. + * <p> + * In addition, this generates all subsets of this SubsetValue instance which extends + * the order definition given in SubsetValue.tla such that a subset s is considered + * lower than t (s < t) if Cardinality(s) < Cardinality(t) \/ Definition in SubsetValue.tla. + * <p> + * The most noteworthy difference between bElements and + */ + final ValueEnumeration elementsNormalized() { + final int n = set.size(); + if (n == 0) { + emptyEnumeration.reset(); + return emptyEnumeration; + } + // Only normalized inputs will yield a normalized output. Note that SEV#convert + // (unfortunately) enumerates the input. Thus "SUBSET SUBSET 1..10" will result + // in the nested/right SUBSET to be fully enumerated (1..10 obviously too). + final ValueVec elems = ((SetEnumValue) set.toSetEnum().normalize()).elems; + return new ValueEnumeration() { + + private int k = 0; + private final int[] indices = new int[n]; + + @Override + public void reset() { + reset(0); + } + + private void reset(final int k) { + this.k = k; + if (k > n) { + return; + } + for (int i = 0; i < k; i++) { + indices[i] = i; + } + } + + @Override + public Value nextElement() { + if (k > n) { + return null; + } else if (k == 0) { + reset(k + 1); + return new SetEnumValue(cm); + } + + final ValueVec vals = new ValueVec(k); + int i = k - 1; + for (int j = i; j >= 0; j--) { + vals.addElementAt(elems.elementAt(indices[j]), j); + if (indices[j] + k - j == n) { + i = j - 1; + } + } + final SetEnumValue result = new SetEnumValue(vals, true, cm); + + if (indices[0] == n - k) { + // Increment k to generate the set of k-subset for this k. + reset(k + 1); + return result; + } + + // Increment the right element r by one and remember its old value. + indices[i]++; + + // Adjust all the elements right to the right element r. For all indices j > i, + // the element at j is set to p[i] + j. + for (i++; i < k; i++) { + indices[i] = indices[i - 1] + 1; + } + return result; + } + }; + } + + /** + * @see SubsetValue#kElements(int) + */ + public final long numberOfKElements(final int k) { + final int size = this.set.size(); + if (k < 0 || size < k || size > 62) { + throw new IllegalArgumentException(String.format("k=%s and n=%s", k, size)); + } + if (k == 0 || k == size) { + return 1; + } + return Combinatorics.choose(size, k); + } + + /** + * [S]^k (sometimes denoted S^[k]) == { t \in SUBSET S : Cardinality(t) = k } + * @param k + * @return + */ + public final ValueEnumeration kElements(final int k) { + if (k < 0 || this.set.size() < k) { + throw new IllegalArgumentException(); + } + if (k == 0) { + emptyEnumeration.reset(); + return emptyEnumeration; + } + + return new KElementEnumerator(k); + } + + public final class KElementEnumerator implements ValueEnumeration { + private final ValueVec elems; + private final int numKSubsetElems; + private final int k; + + private int index; + private int cnt; + + public KElementEnumerator(final int k) { + this.k = k; + + this.numKSubsetElems = (int) numberOfKElements(k); + if (numKSubsetElems < 0) { + throw new IllegalArgumentException("Subset too large."); + } + + final SetEnumValue convert = (SetEnumValue) set.toSetEnum(); + convert.normalize(); + elems = convert.elems; + + reset(); + } + + @Override + public void reset() { + index = (1 << k) - 1; + cnt = 0; + } + + // see "Compute the lexicographically next bit permutation" at + // http://graphics.stanford.edu/~seander/bithacks.html#NextBitPermutation + private int nextIndex() { + final int oldIdx = this.index; + + final int t = (index | (index - 1)) + 1; + this.index = t | ((((t & -t) / (index & -index)) >> 1) - 1); + + return oldIdx; + } + + @Override + public Value nextElement() { + if (cnt >= numKSubsetElems) { + return null; + } + cnt++; + + int bits = nextIndex(); + final ValueVec vals = new ValueVec(Integer.bitCount(bits)); + for (int i = 0; bits > 0 && i < elems.size(); i++) { + // Treat bits as a bitset and add the element of elem at current + // position i if the LSB of bits happens to be set. + if ((bits & 0x1) > 0) { + vals.addElement(elems.elementAt(i)); + } + // ...right-shift zero-fill bits by one afterwards. + bits = bits >>> 1; + } + return new SetEnumValue(vals, false, cm); + } + + public KElementEnumerator sort() { + this.elems.sort(true); + return this; + } + } + + @Override + public ValueEnumeration elements(final Ordering ordering) { + // Use elementsNormalized regardless of requested ordering. Even for ordering + // UNDEFINED, elementsNormalized is fastest. + return elements(); + } + + @Override + public final ValueEnumeration elements() { + try { + if (this.pset == null || this.pset == SetEnumValue.DummyEnum) { + // See note on SetEnumValue#convert for SubsetValue wrt + // the normalized SetEnumValue result. + return elementsNormalized(); + } + return this.pset.elements(); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + final ValueEnumeration elementsLexicographic() { + return new Enumerator(); + } + + final class Enumerator implements ValueEnumeration { + private ValueVec elems; + private BitSet descriptor; + + public Enumerator() { + //WARNING! Mutates the outer instance!? + set = set.toSetEnum(); + set.normalize(); + this.elems = ((SetEnumValue)set).elems; + this.descriptor = new BitSet(this.elems.size()); + } + + @Override + public final void reset() { + this.descriptor = new BitSet(this.elems.size()); + } + + @Override + public final Value nextElement() { + if (this.descriptor == null) + return null; + ValueVec vals; + int sz = this.elems.size(); + if (sz == 0) { + vals = new ValueVec(0); + this.descriptor = null; + } else { + vals = new ValueVec(this.descriptor.cardinality()); + for (int i = 0; i < sz; i++) { + if (this.descriptor.get(i)) { + vals.addElement(this.elems.elementAt(i)); + } + } + for (int i = 0; i < sz; i++) { + if (this.descriptor.get(i)) { + this.descriptor.clear(i); + if (i >= sz - 1) { + this.descriptor = null; + break; + } + } else { + this.descriptor.set(i); + break; + } + } + } + if (coverage) { cm.incSecondary(vals.size()); } + return new SetEnumValue(vals, true, cm); + } + + } + + @Override + public ValueEnumeration elements(final int k) { + final int sz = this.set.size(); + + // Use probabilistic CTSE if size of input set or k are too large. CoinTossing + // can yield duplicates though, thus k means number of picks. + if (sz >= 31 || k > (1 << 16)) { + return new CoinTossingSubsetEnumerator(k); + } + return new SubsetEnumerator(k); + } + + class SubsetEnumerator extends EnumerableValue.SubsetEnumerator { + + private final ValueVec elems; + + SubsetEnumerator(final int k) { + super(k, 1 << set.size()); + final SetEnumValue convert = (SetEnumValue) set.toSetEnum(); + convert.normalize(); + this.elems = convert.elems; + } + + @Override + public Value nextElement() { + if (!hasNext()) { + return null; + } + int bits = nextIndex(); + final ValueVec vals = new ValueVec(Integer.bitCount(bits)); + for (int i = 0; bits > 0 && i < this.elems.size(); i++) { + // Treat bits as a bitset and add the element of this.elem at current + // position i if the LSB of bits happens to be set. + if ((bits & 0x1) > 0) { + vals.addElement(this.elems.elementAt(i)); + } + // ...right-shift zero-fill bits by one afterwards. + bits = bits >>> 1; + } + return new SetEnumValue(vals, false, cm); + } + } + + /* + * LL: I realized that efficiently choosing a random set of k elements in "SUBSET S" + * is simple. Just compute S and randomly choose k elements SS of SUBSET S by + * including each element of S in SS with probability 1/2. This looks to me as + * if it's completely equivalent to enumerating all the elements of SUBSET S and + * choosing a random subset of those elements--except that if we want to choose + * exactly k elements, then we'll have to throw away duplicates. + */ + class CoinTossingSubsetEnumerator implements ValueEnumeration { + + private final ValueVec elems; + private final double probability; + private final int numOfPicks; + private int i; + + public CoinTossingSubsetEnumerator(final int numOfPicks) { + this(numOfPicks, .5d); + } + + public CoinTossingSubsetEnumerator(final int numOfPicks, final double probability) { + this.i = 0; + this.numOfPicks = numOfPicks; + this.probability = probability; + + final SetEnumValue convert = (SetEnumValue) set.toSetEnum(); + convert.normalize(); + this.elems = convert.elems; + } + + // Repeated invocation can yield duplicate elements due to the probabilistic + // nature of CoinTossingSubsetEnumerator. + @Override + public Value nextElement() { + if (!hasNext()) { + return null; + } + final ValueVec vals = new ValueVec(elems.size()); + for (int i = 0; i < elems.size(); i++) { + if (RandomEnumerableValues.get().nextDouble() < probability) { + vals.addElement(elems.elementAt(i)); + } + } + this.i++; + return new SetEnumValue(vals, false, cm); + } + + private boolean hasNext() { + return this.i < this.numOfPicks; + } + + @Override + public void reset() { + this.i = 0; + } + + int getNumOfPicks() { + return numOfPicks; + } + } +} diff --git a/tlatools/src/tlc2/value/impl/SubsetValue.tla b/tlatools/src/tlc2/value/impl/SubsetValue.tla new file mode 100644 index 0000000000000000000000000000000000000000..d87d33e83d9e7f80782c17354762326bd719635b --- /dev/null +++ b/tlatools/src/tlc2/value/impl/SubsetValue.tla @@ -0,0 +1,124 @@ +-------------------------- MODULE SubsetValue -------------------------- +EXTENDS Sequences, FiniteSets, Integers + +(* + ASSUME Len(u) = Len(v) + + There exists a prefix p of u with length 0..Len(u)-1 s.t. + /\ \A i \in 1..p: u[i] <= v[i] + /\ u[p+1] < v[p+1] + In other words, if u < v then there exists a prefix of u of length 0..(Len(u)-1) + s.t. for all elements in the prefix of u are lower than the corresponding elements + in the prefix of v. +*) +Sorted(u, v) == \E p \in 0..(Len(u) - 1) : /\ \A q \in 1..p : u[q] <= v[q] + /\ (u[p+1] + 1) = v[p+1] + +----------------------------------------------------------------------------- + +CONSTANT N, + K + +ASSUME /\ \A nn \in N: nn \in Nat + /\ \A kk \in K: kk \in (Nat \ {0}) + \* n >= k \* Via a state constraint, to not explicitly handle this case in the + \* algorithm. It makes no sense to draw subsets of cardinality k + \* out of n elements if k > n. + +(* See Kunth's Volume 4 Fascicle 3, Generating All Combinations and Partitions (2005), + vi+150pp. ISBN 0-201-85394-9, http://www-cs-faculty.stanford.edu/~knuth/taocp.html + (also http://www.cs.utsa.edu/~wagner/knuth/). + "Efficiently Enumerating the Subsets of a Set" 2000 by Loughry, van Hemmert, and Schoofs + found at http://www.applied-math.org/subset.pdf describe the same idea from a different + perspective (providing a recursive algorithm). + + The purpuse of this algorithm is to generate the k-Subsets + { t \in SUBSET S : Cardinality(t) = k } out of the input set 0..(n-1) in a predefined + order. Even though sets have no notion of order, the technical representation of a set + in TLC has an order. This is e.g. important to efficiently determine equality of two + or more sets. If all sets adhere to a predefined order, equality can be determined + in O(n) time by a single pass over both sets. + Let u and v be two k-Subsets for a given k. The (total) order defined by this algorithm + is such that there exists a prefix p of u with length 0..Len(u)-1 s.t. + \A i \in 1..p: u[i] <= v[i] /\ u[p+1] < v[p+1] (see Sorted operator above). +--algorithm EnumerateSubsets { + variables n \in N; k \in K; + (* Represent sets as sequences because we are interested in order. *) + s = <<>>; r = <<>>; + i = 0; j = 0; + (* A strictly monotonically increasing sequence from 0 to k-1 used to + maintain the state of the algorithm. + Let k = 4 and n = 5. Initially, idx is [0,1,2,3] which corresponds to the + first k-Subset with the first, second, third, fourth element of the + original input set (which does not appear in this spec because it is + irrelevant for the actual algorithm). Next idx will change to [0,1,2,4], + followed by [0,1,2,5], [0,1,3,4], [0,1,3,5], ... *) + idx = [ii \in 0..(k - 1) |-> ii ]; + { + (* The while below first generates the current k-subset determined by the state + of idx and then mutates the state of idx to be ready to generate the next + k-subset in the subsequent iteration. *) + start: + while (TRUE) { + j := k - 1; i := k - 1; s := <<>>; + + (* The l1 loop serves two purposes: + 1) Generate the next k-Subset (depending on the state of idx) + by doing a full pass over idx from end to front. + 2) As a byproduct, determine the next index i of idx which + has to be incremented. + *) + l1: + while (j >= 0) { + \* 2) Determine next i. + if (idx[j] + k - j = n) { + i := j - 1; + }; + \* 1) Prepend idx[j] to seq s. + s := <<idx[j]>> \o s; j := j - 1; + }; + \* Add s to the set r of k-Subsets. + r := Append(r, s); + + l2: + if (idx[0] = n - k) { + goto Done; + } else { + (* Increment the i-th idx (by one) and for all higher indices + h of i set idx[h] to idx[i] + (h - i). *) + idx[i] := idx[i] + 1; i := i + 1; + + l3: + while (i < k) { + idx[i] := idx[i - 1] + 1; i := i + 1; + }; + }; + }; + } +} +*) +\* BEGIN TRANSLATION +\* omitted +\* END TRANSLATION + +----------------------------------------------------------------------------- +\* Invariant and helpers: + +SeqOfSeqsToSetOfSets(seq) == LET (* The image of function f with Op applied to each element f[x]. *) + ImageApply(f, Op(_)) == { Op(f[x]) : x \in DOMAIN f } + Image(f) == ImageApply(f, LAMBDA x : x) \* Lambda is just identy + IN ImageApply(seq, Image) + +(* The set S of all k-subsets ks (Cardinality(ks) = k) for the range 0 to n - 1. + In other words, this is the expected set of subsets to be generated by the + EnumerateSubsets above. *) +Expected == {ss \in SUBSET (0..(n-1)) : Cardinality(ss) = k } + +Inv == (pc = "Done") => \* When terminated... + \* ... the correct subsets have been generated... + /\ Expected = SeqOfSeqsToSetOfSets(r) + \* ... in the predefined order. + /\ \A ii \in 1..Len(r) - 1: Sorted(r[ii], r[ii+1]) + +============================================================================= + diff --git a/tlatools/src/tlc2/value/impl/TupleValue.java b/tlatools/src/tlc2/value/impl/TupleValue.java new file mode 100644 index 0000000000000000000000000000000000000000..d0020e6098ce1d91712967e84a13971c7d931242 --- /dev/null +++ b/tlatools/src/tlc2/value/impl/TupleValue.java @@ -0,0 +1,419 @@ +// Copyright (c) 2003 Compaq Corporation. All rights reserved. +// Portions Copyright (c) 2003 Microsoft Corporation. All rights reserved. +// Last modified on Wed 12 Jul 2017 at 16:10:00 PST by ian morris nieves +// modified on Mon 30 Apr 2007 at 15:30:09 PST by lamport +// modified on Fri Aug 10 15:10:22 PDT 2001 by yuanyu + +package tlc2.value.impl; + +import java.io.IOException; +import java.util.Map; + +import tlc2.output.EC; +import tlc2.output.MP; +import tlc2.tool.EvalControl; +import tlc2.tool.FingerprintException; +import tlc2.tool.coverage.CostModel; +import tlc2.util.FP64; +import tlc2.value.IMVPerm; +import tlc2.value.ITupleValue; +import tlc2.value.IValue; +import tlc2.value.IValueInputStream; +import tlc2.value.IValueOutputStream; +import tlc2.value.ValueInputStream; +import tlc2.value.Values; +import util.Assert; +import util.UniqueString; + +public class TupleValue extends Value implements Applicable, ITupleValue { + public final Value[] elems; // the elements of this tuple. + public static final TupleValue EmptyTuple = new TupleValue(new Value[0]); + + /* Constructor */ + public TupleValue(Value[] elems) { this.elems = elems; } + + public TupleValue(Value v) { + this(new Value[1]); + this.elems[0] = v; + } + + public TupleValue(Value v1, Value v2) { + this(new Value[2]); + this.elems[0] = v1; + this.elems[1] = v2; + } + + public TupleValue(Value[] elems, CostModel cm) { + this(elems); + this.cm = cm; + } + + @Override + public IValue getElem(int idx) { + return elems[idx]; + } + + @Override + public IValue[] getElems() { + return elems; + } + + @Override + public final byte getKind() { return TUPLEVALUE; } + + @Override + public final int compareTo(Object obj) { + try { + TupleValue tv = obj instanceof Value ? (TupleValue) ((Value)obj).toTuple() : null; + if (tv == null) { + // Well, we have to convert this to function and compare. + return this.toFcnRcd().compareTo(obj); + } + int len = this.elems.length; + int cmp = len - tv.elems.length; + if (cmp == 0) { + for (int i = 0; i < len; i++) { + cmp = this.elems[i].compareTo(tv.elems[i]); + if (cmp != 0) break; + } + } + return cmp; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + public final boolean equals(Object obj) { + try { + TupleValue tv = obj instanceof Value ? (TupleValue) ((Value)obj).toTuple() : null; + if (tv == null) { + // Well, we have to convert this to function and compare. + return this.toFcnRcd().equals(obj); + } + int len = this.elems.length; + if (len != tv.elems.length) + return false; + for (int i = 0; i < len; i++) { + if (!this.elems[i].equals(tv.elems[i])) + return false; + } + return true; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final boolean member(Value elem) { + try { + Assert.fail("Attempted to check set membership in a tuple value."); + return false; // make compiler happy + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final boolean isFinite() { return true; } + + @Override + public final Value apply(Value arg, int control) { + try { + if (!(arg instanceof IntValue)) { + Assert.fail("Attempted to apply tuple to a non-integer argument."); + } + int idx = ((IntValue)arg).val; + if (idx <= 0 || idx > this.elems.length) { + Assert.fail("Attempted to apply tuple\n" + Values.ppr(this.toString()) + + "\nto integer " + idx + " which is out of domain."); + } + return (Value) this.elems[idx-1]; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final Value apply(Value[] args, int control) { + try { + if (args.length != 1) { + Assert.fail("Attetmpted to apply tuple with wrong number of arguments."); + } + return this.apply(args[0], EvalControl.Clear); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final Value select(Value arg) { + try { + if (!(arg instanceof IntValue)) { + Assert.fail("Attempted to apply tuple to a non-integer argument " + + Values.ppr(arg.toString()) + "."); + } + int idx = ((IntValue)arg).val; + if (idx > 0 && idx <= this.elems.length) { + return (Value) this.elems[idx-1]; + } + return null; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final Value takeExcept(ValueExcept ex) { + try { + if (ex.idx < ex.path.length) { + int tlen = this.elems.length; + Value[] newElems = new Value[tlen]; + Value arcVal = ex.path[ex.idx]; + if (arcVal instanceof IntValue) { + int idx = ((IntValue)arcVal).val - 1; + if (0 <= idx && idx < tlen) { + for (int i = 0; i < tlen; i++) { + newElems[i] = this.elems[i]; + } + ex.idx++; + newElems[idx] = this.elems[idx].takeExcept(ex); + } + return new TupleValue(newElems); + } + MP.printWarning(EC.TLC_WRONG_TUPLE_FIELD_NAME, new String[]{Values.ppr(arcVal.toString())}); + } + return ex.value; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final Value takeExcept(ValueExcept[] exs) { + try { + Value val = this; + for (int i = 0; i < exs.length; i++) { + val = val.takeExcept(exs[i]); + } + return val; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final Value getDomain() { + try { + return new IntervalValue(1, this.size()); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final int size() { return this.elems.length; } + + @Override + public final void deepNormalize() { + try { + for (int i = 0; i < elems.length; i++) { + elems[i].deepNormalize(); + } + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final Value toTuple() { + return this; + } + + @Override + public final Value toRcd() { + return size() == 0 ? RecordValue.EmptyRcd : super.toRcd(); + } + + @Override + public final Value toFcnRcd() { + final IntervalValue intv = new IntervalValue(1, this.elems.length); + if (coverage) {cm.incSecondary(this.elems.length);} + return new FcnRcdValue(intv, this.elems, cm); + } + + /* The normalization of the value. */ + @Override + public final boolean isNormalized() { return true; } + + @Override + public final Value normalize() { /*nop*/return this; } + + @Override + public final boolean isDefined() { + try { + boolean defined = true; + for (int i = 0; i < this.elems.length; i++) { + defined = defined && this.elems[i].isDefined(); + } + return defined; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final IValue deepCopy() { + try { + Value[] vals = new Value[this.elems.length]; + for (int i = 0; i < this.elems.length; i++) { + vals[i] = (Value) this.elems[i].deepCopy(); + } + return new TupleValue(vals); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final boolean assignable(Value val) { + try { + boolean canAssign = ((val instanceof TupleValue) && + (this.elems.length == ((TupleValue)val).elems.length)); + if (!canAssign) return false; + for (int i = 0; i < this.elems.length; i++) { + canAssign = canAssign && this.elems[i].assignable(((TupleValue)val).elems[i]); + } + return canAssign; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final void write(IValueOutputStream vos) throws IOException { + final int index = vos.put(this); + if (index == -1) { + vos.writeByte(TUPLEVALUE); + final int len = elems.length; + vos.writeNat(len); + for (int i = 0; i < len; i++) { + elems[i].write(vos); + } + } else { + vos.writeByte(DUMMYVALUE); + vos.writeNat(index); + } + } + + /* The fingerprint method: tuples are functions. */ + @Override + public final long fingerPrint(long fp) { + try { + int len = this.elems.length; + fp = FP64.Extend(fp, FCNRCDVALUE); + fp = FP64.Extend(fp, len); + for (int i = 0; i < len; i++) { + fp = FP64.Extend(fp, INTVALUE); + fp = FP64.Extend(fp, i+1); + fp = this.elems[i].fingerPrint(fp); + } + return fp; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final IValue permute(IMVPerm perm) { + try { + Value[] vals = new Value[this.elems.length]; + boolean changed = false; + for (int i = 0; i < vals.length; i++) { + vals[i] = (Value) this.elems[i].permute(perm); + changed = changed || (vals[i] != this.elems[i]); + } + if (changed) { + return new TupleValue(vals); + } + return this; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + /* The string representation of this value. */ + @Override + public final StringBuffer toString(StringBuffer sb, int offset, boolean swallow) { + try { + sb.append("<<"); + int len = this.elems.length; + if (len > 0) { + sb = this.elems[0].toString(sb, offset, swallow); + } + for (int i = 1; i < len; i++) { + sb = sb.append(", "); + sb = this.elems[i].toString(sb, offset, swallow); + } + sb.append(">>"); + return sb; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + public static IValue createFrom(final IValueInputStream vos) throws IOException { + final int index = vos.getIndex(); + final int len = vos.readNat(); + final Value[] elems = new Value[len]; + for (int i = 0; i < len; i++) { + elems[i] = (Value) vos.read(); + } + final Value res = new TupleValue(elems); + vos.assign(res, index); + return res; + } + + public static IValue createFrom(final ValueInputStream vos, final Map<String, UniqueString> tbl) throws IOException { + final int index = vos.getIndex(); + final int len = vos.readNat(); + final Value[] elems = new Value[len]; + for (int i = 0; i < len; i++) { + elems[i] = (Value) vos.read(tbl); + } + final Value res = new TupleValue(elems); + vos.assign(res, index); + return res; + } +} diff --git a/tlatools/src/tlc2/value/impl/UndefValue.java b/tlatools/src/tlc2/value/impl/UndefValue.java new file mode 100644 index 0000000000000000000000000000000000000000..81d0062cf44eba330600da1f1d88f3d68b477de5 --- /dev/null +++ b/tlatools/src/tlc2/value/impl/UndefValue.java @@ -0,0 +1,145 @@ +// Copyright (c) 2003 Compaq Corporation. All rights reserved. +// Portions Copyright (c) 2003 Microsoft Corporation. All rights reserved. +// Last modified on Wed 12 Jul 2017 at 16:10:00 PST by ian morris nieves +// modified on Mon 30 Apr 2007 at 13:21:06 PST by lamport +// modified on Tue Aug 15 23:08:23 PDT 2000 by yuanyu + +package tlc2.value.impl; + +import tlc2.tool.FingerprintException; +import tlc2.value.IValue; +import tlc2.value.Values; +import util.Assert; + +public class UndefValue extends Value { + + public static final UndefValue ValUndef = new UndefValue(); + + public UndefValue() { /*SKIP*/ } + + @Override + public byte getKind() { return UNDEFVALUE; } + + @Override + public final int compareTo(Object obj) { + try { + return (obj instanceof UndefValue) ? 0 : 1; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + public final boolean equals(Object obj) { + try { + return (obj instanceof UndefValue); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final boolean member(Value elem) { + try { + Assert.fail("Attempted to check if the value:\n" + Values.ppr(elem.toString()) + + "\nis an element " + Values.ppr(this.toString())); + return false; // make compiler happy + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final boolean isFinite() { + try { + Assert.fail("Attempted to check if the value " + Values.ppr(this.toString()) + + " is a finite set."); + return false; // make compiler happy + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final Value takeExcept(ValueExcept ex) { + try { + if (ex.idx < ex.path.length) { + Assert.fail("Attempted to apply EXCEPT construct to the value " + + Values.ppr(this.toString()) + "."); + } + return ex.value; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final Value takeExcept(ValueExcept[] exs) { + try { + if (exs.length != 0) { + Assert.fail("Attempted to apply EXCEPT construct to the value " + + Values.ppr(this.toString()) + "."); + } + return this; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final int size() { + try { + Assert.fail("Attempted to compute the number of elements in the value " + + Values.ppr(this.toString()) + "."); + return 0; // make compiler happy + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public boolean mutates() { + return false; + } + + @Override + public final boolean isNormalized() { return true; } + + @Override + public final Value normalize() { /*nop*/return this; } + + @Override + public final boolean isDefined() { return false; } + + @Override + public final IValue deepCopy() { return this; } + + @Override + public final boolean assignable(Value val) { return true; } + + /* The string representation. */ + @Override + public final StringBuffer toString(StringBuffer sb, int offset, boolean swallow) { + try { + return sb.append("UNDEF"); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + +} diff --git a/tlatools/src/tlc2/value/impl/UnionValue.java b/tlatools/src/tlc2/value/impl/UnionValue.java new file mode 100644 index 0000000000000000000000000000000000000000..c3d78f9152251f14e582ee05dc026c89b41a9532 --- /dev/null +++ b/tlatools/src/tlc2/value/impl/UnionValue.java @@ -0,0 +1,399 @@ +// Copyright (c) 2003 Compaq Corporation. All rights reserved. +// Portions Copyright (c) 2003 Microsoft Corporation. All rights reserved. +// Last modified on Wed 12 Jul 2017 at 16:10:00 PST by ian morris nieves +// modified on Mon 30 Apr 2007 at 13:46:50 PST by lamport +// modified on Fri Aug 10 15:10:39 PDT 2001 by yuanyu + +package tlc2.value.impl; + +import java.io.IOException; + +import tlc2.TLCGlobals; +import tlc2.tool.FingerprintException; +import tlc2.tool.coverage.CostModel; +import tlc2.value.IMVPerm; +import tlc2.value.IValue; +import tlc2.value.IValueOutputStream; +import tlc2.value.Values; +import util.Assert; + +public class UnionValue extends EnumerableValue implements Enumerable { + public final Value set; + protected SetEnumValue realSet; + + /* Constructor */ + public UnionValue(Value set) { + this.set = set; + this.realSet = null; + } + + public UnionValue(Value val, CostModel cm) { + this(val); + this.cm = cm; + } + + @Override + public byte getKind() { return UNIONVALUE; } + + @Override + public final int compareTo(Object obj) { + try { + this.convertAndCache(); + return this.realSet.compareTo(obj); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + public final boolean equals(Object obj) { + try { + this.convertAndCache(); + return this.realSet.equals(obj); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final boolean member(Value elem) { + try { + if (!(this.set instanceof Enumerable)) { + Assert.fail("Attempted to check if:\n " + Values.ppr(elem.toString()) + + "\nis an element of the non-enumerable set:\n " + + Values.ppr(this.toString())); + } + ValueEnumeration Enum = ((Enumerable)this.set).elements(); + Value val; + while ((val = Enum.nextElement()) != null) { + if (val.member(elem)) return true; + } + return false; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final boolean isFinite() { + try { + if (!(this.set instanceof Enumerable)) { + Assert.fail("Attempted to check if the nonenumerable set:\n" + Values.ppr(this.toString()) + + "\nis a finite set."); + } + ValueEnumeration Enum = ((Enumerable)this.set).elements(); + Value val; + while ((val = Enum.nextElement()) != null) { + if (!val.isFinite()) return false; + } + return true; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final Value takeExcept(ValueExcept ex) { + try { + if (ex.idx < ex.path.length) { + Assert.fail("Attempted to apply EXCEPT to the set:\n" + Values.ppr(this.toString())); + } + return ex.value; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final Value takeExcept(ValueExcept[] exs) { + try { + if (exs.length != 0) { + Assert.fail("Attempted to apply EXCEPT to the set:\n " + Values.ppr(this.toString()) + "."); + } + return this; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final int size() { + try { + this.convertAndCache(); + return this.realSet.size(); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final boolean isNormalized() { + try { + return (this.realSet != null && + this.realSet != SetEnumValue.DummyEnum && + this.realSet.isNormalized()); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final Value normalize() { + try { + if (this.realSet != null && this.realSet != SetEnumValue.DummyEnum) { + this.realSet.normalize(); + } + return this; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final void deepNormalize() { + try { + // MAK 09/17/2019: Added call to this.set.deepNormalize() to align with pattern + // generally found in overwrites of Value#deepNormalize. + // This omission surfaced through a race condition that led to a spurious + // safety violation (https://github.com/tlaplus/tlaplus/issues/361): + // 1) A TLA+ spec defines a (zero-arity) operator s.a. "Foo == UNION { ... }" + // that appears in an invariant. + // 2) SpecProcessor#processConstantDefns eagerly evaluates the operator Foo at startup + // and inserts its Value result UV into the corresponding node of the semantic graph. + // 3) Two workers check if two states violate the invariant which triggers UnionValue#member, + // which internally causes this.set to be normalized. Since Value instances are not thread-safe + // because they are expected to be fully normalized during state space exploration, the + // two workers race to normalize this.set. + // 4) Worker A gets ahead and loops over the elements in UV#member while worker B still normalizes UV. + // Worker A reads inconsistent data and thus reports the invariant to be violated. + // Thanks to Calvin Loncaric for suggesting this fix. + this.set.deepNormalize(); + + if (realSet == null) { + realSet = SetEnumValue.DummyEnum; + } + else if (realSet != SetEnumValue.DummyEnum) { + realSet.deepNormalize(); + } + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final boolean isDefined() { + try { + return this.set.isDefined(); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final IValue deepCopy() { return this; } + + @Override + public final boolean assignable(Value val) { + try { + return this.equals(val); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + public static Value union(Value val) { + boolean canCombine = (val instanceof SetEnumValue); + if (canCombine) { + ValueVec elems = ((SetEnumValue)val).elems; + for (int i = 0; i < elems.size(); i++) { + canCombine = (canCombine && + (elems.elementAt(i) instanceof SetEnumValue)); + } + if (canCombine) { + ValueVec resElems = new ValueVec(); + Value result = new SetEnumValue(resElems, false, val.getCostModel()); + for (int i = 0; i < elems.size(); i++) { + ValueVec elems1 = ((SetEnumValue)elems.elementAt(i)).elems; + for (int j = 0; j < elems1.size(); j++) { + Value elem = elems1.elementAt(j); + if (!result.member(elem)) { + resElems.addElement(elem); + } + } + } + return result; + } + } + return new UnionValue(val, val.getCostModel()); + } + + @Override + public void write(final IValueOutputStream vos) throws IOException { + realSet.write(vos); + } + + /* The fingerprint */ + @Override + public final long fingerPrint(long fp) { + try { + this.convertAndCache(); + return this.realSet.fingerPrint(fp); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final IValue permute(IMVPerm perm) { + try { + this.convertAndCache(); + return this.realSet.permute(perm); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + private final void convertAndCache() { + if (this.realSet == null) { + this.realSet = (SetEnumValue) this.toSetEnum(); + } + else if (this.realSet == SetEnumValue.DummyEnum) { + SetEnumValue val = null; + synchronized(this) { + if (this.realSet == SetEnumValue.DummyEnum) { + val = (SetEnumValue) this.toSetEnum(); + val.deepNormalize(); + } + } + synchronized(this) { + if (this.realSet == SetEnumValue.DummyEnum) { this.realSet = val; } + } + } + } + + @Override + public final Value toSetEnum() { + if (this.realSet != null && this.realSet != SetEnumValue.DummyEnum) { + return this.realSet; + } + ValueVec vals = new ValueVec(); + ValueEnumeration Enum = this.elements(); + Value elem; + while ((elem = Enum.nextElement()) != null) { + vals.addElement(elem); + } + if (coverage) {cm.incSecondary(vals.size());} + return new SetEnumValue(vals, false, cm); + } + + /* String representation of this value. */ + @Override + public final StringBuffer toString(StringBuffer sb, int offset, boolean swallow) { + try { + if (TLCGlobals.expand) { + Value val = this.toSetEnum(); + return val.toString(sb, offset, swallow); + } + else { + sb = sb.append("UNION("); + sb = this.set.toString(sb, offset, swallow); + sb.append(")"); + return sb; + } + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final ValueEnumeration elements() { + try { + if (this.realSet == null || this.realSet == SetEnumValue.DummyEnum) { + return new Enumerator(); + } + return this.realSet.elements(); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + final class Enumerator implements ValueEnumeration { + ValueEnumeration Enum; + Value elemSet; + ValueEnumeration elemSetEnum; + + public Enumerator() { + if (!(set instanceof Enumerable)) { + Assert.fail("Attempted to enumerate the nonenumerable set:\n"+ + Values.ppr(this.toString())); + } + this.Enum = ((Enumerable)set).elements(); + this.elemSet = this.Enum.nextElement(); + if (this.elemSet != null) { + if (!(this.elemSet instanceof Enumerable)) { + Assert.fail("Attempted to enumerate UNION(s), but some element of s is nonenumerable."); + } + this.elemSetEnum = ((Enumerable)this.elemSet).elements(); + } + } + + @Override + public final void reset() { + this.Enum.reset(); + this.elemSet = this.Enum.nextElement(); + this.elemSetEnum = ((Enumerable)this.elemSet).elements(); + } + + @Override + public final Value nextElement() { + if (this.elemSet == null) return null; + Value val = this.elemSetEnum.nextElement(); + if (val == null) { + this.elemSet = this.Enum.nextElement(); + if (this.elemSet == null) return null; + if (!(this.elemSet instanceof Enumerable)) { + Assert.fail("Attempted to enumerate the nonenumerable set:\n" + + Values.ppr(this.elemSet.toString()) + + "\nwhen enumerating:\n" + Values.ppr(this.toString())); + } + this.elemSetEnum = ((Enumerable)this.elemSet).elements(); + val = this.nextElement(); + } + if (coverage) { cm.incSecondary(); } + return val; + } + + } + +} diff --git a/tlatools/src/tlc2/value/UserObj.java b/tlatools/src/tlc2/value/impl/UserObj.java similarity index 90% rename from tlatools/src/tlc2/value/UserObj.java rename to tlatools/src/tlc2/value/impl/UserObj.java index 63f5c81d63e8a6e86f3a902f0f59eec51e850b10..a12baa077ea76693db032a66c21484fd82ad9e4c 100644 --- a/tlatools/src/tlc2/value/UserObj.java +++ b/tlatools/src/tlc2/value/impl/UserObj.java @@ -3,8 +3,7 @@ // Last modified on Mon 30 Apr 2007 at 13:21:09 PST by lamport // modified on Mon Aug 20 10:53:55 PDT 2001 by yuanyu -package tlc2.value; - +package tlc2.value.impl; public abstract class UserObj { @@ -17,11 +16,11 @@ public abstract class UserObj { public abstract boolean isFinite(); /* The String representation. */ - public abstract StringBuffer toString(StringBuffer sb, int offset); + public abstract StringBuffer toString(StringBuffer sb, int offset, boolean swallow); public final String toString() { StringBuffer sb = new StringBuffer(); - sb = this.toString(sb, 0); + sb = this.toString(sb, 0, true); return sb.toString(); } diff --git a/tlatools/src/tlc2/value/impl/UserValue.java b/tlatools/src/tlc2/value/impl/UserValue.java new file mode 100644 index 0000000000000000000000000000000000000000..9518af3f3c6e598bffbf6d22ccd6813d73b5b7ba --- /dev/null +++ b/tlatools/src/tlc2/value/impl/UserValue.java @@ -0,0 +1,150 @@ +// Copyright (c) 2003 Compaq Corporation. All rights reserved. +// Portions Copyright (c) 2003 Microsoft Corporation. All rights reserved. +// Last modified on Wed 12 Jul 2017 at 16:10:00 PST by ian morris nieves +// modified on Mon 30 Apr 2007 at 13:21:09 PST by lamport +// modified on Fri May 11 15:14:04 PDT 2001 by yuanyu + +package tlc2.value.impl; + +import tlc2.tool.FingerprintException; +import tlc2.value.IValue; +import tlc2.value.Values; +import util.Assert; + +public class UserValue extends Value { + public UserObj userObj; + + public UserValue(UserObj obj) { this.userObj = obj; } + + @Override + public final byte getKind() { return USERVALUE; } + + @Override + public final int compareTo(Object obj) { + try { + if (obj instanceof UserValue) { + return this.userObj.compareTo((Value)obj); + } + if (!(obj instanceof ModelValue)) + Assert.fail("Attempted to compare overridden value " + Values.ppr(this.toString()) + + " with non-overridden value:\n" + Values.ppr(obj.toString())); + return 1; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + public final boolean equals(Object obj) { + try { + return (this.compareTo(obj) == 0); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final boolean member(Value val) { + try { + return this.userObj.member(val); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final boolean isFinite() { + try { + return this.userObj.isFinite(); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final Value takeExcept(ValueExcept ex) { + try { + if (ex.idx < ex.path.length) { + Assert.fail("Attempted to apply EXCEPT to the overridden value " + + Values.ppr(this.toString()) + "."); + } + return ex.value; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final Value takeExcept(ValueExcept[] exs) { + try { + if (exs.length != 0) { + Assert.fail("Attempted to apply EXCEPT to the overridden value " + + Values.ppr(this.toString()) + "."); + } + return this; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + @Override + public final int size() { + try { + Assert.fail("Attempted to compute the number of elements in the overridden value " + + Values.ppr(this.toString()) + "."); + return 0; // make compiler happy + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + /* Nothing to normalize. */ + @Override + public final boolean isNormalized() { return true; } + + @Override + public final Value normalize() { /*SKIP*/return this; } + + @Override + public final boolean isDefined() { return true; } + + @Override + public final IValue deepCopy() { return this; } + + @Override + public final boolean assignable(Value val) { + try { + return this.equals(val); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + /* The string representation. */ + @Override + public final StringBuffer toString(StringBuffer sb, int offset, boolean swallow) { + try { + return this.userObj.toString(sb, offset, swallow); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + +} diff --git a/tlatools/src/tlc2/value/impl/Value.java b/tlatools/src/tlc2/value/impl/Value.java new file mode 100644 index 0000000000000000000000000000000000000000..e9b07185d7ff6591f77fa7f4152c5265f8a766bb --- /dev/null +++ b/tlatools/src/tlc2/value/impl/Value.java @@ -0,0 +1,358 @@ +// Copyright (c) 2003 Compaq Corporation. All rights reserved. +// Portions Copyright (c) 2003 Microsoft Corporation. All rights reserved. +// Last modified on Wed 12 Jul 2017 at 16:10:00 PST by ian morris nieves +// modified on Mon 30 Apr 2007 at 15:30:13 PST by lamport +// modified on Wed Dec 5 23:18:07 PST 2001 by yuanyu + +package tlc2.value.impl; + +import java.io.IOException; +import java.io.Serializable; + +import tla2sany.semantic.SemanticNode; +import tlc2.TLCGlobals; +import tlc2.tool.FingerprintException; +import tlc2.tool.coverage.CostModel; +import tlc2.util.FP64; +import tlc2.value.IMVPerm; +import tlc2.value.IValue; +import tlc2.value.IValueOutputStream; +import tlc2.value.ValueConstants; +import tlc2.value.Values; +import util.Assert; +import util.WrongInvocationException; + +public abstract class Value implements ValueConstants, Serializable, IValue { + + private static final String[] ValueImage = { + "a Boolean value", // "BoolValue", + "an integer", // "IntValue", + "a real", // "RealValue", + "a string", // "StringValue", + "a record", // "RecordValue", + "a set of the form {e1, ... ,eN}", // "SetEnumValue", + "a set of the form {x \\in S : expr}", // "SetPredValue", + "a tuple", // "TupleValue", + "a function of the form [x \\in S |-> expr]", // "FcnLambdaValue", + "a function of the form (d1 :> e1 @@ ... @@ dN :> eN)", // "FcnRcdValue", + "an operator", // "OpLambdaValue", + "a constant operator", // "OpRcdValue", + "a java method", // "MethodValue", + "a set of the form [S -> T]", // "SetOfFcnsValue", + "a set of the form [d1 : S1, ... , dN : SN]", // "SetOfRcdsValue", + "a cartesian product", // "SetOfTuplesValue", + "a set of the form SUBSET S", // "SubsetValue", + "a set of the form S \\ T", // "SetDiffValue", + "a set of the form S \\cap T", // "SetCapValue", + "a set of the form S \\cup T", // "SetCupValue", + "a set of the form UNION S", // "UnionValue", + "a model value", // "ModelValue", + "a special set constant", // "UserValue", + "a set of the form i..j", // "IntervalValue", + "an undefined value", // "UndefValue", + "a value represented in lazy form", // "LazyValue", + "a dummy for not-a-value", // "DummyValue", + }; + + /** + * @see See note on performance in CostModelCreator. + */ + protected static final boolean coverage = TLCGlobals.isCoverageEnabled(); + /** + * For each kind of value, we introduce a subclass of Value. + * All the subclasses are given in this value package. + * This method returns the value kind: an integer that represents + * the kind of this value. See the interface ValueConstants.java. + */ + public abstract byte getKind(); + + public String getKindString() { + try { + return ValueImage[this.getKind()]; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + /* This method returns true iff elem is a member of this. */ + public abstract boolean member(Value elem); + + /* This method returns a new value after taking the except. */ + public abstract Value takeExcept(ValueExcept ex); + + /* This method returns a new value after taking the excepts. */ + public abstract Value takeExcept(ValueExcept[] exs); + + /* This method returns true iff val can be assigned to this. */ + abstract boolean assignable(Value val); + + public abstract Value normalize(); + + @Override + public void write(IValueOutputStream vos) throws IOException { + throw new WrongInvocationException("ValueOutputStream: Can not pickle the value\n" + + Values.ppr(toString())); + } + + public transient CostModel cm = CostModel.DO_NOT_RECORD; + + @Override + public IValue setCostModel(CostModel cm) { + this.cm = cm; + return this; + } + + @Override + public CostModel getCostModel() { + return this.cm; + } + + /** + * These methods allow storage and retrieval of the SemanticNode used to create the Value, + * which is helpful for FingerprintException. + */ + private transient SemanticNode source = null; + + @Override + public void setSource(final SemanticNode semanticNode) { + source = semanticNode; + } + + @Override + public SemanticNode getSource() { + return source; + } + + @Override + public boolean hasSource() { + return source != null; + } + + public boolean hasData() { + return false; + } + + public Object getData() { + return null; + } + + public Object setData(final Object obj) { + throw new WrongInvocationException("Value: Can not set data\n" + + Values.ppr(toString())); + } + + public final boolean isEmpty() { + try { + + switch (this.getKind()) { + case SETENUMVALUE: + { + SetEnumValue set = (SetEnumValue)this; + return set.elems.size() == 0; + } + case INTERVALVALUE: + { + IntervalValue intv = (IntervalValue)this; + return intv.size() == 0; + } + case SETCAPVALUE: + { + SetCapValue cap = (SetCapValue)this; + return cap.elements().nextElement() == null; + } + case SETCUPVALUE: + { + SetCupValue cup = (SetCupValue)this; + return cup.elements().nextElement() == null; + } + case SETDIFFVALUE: + { + SetDiffValue diff = (SetDiffValue)this; + return diff.elements().nextElement() == null; + } + case SETOFFCNSVALUE: + { + SetOfFcnsValue fcns = (SetOfFcnsValue)this; + return fcns.elements().nextElement() == null; + } + case SETOFRCDSVALUE: + { + SetOfRcdsValue srv = (SetOfRcdsValue)this; + return srv.elements().nextElement() == null; + } + case SETOFTUPLESVALUE: + { + SetOfTuplesValue stv = (SetOfTuplesValue)this; + return stv.elements().nextElement() == null; + } + case SUBSETVALUE: + { + // SUBSET S is never empty. (It always contains {}.) + return false; + } + case UNIONVALUE: + { + UnionValue uv = (UnionValue)this; + return uv.elements().nextElement() == null; + } + case SETPREDVALUE: + { + SetPredValue spv = (SetPredValue)this; + return spv.elements().nextElement() == null; + } + default: + Assert.fail("Shouldn't call isEmpty() on value " + Values.ppr(this.toString())); + return false; + } + + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + /* Fully normalize this (composite) value. */ + @Override + public void deepNormalize() { + } + + /* This method returns the fingerprint of this value. */ + @Override + public long fingerPrint(long fp) { + try { + Assert.fail("TLC has found a state in which the value of a variable contains " + + Values.ppr(this.toString())); // SZ Feb 24, 2009: changed to static access + return 0; // make compiler happy + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + /** + * This method returns the value permuted by the permutation. It + * returns this if nothing is permuted. + */ + @Override + public IValue permute(IMVPerm perm) { + try { + Assert.fail("TLC has found a state in which the value of a variable contains " + + Values.ppr(this.toString())); // SZ Feb 24, 2009: changed to static access + return null; // make compiler happy + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + /* This method returns the hash code of this value. */ + @Override + public final int hashCode() { + try { + long fp = this.fingerPrint(FP64.New()); + int high = (int)(fp >> 32); + int low = (int)fp; + return high ^ low; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + /** + * This method selects the component of this value. The component is + * specified by path. + */ + public final Value select(Value[] path) { + try { + Value result = this; + for (int i = 0; i < path.length; i++) { + if (!(result instanceof Applicable)) { + Assert.fail("Attempted to apply EXCEPT construct to the value " + + Values.ppr(result.toString()) + "."); + } + Value elem = path[i]; + result = ((Applicable)result).select(elem); + if (result == null) return null; + } + return result; + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } + + /* Convert val into a SetEnumValue. Returns null if not possible. */ + public Value toSetEnum() { + return null; + } + + /* + * This method converts a value to a function value. It returns + * null if the conversion fails. + */ + public Value toFcnRcd() { + return null; + } + + /* + * This method converts a value to a function value. It returns + * null if the conversion fails. + */ + public Value toRcd() { + return null; + } + + /* + * This method converts a value to a tuple value. It returns + * null if the conversion fails. + */ + public Value toTuple() { + return null; + } + + /* The string representation of this value */ + @Override + public final String toString() { + return toStringImpl("", true); + } + + /* Same as toString except that nested exceptions won't be silently discarded */ + public final String toStringUnchecked() { + return toStringImpl("", false); + } + + /* Same as toString. */ + @Override + public String toUnquotedString() { + return toString(); + } + + @Override + public final String toString(final String delim) { + return toStringImpl(delim, true); + } + + public final String toStringUnchecked(final String delim) { + return toStringImpl(delim, false); + } + + private final String toStringImpl(final String delim, final boolean checked) { + try { + final StringBuffer sb = this.toString(new StringBuffer(), 0, checked); + sb.append(delim); + return sb.toString(); + } + catch (RuntimeException | OutOfMemoryError e) { + if (hasSource()) { throw FingerprintException.getNewHead(this, e); } + else { throw e; } + } + } +} diff --git a/tlatools/src/tlc2/value/impl/ValueEnumeration.java b/tlatools/src/tlc2/value/impl/ValueEnumeration.java new file mode 100644 index 0000000000000000000000000000000000000000..fc4d77947c3c249bb37c3fa89aebe4d09e5d9c96 --- /dev/null +++ b/tlatools/src/tlc2/value/impl/ValueEnumeration.java @@ -0,0 +1,34 @@ +// Copyright (c) 2003 Compaq Corporation. All rights reserved. +// Portions Copyright (c) 2003 Microsoft Corporation. All rights reserved. +// Last modified on Mon 30 Apr 2007 at 13:21:10 PST by lamport +// modified on Tue Aug 22 11:56:52 PDT 2000 by yuanyu + +package tlc2.value.impl; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.Consumer; + +public interface ValueEnumeration { + /* Reset allows repeated use of this enumerator. */ + void reset(); + + /* Return the next element if there is one. Otherwise return null. */ + Value nextElement(); + + default List<Value> all() { + final List<Value> values = new ArrayList<Value>(); + Value elem; + while ((elem = nextElement()) != null) { + values.add(elem); + } + return values; + } + + default void forEach(final Consumer<? super Value> action) { + Value elem; + while ((elem = nextElement()) != null) { + action.accept(elem); + } + } +} diff --git a/tlatools/src/tlc2/value/ValueExcept.java b/tlatools/src/tlc2/value/impl/ValueExcept.java similarity index 72% rename from tlatools/src/tlc2/value/ValueExcept.java rename to tlatools/src/tlc2/value/impl/ValueExcept.java index 8f4e67aad7a54fd8953c07912ecc1f5e62f77665..6dcdf261e15e16b3b5f884c1da6b76e534a18355 100644 --- a/tlatools/src/tlc2/value/ValueExcept.java +++ b/tlatools/src/tlc2/value/impl/ValueExcept.java @@ -3,7 +3,7 @@ // Last modified on Wed 4 Jul 2007 at 17:31:51 PST by lamport // modified on Wed Jul 25 11:04:30 PDT 2001 by yuanyu -package tlc2.value; +package tlc2.value.impl; import tla2sany.semantic.FormalParamNode; @@ -12,7 +12,7 @@ public class ValueExcept { public Value value; public int idx; - public ValueExcept(Value[] lhs, Value rhs) { + public ValueExcept(Value [] lhs, Value rhs) { this.path = lhs; this.value = rhs; this.idx = 0; @@ -25,17 +25,17 @@ public class ValueExcept { } public final ValueExcept checkArg(FcnLambdaValue fcn) { - Value argv = this.path[idx]; - if (fcn.params.length() == 1) { - if (!fcn.params.domains[0].member(argv)) return null; + Value argv = this.path[idx]; + if (fcn.getParams().length() == 1) { + if (!fcn.getParams().domains[0].member(argv)) return null; } else { TupleValue tval = (TupleValue)argv; - Value[] argList = tval.elems; - FormalParamNode[][] formals = fcn.params.formals; - Value[] domains = fcn.params.domains; + Value [] argList = tval.elems; + FormalParamNode[][] formals = fcn.getParams().formals; + Value [] domains = fcn.getParams().domains; int argn = 0; - for (int i = 0; i < fcn.params.formals.length; i++) { + for (int i = 0; i < fcn.getParams().formals.length; i++) { FormalParamNode[] formal = formals[i]; for (int j = 0; j < formal.length; j++) { if (!domains[i].member(argList[argn++])) return null; @@ -45,7 +45,7 @@ public class ValueExcept { return this; } - public final Value current() { return this.path[this.idx]; } + public final Value current() { return this.path[this.idx]; } public final boolean isLast() { return this.idx == (this.path.length - 1); diff --git a/tlatools/src/tlc2/value/ValueVec.java b/tlatools/src/tlc2/value/impl/ValueVec.java similarity index 75% rename from tlatools/src/tlc2/value/ValueVec.java rename to tlatools/src/tlc2/value/impl/ValueVec.java index e2a0dab2889d3929e31a4811453a9720b5ba195d..e2c89fd9758eb24333e85809956c303013f8365b 100644 --- a/tlatools/src/tlc2/value/ValueVec.java +++ b/tlatools/src/tlc2/value/impl/ValueVec.java @@ -3,18 +3,19 @@ // Last modified on Tue 1 May 2007 at 13:40:06 PST by lamport // modified on Tue Aug 21 17:02:26 PDT 2001 by yuanyu -package tlc2.value; +package tlc2.value.impl; import java.io.Serializable; +import java.util.Collection; import tlc2.TLCGlobals; import util.WrongInvocationException; public class ValueVec implements Cloneable, Serializable { - private Value[] elementData; + private Value [] elementData; private int elementCount; - static private final Value[] empty = new Value[0]; + private static final Value [] empty = new Value [0]; public ValueVec() { this(10); } @@ -24,15 +25,26 @@ public class ValueVec implements Cloneable, Serializable { this.elementData = empty; } else { - this.elementData = new Value[initialCapacity]; + this.elementData = new Value [initialCapacity]; } } - public ValueVec(Value[] elems) { + public ValueVec(Value [] elems) { this.elementData = elems; this.elementCount = elems.length; } - + + public ValueVec(Collection<Value > elems) { + this(elems.size()); + for (Value value : elems) { + addElement(value); + } + } + public final void addElementAt(Value val, int index) { + this.elementData[index] = val; + this.elementCount++; + } + public final void addElement(Value val) { if (this.elementCount == this.elementData.length) { ensureCapacity(this.elementCount+1); @@ -40,7 +52,7 @@ public class ValueVec implements Cloneable, Serializable { this.elementData[this.elementCount++] = val; } - public final void addElement1(Value val) { + public final void addElement1(Value val) { if (this.elementCount == this.elementData.length) { ensureCapacity(this.elementCount+1); } @@ -61,6 +73,7 @@ public class ValueVec implements Cloneable, Serializable { public final int capacity() { return elementData.length; } + @Override public final Object clone() { ValueVec v = new ValueVec(this.elementData.length); @@ -69,15 +82,15 @@ public class ValueVec implements Cloneable, Serializable { return v; } - public final boolean contains(Value elem) { + public final boolean contains(Value elem) { return (indexOf(elem) != -1); } - public final void copyInto(Value anArray[]) { + public final void copyInto(Value anArray[]) { System.arraycopy(elementData, 0, anArray, 0, elementCount); } - public final Value elementAt(int index) { + public final Value elementAt(int index) { // Assert.check(index < this.elementCount); return this.elementData[index]; } @@ -95,25 +108,25 @@ public class ValueVec implements Cloneable, Serializable { if (newCapacity > TLCGlobals.setBound) { newCapacity = TLCGlobals.setBound; } - Value oldData[] = this.elementData; - this.elementData = new Value[newCapacity]; + Value oldData[] = this.elementData; + this.elementData = new Value [newCapacity]; System.arraycopy(oldData, 0, elementData, 0, elementCount); } } - public final Value firstElement() { return this.elementData[0]; } + public final Value firstElement() { return this.elementData[0]; } - public final int indexOf(Value elem) { return indexOf(elem, 0); } + public final int indexOf(Value elem) { return indexOf(elem, 0); } - public final int indexOf(Value elem, int index) { + public final int indexOf(Value elem, int index) { for (int pos = index; pos < elementCount; pos++) { if (elem.equals(elementData[pos])) return pos; } return -1; } - public final void insertElementAt(Value obj, int index) { + public final void insertElementAt(Value obj, int index) { if (elementCount == elementData.length) { this.ensureCapacity(elementCount+1); } @@ -124,18 +137,18 @@ public class ValueVec implements Cloneable, Serializable { public final boolean isEmpty() { return (elementCount == 0); } - public final Value lastElement() { + public final Value lastElement() { return this.elementData[this.elementCount-1]; } - public final void setElementAt(Value obj, int index) { + public final void setElementAt(Value obj, int index) { this.elementData[index] = obj; } public final int size() { return this.elementCount; } /* Assume that the elements are sorted. */ - public final boolean search(Value elem, boolean sorted) { + public final boolean search(Value elem, boolean sorted) { if (sorted) { int cmp = 0, mid = 0, low = 0, high = this.elementCount; while (low < high) { @@ -160,10 +173,10 @@ public class ValueVec implements Cloneable, Serializable { return false; } - public final void sort(boolean noDup) { + public final ValueVec sort(boolean noDup) { int newCount = (this.elementCount == 0) ? 0 : 1; for (int i = 1; i < this.elementCount; i++) { - Value elem = this.elementData[i]; + Value elem = this.elementData[i]; int cmp = 0, idx = 0, low = 0, high = newCount; while (low < high) { idx = (low + high) >> 1; @@ -186,6 +199,7 @@ public class ValueVec implements Cloneable, Serializable { } } this.elementCount = newCount; + return this; } public final String toString() { @@ -203,4 +217,10 @@ public class ValueVec implements Cloneable, Serializable { return sb.toString(); } + public Value [] toArray() { + final Value [] copy = new Value [elementCount]; + System.arraycopy(elementData, 0, copy, 0, elementCount); + return copy; + } + } diff --git a/tlatools/src/util/Assert.java b/tlatools/src/util/Assert.java index 3984967242870b9b71a1e248a283d3b7f488bf69..99a054407afad41c6373b9b33f364e7352fb51f4 100644 --- a/tlatools/src/util/Assert.java +++ b/tlatools/src/util/Assert.java @@ -11,18 +11,16 @@ import tlc2.output.MP; * A toolkit for checking conditions and throwing unchecked exceptions if they are not met. * * @author Yuan Yu, Simon Zambrovski - * @version $Id$ */ public class Assert { /** * Unconditioned way to throw an exception * @param reason the explaining message to be enclosed into the exception - * @deprecated Use {@link EC} constants instead */ public static void fail(String reason) throws RuntimeException { - throw new RuntimeException(reason); + throw new TLCRuntimeException(reason); } /** @@ -32,7 +30,7 @@ public class Assert */ public static void fail(int errorCode, String[] parameters) { - throw new RuntimeException(MP.getMessage(errorCode, parameters)); + throw new TLCRuntimeException(errorCode, parameters, MP.getMessage(errorCode, parameters)); } /** @@ -42,7 +40,7 @@ public class Assert */ public static void fail(int errorCode, String parameter) { - throw new RuntimeException(MP.getMessage(errorCode, parameter)); + throw new TLCRuntimeException(errorCode, new String[] {parameter}, MP.getMessage(errorCode, parameter)); } /** @@ -52,7 +50,7 @@ public class Assert */ public static void fail(int errorCode, Throwable cause) { - throw new RuntimeException(MP.getMessage(errorCode, cause.getMessage()), cause); + throw new TLCRuntimeException(errorCode, MP.getMessage(errorCode, cause.getMessage()), cause); } /** @@ -61,7 +59,7 @@ public class Assert */ public static void fail(int errorCode) { - throw new RuntimeException(MP.getMessage(errorCode)); + throw new TLCRuntimeException(errorCode, MP.getMessage(errorCode)); } /** @@ -75,7 +73,7 @@ public class Assert { if (!condition) { - throw new RuntimeException(MP.getMessage(errorCode, parameters)); + throw new TLCRuntimeException(errorCode, parameters, MP.getMessage(errorCode, parameters)); } } @@ -90,7 +88,7 @@ public class Assert { if (!condition) { - throw new RuntimeException(MP.getMessage(errorCode, parameter)); + throw new TLCRuntimeException(errorCode, new String[] {parameter}, MP.getMessage(errorCode, parameter)); } } @@ -104,7 +102,7 @@ public class Assert { if (!condition) { - throw new RuntimeException(MP.getMessage(errorCode)); + throw new TLCRuntimeException(errorCode, MP.getMessage(errorCode)); } } @@ -126,8 +124,34 @@ public class Assert { if (!condition) { - throw new RuntimeException(errorMsg); + throw new TLCRuntimeException(errorMsg); } } + @SuppressWarnings("serial") + public static class TLCRuntimeException extends RuntimeException { + + public final int errorCode; + public String[] parameters = null; + + public TLCRuntimeException(String errorMsg) { + super(errorMsg); + this.errorCode = EC.GENERAL; // Unknown error code. + } + + public TLCRuntimeException(int errorCode, String message) { + super(message); + this.errorCode = errorCode; + } + + public TLCRuntimeException(int errorCode, String message, Throwable cause) { + super(message, cause); + this.errorCode = errorCode; + } + + public TLCRuntimeException(int errorCode, String[] parameters, String message) { + this(errorCode, message); + this.parameters = parameters; + } + } } diff --git a/tlatools/src/util/BufferedDataInputStream.java b/tlatools/src/util/BufferedDataInputStream.java index 713deed933787a00b84edabd11c1bf181d33584f..c9c53cac9da192f514f45c85d91552679961e8a9 100644 --- a/tlatools/src/util/BufferedDataInputStream.java +++ b/tlatools/src/util/BufferedDataInputStream.java @@ -22,7 +22,7 @@ import tlc2.output.EC; the comments of this class by the specification <TT>REQUIRES LL = SELF</TT>. */ -public class BufferedDataInputStream extends FilterInputStream { +public final class BufferedDataInputStream extends FilterInputStream implements IDataInputStream { /* protected by SELF */ private byte[] buff; /* buffer of bytes to read */ private int len; /* number of valid bytes in "buff" */ @@ -99,7 +99,7 @@ public class BufferedDataInputStream extends FilterInputStream { /** REQUIRES LL = SELF */ /** Returns <code>true</code> iff the stream is exhausted. */ - public boolean atEOF() { + public final boolean atEOF() { return (this.len < 0); } @@ -107,7 +107,7 @@ public class BufferedDataInputStream extends FilterInputStream { /** Reads up to <code>b.length</code> bytes into <code>b</code>, and returns the number of bytes read, or -1 if the stream is exhausted on entry. */ - public int read(byte[] b) throws IOException { + public final int read(byte[] b) throws IOException { return this.read(b, 0, b.length); } @@ -115,7 +115,7 @@ public class BufferedDataInputStream extends FilterInputStream { /** Reads up to <code>n</code> bytes into <code>b</code> starting at position <code>off</code>, and returns the number of bytes read, or -1 if the stream is exhausted on entry. */ - public int read(byte[] b, int off, int n) throws IOException { + public final int read(byte[] b, int off, int n) throws IOException { if (this.len < 0) return -1; int offInit = off; while (n > 0 && this.len > 0) { @@ -136,7 +136,7 @@ public class BufferedDataInputStream extends FilterInputStream { /** Reads <code>b.length</code> bytes into <code>b</code>, or throws <code>EOFException</code> if the stream contains fewer than <code>b.length</code> bytes. */ - public void readFully(byte[] b) throws IOException, EOFException { + public final void readFully(byte[] b) throws IOException, EOFException { this.readFully(b, 0, b.length); } @@ -144,7 +144,7 @@ public class BufferedDataInputStream extends FilterInputStream { /** Reads <code>n</code> bytes into <code>b</code> starting at position <code>off</code>, or throws <code>EOFException</code> if the stream contains fewer than <code>n</code> bytes. */ - public void readFully(byte[] b, int off, int n) + public final void readFully(byte[] b, int off, int n) throws IOException, EOFException { while (n > 0) { int numRead = this.read(b, off, n); @@ -156,7 +156,7 @@ public class BufferedDataInputStream extends FilterInputStream { /** REQUIRES LL = SELF */ /** Reads and returns the next byte of this stream, or throws <code>EOFException</code> if the stream is exhausted. */ - public byte readByte() throws IOException, EOFException { + public final byte readByte() throws IOException, EOFException { if (this.len < 0) throw new EOFException(); byte res = this.buff[this.curr++]; if (this.curr == this.len) { @@ -173,7 +173,7 @@ public class BufferedDataInputStream extends FilterInputStream { encoded in the next byte of this stream, or throws <code>EOFException</code> if the stream is exhausted. */ - public boolean readBoolean() throws IOException, EOFException { + public final boolean readBoolean() throws IOException, EOFException { return (this.readByte() != 0); } @@ -182,7 +182,7 @@ public class BufferedDataInputStream extends FilterInputStream { encoded in the next two bytes of this stream, or throw <code>EOFException</code> if the stream contains fewer than two bytes. */ - public short readShort() throws IOException, EOFException { + public final short readShort() throws IOException, EOFException { this.readFully(this.temp, 0, 2); return (short) ((temp[0] << 8) | (temp[1] & 0xff)); } @@ -192,7 +192,7 @@ public class BufferedDataInputStream extends FilterInputStream { encoded in the next four bytes of this stream, or throws <code>EOFException</code> if the stream contains fewer than four bytes. */ - public int readInt() throws IOException, EOFException { + public final int readInt() throws IOException, EOFException { this.readFully(this.temp, 0, 4); int res = temp[0]; res <<= 8; res |= (temp[1] & 0xff); @@ -206,7 +206,7 @@ public class BufferedDataInputStream extends FilterInputStream { encoded in the next eight bytes of this stream, or throws <code>EOFException</code> if the stream contains fewer than eight bytes. */ - public long readLong() throws IOException, EOFException { + public final long readLong() throws IOException, EOFException { this.readFully(this.temp, 0, 8); long res = temp[0]; res <<= 8; res |= (temp[1] & 0xff); @@ -226,7 +226,7 @@ public class BufferedDataInputStream extends FilterInputStream { by a carriage return character (<code>'\r'</code>), a newline character (<code>'\n'</code>), a carriage return immediately followed by a newline, or by the end of the stream. */ - public String readLine() throws IOException { + public final String readLine() throws IOException { String res = null; while (this.len > 0) { for (int i = this.curr; i < this.len; i++) { @@ -262,7 +262,7 @@ public class BufferedDataInputStream extends FilterInputStream { return res; } - public String readString(int n) throws IOException { + public final String readString(int n) throws IOException { char[] b = new char[n]; int off = 0; while (n > 0) { @@ -291,7 +291,7 @@ public class BufferedDataInputStream extends FilterInputStream { /** Skips over the next <code>n</code> bytes in this stream, or throws <code>EOFException</code> if it contains fewer than <code>n</code> bytes. */ - public void skip(int n) throws IOException, EOFException { + public final void skip(int n) throws IOException, EOFException { while (this.len > 0 && this.curr + n >= this.len) { n -= (this.len - this.curr); // refill buffer from underlying input stream diff --git a/tlatools/src/util/BufferedDataOutputStream.java b/tlatools/src/util/BufferedDataOutputStream.java index fc136e422abd4343727f2e8ea039b68cc3c92901..fa2dd63602ce578c68c3987facf9b89fb67926d3 100644 --- a/tlatools/src/util/BufferedDataOutputStream.java +++ b/tlatools/src/util/BufferedDataOutputStream.java @@ -16,7 +16,7 @@ import java.io.OutputStream; unmonitored. Hence, it is the client's responsibility to lock the stream before using it. */ -public class BufferedDataOutputStream extends FilterOutputStream { +public final class BufferedDataOutputStream extends FilterOutputStream implements IDataOutputStream { private byte[] buff; /* buffer of bytes to write */ private int len; /* number of valid bytes in "buff" */ private byte[] temp; /* temporary array used by various methods */ @@ -83,7 +83,7 @@ public class BufferedDataOutputStream extends FilterOutputStream { /** Flush all bytes written to this stream to the underlying output stream. */ - public void flush() throws IOException { + public final void flush() throws IOException { this.out.write(this.buff, 0, this.len); this.out.flush(); this.len = 0; @@ -91,26 +91,26 @@ public class BufferedDataOutputStream extends FilterOutputStream { /** Closes this stream and its underlying stream, after first flushing any buffered data. */ - public void close() throws IOException { + public final void close() throws IOException { this.flush(); this.out.close(); this.out = null; } /** Write <code>b</code> to this stream. */ - public void write(byte b) throws IOException { + public final void write(byte b) throws IOException { this.writeByte(b); } /** Write the <code>b.length</code> bytes of <code>b</code> to this stream. */ - public void write(byte[] b) throws IOException { + public final void write(byte[] b) throws IOException { this.write(b, 0, b.length); } /** Write <code>n</code> bytes of <code>b</code> starting - at possition <code>off</code> to this stream. */ - public void write(byte[] b, int off, int n) throws IOException { + at position <code>off</code> to this stream. */ + public final void write(byte[] b, int off, int n) throws IOException { while (n > 0) { int toCopy = Math.min(n, this.buff.length - this.len); System.arraycopy(b, off, this.buff, this.len, toCopy); @@ -124,7 +124,7 @@ public class BufferedDataOutputStream extends FilterOutputStream { } /** Write the byte <code>b</code> to this stream. */ - public void writeByte(byte b) throws IOException { + public final void writeByte(byte b) throws IOException { this.buff[this.len++] = b; if (this.buff.length == this.len) { // write buffer to underlying stream @@ -135,14 +135,14 @@ public class BufferedDataOutputStream extends FilterOutputStream { /** Write the boolean value <code>b</code> to this stream as a single byte. */ - public void writeBoolean(boolean bool) throws IOException { + public final void writeBoolean(boolean bool) throws IOException { byte b = (bool ? (byte)1 : (byte)0); this.writeByte(b); } /** Write the short value <code>s</code> to this stream as two bytes. */ - public void writeShort(short s) throws IOException { + public final void writeShort(short s) throws IOException { this.temp[0] = (byte) ((s >>> 8) & 0xff); this.temp[1] = (byte) (s & 0xff); this.write(this.temp, 0, 2); @@ -150,7 +150,7 @@ public class BufferedDataOutputStream extends FilterOutputStream { /** Write the integer value <code>i</code> to this stream as four bytes. */ - public void writeInt(int i) throws IOException { + public final void writeInt(int i) throws IOException { this.temp[0] = (byte) ((i >>> 24) & 0xff); this.temp[1] = (byte) ((i >>> 16) & 0xff); this.temp[2] = (byte) ((i >>> 8) & 0xff); @@ -160,7 +160,7 @@ public class BufferedDataOutputStream extends FilterOutputStream { /** Write the long value <code>l</code> to this stream as eight bytes. */ - public void writeLong(long l) throws IOException { + public final void writeLong(long l) throws IOException { this.temp[0] = (byte) ((l >>> 56) & 0xff); this.temp[1] = (byte) ((l >>> 48) & 0xff); this.temp[2] = (byte) ((l >>> 40) & 0xff); @@ -174,19 +174,19 @@ public class BufferedDataOutputStream extends FilterOutputStream { /** Write the float value <code>f</code> to this stream as four bytes. */ - public void writeFloat(float f) throws IOException { + public final void writeFloat(float f) throws IOException { this.writeInt(Float.floatToIntBits(f)); } /** Write the double value <code>d</code> to this stream as eight bytes. */ - public void writeDouble(double d) throws IOException { + public final void writeDouble(double d) throws IOException { this.writeLong(Double.doubleToLongBits(d)); } /** Write the characters of the string <code>s</code> to this stream as a sequence of bytes. */ - public void writeString(String s) throws IOException { + public final void writeString(String s) throws IOException { int n = s.length(); int off = 0; while (n > 0) { @@ -203,7 +203,7 @@ public class BufferedDataOutputStream extends FilterOutputStream { /** Write <code>n</code> characters of <code>chars</code> starting at offset <code>off</code> to this stream as a sequence of bytes. */ - public void writeChars(char[] chars, int off, int n) throws IOException { + public final void writeChars(char[] chars, int off, int n) throws IOException { int finOff = off + n; while (off < finOff) { // Copy (part of) chars to this.buff @@ -223,7 +223,7 @@ public class BufferedDataOutputStream extends FilterOutputStream { can be read back by <code>BufferedDataInputStream.readAnyString</code>, even if <code>s</code> is <code>null</code> or if it contains newline characters. */ - public void writeAnyString(String s) throws IOException { + public final void writeAnyString(String s) throws IOException { if (s == null) { this.writeInt(-1); } @@ -235,7 +235,7 @@ public class BufferedDataOutputStream extends FilterOutputStream { /** Write the characters of the string <code>s</code> to this stream as a sequence of bytes, followed by a newline. */ - public void writeLine(String s) throws IOException { + public final void writeLine(String s) throws IOException { this.writeString(s); this.writeByte((byte) '\n'); } diff --git a/tlatools/src/util/ByteArrayInputStream.java b/tlatools/src/util/ByteArrayInputStream.java new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/tlatools/src/util/ExecutionStatisticsCollector.java b/tlatools/src/util/ExecutionStatisticsCollector.java new file mode 100644 index 0000000000000000000000000000000000000000..376f843fdc177790582f8d536b491aeb3880d76c --- /dev/null +++ b/tlatools/src/util/ExecutionStatisticsCollector.java @@ -0,0 +1,247 @@ +/******************************************************************************* + * Copyright (c) 2019 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package util; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.net.HttpURLConnection; +import java.net.URL; +import java.net.URLEncoder; +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +public class ExecutionStatisticsCollector { + + static final String RND_ID_STR = "RANDOM_IDENTIFIER"; + static final String NO_ESC_STR = "NO_STATISTICS"; + + public enum Selection { + ON, RANDOM_IDENTIFIER, NO_ESC; + + @Override + public String toString() { + if (this == ON) { + return getRandomIdentifier(); + } else if (this == RANDOM_IDENTIFIER) { + return RND_ID_STR; + } else { + return NO_ESC_STR; + } + } + } + + private static final String PATH = System.getProperty("user.home", "") + File.separator + ".tlaplus" + File.separator + "esc.txt"; + + public static final String PROP = ExecutionStatisticsCollector.class.getName() + ".id"; + + private final boolean isOptOut; + private final String pathname; + + public ExecutionStatisticsCollector() { + this(isOptOut(), PATH); + } + + ExecutionStatisticsCollector(String path) { + this(false, path); + } + + ExecutionStatisticsCollector(final boolean optOut, final String path) { + this.isOptOut = optOut; + this.pathname = path; + } + + public void collect(final Map<String, String> parameters) { + collect(parameters, true); + } + + private void collect(final Map<String, String> parameters, final boolean dontWaitForCompletion) { + // Do not block TLC startup but send this to the background immediately. If + // dontWaitForCompletion is true, the VM will terminate this thread regardless + // of its state if the VM decides to shutdown (e.g. because TLC is done). + final Thread thread = new Thread(new Runnable() { + @Override + public void run() { + if (isEnabled()) { + // Include identifier to track individual installations (not users!). + parameters.put("id", getIdentifier()); + submit(parameters, dontWaitForCompletion); + } + } + }, "TLC Execution Statistics Collector"); + thread.setDaemon(dontWaitForCompletion); + thread.start(); + } + + /* + * file == ~/.tlaplus/esc.txt + * fl == first line of file interpreted as a string without terminal chars + * in/out == opt-in & opt-out + * y/r/n == data collected with constant id/data collected with random id/data not collected + * + * | No file | fl unreadable | fl empty | fl = "NO_UDC" | fl = "RANDOM_IDENTIFIER" | fl any other string + * ========================================================================================================== + * | out | y | n | n | n | r | y | + * ---------------------------------------------------------------------------------------------------------- + * | in | n | n | n | n | r | y | + * ---------------------------------------------------------------------------------------------------------- + */ + public String getIdentifier() { + if (System.getProperty(PROP) != null) { + return System.getProperty(PROP); + } + String identifier; + + final File udcFile = new File(pathname); + if (!udcFile.exists() && isOptOut) { + try (BufferedWriter br = new BufferedWriter(new FileWriter(udcFile))) { + br.write(getRandomIdentifier()); + } catch (Exception e) { + // Something went wrong writing to file ~/.tlaplus/esc.txt. Consider ESC failed. + return null; + } + } + if (!udcFile.exists()) { + // No file ~/.tlaplus/esc.txt. + return null; + } + try (BufferedReader br = new BufferedReader(new FileReader(udcFile))) { + identifier = br.readLine(); + } catch (Exception e) { + // Something went wrong reading file ~/.tlaplus/esc.txt + return null; + } + if (identifier == null || NO_ESC_STR.equals(identifier.trim())) { + // File is empty or its first line is "NO_STATISTICS". + return null; + } else if (identifier == null || RND_ID_STR.equals(identifier.trim())) { + identifier = getRandomIdentifier(); + } + + // truncate the identifier no matter what, but first remove leading and trailing whitespaces. + final String trimmed = identifier.trim(); + return trimmed.substring(0, Math.min(trimmed.length(), 32)); + } + + private boolean escFileExists() { + return new File(pathname).exists(); + } + + public boolean isEnabled() { + return getIdentifier() != null; + } + + public void set(final Selection c) throws IOException { + final File udcFile = new File(PATH); + udcFile.createNewFile(); + + try (BufferedWriter br = new BufferedWriter(new FileWriter(udcFile))) { + br.write(c.toString() + "\n"); + } catch (IOException e) { + throw e; + } + } + + public Selection get() { + if (isEnabled()) { + try (BufferedReader br = new BufferedReader(new FileReader(new File(pathname)))) { + String line = br.readLine(); + if (RND_ID_STR.equals(line)) { + return Selection.RANDOM_IDENTIFIER; + } else { + return Selection.ON; + } + } catch (Exception e) { + } + } + return Selection.NO_ESC; + } + + public static boolean promptUser() { + return !(new ExecutionStatisticsCollector().escFileExists()); + } + + private static String getRandomIdentifier() { + return UUID.randomUUID().toString().replaceAll("-", ""); + } + + private static boolean isOptOut() { + return false; // Nobody is opt-out right now + + // Below is a way how we could detect Microsoft corpnet machines: This check is + // conservative because we don't identify Microsoft employees but corporate + // Microsoft computers. We don't set ESC to opt-out for Microsoft machines yet + // but might in the future. +// final String userDNSDomain = System.getenv("USERDNSDOMAIN"); +// return userDNSDomain != null && userDNSDomain.toUpperCase().endsWith(".CORP.MICROSOFT.COM"); + } + + // Send the request. + private static void submit(final Map<String, String> parameters, final boolean dontWaitForCompletion) { + // Include a timestamp to cause HEAD to be un-cachable. + parameters.put("ts", Long.toString(System.currentTimeMillis())); + parameters.put("optout", Boolean.toString(isOptOut())); + + try { + // Opt-out data doesn't report to the opt-in endpoint. The opt-in endpoint is + // public data, the opt-out endpoint doesn't get shared with the public. + final URL url = new URL( + "https://" + (isOptOut() ? "esc02" : "esc01") + ".tlapl.us/?" + encode(parameters)); + + final HttpURLConnection con = (HttpURLConnection) url.openConnection(); + con.setRequestMethod("HEAD"); + con.getResponseMessage(); + con.disconnect(); + } catch (Exception ignoreCompletely) { + // A TLC user doesn't care if execution statistics collection doesn't work. + } + } + + private static String encode(final Map<String, String> parameters) throws UnsupportedEncodingException { + final StringBuffer buf = new StringBuffer(); + + for (String key : parameters.keySet()) { + String value = parameters.get(key); + buf.append(URLEncoder.encode(key, "UTF-8")); + buf.append("="); + buf.append(URLEncoder.encode(value, "UTF-8")); + buf.append(","); + } + + return buf.toString().replaceFirst(",$", ""); + } + + // for manual testing // + + public static void main(String[] args) { + new ExecutionStatisticsCollector().collect(new HashMap<>(), false); + } +} diff --git a/tlatools/src/util/ExecutionStatisticsCollector.md b/tlatools/src/util/ExecutionStatisticsCollector.md new file mode 100644 index 0000000000000000000000000000000000000000..e4b51b4429217adba6d603c1dd923aeac9e6f43f --- /dev/null +++ b/tlatools/src/util/ExecutionStatisticsCollector.md @@ -0,0 +1,14 @@ +You can help make the TLA+ tools better by allowing TLC to collect execution statistics. Execution statistics are made [publicly available](https://exec-stats.tlapl.us) on the web and contain the following information: + +* Total number of cores and cores assigned to TLC +* Heap and off-heap memory allocated to TLC +* TLC version (git commit SHA) +* If breadth-first search, depth-first search or simulation mode is active +* TLC implemenation for the sets of seen and unseen states +* If TLC has been launched from an IDE +* Name, version, and architecture of the OS +* Vendor, version, and architecture of JVM +* The current date and time +* An installation ID which allows to group execution statistic (optionally) + +Execution statistics do not contain personal information. You can opt-out any time. diff --git a/tlatools/src/util/FileUtil.java b/tlatools/src/util/FileUtil.java index d5c18f4a6c6891078faa8abf8c354265c594a280..401984fb906e7e686996c08a9cd7082def0adb6a 100644 --- a/tlatools/src/util/FileUtil.java +++ b/tlatools/src/util/FileUtil.java @@ -16,6 +16,8 @@ import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.OutputStream; import java.math.BigInteger; +import java.nio.file.Files; +import java.nio.file.StandardCopyOption; import java.text.SimpleDateFormat; import java.util.Date; import java.util.zip.GZIPInputStream; @@ -37,6 +39,19 @@ public class FileUtil public static final String separator = File.separator; public static final String pathSeparator = File.pathSeparator; + /** + * Parses the directory path from a filename. If the filename + * is already a basename, returns the empty string. + */ + public static String parseDirname(String filename) { + int lastSep = filename.lastIndexOf(separatorChar); + if (lastSep == -1) { + // No parent directory. + return ""; + } + return filename.substring(0, lastSep + 1); + } + /** * Deletes the file or directory. Returns true iff the deletion * succeeds. The argument recurse forces the deletion of non-empty @@ -180,21 +195,24 @@ public class FileUtil } } - public static void copyFile(String fromName, String toName) throws IOException - { - // REFACTOR - FileInputStream fis = new FileInputStream(fromName); - FileOutputStream fos = new FileOutputStream(toName); - byte[] buf = new byte[8192]; - int n; - while ((n = fis.read(buf)) != -1) - { - fos.write(buf, 0, n); - } - fis.close(); - fos.close(); + public static void copyFile(final String fromName, final String toName) throws IOException { + copyFile(new File(fromName), new File(toName)); + } + + + public static void copyFile(final File source, final File destination) throws IOException { + Files.copy(source.toPath(), destination.toPath(), StandardCopyOption.REPLACE_EXISTING); } + /** + * Atomically replaces the file targetName with the file sourceName. + * @param sourceName + * @param targetName + * @throws IOException + */ + public static void replaceFile(String sourceName, String targetName) throws IOException { + Files.move(new File(sourceName).toPath(), new File(targetName).toPath(), StandardCopyOption.REPLACE_EXISTING); + } /** * The MetaDir is fromChkpt if it is not null. Otherwise, create a @@ -204,6 +222,11 @@ public class FileUtil * */ public static String makeMetaDir(String specDir, String fromChkpt) + { + return makeMetaDir(new Date(), specDir, fromChkpt); + } + + public static String makeMetaDir(Date date, String specDir, String fromChkpt) { if (fromChkpt != null) { @@ -216,15 +239,20 @@ public class FileUtil metadir = specDir + TLCGlobals.metaRoot + FileUtil.separator; } - SimpleDateFormat sdf = new SimpleDateFormat("yy-MM-dd-HH-mm-ss"); - metadir += sdf.format(new Date()); + SimpleDateFormat sdf; + if (Boolean.getBoolean(FileUtil.class.getName() + ".milliseconds")) { + sdf = new SimpleDateFormat("yy-MM-dd-HH-mm-ss.SSS"); + } else { + sdf = new SimpleDateFormat("yy-MM-dd-HH-mm-ss"); + } + metadir += sdf.format(date); File filedir = new File(metadir); // ensure the non-existence - Assert.check(!filedir.exists(), EC.SYSTEM_METADIR_EXISTS, metadir); + Assert.check(!filedir.exists(), EC.SYSTEM_METADIR_EXISTS, filedir.getAbsolutePath()); // ensure the dirs are created - Assert.check(filedir.mkdirs(), EC.SYSTEM_METADIR_CREATION_ERROR, metadir); + Assert.check(filedir.mkdirs(), EC.SYSTEM_METADIR_CREATION_ERROR, filedir.getAbsolutePath()); return metadir; } @@ -251,16 +279,16 @@ public class FileUtil // or name=/frob/bar/somemod // Make sure the file name ends with ".tla". - if (name.toLowerCase().endsWith(".tla")) + if (name.toLowerCase().endsWith(TLAConstants.Files.TLA_EXTENSION)) { - name = name.substring(0, name.length() - 4); + name = name.substring(0, (name.length() - TLAConstants.Files.TLA_EXTENSION.length())); } // now name=/frob/bar/somemod // filename is a path ending with .tla // sourceFilename=/frob/bar/somemod - sourceFileName = name + ".tla"; + sourceFileName = name + TLAConstants.Files.TLA_EXTENSION; // module name is =somemod sourceModuleName = name.substring(name.lastIndexOf(FileUtil.separator) + 1); @@ -333,27 +361,18 @@ public class FileUtil * retrieves a new buffered file output stream * @param name * @return + * @throws FileNotFoundException */ - public static OutputStream newBFOS(String name) + public static OutputStream newBFOS(String name) throws FileNotFoundException { - File file = new File(name); - - // LL removed file.exists() test on 10 Nov 2012 because - // it causes an error when TLC called with -dump option - // for a file that doesn't already exist. Also changed - // the error message to something more helpful. - if (file != null /* && file.exists() */) + try { - try - { - FileOutputStream fos = new FileOutputStream(file); - return fos; - } catch (FileNotFoundException e) - { - ToolIO.out.println("Error: Unable to write to file " + name); - } + return new FileOutputStream(new File(name)); + } catch (FileNotFoundException e) + { + ToolIO.out.println("Error: Unable to write to file " + name); + throw e; } - return null; } public static BufferedDataInputStream newBdFIS(boolean useGZIP, File file) throws IOException diff --git a/tlatools/src/util/FilenameToStream.java b/tlatools/src/util/FilenameToStream.java index 11dc64cb397173c415de83de4632c3f7c67e1086..87dec15b19795182ce142de425b73aaea3966467 100644 --- a/tlatools/src/util/FilenameToStream.java +++ b/tlatools/src/util/FilenameToStream.java @@ -3,6 +3,7 @@ package util; import java.io.File; +import java.util.regex.Pattern; /** @@ -14,11 +15,65 @@ import java.io.File; * * @author Leslie Lamport * @author Simon Zambrovski - * @version $Id$ */ public interface FilenameToStream { + /* + * Higher layers of TLC (and the Toolbox) have to determine if a module was + * loaded from a library location s.a. those defined by TLA_LIBRARY (see + * SimpleFilenameToStream). Thus, capture this information at module load + * time when it is known where a module was loaded from. + */ + @SuppressWarnings("serial") + public static class TLAFile extends File { + // The following regex is concerned with determining whether the provided 'parent' string to our + // parent/child constructor looks like the start of a legal absolute file path potentially including + // a drive letter. + private static final String ROOT_PATH_REGEX + = "^([a-zA-Z]+:)?" + ((File.separatorChar == '\\') ? "\\\\" : File.separator); + private static final Pattern ROOT_PATH_PATTERN = Pattern.compile(ROOT_PATH_REGEX); + + + private final boolean isLibraryModule; + private transient final FilenameToStream resolver; + + public TLAFile(String pathname, FilenameToStream fts) { + this(pathname, false, fts); + } + + public TLAFile(String pathname, boolean isLibraryModule, FilenameToStream fts) { + super(pathname); + this.isLibraryModule = isLibraryModule; + this.resolver = fts; + } + + public TLAFile(String parent, String child, FilenameToStream fts) { + super(parent, + ((ROOT_PATH_PATTERN.matcher(parent).find() && child.startsWith(parent)) + ? child.substring(parent.length()) + : child)); + this.isLibraryModule = false; + this.resolver = fts; + } + + public boolean isLibraryModule() { + return isLibraryModule; + } + + /** + * @return null if no TLC module override for this module exists. + */ + public File getModuleOverride() { + final File moduleOverride = resolver.resolve(getName().replaceAll(".tla$", ".class"), false); + if (moduleOverride.exists()) { + // resolve(...) return a File instance even if the file doesn't exist. + return moduleOverride; + } + return null; + } + } + /** * This method resolves the logical name to the OS-resource * @param filename @@ -39,6 +94,22 @@ public interface FilenameToStream * * @param moduleName * @return + * @see tla2sany.modanalyzer.ParseUnit.isLibraryModule() + * @see StandardModules.isDefinedInStandardModule() */ public boolean isStandardModule(String moduleName) ; + + default boolean isLibraryModule(String moduleName) { + return isStandardModule(moduleName); + } + + static final String TMPDIR = System.getProperty("java.io.tmpdir"); + + static boolean isInJar(final String aString) { + return aString.startsWith("jar:") || aString.endsWith(".jar"); + } + + static boolean isArchive(String aString) { + return isInJar(aString) || aString.endsWith(".zip"); + } } diff --git a/tlatools/src/util/IDataInputStream.java b/tlatools/src/util/IDataInputStream.java new file mode 100644 index 0000000000000000000000000000000000000000..90d944f5f94f6dc055bddb176246bd83b9540bd7 --- /dev/null +++ b/tlatools/src/util/IDataInputStream.java @@ -0,0 +1,37 @@ +/******************************************************************************* + * Copyright (c) 2019 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package util; + +import java.io.EOFException; +import java.io.IOException; + +public interface IDataInputStream { + + int readInt() throws IOException, EOFException; + + String readString(int slen) throws IOException; + +} diff --git a/tlatools/src/util/IDataOutputStream.java b/tlatools/src/util/IDataOutputStream.java new file mode 100644 index 0000000000000000000000000000000000000000..d4ac8bb7c36e2c9f4bcb36766bd8893e8385ab8c --- /dev/null +++ b/tlatools/src/util/IDataOutputStream.java @@ -0,0 +1,36 @@ +/******************************************************************************* + * Copyright (c) 2019 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package util; + +import java.io.IOException; + +public interface IDataOutputStream { + + void writeInt(int varLoc) throws IOException; + + void writeString(String s) throws IOException; + +} diff --git a/tlatools/src/util/InternTable.java b/tlatools/src/util/InternTable.java index aec111a2f4ac973b67ab766bd22d43e244f48945..92dba8b5348ee9d5c72ca5a59ebfff6131b478f3 100644 --- a/tlatools/src/util/InternTable.java +++ b/tlatools/src/util/InternTable.java @@ -6,6 +6,8 @@ import java.io.EOFException; import java.io.File; import java.io.IOException; import java.io.Serializable; +import java.util.HashMap; +import java.util.Map; import tlc2.output.EC; import tlc2.tool.distributed.InternRMI; @@ -196,4 +198,26 @@ public final class InternTable implements Serializable this.internSource = source; } + public UniqueString find(final String str) { + for (int i = 0; i < this.table.length; i++) + { + UniqueString var = this.table[i]; + if (var != null && str.equals(var.toString())) + { + return var; + } + } + return null; + } + + public final Map<String, UniqueString> toMap() { + final Map<String, UniqueString> map = new HashMap<String, UniqueString>(); + for (int i = 0; i < this.table.length; i++) { + UniqueString var = this.table[i]; + if (var != null) { + map.put(var.toString(), var); + } + } + return map; + } } diff --git a/tlatools/src/util/MailSender.java b/tlatools/src/util/MailSender.java index a9e8a0d09063d8fd7c1fa92afbd1c7e41d6b6033..c4e36a820d19525ac9649ef9fb9845c86bfe460d 100644 --- a/tlatools/src/util/MailSender.java +++ b/tlatools/src/util/MailSender.java @@ -3,19 +3,21 @@ package util; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; +import java.io.IOException; import java.io.PrintStream; import java.net.InetAddress; import java.net.UnknownHostException; import java.util.ArrayList; import java.util.Collections; +import java.util.Date; import java.util.List; import java.util.Properties; +import java.util.Scanner; -import javax.activation.DataHandler; -import javax.activation.FileDataSource; import javax.mail.Message; import javax.mail.MessagingException; import javax.mail.Multipart; +import javax.mail.SendFailedException; import javax.mail.Session; import javax.mail.Transport; import javax.mail.internet.AddressException; @@ -28,48 +30,78 @@ import javax.naming.directory.Attribute; import javax.naming.directory.Attributes; import javax.naming.directory.InitialDirContext; -// Requires Java >=6 due to javax.activation only part starting with 6 +import model.ModelInJar; +import tlc2.output.MP; + public class MailSender { + public static final String MODEL_NAME = "modelName"; + public static final String SPEC_NAME = "specName"; + public static final String MAIL_ADDRESS = "result.mail.address"; + /** * @param from "Foo bar <foo@bar.com>" * @param to An email address _with_ domain part (foo@bar.com) - * @param domain domain part (bar.com). Used to lookup mx records * @param subject * @param messages */ - private static boolean send(final String from, final String to, - final String domain, final String subject, final File[] files) { + private static boolean send(final InternetAddress from, final InternetAddress to, final String subject, final String body, final File[] files) { + // https://javaee.github.io/javamail/docs/api/com/sun/mail/smtp/package-summary.html final Properties properties = System.getProperties(); //properties.put("mail.debug", "true"); + if (!to.getAddress().contains("@")) { + // no domain, no MX record to lookup + return false; + } else if (!to.getAddress().endsWith("localhost")) { + // Prefer email to be delivered encrypted (assumes to lower likelihood of SMTP + // rejection or classification as spam too). Falls back to plain text if SMTP + // server does not support starttls. Only activate unless sending to localhost + // which is the (postfix) SMTP running locally which only has a self-signed + // certificate which gets rejected by java mail. + properties.put("mail.smtp.starttls.enable", "true"); + } + + List<MXRecord> mailhosts; try { - final List<MXRecord> mailhosts = getMXForDomain(domain); - - // retry all mx host - for (MXRecord mxRecord : mailhosts) { - properties.put("mail.smtp.host", mxRecord.hostname); + mailhosts = getMXForDomain(to.getAddress().split("@")[1]); + } catch (NamingException e) { + e.printStackTrace(); + return false; + } + // retry all mx host + for (int i = 0; i < mailhosts.size(); i++) { + final MXRecord mxRecord = mailhosts.get(i); + properties.put("mail.smtp.host", mxRecord.hostname); + try { final Session session = Session.getDefaultInstance(properties); final Message msg = new MimeMessage(session); - msg.setFrom(new InternetAddress(from)); - msg.addRecipient(Message.RecipientType.TO, new InternetAddress(to)); + msg.setFrom(from); + msg.addRecipient(Message.RecipientType.TO, to); msg.setSubject(subject); // not sure why the extra body part is needed here MimeBodyPart messageBodyPart = new MimeBodyPart(); - + final Multipart multipart = new MimeMultipart(); - + + // The main body part. Having a main body appears to have a very + // positive effect on the spam score compared to emails with + // just attachments. It is also visually more appealing to e.g. + // Outlook users who otherwise see an empty mail. + messageBodyPart = new MimeBodyPart(); + messageBodyPart.setContent(body, "text/plain"); + multipart.addBodyPart(messageBodyPart); + // attach file(s) for (File file : files) { if (file == null) { continue; } messageBodyPart = new MimeBodyPart(); - messageBodyPart.setDataHandler(new DataHandler( - new FileDataSource(file))); + messageBodyPart.attachFile(file); messageBodyPart.setFileName(file.getName()); messageBodyPart.setHeader("Content-Type", "text/plain"); multipart.addBodyPart(messageBodyPart); @@ -78,16 +110,41 @@ public class MailSender { Transport.send(msg); return true; + } catch (SendFailedException e) { + final Exception next = e.getNextException(); + if (next != null && next.getMessage() != null && next.getMessage().toLowerCase().contains("greylist") + && !properties.containsKey((String) properties.get("mail.smtp.host") + ".greylisted")) { + // mark receiver as greylisted to not retry over and over again. + properties.put((String) properties.get("mail.smtp.host") + ".greylisted", "true"); + throttleRetry(String.format( + "%s EMail Report: Detected greylisting when sending to %s at %s, will retry in %s minutes...", + new Date(), to.getAddress(), mxRecord.hostname, 10L), 10L); + i = i - 1; + } else { + throttleRetry(String.format( + "%s EMail Report: Slowing down due to errors when sending to %s at %s, will continue in %d minute...", + new Date(), to.getAddress(), mxRecord.hostname, 1L), 1L); + } + } catch (AddressException e) { + e.printStackTrace(); + } catch (MessagingException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); } - } catch (NamingException e) { - e.printStackTrace(); - } catch (AddressException e) { - e.printStackTrace(); - } catch (MessagingException e) { - e.printStackTrace(); } return false; } + + private static void throttleRetry(final String msg, long minutes) { + try { + System.err.println(msg); + System.out.println(msg); + Thread.sleep(minutes * 60L * 1000L); + } catch (InterruptedException e1) { + e1.printStackTrace(); + } + } private static List<MXRecord> getMXForDomain(String aDomain) throws NamingException { final InitialDirContext ctx = new InitialDirContext(); @@ -100,16 +157,16 @@ public class MailSender { // RFC 974 if (attr == null) { list.add(new MXRecord(0, aDomain)); - } - - // split pref from hostname - for (int i = 0; i < attr.size(); i++) { - Object object = attr.get(i); - if (object != null && object instanceof String) { - String[] split = ((String) object).split("\\s+"); - if (split != null && split.length == 2) { - Integer weight = Integer.parseInt(split[0]); - list.add(new MXRecord(weight, split[1])); + } else { + // split pref from hostname + for (int i = 0; i < attr.size(); i++) { + Object object = attr.get(i); + if (object != null && object instanceof String) { + String[] split = ((String) object).split("\\s+"); + if (split != null && split.length == 2) { + Integer weight = Integer.parseInt(split[0]); + list.add(new MXRecord(weight, split[1])); + } } } } @@ -133,57 +190,110 @@ public class MailSender { return weight.compareTo(o.weight); } } + + // For testing only. + public static void main(String[] args) throws AddressException, FileNotFoundException, UnknownHostException { + MailSender mailSender = new MailSender(); + mailSender.send(); + } - // if null, no Mail is going to be send - private final String mailto; - private String mainFile; + private String modelName = "unknown model"; + private String specName = "unknown spec"; private File err; private File out; - private String from; - private String domain; + // if null, no Mail is going to be send + private InternetAddress[] toAddresses; + private InternetAddress from; + private InternetAddress fromAlt; - public MailSender(String aMainFile) throws FileNotFoundException, UnknownHostException - { - mailto = System.getProperty("result.mail.address"); + public MailSender() throws FileNotFoundException, UnknownHostException, AddressException { + ModelInJar.loadProperties(); // Reads result.mail.address and so on. + final String mailto = System.getProperty(MAIL_ADDRESS); if (mailto != null) { - domain = mailto.split("@")[1]; + this.toAddresses = InternetAddress.parse(mailto); - from = "TLC - The friendly model checker <" + this.from = new InternetAddress("TLC - The friendly model checker <" + + toAddresses[0].getAddress() + ">"); + this.fromAlt = new InternetAddress("TLC - The friendly model checker <" + System.getProperty("user.name") + "@" - + InetAddress.getLocalHost().getHostName() + ">"; - - mainFile = aMainFile; + + InetAddress.getLocalHost().getHostName() + ">"); // Record/Log output to later send it by email final String tmpdir = System.getProperty("java.io.tmpdir"); - out = new File(tmpdir + File.separator + "MC.out"); + this.out = new File(tmpdir + File.separator + TLAConstants.Files.MODEL_CHECK_OUTPUT_FILE); ToolIO.out = new LogPrintStream(out); - err = new File(tmpdir + File.separator + "MC.err"); - ToolIO.err = new LogPrintStream(err); + this.err = new File(tmpdir + File.separator + TLAConstants.Files.MODEL_CHECK_ERROR_FILE); + ToolIO.err = new ErrLogPrintStream(err); } } + public MailSender(String mainFile) throws FileNotFoundException, UnknownHostException, AddressException { + this(); + setModelName(mainFile); + } + + public void setModelName(String modelName) { + this.modelName = modelName; + } + + public void setSpecName(String specName) { + this.specName = specName; + } + public boolean send() { return send(new ArrayList<File>()); } public boolean send(List<File> files) { - if (mailto != null) { + if (toAddresses != null) { + files.add(0, out); // Only add the err file if there is actually content - files.add(out); if (err.length() != 0L) { - files.add(err); + files.add(0, err); } - // Try sending the mail with the model checking result to the receiver - return send(from, mailto, domain, "Model Checking result for " + mainFile, - files.toArray(new File[files.size()])); + // Try sending the mail with the model checking result to the receivers. Returns + // true if a least one email was delivered successfully. + boolean success = false; + for (final InternetAddress toAddress : toAddresses) { + if (send(from, toAddress, "Model Checking result for " + modelName + " with spec " + specName, + extractBody(out), files.toArray(new File[files.size()]))) { + success = true; + } else if (send(fromAlt, toAddress, "Model Checking result for " + modelName + " with spec " + specName, + extractBody(out), files.toArray(new File[files.size()]))) { + // Try with alternative from address which some receivers might actually accept. + success = true; + } + } + return success; } else { // ignore, just signal everything is fine return true; } } + /** + * @return The human readable lines in the log file. + */ + private String extractBody(File out) { + StringBuffer result = new StringBuffer(); + try { + final Scanner scanner = new Scanner(out); + while (scanner.hasNext()) { + final String line = scanner.nextLine(); + if (line != null && !line.startsWith(MP.DELIM)) { + result.append(line); + result.append("\n"); + } + } + scanner.close(); + } catch (FileNotFoundException e) { + e.printStackTrace(); + result.append("Failed to find file " + out.getAbsolutePath()); + } + return result.toString(); + } + /** * A LogPrintStream writes the logging statements to a file _and_ to * System.out. @@ -193,20 +303,26 @@ public class MailSender { public LogPrintStream(File file) throws FileNotFoundException { super(new FileOutputStream(file)); } - + /* (non-Javadoc) - * @see java.io.PrintStream#print(java.lang.String) + * @see java.io.PrintStream#println(java.lang.String) */ - public void print(String str) { - System.out.print(str); - super.print(str); + public void println(String str) { + System.out.println(str); + super.println(str); } + } + + private static class ErrLogPrintStream extends PrintStream { + public ErrLogPrintStream(File file) throws FileNotFoundException { + super(new FileOutputStream(file)); + } /* (non-Javadoc) * @see java.io.PrintStream#println(java.lang.String) */ public void println(String str) { - System.out.println(str); + System.err.println(str); super.println(str); } } diff --git a/tlatools/src/util/NamedInputStream.java b/tlatools/src/util/NamedInputStream.java index e635475b00a543ad329bacfe8a621bcdccaae0e7..273dcd4dd0f7c6c248bce436c5a11d427d92dde0 100644 --- a/tlatools/src/util/NamedInputStream.java +++ b/tlatools/src/util/NamedInputStream.java @@ -6,6 +6,7 @@ import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; +import java.nio.file.Path; // SZ Feb 20, 2009: moved to util and reformatted @@ -65,6 +66,15 @@ public class NamedInputStream extends FileInputStream { return inputFile; } + + /** + * @return The absolute, resolved path. In case a file is symlinked, resolve the + * final target (doesn't work for cygwin symlinks but for mklink) + * @throws IOException + */ + public final Path getAbsoluteResolvedPath() throws IOException { + return inputFile.toPath().toRealPath(); + } public final String toString() { diff --git a/tlatools/src/util/SimpleFilenameToStream.java b/tlatools/src/util/SimpleFilenameToStream.java index 62a5ce87528532291efbb9ead56883e07566664b..e1a088cb9c7de6f49417e9eaf3c407ec3d28f931 100644 --- a/tlatools/src/util/SimpleFilenameToStream.java +++ b/tlatools/src/util/SimpleFilenameToStream.java @@ -36,7 +36,6 @@ public class SimpleFilenameToStream implements FilenameToStream { private static final ClassLoader cl = SimpleFilenameToStream.class.getClassLoader(); - private static final String TMPDIR = System.getProperty("java.io.tmpdir"); private static final String STANDARD_MODULES = "tla2sany" + '/' + STANDARD_MODULES_FOLDER + '/'; @@ -51,6 +50,10 @@ public class SimpleFilenameToStream implements FilenameToStream { libraryPaths = getLibraryPaths(getInstallationBasePath(), null); } + public SimpleFilenameToStream(final String libraryPath) { + this(new String[] {libraryPath}); + } + /** * August 2014 - TL * This constructor was on the interface but was not implemented. @@ -75,7 +78,7 @@ public class SimpleFilenameToStream implements FilenameToStream { final String path = url.toString(); try { // convert to URI which handles paths correctly (even OS dependently) - if(!isInJar(path)) { + if(!FilenameToStream.isInJar(path)) { final URI uri = new URI(path); return new File(uri).getAbsolutePath(); } @@ -91,10 +94,6 @@ public class SimpleFilenameToStream implements FilenameToStream { return path; } - private static boolean isInJar(String aString) { - return aString.startsWith("jar:"); - } - /** * August 2014 - TL * added an informative method for returning the actual path used by this resolver @@ -189,10 +188,11 @@ public class SimpleFilenameToStream implements FilenameToStream { * field in util/ToolIO. * ***********************************************************************/ int idx = 0; + InputStream is; while (true) { if ((idx == 0) && (ToolIO.getUserDir() != null)) { - sourceFile = new File(ToolIO.getUserDir(), name ); + sourceFile = new TLAFile(ToolIO.getUserDir(), name, this ); } else { @@ -202,38 +202,31 @@ public class SimpleFilenameToStream implements FilenameToStream { // // This would be a lot simpler if TLC would not depend on // File but on InputStream instead - if(isInJar(prefix)) { - InputStream is = cl - .getResourceAsStream(STANDARD_MODULES - + name); - + if(FilenameToStream.isInJar(prefix)) { + is = cl.getResourceAsStream(STANDARD_MODULES + name); if(is != null) { - try { - sourceFile = new File(TMPDIR + File.separator + name); - sourceFile.deleteOnExit(); - - FileOutputStream fos = new FileOutputStream(sourceFile); - - byte buf[] = new byte[1024]; - int len; - while ((len = is.read(buf)) > 0) { - fos.write(buf, 0, len); - } - fos.close(); - is.close(); - } catch (IOException e) { - // must not happen - e.printStackTrace(); - } + sourceFile = read(name, is); } } else { - sourceFile = new File( prefix + name ); + sourceFile = new TLAFile( prefix + name, true, this ); } } // Debug // System.out.println("Looking for file " + sourceFile); if ( sourceFile.exists() ) break; - if (idx >= libraryPaths.length) break; + if (idx >= libraryPaths.length) { + // As a last resort, try to load resource from the Java classpath. Give up, if it + // fails. + // The use case for this strategy is to load additional TLA+ module collections + // - e.g. community-driven ones - which ship a single jar containing the .tla + // operator definitions as well as Java module overwrites as .class files. + is = cl.getResourceAsStream(name); + if(is != null) { + return read(name, is); + } else { + break; + } + } prefix = libraryPaths[idx++]; } // end while @@ -241,6 +234,27 @@ public class SimpleFilenameToStream implements FilenameToStream { } // end locate() + private File read(String name, InputStream is) { + final File sourceFile = new TLAFile(TMPDIR + File.separator + name, true, this); + sourceFile.deleteOnExit(); + try { + + final FileOutputStream fos = new FileOutputStream(sourceFile); + + byte buf[] = new byte[1024]; + int len; + while ((len = is.read(buf)) > 0) { + fos.write(buf, 0, len); + } + fos.close(); + is.close(); + } catch (IOException e) { + // must not happen + e.printStackTrace(); + } + return sourceFile; + } + /** * Returns a file * obtained by some standard method from the string name. For example, @@ -266,11 +280,11 @@ public class SimpleFilenameToStream implements FilenameToStream { { // SZ Feb 20, 2009: // Make sure the file name ends with ".tla". - if (name.toLowerCase().endsWith(".tla")) + if (name.toLowerCase().endsWith(TLAConstants.Files.TLA_EXTENSION)) { - name = name.substring(0, name.length() - 4); + name = name.substring(0, (name.length() - TLAConstants.Files.TLA_EXTENSION.length())); } - sourceFileName = name + ".tla"; + sourceFileName = name + TLAConstants.Files.TLA_EXTENSION; } else { // SZ Feb 20, 2009: for other files @@ -295,6 +309,8 @@ public class SimpleFilenameToStream implements FilenameToStream { * This method is used to set the isStandard field of the module's ModuleNode. * I don't know if this is every called with a module that * Added by LL on 24 July 2013. + * @see tla2sany.modanalyzer.ParseUnit.isLibraryModule() + * @see StandardModules.isDefinedInStandardModule() */ public boolean isStandardModule(String moduleName) { File file = this.resolve(moduleName, true) ; diff --git a/tlatools/src/util/StringHelper.java b/tlatools/src/util/StringHelper.java new file mode 100644 index 0000000000000000000000000000000000000000..8c13a928c5400c3d163bbe5a7fd4fb4f18b75edc --- /dev/null +++ b/tlatools/src/util/StringHelper.java @@ -0,0 +1,149 @@ +package util; + +/** + * Contains some useful methods for manipulating strings. + * + * @author lamport + */ +public class StringHelper { + /* + * The following defines newline to be a new-line character in whatever + * system this is. + */ + public static String PLATFORM_NEWLINE = System.getProperty("line.separator"); + + /** + * Returns the result of concatenating 'copies' copies of string str, + * or the empty string if copies < 0. + */ + /* + * It uses the following algorithm, which produces a sequence of 1s + * of length 'copies'. + * + * --algorithm copy { + * variables copies \in -5..100, \* input: The number of repetitions + * result = << >>, \* set to the output + * powerOf2Copies = << 1 >>, \* 2^k copies of the input, for some k + * remaining = copies ; \* see the invariant in the assert statement + * { while (remaining > 0) + * { assert copies = remaining * Len(powerOf2Copies) + Len(result) ; + * if (remaining % 2 # 0) { result := result \o powerOf2Copies }; + * remaining := remaining \div 2; + * if (remaining # 0) {powerOf2Copies := powerOf2Copies \o powerOf2Copies ;} ; + * }; + * assert (copies > 0) => (copies = Len(result)) ; + * } + * } + */ + public static final String copyString(final String str, final int copies) { + String result = ""; + String powerOf2Copies = str; + int remaining = copies; + while (remaining > 0) { + if (remaining %2 != 0) { + result = result + powerOf2Copies; + } + remaining = remaining / 2; + if (remaining != 0) { + powerOf2Copies = powerOf2Copies + powerOf2Copies; + } + } + return result; + } + + /** + * Returns true if the string str contains only whitespace + */ + public static final boolean onlySpaces(final String str) { + return str.trim().equals(""); + } + + /** + * Returns str with any leading whitespace removed. + */ + public static final String trimFront(final String str) { + int position = 0; + while ((position < str.length()) && + Character.isWhitespace(str.charAt(position))) { + position++; + } + return str.substring(position, str.length()); + + // Alternatively + //return s.replaceAll("^\\s+", ""); + } + + /** + * Returns str with any terminating whitespace removed. + */ + public static final String trimEnd(final String str) { + int position = str.length(); + while ((position > 0) && + Character.isWhitespace(str.charAt(position - 1))) { + position--; + } + return str.substring(0, position); + + // Alternatively + //return s.replaceAll("\\s+$", ""); + } + + /** + * Returns the number of leading spaces in the string str. + * @param str + * @return + */ + public static final int leadingSpaces(final String str) { + return str.length() - trimFront(str).length() ; + } + + /** + * Prints the elements of the array, one per line, enclosed between + * *- and -*, except with the first line enclosed with 0- and -0. + */ + public static final void printArray(final Object[] array) { + if (array == null) { + System.out.println("null array"); + return; + } + if (array.length == 0) { + System.out.println("zero-length array"); + return; + } + System.out.println("0-" + array[0].toString() + "-0"); + for (int i = 1; i < array.length; i++) { + System.out.println("*-" + array[i].toString() + "-*"); + } + } + + /** + * Returns the sequence of words contained in str, where + * a word is any sequence of non-space characters. + * @param str + * @return + */ + public static final String[] getWords(final String str) { + final String[] result = trimFront(str).split("\\s+") ; + return result; + } + + /** + * Returns true iff str is a sequence of letters, "_" characters, and digits + * that is not all digits. + * + * @param str + * @return + */ + public static final boolean isIdentifier(final String str) { + boolean result = true ; + boolean allChars = true ; + int i = 0; + while (result && (i < str.length())) { + char ch = str.charAt(i) ; + result = Character.isLetterOrDigit(ch) || (ch == '_') ; + allChars = allChars && Character.isDigit(ch) ; + i++; + } + return result && (! allChars) ; + } +} diff --git a/tlatools/src/util/TLAConstants.java b/tlatools/src/util/TLAConstants.java new file mode 100644 index 0000000000000000000000000000000000000000..a8a4aaa1cae79d4bdd00da8a50ab447be472bf3a --- /dev/null +++ b/tlatools/src/util/TLAConstants.java @@ -0,0 +1,143 @@ +package util; + +/** + * A class of bits of keywords, operators, and bears. + */ +public final class TLAConstants { + public final class ANSI { + public static final String BOLD_CODE = "\033[1m"; + public static final String ITALIC_CODE = "\033[3m"; + public static final String RESET_CODE = "\033[0m"; + } + + public final class BuiltInModules { + public static final String TLC = "TLC"; + public static final String TRACE_EXPRESSIONS = "Toolbox"; + } + + public final class BuiltInOperators { + public static final String PERMUTATIONS = "Permutations"; + } + + public final class Files { + public static final String CONFIG_EXTENSION = ".cfg"; + public static final String ERROR_EXTENSION = ".err"; + public static final String OUTPUT_EXTENSION = ".out"; + public static final String TLA_EXTENSION = ".tla"; + + public static final String MODEL_CHECK_FILE_BASENAME = "MC"; + public static final String MODEL_CHECK_CONFIG_FILE = MODEL_CHECK_FILE_BASENAME + CONFIG_EXTENSION; + public static final String MODEL_CHECK_ERROR_FILE = MODEL_CHECK_FILE_BASENAME + ERROR_EXTENSION; + public static final String MODEL_CHECK_OUTPUT_FILE = MODEL_CHECK_FILE_BASENAME + OUTPUT_EXTENSION; + public static final String MODEL_CHECK_TLA_FILE = MODEL_CHECK_FILE_BASENAME + TLA_EXTENSION; + } + + public final class KeyWords { + public static final String ACTION_CONSTRAINT = "ACTION_CONSTRAINT"; + public static final String CONSTANT = "CONSTANT"; + public static final String CONSTANTS = CONSTANT + 'S'; + public static final String EXTENDS = "EXTENDS"; + public static final String INIT = "INIT"; + public static final String INVARIANT = "INVARIANT"; + public static final String MODULE = "MODULE"; + public static final String NEXT = "NEXT"; + public static final String PROPERTY = "PROPERTY"; + public static final String SPECIFICATION = "SPECIFICATION"; + public static final String SYMMETRY = "SYMMETRY"; + public static final String TRUE = "TRUE"; + public static final String UNION = "\\union"; + public static final String VARIABLE = "VARIABLE"; + } + + public final class LoggingAtoms { + public static final String PARSING_FILE = "Parsing file"; + } + + public final class Schemes { + public static final String SPEC_SCHEME = "spec"; + public static final String INIT_SCHEME = "init"; + public static final String NEXT_SCHEME = "next"; + public static final String CONSTANT_SCHEME = "const"; + public static final String SYMMETRY_SCHEME = "symm"; + public static final String DEFOV_SCHEME = "def_ov"; + public static final String CONSTRAINT_SCHEME = "constr"; + public static final String ACTIONCONSTRAINT_SCHEME = "action_constr"; + public static final String INVARIANT_SCHEME = "inv"; + public static final String PROP_SCHEME = "prop"; + public static final String VIEW_SCHEME = "view"; + public static final String CONSTANTEXPR_SCHEME = "const_expr"; + public static final String TRACE_EXPR_VAR_SCHEME = "__trace_var"; + public static final String TRACE_EXPR_DEF_SCHEME = "trace_def"; + } + + public final class TraceExplore { + public static final String ERROR_STATES_MODULE_NAME = "SpecTE"; + public static final String EXPLORATION_MODULE_NAME = "TE"; + public static final String ACTION = "_TEAction"; + public static final String POSITION = "_TEPosition"; + public static final String TRACE = "_TETrace"; + /** + * expressions to be evaluated at each state of the trace + * when the trace explorer is run + */ + public static final String TRACE_EXPLORE_EXPRESSIONS = "traceExploreExpressions"; + /** + * Conjunction of variable values in the initial state of a trace + * Should only include spec variables, not trace expression variables. + */ + public static final String TRACE_EXPLORE_INIT = "traceExploreInit"; + /** + * Disjunction of actions used for trace exploration without the trace + * expression variables. + */ + public static final String TRACE_EXPLORE_NEXT = "traceExploreNext"; + /** + * The tuple of ordered sub-action names representing the trace states. + */ + public static final String TRACE_EXPLORE_ACTION_CONSTRAINT = "traceExploreActionConstraint"; + } + + + public static final String BACK_TO_STATE = "Back to state"; + public static final String GENERATION_TIMESTAMP_PREFIX = "\\* Generated on "; + public static final String LINE = "line "; + public static final String STUTTERING = " Stuttering"; + + public static final String MODULE_OPENING_PREFIX_REGEX = "^([-]+ " + TLAConstants.KeyWords.MODULE + ") "; + public static final String MODULE_CLOSING_REGEX = "^[=]+$"; + + public static final String SPACE = " "; + public static final String INDENT = " "; + public static final String CR = "\n"; + public static final String SEP = "----"; + public static final String EQ = " = "; + public static final String ARROW = " <- "; + public static final String RECORD_ARROW = " |-> "; + public static final String DEFINES = " == "; + public static final String DEFINES_CR = " ==\n"; + public static final String COMMENT = "\\* "; + public static final String ATTRIBUTE = "@"; + public static final String COLON = ":"; + public static final String EMPTY_STRING = ""; + public static final String CONSTANT_EXPRESSION_EVAL_IDENTIFIER = "\"$!@$!@$!@$!@$!\""; + public static final String COMMA = ","; + public static final String BEGIN_TUPLE = "<<"; + public static final String END_TUPLE = ">>"; + public static final String PRIME = "'"; + public static final String QUOTE = "\""; + public static final String TLA_AND = "/\\"; + public static final String TLA_OR = "\\/"; + public static final String TLA_NOT = "~"; + public static final String TLA_EVENTUALLY_ALWAYS = "<>[]"; + public static final String TLA_INF_OFTEN = "[]<>"; + public static final String TRACE_NA = "\"--\""; + public static final String L_PAREN = "("; + public static final String R_PAREN = ")"; + public static final String L_SQUARE_BRACKET = "["; + public static final String R_SQUARE_BRACKET = "]"; + + public static final String INDENTED_CONJUNCTIVE = TLAConstants.INDENT + TLAConstants.TLA_AND + TLAConstants.SPACE; + public static final String INDENTED_DISJUNCTIVE = TLAConstants.INDENT + TLAConstants.TLA_OR + TLAConstants.SPACE; + + private TLAConstants() { } +} diff --git a/tlatools/src/util/TLAFlightRecorder.java b/tlatools/src/util/TLAFlightRecorder.java new file mode 100644 index 0000000000000000000000000000000000000000..a2ca81232961c5b29e8cc5f14b244cdf9a82fbd1 --- /dev/null +++ b/tlatools/src/util/TLAFlightRecorder.java @@ -0,0 +1,92 @@ +/******************************************************************************* + * Copyright (c) 2019 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package util; + +// Label is only used by Mission control but not relevant for the recording itself. +public class TLAFlightRecorder { + + @jdk.jfr.Label("Progress") + @jdk.jfr.Category({"TLC", "Progress"}) + @jdk.jfr.StackTrace(false)// No need to capture the stack of the reporting (main) thread when it emits the event. + private static class ProgressEvent extends jdk.jfr.Event { + @jdk.jfr.Label("States generated per minute") + @jdk.jfr.Unsigned + private long spm; + @jdk.jfr.Label("Distinct states generated per minute") + @jdk.jfr.Unsigned + private long dspm; + @jdk.jfr.Label("Unseen States") + @jdk.jfr.Unsigned + private long unseen; + @jdk.jfr.Label("Diameter") + @jdk.jfr.Unsigned + private int diameter; + @jdk.jfr.Label("States Generated") + @jdk.jfr.Unsigned + private long states; + @jdk.jfr.Label("Distinct States Generated") + @jdk.jfr.Unsigned + private long distStates; + @jdk.jfr.Label("Model Checking done") + private boolean isFinal; + } + + public static void progress(boolean isFinal, final int diameter, final long states, final long distinctStates, final long unseen, + final long statesPerMinute, final long distinctStatesPerMinute) { + try { + final ProgressEvent e = new ProgressEvent(); + e.isFinal = isFinal; + e.spm = statesPerMinute; + e.dspm = distinctStatesPerMinute; + e.diameter = diameter; + e.unseen = unseen; + e.states = states; + e.distStates = distinctStates; + e.commit(); + } catch (NoClassDefFoundError e) { + // Java 1.8 doesn't have jdk.jfr.Event and thus this flight recording is broken. + } + } + + @jdk.jfr.Label("Message") + @jdk.jfr.Category({"TLC"}) + @jdk.jfr.StackTrace(false)// No need to capture the stack of the reporting (main) thread when it emits the event. + private static class MessageEvent extends jdk.jfr.Event { + @jdk.jfr.Label("Message") + public String message; + } + + public static String message(final String message) { + try { + final MessageEvent e = new MessageEvent(); + e.message = message; + e.commit(); + } catch (NoClassDefFoundError e) { + // Java 1.8 doesn't have jdk.jfr.Event and thus this flight recording is broken. + } + return message; + } +} \ No newline at end of file diff --git a/tlatools/src/util/TLCRuntime.java b/tlatools/src/util/TLCRuntime.java index 64ceb5765f6feb8bfed7c0ea4233da98ce00b55d..93e9354e1ff0263e8315f98078f0b4d2bc8b6867 100644 --- a/tlatools/src/util/TLCRuntime.java +++ b/tlatools/src/util/TLCRuntime.java @@ -2,12 +2,19 @@ package util; +import java.lang.management.GarbageCollectorMXBean; import java.lang.management.ManagementFactory; -import java.lang.management.OperatingSystemMXBean; import java.lang.management.RuntimeMXBean; -import java.lang.reflect.Method; import java.util.List; +import javax.management.AttributeNotFoundException; +import javax.management.InstanceNotFoundException; +import javax.management.MBeanException; +import javax.management.MBeanServer; +import javax.management.MalformedObjectNameException; +import javax.management.ObjectName; +import javax.management.ReflectionException; + import tlc2.tool.fp.FPSet; /** @@ -30,33 +37,23 @@ public class TLCRuntime { } return runtime; } + private long physicalSystemMemory = -1; - + /** * @return the total amount of memory, measured in bytes. */ private long getPhysicalSystemMemory() { - - // try to read the total physical memory via a MXBean. Unfortunately, - // these methods are not meant as public API, which requires us to pull - // a visibility reflection hack. - // This hack is expected to work on Linux, Windows (up to 7) and Max OSX - final OperatingSystemMXBean operatingSystemMXBean = ManagementFactory - .getOperatingSystemMXBean(); - for (Method method : operatingSystemMXBean.getClass() - .getDeclaredMethods()) { - if (method.getName().equals("getTotalPhysicalMemorySize")) { - method.setAccessible(true); - try { - return (Long) method.invoke(operatingSystemMXBean); - } catch (Exception e) { - break; - } - } + final MBeanServer mBeanServer = ManagementFactory.getPlatformMBeanServer(); + try { + return (Long) mBeanServer.getAttribute(new ObjectName("java.lang", "type", "OperatingSystem"), + "TotalPhysicalMemorySize"); + } catch (InstanceNotFoundException | AttributeNotFoundException | MalformedObjectNameException + | ReflectionException | MBeanException | ClassCastException e) { + // as a safeguard default to the total memory available to this JVM + return Runtime.getRuntime().totalMemory(); } - // as a safeguard default to the total memory available to this JVM - return Runtime.getRuntime().totalMemory(); } /** @@ -148,4 +145,54 @@ public class TLCRuntime { } return (long) fpMemSize; } + + public enum ARCH { + x86, + x86_64; + } + + public ARCH getArchitecture() { + if (System.getProperty("sun.arch.data.model") != null + && System.getProperty("sun.arch.data.model").equals("64")) { + return ARCH.x86_64; + } + if (System.getProperty("com.ibm.vm.bitmode") != null + && System.getProperty("com.ibm.vm.bitmode").equals("64")) { + return ARCH.x86_64; + } + if (System.getProperty("java.vm.version") != null + && System.getProperty("java.vm.version").contains("_64")) { + return ARCH.x86_64; + } + return ARCH.x86; + } + + // See java.lang.ProcessHandle.current().pid() or -1 when Java version -lt 9. + @SuppressWarnings({ "unchecked", "rawtypes" }) + public long pid() { + // Once Java9 is minimum BREE, change to: + // return java.lang.ProcessHandle.current().pid(); + try { + // Get class. + final ClassLoader classLoader = getClass().getClassLoader(); + final Class aClass = classLoader.loadClass("java.lang.ProcessHandle"); + // Execute static current() + final Object o = aClass.getMethod("current").invoke(null, (Object[]) null); + // Execute instance method pid() + return (long) aClass.getMethod("pid").invoke(o, (Object[]) null); + } catch (Exception e) { + return -1; + } + } + + public boolean isThroughputOptimizedGC() { + final List<GarbageCollectorMXBean> gcs = ManagementFactory.getGarbageCollectorMXBeans(); + for (GarbageCollectorMXBean gc : gcs) { + // This might not be a reliable way to identify the currently active GC. + if ("PS Scavenge".equals(gc.getName())) { + return true; + } + } + return false; + } } diff --git a/tlatools/src/util/ToolIO.java b/tlatools/src/util/ToolIO.java index 02378d628d149a42e43f2ee472edb89268b4e7f1..6be583f9f3b7a859635899d1ddf0970b5a886aed 100644 --- a/tlatools/src/util/ToolIO.java +++ b/tlatools/src/util/ToolIO.java @@ -2,11 +2,7 @@ package util; import java.io.PipedOutputStream; import java.io.PrintStream; -import java.util.Iterator; -import java.util.LinkedList; -import java.util.List; - -import tla2sany.semantic.SemanticNode; +import java.util.Locale; /*************************************************************************** * SANY and TLC were written to communicate only by calling the * @@ -76,19 +72,13 @@ public class ToolIO * Care must be taken with out and err not being synchronized. Concurrent * writes will cause interleaved output. * - * @see https://bugzilla.tlaplus.net/show_bug.cgi?id=221 + * @see Bug #221 in general/bugzilla/index.html */ public static PrintStream err = System.err; // = 1 for testing. Should be set to reasonable value like 1000. private static final int InitialMaxLength = 1; - /** - * List of semantic nodes which are used by tools - * @see ToolIO#registerSemanticNode() - */ - private static List semanticNodes = new LinkedList(); - /** * The current sequence of messages is messages[0] ... * messages[length-1] A single message may contain \n characters. @@ -229,46 +219,6 @@ public class ToolIO ToolIO.defaultResolver = resolver; } - /** - * Registers the semantic node - * @param node the node containing tool specific information - * @param toolId the id of the tool (currently not used) - * - * <br><b>Note:</b><br> - * This method is called from {@link SemanticNode#setToolObject(int, Object)} - * and identifies the semantic node that carries tool specific information. - * This information can be deleted using {@link ToolIO#cleanToolObjects(int)} - */ - public static void registerSemanticNode(SemanticNode node, int toolId) - { - if (!semanticNodes.contains(node)) - { - semanticNodes.add(node); - } - } - - /** - * Sets the tool-specific object for all listed semantic nodes to <code>null</code> - * @param toolId the Id of the tool to reset the tool specific information - */ - public static void cleanToolObjects(int toolId) - { - Iterator iter = semanticNodes.iterator(); - while(iter.hasNext()) - { - SemanticNode node = (SemanticNode) iter.next(); - node.setToolObject(toolId, null); - } - } - - /** - * Deletes the information about semantic nodes used with tool-specific information - */ - public static void unregisterSemanticNodes() - { - semanticNodes = new LinkedList(); - } - } // class ToolIO class ToolPrintStream extends PrintStream @@ -282,8 +232,28 @@ class ToolPrintStream extends PrintStream ToolIO.out = this; ToolIO.err = this; } + + /* (non-Javadoc) + * @see java.io.PrintStream#printf(java.lang.String, java.lang.Object[]) + */ + @Override + public PrintStream printf(String format, Object... args) { + // See special logic in println. If super.printf(...) gets used, Toolbox + // functionality breaks. + throw new UnsupportedOperationException("use println instead"); + } + + /* (non-Javadoc) + * @see java.io.PrintStream#printf(java.util.Locale, java.lang.String, java.lang.Object[]) + */ + @Override + public PrintStream printf(Locale l, String format, Object... args) { + // See special logic in println. If super.printf(...) gets used, Toolbox + // functionality breaks. + throw new UnsupportedOperationException("use println instead"); + } - /** + /** * Prints a string in to the ToolIO buffer in a separate line * @param str String to be printed */ diff --git a/tlatools/src/util/UniqueString.java b/tlatools/src/util/UniqueString.java index d550b446a5d46a79a4d3608548746d92e373e7a8..f9846b218089c832b03ffa8fe5c72c27ad1ee6b6 100644 --- a/tlatools/src/util/UniqueString.java +++ b/tlatools/src/util/UniqueString.java @@ -4,6 +4,7 @@ package util; import java.io.IOException; import java.io.Serializable; +import java.util.Map; import tlc2.tool.Defns; import tlc2.tool.TLCState; @@ -260,6 +261,10 @@ public final class UniqueString implements Serializable return internTbl.put(str); } + public static UniqueString of(String str) { + return uniqueStringOf(str); + } + /** * If there exists a UniqueString object obj such that obj.getTok() * equals tok, then uidToUniqueString(i) returns obj; otherwise, @@ -279,7 +284,7 @@ public final class UniqueString implements Serializable * @return * @throws IOException */ - public final void write(BufferedDataOutputStream dos) throws IOException + public final void write(IDataOutputStream dos) throws IOException { dos.writeInt(this.tok); dos.writeInt(this. getVarLoc()); @@ -296,7 +301,7 @@ public final class UniqueString implements Serializable * * The method does not change member/class variables */ - public static UniqueString read(BufferedDataInputStream dis) throws IOException + public static UniqueString read(IDataInputStream dis) throws IOException { int tok1 = dis.readInt(); int loc1 = dis.readInt(); @@ -304,6 +309,15 @@ public final class UniqueString implements Serializable String str = dis.readString(slen); return new UniqueString(str, tok1, loc1); } + + public static UniqueString read(IDataInputStream dis, final Map<String, UniqueString> tbl) throws IOException + { + dis.readInt(); // skip, because invalid for the given internTbl + dis.readInt(); // skip, because invalid for the given internTbl + final int slen = dis.readInt(); + final String str = dis.readString(slen); + return tbl.get(str); + } /** @@ -314,5 +328,4 @@ public final class UniqueString implements Serializable { internTbl.setSource(source); } - } diff --git a/tlatools/src/util/UsageGenerator.java b/tlatools/src/util/UsageGenerator.java new file mode 100644 index 0000000000000000000000000000000000000000..6f00329bbc3999dd5c0742ab5520da602b4b5943 --- /dev/null +++ b/tlatools/src/util/UsageGenerator.java @@ -0,0 +1,373 @@ +package util; + +import java.io.PrintStream; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashSet; +import java.util.List; +import java.util.Objects; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * A helper class to print usage text for a command-line-application in a motif reminiscent of manpages. + * + * In a world where we were not concerned with jar size, i would import Apache Commons CLI and take advantage + * of those classes. + */ +public class UsageGenerator { + private static final String NAME = "NAME"; + private static final String SYNOPSIS = "SYNOPSIS"; + private static final String DESCRIPTION = "DESCRIPTION"; + private static final String OPTIONS = "OPTIONS"; + private static final String TIPS = "TIPS"; + + private static Comparator<Argument> NAME_COMPARATOR = new Comparator<Argument>() { + @Override + public int compare(final Argument a, final Argument b) { + return a.getArgumentName().compareTo(b.getArgumentName()); + } + }; + + private static Comparator<Argument> NAME_DASH_COMPARATOR = new Comparator<Argument>() { + @Override + public int compare(final Argument a, final Argument b) { + final boolean aDash = a.isDashArgument(); + final boolean bDash = b.isDashArgument(); + + if (aDash != bDash) { + return aDash ? -1 : 1; + } + + return a.getArgumentName().compareTo(b.getArgumentName()); + } + }; + + + public static void displayUsage(final PrintStream ps, final String commandName, final String version, + final String commandShortSummary, final String commandDescription, + final List<List<Argument>> commandVariants, final List<String> tips, + final char valuedArgumentsSeparator) { + ps.println(); + ps.println(generateSectionHeader(NAME)); + ps.println('\t' + commandName + " - " + commandShortSummary + + ((version != null) ? (" - " + version) : "") + "\n\n"); + + + final String boldName = markupWord(commandName, true); + + + final HashSet<Argument> arguments = new HashSet<>(); + ps.println(generateSectionHeader(SYNOPSIS)); + for (final List<Argument> variant : commandVariants) { + ps.println("\t" + generateCommandForVariant(boldName, variant, arguments, valuedArgumentsSeparator)); + } + ps.println(); + + + final String commandNameRegex = commandName + "(\\)|\\s|$)"; + final Pattern p = Pattern.compile(commandNameRegex); + final Matcher m = p.matcher(commandDescription); + final String markedUpDescription; + if (m.find()) { + final StringBuilder sb = new StringBuilder(); + + if (m.start() > 0) { + sb.append(commandDescription.substring(0, m.start())); + } + sb.append(boldName).append(m.group(1)); + + int lastEnd = m.end(); + while (m.find()) { + sb.append(commandDescription.substring(lastEnd, m.start())).append(boldName).append(m.group(1)); + lastEnd = m.end(); + } + sb.append(commandDescription.substring(lastEnd, commandDescription.length())); + + markedUpDescription = sb.toString(); + } else { + markedUpDescription = commandDescription; + } + + ps.println(generateSectionHeader(DESCRIPTION)); + ps.println("\t" + markedUpDescription.replaceAll("(\\r\\n|\\r|\\n)", "\n\t")); + ps.println(); + + + final List<Argument> orderedArguments = new ArrayList<>(arguments); + Collections.sort(orderedArguments, NAME_COMPARATOR); + ps.println(generateSectionHeader(OPTIONS)); + for (final Argument arg : orderedArguments) { + if (arg.expectsValue() || arg.isOptional() || (!arg.expectsValue() && arg.isDashArgument())) { + ps.println(generateOptionText(arg, valuedArgumentsSeparator)); + } + } + ps.println(); + + + if ((tips != null) && (tips.size() > 0)) { + ps.println(generateSectionHeader(TIPS)); + for (final String tip : tips) { + ps.println("\t" + tip.replaceAll("(\\r\\n|\\r|\\n)", "\n\t") + "\n"); + } + } + } + + private static String generateCommandForVariant(final String boldedCommandName, final List<Argument> variant, + final HashSet<Argument> arguments, + final char valuedArgumentsSeparator) { + final List<Argument> optionalSingleDashValueless = new ArrayList<>(); + final List<Argument> optionalDoubleDashValueless = new ArrayList<>(); + final List<Argument> optionalValued = new ArrayList<>(); + final List<Argument> requiredValued = new ArrayList<>(); + final List<Argument> requiredValueless = new ArrayList<>(); + + for (final Argument arg : variant) { + if (arg.expectsValue()) { + if (arg.isOptional()) { + optionalValued.add(arg); + } else { + requiredValued.add(arg); + } + } else { + if (arg.isOptional()) { + if (arg.isDashArgument()) { + optionalSingleDashValueless.add(arg); + } else if (arg.isDashDashArgument()) { + optionalDoubleDashValueless.add(arg); + } + } else { + requiredValueless.add(arg); + } + } + } + + Collections.sort(optionalSingleDashValueless, NAME_COMPARATOR); + Collections.sort(optionalDoubleDashValueless, NAME_COMPARATOR); + Collections.sort(optionalValued, NAME_COMPARATOR); + Collections.sort(requiredValued, NAME_COMPARATOR); + Collections.sort(requiredValueless, NAME_DASH_COMPARATOR); + + final StringBuilder sb = new StringBuilder(boldedCommandName); + + if (optionalSingleDashValueless.size() > 0) { + final StringBuilder concatenation = new StringBuilder("-"); + final List<Argument> nonShortArguments = new ArrayList<>(); + for (final Argument arg : optionalSingleDashValueless) { + if (arg.isShortArgument()) { + concatenation.append(arg.getDashlessArgumentName()); + } else { + nonShortArguments.add(arg); + } + } + if (concatenation.length() > 1) { + sb.append(" [").append(markupWord(concatenation.toString(), true)).append(']'); + } + + for (final Argument arg : nonShortArguments) { + sb.append(" [").append(markupWord(("-" + arg.getDashlessArgumentName()), true)); + if (arg.hasSubOptions()) { + sb.append(" [").append(arg.getSubOptions()).append("]"); + } + sb.append(']'); + } + } + + if (optionalDoubleDashValueless.size() > 0) { + for (final Argument arg : optionalDoubleDashValueless) { + sb.append(" [").append(markupWord(arg.getArgumentName(), true)); + if (arg.hasSubOptions()) { + sb.append(" [").append(arg.getSubOptions()).append("]"); + } + sb.append(']'); + } + } + + if (optionalValued.size() > 0) { + for (final Argument arg : optionalValued) { + sb.append(" [").append(markupWord(arg.getArgumentName(), true)).append(valuedArgumentsSeparator); + if (arg.hasSubOptions()) { + sb.append("[").append(arg.getSubOptions()).append("] "); + } + sb.append(markupWord(arg.getSampleValue(), false)).append(']'); + } + } + + if (requiredValued.size() > 0) { + for (final Argument arg : requiredValued) { + sb.append(" ").append(markupWord(arg.getArgumentName(), true)).append(valuedArgumentsSeparator); + sb.append(markupWord(arg.getSampleValue(), false)); + } + } + + if (requiredValueless.size() > 0) { + for (final Argument arg : requiredValueless) { + sb.append(" ").append(arg.getArgumentName()); + if (arg.hasSubOptions()) { + sb.append(" [").append(arg.getSubOptions()).append("]"); + } + } + } + + arguments.addAll(variant); + + return sb.toString(); + } + + private static String generateOptionText(final Argument argument, final char valuedArgumentsSeparator) { + final StringBuilder sb = new StringBuilder("\t"); + + sb.append(markupWord(argument.getArgumentName(), true)); + if (argument.expectsValue()) { + sb.append(valuedArgumentsSeparator).append(markupWord(argument.getSampleValue(), false)); + } + sb.append("\n\t\t").append(argument.getDescription().replaceAll("(\\r\\n|\\r|\\n)", "\n\t\t")); + + return sb.toString(); + } + + private static String generateSectionHeader(final String title) { + final StringBuilder sb = new StringBuilder(markupWord(title, true)); + + sb.append('\n'); + + return sb.toString(); + } + + /** + * @param bold if true, the word will be bolded; false, the word will be italicized + */ + private static String markupWord(final String word, final boolean bold) { + final StringBuilder sb = new StringBuilder(bold ? TLAConstants.ANSI.BOLD_CODE : TLAConstants.ANSI.ITALIC_CODE); + + sb.append(word).append(TLAConstants.ANSI.RESET_CODE); + + return sb.toString(); + } + + + public static class Argument { + private final String argumentName; + private final String sampleValue; + private final String description; + private final boolean optional; + private final String subOptions; + + /** + * This calls {@code this(key, optionDescription, false);} + * + * @param key + * @param optionDescription + */ + public Argument(final String key, final String optionDescription) { + this(key, optionDescription, false); + } + + public Argument(final String key, final String optionDescription, final boolean isOptional) { + this(key, null, optionDescription, isOptional); + } + + /** + * This calls {@code this(key, exampleValue, optionDescription, false);} + */ + public Argument(final String key, final String exampleValue, final String optionDescription) { + this(key, exampleValue, optionDescription, false); + } + + public Argument(final String key, final String exampleValue, final String optionDescription, + final boolean isOptional) { + this(key, exampleValue, optionDescription, isOptional, null); + } + + public Argument(final String key, final String exampleValue, final String optionDescription, + final boolean isOptional, final String concatenatedSuboptions) { + argumentName = key; + sampleValue = exampleValue; + description = optionDescription; + optional = isOptional; + subOptions = concatenatedSuboptions; + } + + public boolean isOptional() { + return optional; + } + + public boolean expectsValue() { + return (sampleValue != null); + } + + /** + * @return if the argument name starts with "-", but not "--", this returns true + */ + public boolean isDashArgument() { + return argumentName.startsWith("-") && !isDashDashArgument(); + } + + public boolean isDashDashArgument() { + return argumentName.startsWith("--"); + } + + /** + * @return true if the argument name is of length 1 (two if this is a dash argument) + */ + public boolean isShortArgument() { + return ((isDashArgument() && (argumentName.length() == 2)) || (argumentName.length() == 1)); + } + + public boolean hasSubOptions() { + return (subOptions != null); + } + + public String getArgumentName() { + return argumentName; + } + + /** + * @return if {@link #isDashArgument()} returns true, this retuns the argument name without the prefacing dash, + * otherwise this returns the entire argument name + */ + public String getDashlessArgumentName() { + if (isDashArgument()) { + return argumentName.substring(1); + } + + return argumentName; + } + + public String getSampleValue() { + return sampleValue; + } + + public String getDescription() { + return description; + } + + public String getSubOptions() { + return subOptions; + } + + @Override + public int hashCode() { + return Objects.hash(argumentName); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + + if (obj == null) { + return false; + } + + if (getClass() != obj.getClass()) { + return false; + } + + final Argument other = (Argument) obj; + return Objects.equals(argumentName, other.argumentName); + } + } +} diff --git a/tlatools/test-benchmark/ModuleOverwrites.java b/tlatools/test-benchmark/ModuleOverwrites.java new file mode 100644 index 0000000000000000000000000000000000000000..b1f922db66bbacfebdc3a407fe169e85ace5e495 --- /dev/null +++ b/tlatools/test-benchmark/ModuleOverwrites.java @@ -0,0 +1,36 @@ + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +import tlc2.value.IValue; +import tlc2.value.impl.BoolValue; +import tlc2.value.impl.FcnRcdValue; +import tlc2.value.impl.IntValue; + +public class ModuleOverwrites { + + public static IValue noDupesOverwrite(final FcnRcdValue frv, final IntValue exclude) { + // LET sub == SelectSeq(t, LAMBDA e: e # emp) + // IN ... + final List<IValue> filtered = Arrays.asList(frv.values).stream().filter(e -> e != exclude).collect(Collectors.toList()); + + // IF Len(sub) < 2 THEN TRUE ... + if (filtered.size() < 2) { + return BoolValue.ValTrue; + } + + // ~n^2: + // \A i \in 1..(Len(sub) - 1): + // \A j \in (i+1)..Len(sub): + // abs(sub[i]) # abs(sub[j]) + for (int i = 0; i < filtered.size() - 1; i++) { + for (int j = i + 1; j < filtered.size(); j++) { + if (filtered.get(i) == filtered.get(j)) { + return BoolValue.ValFalse; + } + } + } + return BoolValue.ValTrue; + } +} diff --git a/tlatools/test-benchmark/README.txt b/tlatools/test-benchmark/README.txt new file mode 100644 index 0000000000000000000000000000000000000000..a8a9061c62999338033248ad375fb31143b6c6d8 --- /dev/null +++ b/tlatools/test-benchmark/README.txt @@ -0,0 +1,18 @@ +Run JMH benchmarks from inside Eclipse: +--------------------------------------- + +a) Activate Annotation Processing in the project preferences of the tlatools project under "Java Compiler" > "Annotation Processing" +b) Add the two jars from lib/jmh/jmh-*.jar as annotation processors to "Java Compiler" > "Annotation Processing" > "Factory Path" +c) Add lib/jmh/commons-math3-*.jar to the launch configs classpath +d) Add a main to the benchmark as shown in the various JMH examples (https://hg.openjdk.java.net/code-tools/jmh/file/tip/jmh-samples/src/main/java/org/openjdk/jmh/samples/) + +Run JMH benchmarks from ant (customBuilds.xml): +----------------------------------------------- + +ant -f customBuild.xml compile compile-test benchmark && +java -jar target/benchmarks.jar -wi 1 -i1 -f1 \ +-rf json \ +-rff DiskQueueBenachmark-$(date +%s)-$(git rev-parse --short HEAD).json \ +-jvmArgsPrepend "-ea -Xms8192m -Xmx8192m" \ +-jvmArgsAppend "-Dtlc2.tool.ModuleOverwritesBenchmark.base=/home/markus/src/TLA/tla/tlatools/test-model" \ +tlc2.tool.queue.DiskQueueBenachmark diff --git a/tlatools/test-benchmark/heapstats.jfc b/tlatools/test-benchmark/heapstats.jfc new file mode 100644 index 0000000000000000000000000000000000000000..5700f60b04b99ac85c9bc2f340746bc6f5bb91de --- /dev/null +++ b/tlatools/test-benchmark/heapstats.jfc @@ -0,0 +1,550 @@ +<?xml version="1.0" encoding="UTF-8"?> +<configuration version="1.0" name="heapstats" description="" provider="Oracle"> + + <producer uri="http://www.oracle.com/hotspot/jvm/" label="Oracle JDK"> + + <control> + + <selection name="gc-level" default="all" label="Garbage Collector"> + <option label="Off" name="off">off</option> + <option label="Normal" name="detailed">normal</option> + <option label="All" name="all">all</option> + </selection> + + <condition name="gc-enabled-normal" true="true" false="false"> + <or> + <test name="gc-level" operator="equal" value="normal"/> + <test name="gc-level" operator="equal" value="all"/> + </or> + </condition> + + <condition name="gc-enabled-all" true="true" false="false"> + <test name="gc-level" operator="equal" value="all"/> + </condition> + + <selection name="compiler-level" default="normal" label="Compiler"> + <option label="Off" name="off">off</option> + <option label="Normal" name="normal">normal</option> + <option label="Detailed" name="detailed">detailed</option> + <option label="All" name="all">all</option> + </selection> + + <condition name="compiler-enabled" true="false" false="true"> + <test name="compiler-level" operator="equal" value="off"/> + </condition> + + <condition name="compiler-enabled-failure" true="true" false="false"> + <or> + <test name="compiler-level" operator="equal" value="detailed"/> + <test name="compiler-level" operator="equal" value="all"/> + </or> + </condition> + + <condition name="compiler-sweeper-threshold" true="0 ms" false="100 ms"> + <test name="compiler-level" operator="equal" value="all"/> + </condition> + + <condition name="compiler-compilation-threshold" true="1000 ms"> + <test name="compiler-level" operator="equal" value="normal"/> + </condition> + + <condition name="compiler-compilation-threshold" true="100 ms"> + <test name="compiler-level" operator="equal" value="detailed"/> + </condition> + + <condition name="compiler-compilation-threshold" true="0 ms"> + <test name="compiler-level" operator="equal" value="all"/> + </condition> + + <condition name="compiler-phase-threshold" true="60 s"> + <test name="compiler-level" operator="equal" value="normal"/> + </condition> + + <condition name="compiler-phase-threshold" true="10 s"> + <test name="compiler-level" operator="equal" value="detailed"/> + </condition> + + <condition name="compiler-phase-threshold" true="0 s"> + <test name="compiler-level" operator="equal" value="all"/> + </condition> + + <selection name="method-sampling-interval" default="maximum" label="Method Sampling"> + <option label="Off" name="off">999 d</option> + <option label="Normal" name="normal">20 ms</option> + <option label="Maximum" name="maximum">10 ms</option> + </selection> + + <condition name="method-sampling-enabled" true="false" false="true"> + <test name="method-sampling-interval" operator="equal" value="999 d"/> + </condition> + + <selection name="thread-dump-interval" default="everySecond" label="Thread Dump"> + <option label="Off" name="off">999 d</option> + <option label="At least Once" name="normal">everyChunk</option> + <option label="Every 60 s" name="everyMinute">60 s</option> + <option label="Every 10 s" name="everyTenSecond">10 s</option> + <option label="Every 1 s" name="everySecond">1 s</option> + </selection> + + <condition name="thread-dump-enabled" true="false" false="true"> + <test name="thread-dump-interval" operator="equal" value="999 d"/> + </condition> + + <selection name="exception-level" default="errors" label="Exceptions"> + <option label="Off" name="off">off</option> + <option label="Errors Only" name="errors">errors</option> + <option label="All Exceptions, including Errors" name="all">all</option> + </selection> + + <condition name="enable-errors" true="true" false="false"> + <or> + <test name="exception-level" operator="equal" value="errors"/> + <test name="exception-level" operator="equal" value="all"/> + </or> + </condition> + + <condition name="enable-exceptions" true="true" false="false"> + <test name="exception-level" operator="equal" value="all"/> + </condition> + + <text name="synchronization-threshold" label="Synchronization Threshold" contentType="timespan" minimum="0 s">10 ms</text> + + <text name="file-io-threshold" label="File I/O Threshold" contentType="timespan" minimum="0 s">10 ms</text> + + <text name="socket-io-threshold" label="Socket I/O Threshold" contentType="timespan" minimum="0 s">10 ms</text> + + <flag name="heap-statistics-enabled" label="Heap Statistics">true</flag> + + <flag name="class-loading-enabled" label="Class Loading">false</flag> + + <flag name="allocation-profiling-enabled" label="Allocation Profiling">true</flag> + + </control> + + <event path="java/statistics/thread_allocation"> + <setting name="enabled">true</setting> + <setting name="period">everyChunk</setting> + </event> + + <event path="java/statistics/class_loading"> + <setting name="enabled">true</setting> + <setting name="period">1000 ms</setting> + </event> + + <event path="java/statistics/threads"> + <setting name="enabled">true</setting> + <setting name="period">1000 ms</setting> + </event> + + <event path="java/thread_start"> + <setting name="enabled">true</setting> + </event> + + <event path="java/thread_end"> + <setting name="enabled">true</setting> + </event> + + <event path="java/thread_sleep"> + <setting name="enabled">true</setting> + <setting name="stackTrace">true</setting> + <setting name="threshold" control="synchronization-threshold">10 ms</setting> + </event> + + <event path="java/thread_park"> + <setting name="enabled">true</setting> + <setting name="stackTrace">true</setting> + <setting name="threshold" control="synchronization-threshold">10 ms</setting> + </event> + + <event path="java/monitor_enter"> + <setting name="enabled">true</setting> + <setting name="stackTrace">true</setting> + <setting name="threshold" control="synchronization-threshold">10 ms</setting> + </event> + + <event path="java/monitor_wait"> + <setting name="enabled">true</setting> + <setting name="stackTrace">true</setting> + <setting name="threshold" control="synchronization-threshold">10 ms</setting> + </event> + + <event path="vm/class/load"> + <setting name="enabled" control="class-loading-enabled">false</setting> + <setting name="stackTrace">true</setting> + <setting name="threshold">0 ms</setting> + </event> + + <event path="vm/class/unload"> + <setting name="enabled" control="class-loading-enabled">false</setting> + </event> + + <event path="vm/info"> + <setting name="enabled">true</setting> + <setting name="period">everyChunk</setting> + </event> + + <event path="vm/initial_system_property"> + <setting name="enabled">true</setting> + <setting name="period">everyChunk</setting> + </event> + + <event path="vm/prof/execution_sample"> + <setting name="enabled" control="method-sampling-enabled">true</setting> + <setting name="period" control="method-sampling-interval">10 ms</setting> + </event> + + <event path="vm/prof/execution_sampling_info"> + <setting name="enabled">false</setting> + <setting name="threshold">1 ms</setting> + </event> + + <event path="vm/runtime/execute_vm_operation"> + <setting name="enabled">true</setting> + <setting name="threshold">10 ms</setting> + </event> + + <event path="vm/runtime/thread_dump"> + <setting name="enabled" control="thread-dump-enabled">true</setting> + <setting name="period" control="thread-dump-interval">1 s</setting> + </event> + + <event path="vm/flag/long"> + <setting name="enabled">true</setting> + <setting name="period">everyChunk</setting> + </event> + + <event path="vm/flag/ulong"> + <setting name="enabled">true</setting> + <setting name="period">everyChunk</setting> + </event> + + <event path="vm/flag/double"> + <setting name="enabled">true</setting> + <setting name="period">everyChunk</setting> + </event> + + <event path="vm/flag/boolean"> + <setting name="enabled">true</setting> + <setting name="period">everyChunk</setting> + </event> + + <event path="vm/flag/string"> + <setting name="enabled">true</setting> + <setting name="period">everyChunk</setting> + </event> + + <event path="vm/flag/long_changed"> + <setting name="enabled">true</setting> + </event> + + <event path="vm/flag/ulong_changed"> + <setting name="enabled">true</setting> + </event> + + <event path="vm/flag/double_changed"> + <setting name="enabled">true</setting> + </event> + + <event path="vm/flag/boolean_changed"> + <setting name="enabled">true</setting> + </event> + + <event path="vm/flag/string_changed"> + <setting name="enabled">true</setting> + </event> + + <event path="vm/gc/detailed/object_count"> + <setting name="enabled" control="heap-statistics-enabled">true</setting> + <setting name="period">everyChunk</setting> + </event> + + <event path="vm/gc/configuration/gc"> + <setting name="enabled" control="gc-enabled-normal">true</setting> + <setting name="period">everyChunk</setting> + </event> + + <event path="vm/gc/configuration/heap"> + <setting name="enabled" control="gc-enabled-normal">true</setting> + <setting name="period">everyChunk</setting> + </event> + + <event path="vm/gc/configuration/young_generation"> + <setting name="enabled" control="gc-enabled-normal">true</setting> + <setting name="period">everyChunk</setting> + </event> + + <event path="vm/gc/configuration/tlab"> + <setting name="enabled" control="gc-enabled-normal">true</setting> + <setting name="period">everyChunk</setting> + </event> + + <event path="vm/gc/configuration/survivor"> + <setting name="enabled" control="gc-enabled-normal">true</setting> + <setting name="period">everyChunk</setting> + </event> + + <event path="vm/gc/detailed/object_count_after_gc"> + <setting name="enabled">false</setting> + </event> + + <event path="vm/gc/heap/summary"> + <setting name="enabled" control="gc-enabled-normal">true</setting> + </event> + + <event path="vm/gc/heap/ps_summary"> + <setting name="enabled" control="gc-enabled-normal">true</setting> + </event> + + <event path="vm/gc/heap/metaspace_summary"> + <setting name="enabled" control="gc-enabled-normal">true</setting> + </event> + + <event path="vm/gc/metaspace/gc_threshold"> + <setting name="enabled" control="gc-enabled-normal">true</setting> + </event> + + <event path="vm/gc/metaspace/allocation_failure"> + <setting name="enabled" control="gc-enabled-normal">true</setting> + <setting name="stackTrace">true</setting> + </event> + + <event path="vm/gc/metaspace/out_of_memory"> + <setting name="enabled" control="gc-enabled-normal">true</setting> + <setting name="stackTrace">true</setting> + </event> + + <event path="vm/gc/metaspace/chunk_free_list_summary"> + <setting name="enabled" control="gc-enabled-normal">true</setting> + </event> + + <event path="vm/gc/collector/garbage_collection"> + <setting name="enabled" control="gc-enabled-normal">true</setting> + <setting name="threshold">0 ms</setting> + </event> + + <event path="vm/gc/collector/parold_garbage_collection"> + <setting name="enabled" control="gc-enabled-normal">true</setting> + <setting name="threshold">0 ms</setting> + </event> + + <event path="vm/gc/collector/young_garbage_collection"> + <setting name="enabled" control="gc-enabled-normal">true</setting> + <setting name="threshold">0 ms</setting> + </event> + + <event path="vm/gc/collector/old_garbage_collection"> + <setting name="enabled" control="gc-enabled-normal">true</setting> + <setting name="threshold">0 ms</setting> + </event> + + <event path="vm/gc/collector/g1_garbage_collection"> + <setting name="enabled" control="gc-enabled-normal">true</setting> + <setting name="threshold">0 ms</setting> + </event> + + <event path="vm/gc/phases/pause"> + <setting name="enabled" control="gc-enabled-normal">true</setting> + <setting name="threshold">0 ms</setting> + </event> + + <event path="vm/gc/phases/pause_level_1"> + <setting name="enabled" control="gc-enabled-normal">true</setting> + <setting name="threshold">0 ms</setting> + </event> + + <event path="vm/gc/phases/pause_level_2"> + <setting name="enabled" control="gc-enabled-normal">true</setting> + <setting name="threshold">0 ms</setting> + </event> + + <event path="vm/gc/phases/pause_level_3"> + <setting name="enabled" control="gc-enabled-all">true</setting> + <setting name="threshold">0 ms</setting> + </event> + + <event path="vm/gc/reference/statistics"> + <setting name="enabled" control="gc-enabled-normal">true</setting> + </event> + + <event path="vm/gc/detailed/promotion_failed"> + <setting name="enabled" control="gc-enabled-normal">true</setting> + </event> + + <event path="vm/gc/detailed/evacuation_failed"> + <setting name="enabled" control="gc-enabled-normal">true</setting> + </event> + + <event path="vm/gc/detailed/evacuation_info"> + <setting name="enabled" control="gc-enabled-normal">true</setting> + </event> + + <event path="vm/gc/detailed/concurrent_mode_failure"> + <setting name="enabled" control="gc-enabled-normal">true</setting> + </event> + + <event path="vm/gc/detailed/allocation_requiring_gc"> + <setting name="enabled" control="gc-enabled-all">true</setting> + <setting name="stackTrace">true</setting> + </event> + + <event path="vm/compiler/config"> + <setting name="enabled" control="compiler-enabled">true</setting> + <setting name="period">everyChunk</setting> + </event> + + <event path="vm/compiler/stats"> + <setting name="enabled" control="compiler-enabled">true</setting> + <setting name="period">1000 ms</setting> + </event> + + <event path="vm/compiler/compilation"> + <setting name="enabled" control="compiler-enabled">true</setting> + <setting name="threshold" control="compiler-compilation-threshold">1000 ms</setting> + </event> + + <event path="vm/compiler/phase"> + <setting name="enabled" control="compiler-enabled">true</setting> + <setting name="threshold" control="compiler-phase-threshold">60 s</setting> + </event> + + <event path="vm/compiler/failure"> + <setting name="enabled" control="compiler-enabled-failure">false</setting> + </event> + + <event path="vm/code_sweeper/config"> + <setting name="enabled" control="compiler-enabled">true</setting> + <setting name="period">everyChunk</setting> + </event> + + <event path="vm/code_sweeper/stats"> + <setting name="enabled" control="compiler-enabled">true</setting> + <setting name="period">everyChunk</setting> + </event> + + <event path="vm/code_sweeper/sweep"> + <setting name="enabled" control="compiler-enabled">true</setting> + <setting name="threshold" control="compiler-sweeper-threshold">100 ms</setting> + </event> + + <event path="vm/code_cache/config"> + <setting name="enabled" control="compiler-enabled">true</setting> + <setting name="period">everyChunk</setting> + </event> + + <event path="vm/code_cache/stats"> + <setting name="enabled" control="compiler-enabled">true</setting> + <setting name="period">everyChunk</setting> + </event> + + <event path="vm/code_cache/full"> + <setting name="enabled" control="compiler-enabled">true</setting> + </event> + + <event path="os/information"> + <setting name="enabled">true</setting> + <setting name="period">everyChunk</setting> + </event> + + <event path="os/processor/cpu_information"> + <setting name="enabled">true</setting> + <setting name="period">everyChunk</setting> + </event> + + <event path="os/processor/context_switch_rate"> + <setting name="enabled" control="compiler-enabled">true</setting> + <setting name="period">10 s</setting> + </event> + + <event path="os/processor/cpu_load"> + <setting name="enabled">true</setting> + <setting name="period">1000 ms</setting> + </event> + + <event path="os/processor/cpu_tsc"> + <setting name="enabled">true</setting> + <setting name="period">everyChunk</setting> + </event> + + <event path="os/system_process"> + <setting name="enabled">true</setting> + <setting name="period">everyChunk</setting> + </event> + + <event path="os/initial_environment_variable"> + <setting name="enabled">true</setting> + <setting name="period">everyChunk</setting> + </event> + + <event path="os/memory/physical_memory"> + <setting name="enabled">true</setting> + <setting name="period">everyChunk</setting> + </event> + + <event path="java/object_alloc_in_new_TLAB"> + <setting name="enabled" control="allocation-profiling-enabled">true</setting> + <setting name="stackTrace">true</setting> + </event> + + <event path="java/object_alloc_outside_TLAB"> + <setting name="enabled" control="allocation-profiling-enabled">true</setting> + <setting name="stackTrace">true</setting> + </event> + + </producer> + + <producer uri="http://www.oracle.com/hotspot/jdk/" label="Oracle JDK"> + + <event path="java/file_read"> + <setting name="enabled">true</setting> + <setting name="stackTrace">true</setting> + <setting name="threshold" control="http://www.oracle.com/hotspot/jvm/file-io-threshold">10 ms</setting> + </event> + + <event path="java/file_write"> + <setting name="enabled">true</setting> + <setting name="stackTrace">true</setting> + <setting name="threshold" control="http://www.oracle.com/hotspot/jvm/file-io-threshold">10 ms</setting> + </event> + + <event path="java/socket_read"> + <setting name="enabled">true</setting> + <setting name="stackTrace">true</setting> + <setting name="threshold" control="http://www.oracle.com/hotspot/jvm/socket-io-threshold">10 ms</setting> + </event> + + <event path="java/socket_write"> + <setting name="enabled">true</setting> + <setting name="stackTrace">true</setting> + <setting name="threshold" control="http://www.oracle.com/hotspot/jvm/socket-io-threshold">10 ms</setting> + </event> + + <event path="java/exception_throw"> + <setting name="enabled" control="http://www.oracle.com/hotspot/jvm/enable-exceptions">false</setting> + <setting name="stackTrace">true</setting> + </event> + + <event path="java/error_throw"> + <setting name="enabled" control="http://www.oracle.com/hotspot/jvm/enable-errors">true</setting> + <setting name="stackTrace">true</setting> + </event> + + <event path="java/statistics/throwables"> + <setting name="enabled">true</setting> + <setting name="period">1000 ms</setting> + </event> + + </producer> + + <producer uri="http://www.oracle.com/hotspot/jfr-info/" label="Oracle JDK"> + + <event path="recordings/recording"> + <setting name="enabled">true</setting> + </event> + + <event path="recordings/recording_setting"> + <setting name="enabled">true</setting> + </event> + + </producer> + +</configuration> diff --git a/tlatools/test-benchmark/tlc2/tool/ModuleOverwrites-1531220029-80dc6de2b.json b/tlatools/test-benchmark/tlc2/tool/ModuleOverwrites-1531220029-80dc6de2b.json new file mode 100644 index 0000000000000000000000000000000000000000..9460d8682ea6f71625e3d2f6b61ea8256c72091a --- /dev/null +++ b/tlatools/test-benchmark/tlc2/tool/ModuleOverwrites-1531220029-80dc6de2b.json @@ -0,0 +1,169 @@ +[ + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.tool.ModuleOverwritesBenchmark.aNoModuleOverwrite", + "mode" : "thrpt", + "threads" : 1, + "forks" : 2, + "jvm" : "/usr/lib/jvm/java-9-oracle/bin/java", + "jvmArgs" : [ + "-Xms8192m", + "-Xmx8192m", + "-Dtlc2.tool.ModuleOverwritesBenchmark.base=/home/markus/src/TLA/tla/tlatools/test-model" + ], + "jdkVersion" : "9.0.4", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "9.0.4+11", + "warmupIterations" : 2, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 2, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "primaryMetric" : { + "score" : 22425.663005813956, + "scoreError" : 5954.899764560438, + "scoreConfidence" : [ + 16470.763241253517, + 28380.562770374396 + ], + "scorePercentiles" : { + "0.0" : 21271.141489690363, + "50.0" : 22502.033943221788, + "90.0" : 23427.442647121898, + "95.0" : 23427.442647121898, + "99.0" : 23427.442647121898, + "99.9" : 23427.442647121898, + "99.99" : 23427.442647121898, + "99.999" : 23427.442647121898, + "99.9999" : 23427.442647121898, + "100.0" : 23427.442647121898 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 21271.141489690363, + 22186.209276647664 + ], + [ + 22817.85860979591, + 23427.442647121898 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.tool.ModuleOverwritesBenchmark.bModuleOverwrite", + "mode" : "thrpt", + "threads" : 1, + "forks" : 2, + "jvm" : "/usr/lib/jvm/java-9-oracle/bin/java", + "jvmArgs" : [ + "-Xms8192m", + "-Xmx8192m", + "-Dtlc2.tool.ModuleOverwritesBenchmark.base=/home/markus/src/TLA/tla/tlatools/test-model" + ], + "jdkVersion" : "9.0.4", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "9.0.4+11", + "warmupIterations" : 2, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 2, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "primaryMetric" : { + "score" : 1142037.5355511934, + "scoreError" : 201257.82920798412, + "scoreConfidence" : [ + 940779.7063432093, + 1343295.3647591774 + ], + "scorePercentiles" : { + "0.0" : 1115758.2755510376, + "50.0" : 1135402.653804632, + "90.0" : 1181586.5590444724, + "95.0" : 1181586.5590444724, + "99.0" : 1181586.5590444724, + "99.9" : 1181586.5590444724, + "99.99" : 1181586.5590444724, + "99.999" : 1181586.5590444724, + "99.9999" : 1181586.5590444724, + "100.0" : 1181586.5590444724 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 1115758.2755510376, + 1181586.5590444724 + ], + [ + 1118561.6168647762, + 1152243.6907444876 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.tool.ModuleOverwritesBenchmark.cModuleOverwriteLinear", + "mode" : "thrpt", + "threads" : 1, + "forks" : 2, + "jvm" : "/usr/lib/jvm/java-9-oracle/bin/java", + "jvmArgs" : [ + "-Xms8192m", + "-Xmx8192m", + "-Dtlc2.tool.ModuleOverwritesBenchmark.base=/home/markus/src/TLA/tla/tlatools/test-model" + ], + "jdkVersion" : "9.0.4", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "9.0.4+11", + "warmupIterations" : 2, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 2, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "primaryMetric" : { + "score" : 3708550.6057697954, + "scoreError" : 824356.4201994061, + "scoreConfidence" : [ + 2884194.185570389, + 4532907.025969202 + ], + "scorePercentiles" : { + "0.0" : 3595787.9785506492, + "50.0" : 3700259.3284566575, + "90.0" : 3837895.787615218, + "95.0" : 3837895.787615218, + "99.0" : 3837895.787615218, + "99.9" : 3837895.787615218, + "99.99" : 3837895.787615218, + "99.999" : 3837895.787615218, + "99.9999" : 3837895.787615218, + "100.0" : 3837895.787615218 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 3798339.118508976, + 3595787.9785506492 + ], + [ + 3602179.538404339, + 3837895.787615218 + ] + ] + }, + "secondaryMetrics" : { + } + } +] + + diff --git a/tlatools/test-benchmark/tlc2/tool/ModuleOverwritesBenchmark.java b/tlatools/test-benchmark/tlc2/tool/ModuleOverwritesBenchmark.java new file mode 100644 index 0000000000000000000000000000000000000000..cbfe6e8b04fd90ab7052e18f6abc286760f00e97 --- /dev/null +++ b/tlatools/test-benchmark/tlc2/tool/ModuleOverwritesBenchmark.java @@ -0,0 +1,98 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool; + +import java.io.File; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.State; + +import tlc2.tool.impl.FastTool; +import tlc2.value.impl.FcnRcdValue; +import tlc2.value.impl.Value; +import util.SimpleFilenameToStream; +import util.ToolIO; +import util.UniqueString; + +@State(Scope.Benchmark) +public class ModuleOverwritesBenchmark { + + /* + * Run with: java -jar target/benchmarks.jar -wi 2 -i 2 -f2 -rf json -rff + * ModuleOverwritesBenchmark-$(de +%s)-$(git rev-parse --short HEAD).json + * -jvmArgsPrepend "-Xms8192m -Xmx8192m" -jvmArgsAppend + * "-Dtlc2.tool.ModuleOverwritesBenchmark.base=/home/markus/src/TLA/tla/tlatools/test-model tlc2.tool.ModuleOverwritesBenchmark " + */ + + private static final String BASE_PATH = System + .getProperty(ModuleOverwritesBenchmark.class.getName() + ".base"); + + private static final ITool tool; + private static final TLCStateMut state; + + static { + String dir = BASE_PATH + File.separator + "ModuleOverwrites"; + System.err.println(dir); + ToolIO.setUserDir(dir); + + tool = new FastTool("", "ModuleOverwrites", "ModuleOverwrites", new SimpleFilenameToStream()); + + state = (TLCStateMut) tool.getInitStates().elementAt(0); + } + + @Benchmark + public boolean aNoModuleOverwrite() { + shuffleValues(); + return tool.isValid(tool.getInvariants()[0], state); + } + + @Benchmark + public boolean bModuleOverwrite() { + shuffleValues(); + return tool.isValid(tool.getInvariants()[1], state); + } + + @Benchmark + public boolean cModuleOverwriteLinear() { + shuffleValues(); + return tool.isValid(tool.getInvariants()[2], state); + } + + private static final void shuffleValues() { + final FcnRcdValue frv = (FcnRcdValue) state.getVals().get(UniqueString.uniqueStringOf("t")); + + final List<Value> values = Arrays.asList(frv.values); + Collections.shuffle(values); + + for (int i = 0; i < values.size(); i++) { + frv.values[i] = values.get(i); + } + } +} diff --git a/tlatools/test-benchmark/tlc2/tool/fp/LongArrayBenchmark-1535138934-548ce71f5.json b/tlatools/test-benchmark/tlc2/tool/fp/LongArrayBenchmark-1535138934-548ce71f5.json new file mode 100644 index 0000000000000000000000000000000000000000..e3705ddc5063cac5811bbe0b9c1740ad76958e22 --- /dev/null +++ b/tlatools/test-benchmark/tlc2/tool/fp/LongArrayBenchmark-1535138934-548ce71f5.json @@ -0,0 +1,128 @@ +[ + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.tool.fp.LongArrayBenchmark.AswapWithCopy", + "mode" : "thrpt", + "threads" : 1, + "forks" : 3, + "jvm" : "/usr/lib/jvm/java-11-openjdk-amd64/bin/java", + "jvmArgs" : [ + "-Xms8192m", + "-Xmx8192m", + "-Dtlc2.tool.ModuleOverwritesBenchmark.base=/home/markus/src/TLA/tla/tlatools/test-model" + ], + "jdkVersion" : "10.0.2", + "vmName" : "OpenJDK 64-Bit Server VM", + "vmVersion" : "10.0.2+13-Ubuntu-1ubuntu0.18.04.1", + "warmupIterations" : 2, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 3, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "primaryMetric" : { + "score" : 1.9284288211942446E8, + "scoreError" : 1.2894808885787597E7, + "scoreConfidence" : [ + 1.7994807323363686E8, + 2.0573769100521207E8 + ], + "scorePercentiles" : { + "0.0" : 1.8376569492012057E8, + "50.0" : 1.9164345746308792E8, + "90.0" : 2.0265063750222045E8, + "95.0" : 2.0265063750222045E8, + "99.0" : 2.0265063750222045E8, + "99.9" : 2.0265063750222045E8, + "99.99" : 2.0265063750222045E8, + "99.999" : 2.0265063750222045E8, + "99.9999" : 2.0265063750222045E8, + "100.0" : 2.0265063750222045E8 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 2.0265063750222045E8, + 2.0163514267585754E8, + 2.0146764720797616E8 + ], + [ + 1.8787724608521232E8, + 1.8376569492012057E8, + 1.8502766664432153E8 + ], + [ + 1.9164345746308792E8, + 1.860197486552854E8, + 1.9549869792073822E8 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.tool.fp.LongArrayBenchmark.BswapGetSet", + "mode" : "thrpt", + "threads" : 1, + "forks" : 3, + "jvm" : "/usr/lib/jvm/java-11-openjdk-amd64/bin/java", + "jvmArgs" : [ + "-Xms8192m", + "-Xmx8192m", + "-Dtlc2.tool.ModuleOverwritesBenchmark.base=/home/markus/src/TLA/tla/tlatools/test-model" + ], + "jdkVersion" : "10.0.2", + "vmName" : "OpenJDK 64-Bit Server VM", + "vmVersion" : "10.0.2+13-Ubuntu-1ubuntu0.18.04.1", + "warmupIterations" : 2, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 3, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "primaryMetric" : { + "score" : 6.087182668510927E8, + "scoreError" : 1.2923203076996244E7, + "scoreConfidence" : [ + 5.957950637740965E8, + 6.216414699280889E8 + ], + "scorePercentiles" : { + "0.0" : 5.959914922634013E8, + "50.0" : 6.098126314774866E8, + "90.0" : 6.174645254898823E8, + "95.0" : 6.174645254898823E8, + "99.0" : 6.174645254898823E8, + "99.9" : 6.174645254898823E8, + "99.99" : 6.174645254898823E8, + "99.999" : 6.174645254898823E8, + "99.9999" : 6.174645254898823E8, + "100.0" : 6.174645254898823E8 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 5.970025135703455E8, + 5.959914922634013E8, + 6.090513375990788E8 + ], + [ + 6.098126314774866E8, + 6.123331210694003E8, + 6.067840687850246E8 + ], + [ + 6.148359838927962E8, + 6.151887275124179E8, + 6.174645254898823E8 + ] + ] + }, + "secondaryMetrics" : { + } + } +] + + diff --git a/tlatools/test-benchmark/tlc2/tool/fp/LongArrayBenchmark.java b/tlatools/test-benchmark/tlc2/tool/fp/LongArrayBenchmark.java new file mode 100644 index 0000000000000000000000000000000000000000..dc345c51099aa67ff7e6e8e5085bc25a5db5a806 --- /dev/null +++ b/tlatools/test-benchmark/tlc2/tool/fp/LongArrayBenchmark.java @@ -0,0 +1,58 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.fp; + +import java.io.IOException; + +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; + +@State(Scope.Benchmark) +public class LongArrayBenchmark { + private LongArray array; + + @Setup + public void up() throws IOException { + final long elements = 1L << 10; + array = new LongArray(elements); + array.zeroMemory(1); + for (long i = 0L; i < elements; i++) { + array.set(i, Long.MAX_VALUE - i); + } + } + + @Benchmark + public void AswapWithCopy() { + array.swapCopy(0, array.size() - 1); + } + + @Benchmark + public void BswapGetSet() { + array.swap(0, array.size() - 1); + } +} diff --git a/tlatools/test-benchmark/tlc2/tool/fp/LongArrayInitializeBenchmark-1540420474-5f451e67c.json b/tlatools/test-benchmark/tlc2/tool/fp/LongArrayInitializeBenchmark-1540420474-5f451e67c.json new file mode 100644 index 0000000000000000000000000000000000000000..527d1818cc98683b02f88d0cd6afa765698c5adc --- /dev/null +++ b/tlatools/test-benchmark/tlc2/tool/fp/LongArrayInitializeBenchmark-1540420474-5f451e67c.json @@ -0,0 +1,829 @@ +[ + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.tool.fp.LongArrayInitializeBenchmark.AputAddressSingle", + "mode" : "thrpt", + "threads" : 1, + "forks" : 2, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + ], + "jdkVersion" : "1.8.0_191", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.191-b12", + "warmupIterations" : 2, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 2, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "params" : { + "size" : "8" + }, + "primaryMetric" : { + "score" : 15691.037304952166, + "scoreError" : 3227.6738320727072, + "scoreConfidence" : [ + 12463.363472879459, + 18918.711137024875 + ], + "scorePercentiles" : { + "0.0" : 15149.456337242189, + "50.0" : 15709.295867528572, + "90.0" : 16196.101147509331, + "95.0" : 16196.101147509331, + "99.0" : 16196.101147509331, + "99.9" : 16196.101147509331, + "99.99" : 16196.101147509331, + "99.999" : 16196.101147509331, + "99.9999" : 16196.101147509331, + "100.0" : 16196.101147509331 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 16025.043319946451, + 15393.548415110692 + ], + [ + 16196.101147509331, + 15149.456337242189 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.tool.fp.LongArrayInitializeBenchmark.AputAddressSingle", + "mode" : "thrpt", + "threads" : 1, + "forks" : 2, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + ], + "jdkVersion" : "1.8.0_191", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.191-b12", + "warmupIterations" : 2, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 2, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "params" : { + "size" : "16" + }, + "primaryMetric" : { + "score" : 10257.952132803022, + "scoreError" : 1609.8651787441936, + "scoreConfidence" : [ + 8648.086954058828, + 11867.817311547216 + ], + "scorePercentiles" : { + "0.0" : 10002.640123830502, + "50.0" : 10219.707935833161, + "90.0" : 10589.752535715264, + "95.0" : 10589.752535715264, + "99.0" : 10589.752535715264, + "99.9" : 10589.752535715264, + "99.99" : 10589.752535715264, + "99.999" : 10589.752535715264, + "99.9999" : 10589.752535715264, + "100.0" : 10589.752535715264 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 10589.752535715264, + 10156.48634104379 + ], + [ + 10282.929530622534, + 10002.640123830502 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.tool.fp.LongArrayInitializeBenchmark.AputAddressSingle", + "mode" : "thrpt", + "threads" : 1, + "forks" : 2, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + ], + "jdkVersion" : "1.8.0_191", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.191-b12", + "warmupIterations" : 2, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 2, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "params" : { + "size" : "24" + }, + "primaryMetric" : { + "score" : 76.898812891649, + "scoreError" : 2.7474408988974544, + "scoreConfidence" : [ + 74.15137199275155, + 79.64625379054645 + ], + "scorePercentiles" : { + "0.0" : 76.48035969067071, + "50.0" : 76.8971373633593, + "90.0" : 77.32061714920668, + "95.0" : 77.32061714920668, + "99.0" : 77.32061714920668, + "99.9" : 77.32061714920668, + "99.99" : 77.32061714920668, + "99.999" : 77.32061714920668, + "99.9999" : 77.32061714920668, + "100.0" : 77.32061714920668 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 77.32061714920668, + 76.48035969067071 + ], + [ + 77.20477337813603, + 76.58950134858259 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.tool.fp.LongArrayInitializeBenchmark.AputAddressSingle", + "mode" : "thrpt", + "threads" : 1, + "forks" : 2, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + ], + "jdkVersion" : "1.8.0_191", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.191-b12", + "warmupIterations" : 2, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 2, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "params" : { + "size" : "28" + }, + "primaryMetric" : { + "score" : 4.8919062820566035, + "scoreError" : 0.13982428649979478, + "scoreConfidence" : [ + 4.752081995556809, + 5.031730568556398 + ], + "scorePercentiles" : { + "0.0" : 4.869023032502967, + "50.0" : 4.892757182572337, + "90.0" : 4.913087730578771, + "95.0" : 4.913087730578771, + "99.0" : 4.913087730578771, + "99.9" : 4.913087730578771, + "99.99" : 4.913087730578771, + "99.999" : 4.913087730578771, + "99.9999" : 4.913087730578771, + "100.0" : 4.913087730578771 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 4.913087730578771, + 4.869023032502967 + ], + [ + 4.907434706339786, + 4.8780796588048885 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.tool.fp.LongArrayInitializeBenchmark.AputAddressSingle", + "mode" : "thrpt", + "threads" : 1, + "forks" : 2, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + ], + "jdkVersion" : "1.8.0_191", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.191-b12", + "warmupIterations" : 2, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 2, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "params" : { + "size" : "31" + }, + "primaryMetric" : { + "score" : 0.6054237172952491, + "scoreError" : 0.057545101881470405, + "scoreConfidence" : [ + 0.5478786154137787, + 0.6629688191767196 + ], + "scorePercentiles" : { + "0.0" : 0.5920856333767331, + "50.0" : 0.6095564484306197, + "90.0" : 0.6104963389430239, + "95.0" : 0.6104963389430239, + "99.0" : 0.6104963389430239, + "99.9" : 0.6104963389430239, + "99.99" : 0.6104963389430239, + "99.999" : 0.6104963389430239, + "99.9999" : 0.6104963389430239, + "100.0" : 0.6104963389430239 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 0.6093206980085581, + 0.5920856333767331 + ], + [ + 0.6097921988526813, + 0.6104963389430239 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.tool.fp.LongArrayInitializeBenchmark.BputAddressConcurrent", + "mode" : "thrpt", + "threads" : 1, + "forks" : 2, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + ], + "jdkVersion" : "1.8.0_191", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.191-b12", + "warmupIterations" : 2, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 2, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "params" : { + "size" : "8" + }, + "primaryMetric" : { + "score" : 5823.508981235277, + "scoreError" : 331.24359175202756, + "scoreConfidence" : [ + 5492.265389483249, + 6154.752572987305 + ], + "scorePercentiles" : { + "0.0" : 5753.76878817967, + "50.0" : 5838.788951447104, + "90.0" : 5862.689233867229, + "95.0" : 5862.689233867229, + "99.0" : 5862.689233867229, + "99.9" : 5862.689233867229, + "99.99" : 5862.689233867229, + "99.999" : 5862.689233867229, + "99.9999" : 5862.689233867229, + "100.0" : 5862.689233867229 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 5861.34019538168, + 5816.237707512529 + ], + [ + 5862.689233867229, + 5753.76878817967 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.tool.fp.LongArrayInitializeBenchmark.BputAddressConcurrent", + "mode" : "thrpt", + "threads" : 1, + "forks" : 2, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + ], + "jdkVersion" : "1.8.0_191", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.191-b12", + "warmupIterations" : 2, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 2, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "params" : { + "size" : "16" + }, + "primaryMetric" : { + "score" : 5123.978800612953, + "scoreError" : 106.38946063324629, + "scoreConfidence" : [ + 5017.589339979707, + 5230.368261246199 + ], + "scorePercentiles" : { + "0.0" : 5100.830231354179, + "50.0" : 5128.5271012328085, + "90.0" : 5138.030768632014, + "95.0" : 5138.030768632014, + "99.0" : 5138.030768632014, + "99.9" : 5138.030768632014, + "99.99" : 5138.030768632014, + "99.999" : 5138.030768632014, + "99.9999" : 5138.030768632014, + "100.0" : 5138.030768632014 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 5138.030768632014, + 5132.914045761264 + ], + [ + 5100.830231354179, + 5124.140156704352 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.tool.fp.LongArrayInitializeBenchmark.BputAddressConcurrent", + "mode" : "thrpt", + "threads" : 1, + "forks" : 2, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + ], + "jdkVersion" : "1.8.0_191", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.191-b12", + "warmupIterations" : 2, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 2, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "params" : { + "size" : "24" + }, + "primaryMetric" : { + "score" : 68.97129891399425, + "scoreError" : 2.413345681248791, + "scoreConfidence" : [ + 66.55795323274546, + 71.38464459524303 + ], + "scorePercentiles" : { + "0.0" : 68.58880458551265, + "50.0" : 68.91555687043092, + "90.0" : 69.4652773296025, + "95.0" : 69.4652773296025, + "99.0" : 69.4652773296025, + "99.9" : 69.4652773296025, + "99.99" : 69.4652773296025, + "99.999" : 69.4652773296025, + "99.9999" : 69.4652773296025, + "100.0" : 69.4652773296025 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 68.81090527067398, + 69.02020847018787 + ], + [ + 68.58880458551265, + 69.4652773296025 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.tool.fp.LongArrayInitializeBenchmark.BputAddressConcurrent", + "mode" : "thrpt", + "threads" : 1, + "forks" : 2, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + ], + "jdkVersion" : "1.8.0_191", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.191-b12", + "warmupIterations" : 2, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 2, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "params" : { + "size" : "28" + }, + "primaryMetric" : { + "score" : 4.295134928414715, + "scoreError" : 0.107034811914763, + "scoreConfidence" : [ + 4.188100116499952, + 4.4021697403294775 + ], + "scorePercentiles" : { + "0.0" : 4.278376300240857, + "50.0" : 4.295239726791536, + "90.0" : 4.311683959834933, + "95.0" : 4.311683959834933, + "99.0" : 4.311683959834933, + "99.9" : 4.311683959834933, + "99.99" : 4.311683959834933, + "99.999" : 4.311683959834933, + "99.9999" : 4.311683959834933, + "100.0" : 4.311683959834933 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 4.278376300240857, + 4.311683959834933 + ], + [ + 4.2836567656737925, + 4.30682268790928 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.tool.fp.LongArrayInitializeBenchmark.BputAddressConcurrent", + "mode" : "thrpt", + "threads" : 1, + "forks" : 2, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + ], + "jdkVersion" : "1.8.0_191", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.191-b12", + "warmupIterations" : 2, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 2, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "params" : { + "size" : "31" + }, + "primaryMetric" : { + "score" : 0.5397866544024822, + "scoreError" : 0.0015897046802224838, + "scoreConfidence" : [ + 0.5381969497222597, + 0.5413763590827047 + ], + "scorePercentiles" : { + "0.0" : 0.5394561862215702, + "50.0" : 0.539844153558601, + "90.0" : 0.5400021242711565, + "95.0" : 0.5400021242711565, + "99.0" : 0.5400021242711565, + "99.9" : 0.5400021242711565, + "99.99" : 0.5400021242711565, + "99.999" : 0.5400021242711565, + "99.9999" : 0.5400021242711565, + "100.0" : 0.5400021242711565 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 0.5400021242711565, + 0.5399424181107918 + ], + [ + 0.5397458890064103, + 0.5394561862215702 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.tool.fp.LongArrayInitializeBenchmark.CsetMemory", + "mode" : "thrpt", + "threads" : 1, + "forks" : 2, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + ], + "jdkVersion" : "1.8.0_191", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.191-b12", + "warmupIterations" : 2, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 2, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "params" : { + "size" : "8" + }, + "primaryMetric" : { + "score" : 1.7117812650403537E7, + "scoreError" : 1858981.951784521, + "scoreConfidence" : [ + 1.5258830698619016E7, + 1.897679460218806E7 + ], + "scorePercentiles" : { + "0.0" : 1.6809496813037805E7, + "50.0" : 1.7132330412883125E7, + "90.0" : 1.7397092962810084E7, + "95.0" : 1.7397092962810084E7, + "99.0" : 1.7397092962810084E7, + "99.9" : 1.7397092962810084E7, + "99.99" : 1.7397092962810084E7, + "99.999" : 1.7397092962810084E7, + "99.9999" : 1.7397092962810084E7, + "100.0" : 1.7397092962810084E7 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 1.6809496813037805E7, + 1.6938938543361507E7 + ], + [ + 1.7325722282404747E7, + 1.7397092962810084E7 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.tool.fp.LongArrayInitializeBenchmark.CsetMemory", + "mode" : "thrpt", + "threads" : 1, + "forks" : 2, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + ], + "jdkVersion" : "1.8.0_191", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.191-b12", + "warmupIterations" : 2, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 2, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "params" : { + "size" : "16" + }, + "primaryMetric" : { + "score" : 79289.53265336789, + "scoreError" : 4239.252439267255, + "scoreConfidence" : [ + 75050.28021410064, + 83528.78509263514 + ], + "scorePercentiles" : { + "0.0" : 78885.94805588268, + "50.0" : 79001.56006617282, + "90.0" : 80269.06242524325, + "95.0" : 80269.06242524325, + "99.0" : 80269.06242524325, + "99.9" : 80269.06242524325, + "99.99" : 80269.06242524325, + "99.999" : 80269.06242524325, + "99.9999" : 80269.06242524325, + "100.0" : 80269.06242524325 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 79039.68258539782, + 80269.06242524325 + ], + [ + 78885.94805588268, + 78963.43754694781 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.tool.fp.LongArrayInitializeBenchmark.CsetMemory", + "mode" : "thrpt", + "threads" : 1, + "forks" : 2, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + ], + "jdkVersion" : "1.8.0_191", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.191-b12", + "warmupIterations" : 2, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 2, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "params" : { + "size" : "24" + }, + "primaryMetric" : { + "score" : 81.08833107753068, + "scoreError" : 2.958405915843286, + "scoreConfidence" : [ + 78.1299251616874, + 84.04673699337397 + ], + "scorePercentiles" : { + "0.0" : 80.60014890852541, + "50.0" : 81.07172218289426, + "90.0" : 81.60973103580875, + "95.0" : 81.60973103580875, + "99.0" : 81.60973103580875, + "99.9" : 81.60973103580875, + "99.99" : 81.60973103580875, + "99.999" : 81.60973103580875, + "99.9999" : 81.60973103580875, + "100.0" : 81.60973103580875 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 80.82876474476265, + 81.60973103580875 + ], + [ + 80.60014890852541, + 81.31467962102589 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.tool.fp.LongArrayInitializeBenchmark.CsetMemory", + "mode" : "thrpt", + "threads" : 1, + "forks" : 2, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + ], + "jdkVersion" : "1.8.0_191", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.191-b12", + "warmupIterations" : 2, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 2, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "params" : { + "size" : "28" + }, + "primaryMetric" : { + "score" : 5.073907234951268, + "scoreError" : 0.14121152721716443, + "scoreConfidence" : [ + 4.932695707734104, + 5.215118762168433 + ], + "scorePercentiles" : { + "0.0" : 5.054526541541467, + "50.0" : 5.074118390666934, + "90.0" : 5.092865616929739, + "95.0" : 5.092865616929739, + "99.0" : 5.092865616929739, + "99.9" : 5.092865616929739, + "99.99" : 5.092865616929739, + "99.999" : 5.092865616929739, + "99.9999" : 5.092865616929739, + "100.0" : 5.092865616929739 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 5.055443635693725, + 5.092793145640142 + ], + [ + 5.054526541541467, + 5.092865616929739 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.tool.fp.LongArrayInitializeBenchmark.CsetMemory", + "mode" : "thrpt", + "threads" : 1, + "forks" : 2, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + ], + "jdkVersion" : "1.8.0_191", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.191-b12", + "warmupIterations" : 2, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 2, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "params" : { + "size" : "31" + }, + "primaryMetric" : { + "score" : 0.6308208674513708, + "scoreError" : 0.0021647577060876096, + "scoreConfidence" : [ + 0.6286561097452832, + 0.6329856251574584 + ], + "scorePercentiles" : { + "0.0" : 0.6303389225544128, + "50.0" : 0.6309177899582723, + "90.0" : 0.6311089673345258, + "95.0" : 0.6311089673345258, + "99.0" : 0.6311089673345258, + "99.9" : 0.6311089673345258, + "99.99" : 0.6311089673345258, + "99.999" : 0.6311089673345258, + "99.9999" : 0.6311089673345258, + "100.0" : 0.6311089673345258 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 0.6308816519037619, + 0.6311089673345258 + ], + [ + 0.6303389225544128, + 0.6309539280127826 + ] + ] + }, + "secondaryMetrics" : { + } + } +] + + diff --git a/tlatools/test-benchmark/tlc2/tool/fp/LongArrayInitializeBenchmark.java b/tlatools/test-benchmark/tlc2/tool/fp/LongArrayInitializeBenchmark.java new file mode 100644 index 0000000000000000000000000000000000000000..de8b2876f46024a0edae589b2a49ce066d82259c --- /dev/null +++ b/tlatools/test-benchmark/tlc2/tool/fp/LongArrayInitializeBenchmark.java @@ -0,0 +1,65 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.fp; + +import java.io.IOException; + +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.Param; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; + +@State(Scope.Benchmark) +public class LongArrayInitializeBenchmark { + + private static final int AVAILABLE_PROCESSORS = Runtime.getRuntime().availableProcessors(); + private LongArray array; + + @Param({"8", "16", "24", "28", "31"}) + public int size; + + @Setup + public void up() throws IOException { + final long elements = 1L << size; + array = new LongArray(elements); + } + + @Benchmark + public void AputAddressSingle() throws IOException { + array.zeroMemory(1); + } + + @Benchmark + public void BputAddressConcurrent() throws IOException { + array.zeroMemory(AVAILABLE_PROCESSORS); + } + + @Benchmark + public void CsetMemory() throws IOException { + array.zeroMemory(); + } +} diff --git a/tlatools/test-benchmark/tlc2/tool/queue/DiskQueueBenachmark.java b/tlatools/test-benchmark/tlc2/tool/queue/DiskQueueBenachmark.java new file mode 100644 index 0000000000000000000000000000000000000000..3f07157a42fd8a003cebbe221c4cc7568f95e133 --- /dev/null +++ b/tlatools/test-benchmark/tlc2/tool/queue/DiskQueueBenachmark.java @@ -0,0 +1,146 @@ +/******************************************************************************* + * Copyright (c) 2019 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.queue; + +import java.io.IOException; + +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Group; +import org.openjdk.jmh.annotations.GroupThreads; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.Param; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.TearDown; +import org.openjdk.jmh.runner.Runner; +import org.openjdk.jmh.runner.RunnerException; +import org.openjdk.jmh.runner.options.Options; +import org.openjdk.jmh.runner.options.OptionsBuilder; + +import tlc2.tool.TLCState; +import tlc2.tool.TLCStates; +import tlc2.util.FlightRecorderProfiler; + +@State(Scope.Group) +@BenchmarkMode(Mode.Throughput) +public class DiskQueueBenachmark { + + @Param({"1", "2", "4", "8", "16", "32", "64"}) + public int vars; + + @Param({"DiskByteArrayQueue", "DiskStateQueue"}) + public String impl; + + private IStateQueue dsq; + + private TLCState state; + + @Setup + public void up() throws IOException { + if (impl.equals("DiskByteArrayQueue")) { + this.dsq = new DiskByteArrayQueue(); + } else { + this.dsq = new DiskStateQueue(); + } + + this.state = TLCStates.createDummyState(vars); + } + + @TearDown + public void down() throws IOException { + this.dsq.delete(); + } + + @Benchmark + @Group("g02") + @GroupThreads(1) + public TLCState consumer1() { + return this.dsq.sDequeue(); + } + + @Benchmark + @Group("g02") + @GroupThreads(1) + public void producer1() { + this.dsq.sEnqueue(this.state); + } + + + @Benchmark + @Group("g04") + @GroupThreads(2) + public TLCState consumer2() { + return this.dsq.sDequeue(); + } + + @Benchmark + @Group("g04") + @GroupThreads(2) + public void producer2() { + this.dsq.sEnqueue(this.state); + } + + + @Benchmark + @Group("g08") + @GroupThreads(4) + public TLCState consumer4() { + return this.dsq.sDequeue(); + } + + @Benchmark + @Group("g08") + @GroupThreads(4) + public void producer4() { + this.dsq.sEnqueue(this.state); + } + + + @Benchmark + @Group("g16") + @GroupThreads(8) + public TLCState consumer8() { + return this.dsq.sDequeue(); + } + + @Benchmark + @Group("g16") + @GroupThreads(8) + public void producer8() { + this.dsq.sEnqueue(this.state); + } + + public static void main(String[] args) throws RunnerException { + Options opt = new OptionsBuilder() + .include(DiskQueueBenachmark.class.getSimpleName()) + .addProfiler(FlightRecorderProfiler.class) + .build(); + + new Runner(opt).run(); + } +} diff --git a/tlatools/test-benchmark/tlc2/tool/queue/StateQueueBenachmark.java b/tlatools/test-benchmark/tlc2/tool/queue/StateQueueBenachmark.java new file mode 100644 index 0000000000000000000000000000000000000000..b8221bab3a15a03748d31e4cc4e6cb0ab71f97ca --- /dev/null +++ b/tlatools/test-benchmark/tlc2/tool/queue/StateQueueBenachmark.java @@ -0,0 +1,125 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.queue; + +import java.io.IOException; + +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.Group; +import org.openjdk.jmh.annotations.GroupThreads; +import org.openjdk.jmh.annotations.Param; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.TearDown; + +import tlc2.tool.TLCState; +import tlc2.tool.TLCStates; + +@State(Scope.Group) +public class StateQueueBenachmark { + + @Param({"1", "2", "4", "8", "16", "32", "64"}) + public int size; + + private IStateQueue s; + + private TLCState[] batch; + + @Setup + public void up() throws IOException { + s = new DiskStateQueue(); + + // balance off the costs for creating the TLCState[]. + this.batch = new TLCState[size]; + for (int i = 0; i < batch.length; i++) { + batch[i] = TLCStates.createDummyState(); + } + } + + @TearDown + public void down() throws IOException { + this.s.delete(); + } + + @Benchmark + @Group("single") + @GroupThreads(2) + public TLCState[] consumerSingle() { + final TLCState[] res = new TLCState[batch.length]; + for (int i = 0; i < batch.length; i++) { + res[i] = this.s.sDequeue(); + } + return res; + } + + @Benchmark + @Group("single") + @GroupThreads(2) + public void producerSingle() { + for (int i = 0; i < batch.length; i++) { + this.s.sEnqueue(batch[i]); + } + } + + + /* Batches of enqueue only */ + + @Benchmark + @Group("batchasym") + @GroupThreads(2) + public TLCState[] consumerBatch() { + final TLCState[] res = new TLCState[batch.length]; + for (int i = 0; i < batch.length; i++) { + res[i] = this.s.sDequeue(); + } + return res; + } + + @Benchmark + @Group("batchasym") + @GroupThreads(2) + public void producerBatch() { + this.s.sEnqueue(batch); + } + + + /* Batches of dequeue & enqueue */ + + @Benchmark + @Group("batchsym") + @GroupThreads(2) + public TLCState[] consumerBatchSym() { + return this.s.sDequeue(size); + } + + @Benchmark + @Group("batchsym") + @GroupThreads(2) + public void producerBatchSym() { + this.s.sEnqueue(batch); + } +} diff --git a/tlatools/test-benchmark/tlc2/tool/simulation/SimulatorBenchmark.java b/tlatools/test-benchmark/tlc2/tool/simulation/SimulatorBenchmark.java new file mode 100644 index 0000000000000000000000000000000000000000..6c2c2176d850fae3d40fa19f1e404eeed6ab3079 --- /dev/null +++ b/tlatools/test-benchmark/tlc2/tool/simulation/SimulatorBenchmark.java @@ -0,0 +1,74 @@ +package tlc2.tool.simulation; + +import java.io.File; +import java.io.IOException; + +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Level; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.Param; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; + +import tla2sany.modanalyzer.SpecObj; +import tlc2.tool.Simulator; +import tlc2.util.RandomGenerator; +import util.SimpleFilenameToStream; +import util.ToolIO; + +/** + * A benchmark to measure multi-threaded simulation performance. + */ +@State(Scope.Benchmark) +public class SimulatorBenchmark { + + @Param({ "1", "2", "3", "4", "5", "6"}) + public int nWorkers; + + Simulator simulator; + SpecObj specObj; + RandomGenerator rng = new RandomGenerator(0); + long seed = 0; + + @Setup + public void up() throws IOException { + ToolIO.setUserDir("test-model" + File.separator + "simulation" + File.separator + "BenchmarkSpec"); + } + + @Setup(Level.Iteration) + public void reseedIter() { + // For each iteration of a benchmark, we set the seed to a known value, so that each + // benchmark fork starts a particular iteration with the same seed. + seed += 1; + rng.setSeed(seed); + } + + @Setup(Level.Trial) + public void reseed() { + // We reset the random number generator to a fixed seed before benchmarking a specific worker count. + // This should help to make these benchmarks more deterministic for a fixed set of benchmark parameters. The initial + // seed of this random number generator should effectively determine what traces are explored in what order by the simulation + // workers, so a particular seed should correspond to a fixed exploration order of the behavior space. + rng.setSeed(0); + seed = 0; + } + + public void simulatorBenchmark(int nWorkers) { + try { + int maxTraceDepth = 20; + Simulator simulator = new Simulator("BenchmarkSpec", "MCInv", null, false, maxTraceDepth, Long.MAX_VALUE, rng, 0, + new SimpleFilenameToStream(), nWorkers); + simulator.simulate(); + } catch (Exception e) { + System.out.println(e.getMessage()); + } + } + + @Benchmark + @BenchmarkMode(Mode.AverageTime) + public void SimulatorWorkers() { + simulatorBenchmark(nWorkers); + } +} diff --git a/tlatools/test-benchmark/tlc2/util/CombinatoricsBenchmark.java b/tlatools/test-benchmark/tlc2/util/CombinatoricsBenchmark.java new file mode 100644 index 0000000000000000000000000000000000000000..db27ffc507566401a0c1404eac6b725a75850cdb --- /dev/null +++ b/tlatools/test-benchmark/tlc2/util/CombinatoricsBenchmark.java @@ -0,0 +1,76 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.util; + +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.List; + +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.Warmup; + +@State(Scope.Benchmark) +public class CombinatoricsBenchmark { + + private static List<BigInteger> bincoef; + private static List<BigInteger> slowBincoef; + + static { + bincoef = new ArrayList<BigInteger>(187489); + slowBincoef = new ArrayList<BigInteger>(187489); + } + + @Benchmark + @Warmup(iterations = 3, time = 1) + @Measurement(iterations = 3, time = 1) + @BenchmarkMode(Mode.Throughput) + public List<BigInteger> bigChoose() { + for (int n = Combinatorics.MAXCHOOSENUM + 1; n < Combinatorics.MAXCHOOSENUM << 3; n++) { + for (int k = Combinatorics.MAXCHOOSENUM + 1; k < Combinatorics.MAXCHOOSENUM << 3; k++) { + bincoef.add(Combinatorics.bigChoose(n, k)); + } + } + return bincoef; + } + + @Benchmark + @Warmup(iterations = 3, time = 1) + @Measurement(iterations = 3, time = 1) + @BenchmarkMode(Mode.Throughput) + public List<BigInteger> slowBigChoose() { + for (int n = Combinatorics.MAXCHOOSENUM + 1; n < Combinatorics.MAXCHOOSENUM << 3; n++) { + for (int k = Combinatorics.MAXCHOOSENUM + 1; k < Combinatorics.MAXCHOOSENUM << 3; k++) { + slowBincoef.add(Combinatorics.slowBigChoose(n, k)); + } + } + return slowBincoef; + } +} diff --git a/tlatools/test-benchmark/tlc2/util/FP64Benchmark.java b/tlatools/test-benchmark/tlc2/util/FP64Benchmark.java new file mode 100644 index 0000000000000000000000000000000000000000..c1c2010f55ce0eed05d59c78a00817e7a5fb625e --- /dev/null +++ b/tlatools/test-benchmark/tlc2/util/FP64Benchmark.java @@ -0,0 +1,59 @@ +/******************************************************************************* + * Copyright (c) 2019 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.util; + +import java.io.IOException; + +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; + +@State(Scope.Benchmark) +public class FP64Benchmark { + + /* + * Run with: java -jar target/benchmarks.jar -wi 2 -i 2 -f2 -rf json -rff + * FP64Benchmark-$(date +%s)-$(git rev-parse --short HEAD).json + * -jvmArgsPrepend "-Xms8192m -Xmx8192m" -jvmArgsAppend tlc2.util.FP64Benchmark " + */ + + @Setup + public void up() throws IOException { + FP64.Init(); + } + + @Benchmark + public long extendIntLoop() { + return FP64.ExtendLoop(72316478964978L, 3876421); + } + + @Benchmark + public long extendIntUnrolled() { + return FP64.Extend(72316478964978L, 3876421); + } + +} diff --git a/tlatools/test-benchmark/tlc2/util/FlightRecorderProfiler.java b/tlatools/test-benchmark/tlc2/util/FlightRecorderProfiler.java new file mode 100644 index 0000000000000000000000000000000000000000..b001afb8464d2ebdcd1b85a3ce236691f4d53e84 --- /dev/null +++ b/tlatools/test-benchmark/tlc2/util/FlightRecorderProfiler.java @@ -0,0 +1,98 @@ +/******************************************************************************* + * Copyright (c) 2019 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.util; + +import java.io.File; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; + +import org.openjdk.jmh.infra.BenchmarkParams; +import org.openjdk.jmh.profile.ExternalProfiler; +import org.openjdk.jmh.results.BenchmarkResult; +import org.openjdk.jmh.results.Result; + +public class FlightRecorderProfiler implements ExternalProfiler { + + // Inspired by https://github.com/nicoulaj/jmh-utils + + /* (non-Javadoc) + * @see org.openjdk.jmh.profile.Profiler#getDescription() + */ + @Override + public String getDescription() { + return FlightRecorderProfiler.class.getSimpleName(); + } + + /* (non-Javadoc) + * @see org.openjdk.jmh.profile.ExternalProfiler#addJVMInvokeOptions(org.openjdk.jmh.infra.BenchmarkParams) + */ + @Override + public Collection<String> addJVMInvokeOptions(BenchmarkParams arg0) { + return new ArrayList<String>(); + } + + /* (non-Javadoc) + * @see org.openjdk.jmh.profile.ExternalProfiler#allowPrintErr() + */ + @Override + public boolean allowPrintErr() { + return true; + } + + /* (non-Javadoc) + * @see org.openjdk.jmh.profile.ExternalProfiler#allowPrintOut() + */ + @Override + public boolean allowPrintOut() { + return true; + } + + /* (non-Javadoc) + * @see org.openjdk.jmh.profile.ExternalProfiler#beforeTrial(org.openjdk.jmh.infra.BenchmarkParams) + */ + @Override + public void beforeTrial(BenchmarkParams arg0) { + //noop + } + + /* (non-Javadoc) + * @see org.openjdk.jmh.profile.ExternalProfiler#afterTrial(org.openjdk.jmh.results.BenchmarkResult, long, java.io.File, java.io.File) + */ + @Override + public Collection<? extends Result> afterTrial(BenchmarkResult arg0, long arg1, File arg2, File arg3) { + return new ArrayList<>(); + } + + /* (non-Javadoc) + * @see org.openjdk.jmh.profile.ExternalProfiler#addJVMOptions(org.openjdk.jmh.infra.BenchmarkParams) + */ + @Override + public Collection<String> addJVMOptions(BenchmarkParams bp) { + // Create the jfr file in the current directory named after the benchmark. + return Arrays.asList("-XX:StartFlightRecording=settings=default,disk=true,dumponexit=true,filename=./" + bp.id() + ".jfr"); + } +} diff --git a/tlatools/test-benchmark/tlc2/value/EnumerateSubsetBenchmark-1537381744-b7b00550b.json b/tlatools/test-benchmark/tlc2/value/EnumerateSubsetBenchmark-1537381744-b7b00550b.json new file mode 100644 index 0000000000000000000000000000000000000000..3fda8d484f95b10a241a2c2e08fdc0899402f417 --- /dev/null +++ b/tlatools/test-benchmark/tlc2/value/EnumerateSubsetBenchmark-1537381744-b7b00550b.json @@ -0,0 +1,3184 @@ +[ + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.EnumerateSubsetBenchmark.elementsAlwaysNormalized", + "mode" : "thrpt", + "threads" : 1, + "forks" : 1, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + "-Xms8192m", + "-Xmx8192m", + "-Dtlc2.tool.ModuleOverwritesBenchmark.base=/home/markus/src/TLA/tla/tlatools/test-model" + ], + "jdkVersion" : "1.8.0_181", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.181-b13", + "warmupIterations" : 1, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 1, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "params" : { + "numOfElements" : "0" + }, + "primaryMetric" : { + "score" : 3.611267082827124E7, + "scoreError" : "NaN", + "scoreConfidence" : [ + "NaN", + "NaN" + ], + "scorePercentiles" : { + "0.0" : 3.611267082827124E7, + "50.0" : 3.611267082827124E7, + "90.0" : 3.611267082827124E7, + "95.0" : 3.611267082827124E7, + "99.0" : 3.611267082827124E7, + "99.9" : 3.611267082827124E7, + "99.99" : 3.611267082827124E7, + "99.999" : 3.611267082827124E7, + "99.9999" : 3.611267082827124E7, + "100.0" : 3.611267082827124E7 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 3.611267082827124E7 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.EnumerateSubsetBenchmark.elementsAlwaysNormalized", + "mode" : "thrpt", + "threads" : 1, + "forks" : 1, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + "-Xms8192m", + "-Xmx8192m", + "-Dtlc2.tool.ModuleOverwritesBenchmark.base=/home/markus/src/TLA/tla/tlatools/test-model" + ], + "jdkVersion" : "1.8.0_181", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.181-b13", + "warmupIterations" : 1, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 1, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "params" : { + "numOfElements" : "1" + }, + "primaryMetric" : { + "score" : 1.0354789744882155E7, + "scoreError" : "NaN", + "scoreConfidence" : [ + "NaN", + "NaN" + ], + "scorePercentiles" : { + "0.0" : 1.0354789744882155E7, + "50.0" : 1.0354789744882155E7, + "90.0" : 1.0354789744882155E7, + "95.0" : 1.0354789744882155E7, + "99.0" : 1.0354789744882155E7, + "99.9" : 1.0354789744882155E7, + "99.99" : 1.0354789744882155E7, + "99.999" : 1.0354789744882155E7, + "99.9999" : 1.0354789744882155E7, + "100.0" : 1.0354789744882155E7 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 1.0354789744882155E7 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.EnumerateSubsetBenchmark.elementsAlwaysNormalized", + "mode" : "thrpt", + "threads" : 1, + "forks" : 1, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + "-Xms8192m", + "-Xmx8192m", + "-Dtlc2.tool.ModuleOverwritesBenchmark.base=/home/markus/src/TLA/tla/tlatools/test-model" + ], + "jdkVersion" : "1.8.0_181", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.181-b13", + "warmupIterations" : 1, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 1, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "params" : { + "numOfElements" : "2" + }, + "primaryMetric" : { + "score" : 7424758.104352328, + "scoreError" : "NaN", + "scoreConfidence" : [ + "NaN", + "NaN" + ], + "scorePercentiles" : { + "0.0" : 7424758.104352328, + "50.0" : 7424758.104352328, + "90.0" : 7424758.104352328, + "95.0" : 7424758.104352328, + "99.0" : 7424758.104352328, + "99.9" : 7424758.104352328, + "99.99" : 7424758.104352328, + "99.999" : 7424758.104352328, + "99.9999" : 7424758.104352328, + "100.0" : 7424758.104352328 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 7424758.104352328 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.EnumerateSubsetBenchmark.elementsAlwaysNormalized", + "mode" : "thrpt", + "threads" : 1, + "forks" : 1, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + "-Xms8192m", + "-Xmx8192m", + "-Dtlc2.tool.ModuleOverwritesBenchmark.base=/home/markus/src/TLA/tla/tlatools/test-model" + ], + "jdkVersion" : "1.8.0_181", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.181-b13", + "warmupIterations" : 1, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 1, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "params" : { + "numOfElements" : "3" + }, + "primaryMetric" : { + "score" : 4520093.423989947, + "scoreError" : "NaN", + "scoreConfidence" : [ + "NaN", + "NaN" + ], + "scorePercentiles" : { + "0.0" : 4520093.423989947, + "50.0" : 4520093.423989947, + "90.0" : 4520093.423989947, + "95.0" : 4520093.423989947, + "99.0" : 4520093.423989947, + "99.9" : 4520093.423989947, + "99.99" : 4520093.423989947, + "99.999" : 4520093.423989947, + "99.9999" : 4520093.423989947, + "100.0" : 4520093.423989947 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 4520093.423989947 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.EnumerateSubsetBenchmark.elementsAlwaysNormalized", + "mode" : "thrpt", + "threads" : 1, + "forks" : 1, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + "-Xms8192m", + "-Xmx8192m", + "-Dtlc2.tool.ModuleOverwritesBenchmark.base=/home/markus/src/TLA/tla/tlatools/test-model" + ], + "jdkVersion" : "1.8.0_181", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.181-b13", + "warmupIterations" : 1, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 1, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "params" : { + "numOfElements" : "4" + }, + "primaryMetric" : { + "score" : 1925178.7174611264, + "scoreError" : "NaN", + "scoreConfidence" : [ + "NaN", + "NaN" + ], + "scorePercentiles" : { + "0.0" : 1925178.7174611264, + "50.0" : 1925178.7174611264, + "90.0" : 1925178.7174611264, + "95.0" : 1925178.7174611264, + "99.0" : 1925178.7174611264, + "99.9" : 1925178.7174611264, + "99.99" : 1925178.7174611264, + "99.999" : 1925178.7174611264, + "99.9999" : 1925178.7174611264, + "100.0" : 1925178.7174611264 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 1925178.7174611264 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.EnumerateSubsetBenchmark.elementsAlwaysNormalized", + "mode" : "thrpt", + "threads" : 1, + "forks" : 1, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + "-Xms8192m", + "-Xmx8192m", + "-Dtlc2.tool.ModuleOverwritesBenchmark.base=/home/markus/src/TLA/tla/tlatools/test-model" + ], + "jdkVersion" : "1.8.0_181", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.181-b13", + "warmupIterations" : 1, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 1, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "params" : { + "numOfElements" : "8" + }, + "primaryMetric" : { + "score" : 133009.5244991998, + "scoreError" : "NaN", + "scoreConfidence" : [ + "NaN", + "NaN" + ], + "scorePercentiles" : { + "0.0" : 133009.5244991998, + "50.0" : 133009.5244991998, + "90.0" : 133009.5244991998, + "95.0" : 133009.5244991998, + "99.0" : 133009.5244991998, + "99.9" : 133009.5244991998, + "99.99" : 133009.5244991998, + "99.999" : 133009.5244991998, + "99.9999" : 133009.5244991998, + "100.0" : 133009.5244991998 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 133009.5244991998 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.EnumerateSubsetBenchmark.elementsAlwaysNormalized", + "mode" : "thrpt", + "threads" : 1, + "forks" : 1, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + "-Xms8192m", + "-Xmx8192m", + "-Dtlc2.tool.ModuleOverwritesBenchmark.base=/home/markus/src/TLA/tla/tlatools/test-model" + ], + "jdkVersion" : "1.8.0_181", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.181-b13", + "warmupIterations" : 1, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 1, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "params" : { + "numOfElements" : "10" + }, + "primaryMetric" : { + "score" : 29226.867534242203, + "scoreError" : "NaN", + "scoreConfidence" : [ + "NaN", + "NaN" + ], + "scorePercentiles" : { + "0.0" : 29226.867534242203, + "50.0" : 29226.867534242203, + "90.0" : 29226.867534242203, + "95.0" : 29226.867534242203, + "99.0" : 29226.867534242203, + "99.9" : 29226.867534242203, + "99.99" : 29226.867534242203, + "99.999" : 29226.867534242203, + "99.9999" : 29226.867534242203, + "100.0" : 29226.867534242203 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 29226.867534242203 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.EnumerateSubsetBenchmark.elementsAlwaysNormalized", + "mode" : "thrpt", + "threads" : 1, + "forks" : 1, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + "-Xms8192m", + "-Xmx8192m", + "-Dtlc2.tool.ModuleOverwritesBenchmark.base=/home/markus/src/TLA/tla/tlatools/test-model" + ], + "jdkVersion" : "1.8.0_181", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.181-b13", + "warmupIterations" : 1, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 1, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "params" : { + "numOfElements" : "12" + }, + "primaryMetric" : { + "score" : 6081.382406132828, + "scoreError" : "NaN", + "scoreConfidence" : [ + "NaN", + "NaN" + ], + "scorePercentiles" : { + "0.0" : 6081.382406132828, + "50.0" : 6081.382406132828, + "90.0" : 6081.382406132828, + "95.0" : 6081.382406132828, + "99.0" : 6081.382406132828, + "99.9" : 6081.382406132828, + "99.99" : 6081.382406132828, + "99.999" : 6081.382406132828, + "99.9999" : 6081.382406132828, + "100.0" : 6081.382406132828 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 6081.382406132828 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.EnumerateSubsetBenchmark.elementsAlwaysNormalized", + "mode" : "thrpt", + "threads" : 1, + "forks" : 1, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + "-Xms8192m", + "-Xmx8192m", + "-Dtlc2.tool.ModuleOverwritesBenchmark.base=/home/markus/src/TLA/tla/tlatools/test-model" + ], + "jdkVersion" : "1.8.0_181", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.181-b13", + "warmupIterations" : 1, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 1, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "params" : { + "numOfElements" : "14" + }, + "primaryMetric" : { + "score" : 1390.845676349573, + "scoreError" : "NaN", + "scoreConfidence" : [ + "NaN", + "NaN" + ], + "scorePercentiles" : { + "0.0" : 1390.845676349573, + "50.0" : 1390.845676349573, + "90.0" : 1390.845676349573, + "95.0" : 1390.845676349573, + "99.0" : 1390.845676349573, + "99.9" : 1390.845676349573, + "99.99" : 1390.845676349573, + "99.999" : 1390.845676349573, + "99.9999" : 1390.845676349573, + "100.0" : 1390.845676349573 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 1390.845676349573 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.EnumerateSubsetBenchmark.elementsAlwaysNormalized", + "mode" : "thrpt", + "threads" : 1, + "forks" : 1, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + "-Xms8192m", + "-Xmx8192m", + "-Dtlc2.tool.ModuleOverwritesBenchmark.base=/home/markus/src/TLA/tla/tlatools/test-model" + ], + "jdkVersion" : "1.8.0_181", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.181-b13", + "warmupIterations" : 1, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 1, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "params" : { + "numOfElements" : "16" + }, + "primaryMetric" : { + "score" : 330.27592509790907, + "scoreError" : "NaN", + "scoreConfidence" : [ + "NaN", + "NaN" + ], + "scorePercentiles" : { + "0.0" : 330.27592509790907, + "50.0" : 330.27592509790907, + "90.0" : 330.27592509790907, + "95.0" : 330.27592509790907, + "99.0" : 330.27592509790907, + "99.9" : 330.27592509790907, + "99.99" : 330.27592509790907, + "99.999" : 330.27592509790907, + "99.9999" : 330.27592509790907, + "100.0" : 330.27592509790907 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 330.27592509790907 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.EnumerateSubsetBenchmark.elementsAlwaysNormalized", + "mode" : "thrpt", + "threads" : 1, + "forks" : 1, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + "-Xms8192m", + "-Xmx8192m", + "-Dtlc2.tool.ModuleOverwritesBenchmark.base=/home/markus/src/TLA/tla/tlatools/test-model" + ], + "jdkVersion" : "1.8.0_181", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.181-b13", + "warmupIterations" : 1, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 1, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "params" : { + "numOfElements" : "18" + }, + "primaryMetric" : { + "score" : 73.27729464188555, + "scoreError" : "NaN", + "scoreConfidence" : [ + "NaN", + "NaN" + ], + "scorePercentiles" : { + "0.0" : 73.27729464188555, + "50.0" : 73.27729464188555, + "90.0" : 73.27729464188555, + "95.0" : 73.27729464188555, + "99.0" : 73.27729464188555, + "99.9" : 73.27729464188555, + "99.99" : 73.27729464188555, + "99.999" : 73.27729464188555, + "99.9999" : 73.27729464188555, + "100.0" : 73.27729464188555 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 73.27729464188555 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.EnumerateSubsetBenchmark.elementsAlwaysNormalized", + "mode" : "thrpt", + "threads" : 1, + "forks" : 1, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + "-Xms8192m", + "-Xmx8192m", + "-Dtlc2.tool.ModuleOverwritesBenchmark.base=/home/markus/src/TLA/tla/tlatools/test-model" + ], + "jdkVersion" : "1.8.0_181", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.181-b13", + "warmupIterations" : 1, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 1, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "params" : { + "numOfElements" : "19" + }, + "primaryMetric" : { + "score" : 27.9553224857132, + "scoreError" : "NaN", + "scoreConfidence" : [ + "NaN", + "NaN" + ], + "scorePercentiles" : { + "0.0" : 27.9553224857132, + "50.0" : 27.9553224857132, + "90.0" : 27.9553224857132, + "95.0" : 27.9553224857132, + "99.0" : 27.9553224857132, + "99.9" : 27.9553224857132, + "99.99" : 27.9553224857132, + "99.999" : 27.9553224857132, + "99.9999" : 27.9553224857132, + "100.0" : 27.9553224857132 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 27.9553224857132 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.EnumerateSubsetBenchmark.elementsNormalized", + "mode" : "thrpt", + "threads" : 1, + "forks" : 1, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + "-Xms8192m", + "-Xmx8192m", + "-Dtlc2.tool.ModuleOverwritesBenchmark.base=/home/markus/src/TLA/tla/tlatools/test-model" + ], + "jdkVersion" : "1.8.0_181", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.181-b13", + "warmupIterations" : 1, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 1, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "params" : { + "numOfElements" : "0" + }, + "primaryMetric" : { + "score" : 1.3197374943346184E7, + "scoreError" : "NaN", + "scoreConfidence" : [ + "NaN", + "NaN" + ], + "scorePercentiles" : { + "0.0" : 1.3197374943346184E7, + "50.0" : 1.3197374943346184E7, + "90.0" : 1.3197374943346184E7, + "95.0" : 1.3197374943346184E7, + "99.0" : 1.3197374943346184E7, + "99.9" : 1.3197374943346184E7, + "99.99" : 1.3197374943346184E7, + "99.999" : 1.3197374943346184E7, + "99.9999" : 1.3197374943346184E7, + "100.0" : 1.3197374943346184E7 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 1.3197374943346184E7 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.EnumerateSubsetBenchmark.elementsNormalized", + "mode" : "thrpt", + "threads" : 1, + "forks" : 1, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + "-Xms8192m", + "-Xmx8192m", + "-Dtlc2.tool.ModuleOverwritesBenchmark.base=/home/markus/src/TLA/tla/tlatools/test-model" + ], + "jdkVersion" : "1.8.0_181", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.181-b13", + "warmupIterations" : 1, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 1, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "params" : { + "numOfElements" : "1" + }, + "primaryMetric" : { + "score" : 6123381.900721031, + "scoreError" : "NaN", + "scoreConfidence" : [ + "NaN", + "NaN" + ], + "scorePercentiles" : { + "0.0" : 6123381.900721031, + "50.0" : 6123381.900721031, + "90.0" : 6123381.900721031, + "95.0" : 6123381.900721031, + "99.0" : 6123381.900721031, + "99.9" : 6123381.900721031, + "99.99" : 6123381.900721031, + "99.999" : 6123381.900721031, + "99.9999" : 6123381.900721031, + "100.0" : 6123381.900721031 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 6123381.900721031 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.EnumerateSubsetBenchmark.elementsNormalized", + "mode" : "thrpt", + "threads" : 1, + "forks" : 1, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + "-Xms8192m", + "-Xmx8192m", + "-Dtlc2.tool.ModuleOverwritesBenchmark.base=/home/markus/src/TLA/tla/tlatools/test-model" + ], + "jdkVersion" : "1.8.0_181", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.181-b13", + "warmupIterations" : 1, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 1, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "params" : { + "numOfElements" : "2" + }, + "primaryMetric" : { + "score" : 4565321.513276472, + "scoreError" : "NaN", + "scoreConfidence" : [ + "NaN", + "NaN" + ], + "scorePercentiles" : { + "0.0" : 4565321.513276472, + "50.0" : 4565321.513276472, + "90.0" : 4565321.513276472, + "95.0" : 4565321.513276472, + "99.0" : 4565321.513276472, + "99.9" : 4565321.513276472, + "99.99" : 4565321.513276472, + "99.999" : 4565321.513276472, + "99.9999" : 4565321.513276472, + "100.0" : 4565321.513276472 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 4565321.513276472 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.EnumerateSubsetBenchmark.elementsNormalized", + "mode" : "thrpt", + "threads" : 1, + "forks" : 1, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + "-Xms8192m", + "-Xmx8192m", + "-Dtlc2.tool.ModuleOverwritesBenchmark.base=/home/markus/src/TLA/tla/tlatools/test-model" + ], + "jdkVersion" : "1.8.0_181", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.181-b13", + "warmupIterations" : 1, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 1, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "params" : { + "numOfElements" : "3" + }, + "primaryMetric" : { + "score" : 2608382.546140382, + "scoreError" : "NaN", + "scoreConfidence" : [ + "NaN", + "NaN" + ], + "scorePercentiles" : { + "0.0" : 2608382.546140382, + "50.0" : 2608382.546140382, + "90.0" : 2608382.546140382, + "95.0" : 2608382.546140382, + "99.0" : 2608382.546140382, + "99.9" : 2608382.546140382, + "99.99" : 2608382.546140382, + "99.999" : 2608382.546140382, + "99.9999" : 2608382.546140382, + "100.0" : 2608382.546140382 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 2608382.546140382 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.EnumerateSubsetBenchmark.elementsNormalized", + "mode" : "thrpt", + "threads" : 1, + "forks" : 1, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + "-Xms8192m", + "-Xmx8192m", + "-Dtlc2.tool.ModuleOverwritesBenchmark.base=/home/markus/src/TLA/tla/tlatools/test-model" + ], + "jdkVersion" : "1.8.0_181", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.181-b13", + "warmupIterations" : 1, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 1, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "params" : { + "numOfElements" : "4" + }, + "primaryMetric" : { + "score" : 1122145.0197631393, + "scoreError" : "NaN", + "scoreConfidence" : [ + "NaN", + "NaN" + ], + "scorePercentiles" : { + "0.0" : 1122145.0197631393, + "50.0" : 1122145.0197631393, + "90.0" : 1122145.0197631393, + "95.0" : 1122145.0197631393, + "99.0" : 1122145.0197631393, + "99.9" : 1122145.0197631393, + "99.99" : 1122145.0197631393, + "99.999" : 1122145.0197631393, + "99.9999" : 1122145.0197631393, + "100.0" : 1122145.0197631393 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 1122145.0197631393 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.EnumerateSubsetBenchmark.elementsNormalized", + "mode" : "thrpt", + "threads" : 1, + "forks" : 1, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + "-Xms8192m", + "-Xmx8192m", + "-Dtlc2.tool.ModuleOverwritesBenchmark.base=/home/markus/src/TLA/tla/tlatools/test-model" + ], + "jdkVersion" : "1.8.0_181", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.181-b13", + "warmupIterations" : 1, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 1, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "params" : { + "numOfElements" : "8" + }, + "primaryMetric" : { + "score" : 25729.526497328603, + "scoreError" : "NaN", + "scoreConfidence" : [ + "NaN", + "NaN" + ], + "scorePercentiles" : { + "0.0" : 25729.526497328603, + "50.0" : 25729.526497328603, + "90.0" : 25729.526497328603, + "95.0" : 25729.526497328603, + "99.0" : 25729.526497328603, + "99.9" : 25729.526497328603, + "99.99" : 25729.526497328603, + "99.999" : 25729.526497328603, + "99.9999" : 25729.526497328603, + "100.0" : 25729.526497328603 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 25729.526497328603 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.EnumerateSubsetBenchmark.elementsNormalized", + "mode" : "thrpt", + "threads" : 1, + "forks" : 1, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + "-Xms8192m", + "-Xmx8192m", + "-Dtlc2.tool.ModuleOverwritesBenchmark.base=/home/markus/src/TLA/tla/tlatools/test-model" + ], + "jdkVersion" : "1.8.0_181", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.181-b13", + "warmupIterations" : 1, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 1, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "params" : { + "numOfElements" : "10" + }, + "primaryMetric" : { + "score" : 2847.859944849876, + "scoreError" : "NaN", + "scoreConfidence" : [ + "NaN", + "NaN" + ], + "scorePercentiles" : { + "0.0" : 2847.859944849876, + "50.0" : 2847.859944849876, + "90.0" : 2847.859944849876, + "95.0" : 2847.859944849876, + "99.0" : 2847.859944849876, + "99.9" : 2847.859944849876, + "99.99" : 2847.859944849876, + "99.999" : 2847.859944849876, + "99.9999" : 2847.859944849876, + "100.0" : 2847.859944849876 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 2847.859944849876 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.EnumerateSubsetBenchmark.elementsNormalized", + "mode" : "thrpt", + "threads" : 1, + "forks" : 1, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + "-Xms8192m", + "-Xmx8192m", + "-Dtlc2.tool.ModuleOverwritesBenchmark.base=/home/markus/src/TLA/tla/tlatools/test-model" + ], + "jdkVersion" : "1.8.0_181", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.181-b13", + "warmupIterations" : 1, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 1, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "params" : { + "numOfElements" : "12" + }, + "primaryMetric" : { + "score" : 324.61093701658825, + "scoreError" : "NaN", + "scoreConfidence" : [ + "NaN", + "NaN" + ], + "scorePercentiles" : { + "0.0" : 324.61093701658825, + "50.0" : 324.61093701658825, + "90.0" : 324.61093701658825, + "95.0" : 324.61093701658825, + "99.0" : 324.61093701658825, + "99.9" : 324.61093701658825, + "99.99" : 324.61093701658825, + "99.999" : 324.61093701658825, + "99.9999" : 324.61093701658825, + "100.0" : 324.61093701658825 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 324.61093701658825 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.EnumerateSubsetBenchmark.elementsNormalized", + "mode" : "thrpt", + "threads" : 1, + "forks" : 1, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + "-Xms8192m", + "-Xmx8192m", + "-Dtlc2.tool.ModuleOverwritesBenchmark.base=/home/markus/src/TLA/tla/tlatools/test-model" + ], + "jdkVersion" : "1.8.0_181", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.181-b13", + "warmupIterations" : 1, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 1, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "params" : { + "numOfElements" : "14" + }, + "primaryMetric" : { + "score" : 29.229042769052267, + "scoreError" : "NaN", + "scoreConfidence" : [ + "NaN", + "NaN" + ], + "scorePercentiles" : { + "0.0" : 29.229042769052267, + "50.0" : 29.229042769052267, + "90.0" : 29.229042769052267, + "95.0" : 29.229042769052267, + "99.0" : 29.229042769052267, + "99.9" : 29.229042769052267, + "99.99" : 29.229042769052267, + "99.999" : 29.229042769052267, + "99.9999" : 29.229042769052267, + "100.0" : 29.229042769052267 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 29.229042769052267 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.EnumerateSubsetBenchmark.elementsNormalized", + "mode" : "thrpt", + "threads" : 1, + "forks" : 1, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + "-Xms8192m", + "-Xmx8192m", + "-Dtlc2.tool.ModuleOverwritesBenchmark.base=/home/markus/src/TLA/tla/tlatools/test-model" + ], + "jdkVersion" : "1.8.0_181", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.181-b13", + "warmupIterations" : 1, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 1, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "params" : { + "numOfElements" : "16" + }, + "primaryMetric" : { + "score" : 1.9081657766508895, + "scoreError" : "NaN", + "scoreConfidence" : [ + "NaN", + "NaN" + ], + "scorePercentiles" : { + "0.0" : 1.9081657766508895, + "50.0" : 1.9081657766508895, + "90.0" : 1.9081657766508895, + "95.0" : 1.9081657766508895, + "99.0" : 1.9081657766508895, + "99.9" : 1.9081657766508895, + "99.99" : 1.9081657766508895, + "99.999" : 1.9081657766508895, + "99.9999" : 1.9081657766508895, + "100.0" : 1.9081657766508895 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 1.9081657766508895 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.EnumerateSubsetBenchmark.elementsNormalized", + "mode" : "thrpt", + "threads" : 1, + "forks" : 1, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + "-Xms8192m", + "-Xmx8192m", + "-Dtlc2.tool.ModuleOverwritesBenchmark.base=/home/markus/src/TLA/tla/tlatools/test-model" + ], + "jdkVersion" : "1.8.0_181", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.181-b13", + "warmupIterations" : 1, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 1, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "params" : { + "numOfElements" : "18" + }, + "primaryMetric" : { + "score" : 0.128569060485454, + "scoreError" : "NaN", + "scoreConfidence" : [ + "NaN", + "NaN" + ], + "scorePercentiles" : { + "0.0" : 0.128569060485454, + "50.0" : 0.128569060485454, + "90.0" : 0.128569060485454, + "95.0" : 0.128569060485454, + "99.0" : 0.128569060485454, + "99.9" : 0.128569060485454, + "99.99" : 0.128569060485454, + "99.999" : 0.128569060485454, + "99.9999" : 0.128569060485454, + "100.0" : 0.128569060485454 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 0.128569060485454 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.EnumerateSubsetBenchmark.elementsNormalized", + "mode" : "thrpt", + "threads" : 1, + "forks" : 1, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + "-Xms8192m", + "-Xmx8192m", + "-Dtlc2.tool.ModuleOverwritesBenchmark.base=/home/markus/src/TLA/tla/tlatools/test-model" + ], + "jdkVersion" : "1.8.0_181", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.181-b13", + "warmupIterations" : 1, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 1, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "params" : { + "numOfElements" : "19" + }, + "primaryMetric" : { + "score" : 0.031828143851718595, + "scoreError" : "NaN", + "scoreConfidence" : [ + "NaN", + "NaN" + ], + "scorePercentiles" : { + "0.0" : 0.031828143851718595, + "50.0" : 0.031828143851718595, + "90.0" : 0.031828143851718595, + "95.0" : 0.031828143851718595, + "99.0" : 0.031828143851718595, + "99.9" : 0.031828143851718595, + "99.99" : 0.031828143851718595, + "99.999" : 0.031828143851718595, + "99.9999" : 0.031828143851718595, + "100.0" : 0.031828143851718595 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 0.031828143851718595 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.EnumerateSubsetBenchmark.elementsNotNormalized", + "mode" : "thrpt", + "threads" : 1, + "forks" : 1, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + "-Xms8192m", + "-Xmx8192m", + "-Dtlc2.tool.ModuleOverwritesBenchmark.base=/home/markus/src/TLA/tla/tlatools/test-model" + ], + "jdkVersion" : "1.8.0_181", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.181-b13", + "warmupIterations" : 1, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 1, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "params" : { + "numOfElements" : "0" + }, + "primaryMetric" : { + "score" : 1.700831123275485E7, + "scoreError" : "NaN", + "scoreConfidence" : [ + "NaN", + "NaN" + ], + "scorePercentiles" : { + "0.0" : 1.700831123275485E7, + "50.0" : 1.700831123275485E7, + "90.0" : 1.700831123275485E7, + "95.0" : 1.700831123275485E7, + "99.0" : 1.700831123275485E7, + "99.9" : 1.700831123275485E7, + "99.99" : 1.700831123275485E7, + "99.999" : 1.700831123275485E7, + "99.9999" : 1.700831123275485E7, + "100.0" : 1.700831123275485E7 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 1.700831123275485E7 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.EnumerateSubsetBenchmark.elementsNotNormalized", + "mode" : "thrpt", + "threads" : 1, + "forks" : 1, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + "-Xms8192m", + "-Xmx8192m", + "-Dtlc2.tool.ModuleOverwritesBenchmark.base=/home/markus/src/TLA/tla/tlatools/test-model" + ], + "jdkVersion" : "1.8.0_181", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.181-b13", + "warmupIterations" : 1, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 1, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "params" : { + "numOfElements" : "1" + }, + "primaryMetric" : { + "score" : 1.1138688178715378E7, + "scoreError" : "NaN", + "scoreConfidence" : [ + "NaN", + "NaN" + ], + "scorePercentiles" : { + "0.0" : 1.1138688178715378E7, + "50.0" : 1.1138688178715378E7, + "90.0" : 1.1138688178715378E7, + "95.0" : 1.1138688178715378E7, + "99.0" : 1.1138688178715378E7, + "99.9" : 1.1138688178715378E7, + "99.99" : 1.1138688178715378E7, + "99.999" : 1.1138688178715378E7, + "99.9999" : 1.1138688178715378E7, + "100.0" : 1.1138688178715378E7 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 1.1138688178715378E7 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.EnumerateSubsetBenchmark.elementsNotNormalized", + "mode" : "thrpt", + "threads" : 1, + "forks" : 1, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + "-Xms8192m", + "-Xmx8192m", + "-Dtlc2.tool.ModuleOverwritesBenchmark.base=/home/markus/src/TLA/tla/tlatools/test-model" + ], + "jdkVersion" : "1.8.0_181", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.181-b13", + "warmupIterations" : 1, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 1, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "params" : { + "numOfElements" : "2" + }, + "primaryMetric" : { + "score" : 7759027.958066327, + "scoreError" : "NaN", + "scoreConfidence" : [ + "NaN", + "NaN" + ], + "scorePercentiles" : { + "0.0" : 7759027.958066327, + "50.0" : 7759027.958066327, + "90.0" : 7759027.958066327, + "95.0" : 7759027.958066327, + "99.0" : 7759027.958066327, + "99.9" : 7759027.958066327, + "99.99" : 7759027.958066327, + "99.999" : 7759027.958066327, + "99.9999" : 7759027.958066327, + "100.0" : 7759027.958066327 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 7759027.958066327 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.EnumerateSubsetBenchmark.elementsNotNormalized", + "mode" : "thrpt", + "threads" : 1, + "forks" : 1, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + "-Xms8192m", + "-Xmx8192m", + "-Dtlc2.tool.ModuleOverwritesBenchmark.base=/home/markus/src/TLA/tla/tlatools/test-model" + ], + "jdkVersion" : "1.8.0_181", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.181-b13", + "warmupIterations" : 1, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 1, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "params" : { + "numOfElements" : "3" + }, + "primaryMetric" : { + "score" : 4547334.765941614, + "scoreError" : "NaN", + "scoreConfidence" : [ + "NaN", + "NaN" + ], + "scorePercentiles" : { + "0.0" : 4547334.765941614, + "50.0" : 4547334.765941614, + "90.0" : 4547334.765941614, + "95.0" : 4547334.765941614, + "99.0" : 4547334.765941614, + "99.9" : 4547334.765941614, + "99.99" : 4547334.765941614, + "99.999" : 4547334.765941614, + "99.9999" : 4547334.765941614, + "100.0" : 4547334.765941614 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 4547334.765941614 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.EnumerateSubsetBenchmark.elementsNotNormalized", + "mode" : "thrpt", + "threads" : 1, + "forks" : 1, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + "-Xms8192m", + "-Xmx8192m", + "-Dtlc2.tool.ModuleOverwritesBenchmark.base=/home/markus/src/TLA/tla/tlatools/test-model" + ], + "jdkVersion" : "1.8.0_181", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.181-b13", + "warmupIterations" : 1, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 1, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "params" : { + "numOfElements" : "4" + }, + "primaryMetric" : { + "score" : 2563987.258485594, + "scoreError" : "NaN", + "scoreConfidence" : [ + "NaN", + "NaN" + ], + "scorePercentiles" : { + "0.0" : 2563987.258485594, + "50.0" : 2563987.258485594, + "90.0" : 2563987.258485594, + "95.0" : 2563987.258485594, + "99.0" : 2563987.258485594, + "99.9" : 2563987.258485594, + "99.99" : 2563987.258485594, + "99.999" : 2563987.258485594, + "99.9999" : 2563987.258485594, + "100.0" : 2563987.258485594 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 2563987.258485594 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.EnumerateSubsetBenchmark.elementsNotNormalized", + "mode" : "thrpt", + "threads" : 1, + "forks" : 1, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + "-Xms8192m", + "-Xmx8192m", + "-Dtlc2.tool.ModuleOverwritesBenchmark.base=/home/markus/src/TLA/tla/tlatools/test-model" + ], + "jdkVersion" : "1.8.0_181", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.181-b13", + "warmupIterations" : 1, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 1, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "params" : { + "numOfElements" : "8" + }, + "primaryMetric" : { + "score" : 131149.70918675946, + "scoreError" : "NaN", + "scoreConfidence" : [ + "NaN", + "NaN" + ], + "scorePercentiles" : { + "0.0" : 131149.70918675946, + "50.0" : 131149.70918675946, + "90.0" : 131149.70918675946, + "95.0" : 131149.70918675946, + "99.0" : 131149.70918675946, + "99.9" : 131149.70918675946, + "99.99" : 131149.70918675946, + "99.999" : 131149.70918675946, + "99.9999" : 131149.70918675946, + "100.0" : 131149.70918675946 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 131149.70918675946 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.EnumerateSubsetBenchmark.elementsNotNormalized", + "mode" : "thrpt", + "threads" : 1, + "forks" : 1, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + "-Xms8192m", + "-Xmx8192m", + "-Dtlc2.tool.ModuleOverwritesBenchmark.base=/home/markus/src/TLA/tla/tlatools/test-model" + ], + "jdkVersion" : "1.8.0_181", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.181-b13", + "warmupIterations" : 1, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 1, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "params" : { + "numOfElements" : "10" + }, + "primaryMetric" : { + "score" : 12308.28613737422, + "scoreError" : "NaN", + "scoreConfidence" : [ + "NaN", + "NaN" + ], + "scorePercentiles" : { + "0.0" : 12308.28613737422, + "50.0" : 12308.28613737422, + "90.0" : 12308.28613737422, + "95.0" : 12308.28613737422, + "99.0" : 12308.28613737422, + "99.9" : 12308.28613737422, + "99.99" : 12308.28613737422, + "99.999" : 12308.28613737422, + "99.9999" : 12308.28613737422, + "100.0" : 12308.28613737422 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 12308.28613737422 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.EnumerateSubsetBenchmark.elementsNotNormalized", + "mode" : "thrpt", + "threads" : 1, + "forks" : 1, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + "-Xms8192m", + "-Xmx8192m", + "-Dtlc2.tool.ModuleOverwritesBenchmark.base=/home/markus/src/TLA/tla/tlatools/test-model" + ], + "jdkVersion" : "1.8.0_181", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.181-b13", + "warmupIterations" : 1, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 1, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "params" : { + "numOfElements" : "12" + }, + "primaryMetric" : { + "score" : 2598.9041516352163, + "scoreError" : "NaN", + "scoreConfidence" : [ + "NaN", + "NaN" + ], + "scorePercentiles" : { + "0.0" : 2598.9041516352163, + "50.0" : 2598.9041516352163, + "90.0" : 2598.9041516352163, + "95.0" : 2598.9041516352163, + "99.0" : 2598.9041516352163, + "99.9" : 2598.9041516352163, + "99.99" : 2598.9041516352163, + "99.999" : 2598.9041516352163, + "99.9999" : 2598.9041516352163, + "100.0" : 2598.9041516352163 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 2598.9041516352163 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.EnumerateSubsetBenchmark.elementsNotNormalized", + "mode" : "thrpt", + "threads" : 1, + "forks" : 1, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + "-Xms8192m", + "-Xmx8192m", + "-Dtlc2.tool.ModuleOverwritesBenchmark.base=/home/markus/src/TLA/tla/tlatools/test-model" + ], + "jdkVersion" : "1.8.0_181", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.181-b13", + "warmupIterations" : 1, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 1, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "params" : { + "numOfElements" : "14" + }, + "primaryMetric" : { + "score" : 621.0476255011224, + "scoreError" : "NaN", + "scoreConfidence" : [ + "NaN", + "NaN" + ], + "scorePercentiles" : { + "0.0" : 621.0476255011224, + "50.0" : 621.0476255011224, + "90.0" : 621.0476255011224, + "95.0" : 621.0476255011224, + "99.0" : 621.0476255011224, + "99.9" : 621.0476255011224, + "99.99" : 621.0476255011224, + "99.999" : 621.0476255011224, + "99.9999" : 621.0476255011224, + "100.0" : 621.0476255011224 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 621.0476255011224 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.EnumerateSubsetBenchmark.elementsNotNormalized", + "mode" : "thrpt", + "threads" : 1, + "forks" : 1, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + "-Xms8192m", + "-Xmx8192m", + "-Dtlc2.tool.ModuleOverwritesBenchmark.base=/home/markus/src/TLA/tla/tlatools/test-model" + ], + "jdkVersion" : "1.8.0_181", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.181-b13", + "warmupIterations" : 1, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 1, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "params" : { + "numOfElements" : "16" + }, + "primaryMetric" : { + "score" : 139.82630658657763, + "scoreError" : "NaN", + "scoreConfidence" : [ + "NaN", + "NaN" + ], + "scorePercentiles" : { + "0.0" : 139.82630658657763, + "50.0" : 139.82630658657763, + "90.0" : 139.82630658657763, + "95.0" : 139.82630658657763, + "99.0" : 139.82630658657763, + "99.9" : 139.82630658657763, + "99.99" : 139.82630658657763, + "99.999" : 139.82630658657763, + "99.9999" : 139.82630658657763, + "100.0" : 139.82630658657763 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 139.82630658657763 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.EnumerateSubsetBenchmark.elementsNotNormalized", + "mode" : "thrpt", + "threads" : 1, + "forks" : 1, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + "-Xms8192m", + "-Xmx8192m", + "-Dtlc2.tool.ModuleOverwritesBenchmark.base=/home/markus/src/TLA/tla/tlatools/test-model" + ], + "jdkVersion" : "1.8.0_181", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.181-b13", + "warmupIterations" : 1, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 1, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "params" : { + "numOfElements" : "18" + }, + "primaryMetric" : { + "score" : 32.359718767696705, + "scoreError" : "NaN", + "scoreConfidence" : [ + "NaN", + "NaN" + ], + "scorePercentiles" : { + "0.0" : 32.359718767696705, + "50.0" : 32.359718767696705, + "90.0" : 32.359718767696705, + "95.0" : 32.359718767696705, + "99.0" : 32.359718767696705, + "99.9" : 32.359718767696705, + "99.99" : 32.359718767696705, + "99.999" : 32.359718767696705, + "99.9999" : 32.359718767696705, + "100.0" : 32.359718767696705 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 32.359718767696705 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.EnumerateSubsetBenchmark.elementsNotNormalized", + "mode" : "thrpt", + "threads" : 1, + "forks" : 1, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + "-Xms8192m", + "-Xmx8192m", + "-Dtlc2.tool.ModuleOverwritesBenchmark.base=/home/markus/src/TLA/tla/tlatools/test-model" + ], + "jdkVersion" : "1.8.0_181", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.181-b13", + "warmupIterations" : 1, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 1, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "params" : { + "numOfElements" : "19" + }, + "primaryMetric" : { + "score" : 15.903491038693316, + "scoreError" : "NaN", + "scoreConfidence" : [ + "NaN", + "NaN" + ], + "scorePercentiles" : { + "0.0" : 15.903491038693316, + "50.0" : 15.903491038693316, + "90.0" : 15.903491038693316, + "95.0" : 15.903491038693316, + "99.0" : 15.903491038693316, + "99.9" : 15.903491038693316, + "99.99" : 15.903491038693316, + "99.999" : 15.903491038693316, + "99.9999" : 15.903491038693316, + "100.0" : 15.903491038693316 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 15.903491038693316 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.EnumerateSubsetBenchmark.kElementsNormalized", + "mode" : "thrpt", + "threads" : 1, + "forks" : 1, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + "-Xms8192m", + "-Xmx8192m", + "-Dtlc2.tool.ModuleOverwritesBenchmark.base=/home/markus/src/TLA/tla/tlatools/test-model" + ], + "jdkVersion" : "1.8.0_181", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.181-b13", + "warmupIterations" : 1, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 1, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "params" : { + "numOfElements" : "0" + }, + "primaryMetric" : { + "score" : 3.0952316339848503E7, + "scoreError" : "NaN", + "scoreConfidence" : [ + "NaN", + "NaN" + ], + "scorePercentiles" : { + "0.0" : 3.0952316339848503E7, + "50.0" : 3.0952316339848503E7, + "90.0" : 3.0952316339848503E7, + "95.0" : 3.0952316339848503E7, + "99.0" : 3.0952316339848503E7, + "99.9" : 3.0952316339848503E7, + "99.99" : 3.0952316339848503E7, + "99.999" : 3.0952316339848503E7, + "99.9999" : 3.0952316339848503E7, + "100.0" : 3.0952316339848503E7 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 3.0952316339848503E7 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.EnumerateSubsetBenchmark.kElementsNormalized", + "mode" : "thrpt", + "threads" : 1, + "forks" : 1, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + "-Xms8192m", + "-Xmx8192m", + "-Dtlc2.tool.ModuleOverwritesBenchmark.base=/home/markus/src/TLA/tla/tlatools/test-model" + ], + "jdkVersion" : "1.8.0_181", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.181-b13", + "warmupIterations" : 1, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 1, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "params" : { + "numOfElements" : "1" + }, + "primaryMetric" : { + "score" : 7560521.642894856, + "scoreError" : "NaN", + "scoreConfidence" : [ + "NaN", + "NaN" + ], + "scorePercentiles" : { + "0.0" : 7560521.642894856, + "50.0" : 7560521.642894856, + "90.0" : 7560521.642894856, + "95.0" : 7560521.642894856, + "99.0" : 7560521.642894856, + "99.9" : 7560521.642894856, + "99.99" : 7560521.642894856, + "99.999" : 7560521.642894856, + "99.9999" : 7560521.642894856, + "100.0" : 7560521.642894856 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 7560521.642894856 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.EnumerateSubsetBenchmark.kElementsNormalized", + "mode" : "thrpt", + "threads" : 1, + "forks" : 1, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + "-Xms8192m", + "-Xmx8192m", + "-Dtlc2.tool.ModuleOverwritesBenchmark.base=/home/markus/src/TLA/tla/tlatools/test-model" + ], + "jdkVersion" : "1.8.0_181", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.181-b13", + "warmupIterations" : 1, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 1, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "params" : { + "numOfElements" : "2" + }, + "primaryMetric" : { + "score" : 3557526.180877572, + "scoreError" : "NaN", + "scoreConfidence" : [ + "NaN", + "NaN" + ], + "scorePercentiles" : { + "0.0" : 3557526.180877572, + "50.0" : 3557526.180877572, + "90.0" : 3557526.180877572, + "95.0" : 3557526.180877572, + "99.0" : 3557526.180877572, + "99.9" : 3557526.180877572, + "99.99" : 3557526.180877572, + "99.999" : 3557526.180877572, + "99.9999" : 3557526.180877572, + "100.0" : 3557526.180877572 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 3557526.180877572 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.EnumerateSubsetBenchmark.kElementsNormalized", + "mode" : "thrpt", + "threads" : 1, + "forks" : 1, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + "-Xms8192m", + "-Xmx8192m", + "-Dtlc2.tool.ModuleOverwritesBenchmark.base=/home/markus/src/TLA/tla/tlatools/test-model" + ], + "jdkVersion" : "1.8.0_181", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.181-b13", + "warmupIterations" : 1, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 1, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "params" : { + "numOfElements" : "3" + }, + "primaryMetric" : { + "score" : 1721980.583759659, + "scoreError" : "NaN", + "scoreConfidence" : [ + "NaN", + "NaN" + ], + "scorePercentiles" : { + "0.0" : 1721980.583759659, + "50.0" : 1721980.583759659, + "90.0" : 1721980.583759659, + "95.0" : 1721980.583759659, + "99.0" : 1721980.583759659, + "99.9" : 1721980.583759659, + "99.99" : 1721980.583759659, + "99.999" : 1721980.583759659, + "99.9999" : 1721980.583759659, + "100.0" : 1721980.583759659 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 1721980.583759659 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.EnumerateSubsetBenchmark.kElementsNormalized", + "mode" : "thrpt", + "threads" : 1, + "forks" : 1, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + "-Xms8192m", + "-Xmx8192m", + "-Dtlc2.tool.ModuleOverwritesBenchmark.base=/home/markus/src/TLA/tla/tlatools/test-model" + ], + "jdkVersion" : "1.8.0_181", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.181-b13", + "warmupIterations" : 1, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 1, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "params" : { + "numOfElements" : "4" + }, + "primaryMetric" : { + "score" : 737795.2258201947, + "scoreError" : "NaN", + "scoreConfidence" : [ + "NaN", + "NaN" + ], + "scorePercentiles" : { + "0.0" : 737795.2258201947, + "50.0" : 737795.2258201947, + "90.0" : 737795.2258201947, + "95.0" : 737795.2258201947, + "99.0" : 737795.2258201947, + "99.9" : 737795.2258201947, + "99.99" : 737795.2258201947, + "99.999" : 737795.2258201947, + "99.9999" : 737795.2258201947, + "100.0" : 737795.2258201947 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 737795.2258201947 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.EnumerateSubsetBenchmark.kElementsNormalized", + "mode" : "thrpt", + "threads" : 1, + "forks" : 1, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + "-Xms8192m", + "-Xmx8192m", + "-Dtlc2.tool.ModuleOverwritesBenchmark.base=/home/markus/src/TLA/tla/tlatools/test-model" + ], + "jdkVersion" : "1.8.0_181", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.181-b13", + "warmupIterations" : 1, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 1, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "params" : { + "numOfElements" : "8" + }, + "primaryMetric" : { + "score" : 14470.141135737491, + "scoreError" : "NaN", + "scoreConfidence" : [ + "NaN", + "NaN" + ], + "scorePercentiles" : { + "0.0" : 14470.141135737491, + "50.0" : 14470.141135737491, + "90.0" : 14470.141135737491, + "95.0" : 14470.141135737491, + "99.0" : 14470.141135737491, + "99.9" : 14470.141135737491, + "99.99" : 14470.141135737491, + "99.999" : 14470.141135737491, + "99.9999" : 14470.141135737491, + "100.0" : 14470.141135737491 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 14470.141135737491 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.EnumerateSubsetBenchmark.kElementsNormalized", + "mode" : "thrpt", + "threads" : 1, + "forks" : 1, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + "-Xms8192m", + "-Xmx8192m", + "-Dtlc2.tool.ModuleOverwritesBenchmark.base=/home/markus/src/TLA/tla/tlatools/test-model" + ], + "jdkVersion" : "1.8.0_181", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.181-b13", + "warmupIterations" : 1, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 1, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "params" : { + "numOfElements" : "10" + }, + "primaryMetric" : { + "score" : 3409.1535618738417, + "scoreError" : "NaN", + "scoreConfidence" : [ + "NaN", + "NaN" + ], + "scorePercentiles" : { + "0.0" : 3409.1535618738417, + "50.0" : 3409.1535618738417, + "90.0" : 3409.1535618738417, + "95.0" : 3409.1535618738417, + "99.0" : 3409.1535618738417, + "99.9" : 3409.1535618738417, + "99.99" : 3409.1535618738417, + "99.999" : 3409.1535618738417, + "99.9999" : 3409.1535618738417, + "100.0" : 3409.1535618738417 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 3409.1535618738417 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.EnumerateSubsetBenchmark.kElementsNormalized", + "mode" : "thrpt", + "threads" : 1, + "forks" : 1, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + "-Xms8192m", + "-Xmx8192m", + "-Dtlc2.tool.ModuleOverwritesBenchmark.base=/home/markus/src/TLA/tla/tlatools/test-model" + ], + "jdkVersion" : "1.8.0_181", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.181-b13", + "warmupIterations" : 1, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 1, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "params" : { + "numOfElements" : "12" + }, + "primaryMetric" : { + "score" : 662.9793727227765, + "scoreError" : "NaN", + "scoreConfidence" : [ + "NaN", + "NaN" + ], + "scorePercentiles" : { + "0.0" : 662.9793727227765, + "50.0" : 662.9793727227765, + "90.0" : 662.9793727227765, + "95.0" : 662.9793727227765, + "99.0" : 662.9793727227765, + "99.9" : 662.9793727227765, + "99.99" : 662.9793727227765, + "99.999" : 662.9793727227765, + "99.9999" : 662.9793727227765, + "100.0" : 662.9793727227765 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 662.9793727227765 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.EnumerateSubsetBenchmark.kElementsNormalized", + "mode" : "thrpt", + "threads" : 1, + "forks" : 1, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + "-Xms8192m", + "-Xmx8192m", + "-Dtlc2.tool.ModuleOverwritesBenchmark.base=/home/markus/src/TLA/tla/tlatools/test-model" + ], + "jdkVersion" : "1.8.0_181", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.181-b13", + "warmupIterations" : 1, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 1, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "params" : { + "numOfElements" : "14" + }, + "primaryMetric" : { + "score" : 89.74606370164268, + "scoreError" : "NaN", + "scoreConfidence" : [ + "NaN", + "NaN" + ], + "scorePercentiles" : { + "0.0" : 89.74606370164268, + "50.0" : 89.74606370164268, + "90.0" : 89.74606370164268, + "95.0" : 89.74606370164268, + "99.0" : 89.74606370164268, + "99.9" : 89.74606370164268, + "99.99" : 89.74606370164268, + "99.999" : 89.74606370164268, + "99.9999" : 89.74606370164268, + "100.0" : 89.74606370164268 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 89.74606370164268 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.EnumerateSubsetBenchmark.kElementsNormalized", + "mode" : "thrpt", + "threads" : 1, + "forks" : 1, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + "-Xms8192m", + "-Xmx8192m", + "-Dtlc2.tool.ModuleOverwritesBenchmark.base=/home/markus/src/TLA/tla/tlatools/test-model" + ], + "jdkVersion" : "1.8.0_181", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.181-b13", + "warmupIterations" : 1, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 1, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "params" : { + "numOfElements" : "16" + }, + "primaryMetric" : { + "score" : 9.497863578110545, + "scoreError" : "NaN", + "scoreConfidence" : [ + "NaN", + "NaN" + ], + "scorePercentiles" : { + "0.0" : 9.497863578110545, + "50.0" : 9.497863578110545, + "90.0" : 9.497863578110545, + "95.0" : 9.497863578110545, + "99.0" : 9.497863578110545, + "99.9" : 9.497863578110545, + "99.99" : 9.497863578110545, + "99.999" : 9.497863578110545, + "99.9999" : 9.497863578110545, + "100.0" : 9.497863578110545 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 9.497863578110545 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.EnumerateSubsetBenchmark.kElementsNormalized", + "mode" : "thrpt", + "threads" : 1, + "forks" : 1, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + "-Xms8192m", + "-Xmx8192m", + "-Dtlc2.tool.ModuleOverwritesBenchmark.base=/home/markus/src/TLA/tla/tlatools/test-model" + ], + "jdkVersion" : "1.8.0_181", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.181-b13", + "warmupIterations" : 1, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 1, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "params" : { + "numOfElements" : "18" + }, + "primaryMetric" : { + "score" : 0.7630303029631332, + "scoreError" : "NaN", + "scoreConfidence" : [ + "NaN", + "NaN" + ], + "scorePercentiles" : { + "0.0" : 0.7630303029631332, + "50.0" : 0.7630303029631332, + "90.0" : 0.7630303029631332, + "95.0" : 0.7630303029631332, + "99.0" : 0.7630303029631332, + "99.9" : 0.7630303029631332, + "99.99" : 0.7630303029631332, + "99.999" : 0.7630303029631332, + "99.9999" : 0.7630303029631332, + "100.0" : 0.7630303029631332 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 0.7630303029631332 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.EnumerateSubsetBenchmark.kElementsNormalized", + "mode" : "thrpt", + "threads" : 1, + "forks" : 1, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + "-Xms8192m", + "-Xmx8192m", + "-Dtlc2.tool.ModuleOverwritesBenchmark.base=/home/markus/src/TLA/tla/tlatools/test-model" + ], + "jdkVersion" : "1.8.0_181", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.181-b13", + "warmupIterations" : 1, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 1, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "params" : { + "numOfElements" : "19" + }, + "primaryMetric" : { + "score" : 0.18791317607485522, + "scoreError" : "NaN", + "scoreConfidence" : [ + "NaN", + "NaN" + ], + "scorePercentiles" : { + "0.0" : 0.18791317607485522, + "50.0" : 0.18791317607485522, + "90.0" : 0.18791317607485522, + "95.0" : 0.18791317607485522, + "99.0" : 0.18791317607485522, + "99.9" : 0.18791317607485522, + "99.99" : 0.18791317607485522, + "99.999" : 0.18791317607485522, + "99.9999" : 0.18791317607485522, + "100.0" : 0.18791317607485522 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 0.18791317607485522 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.EnumerateSubsetBenchmark.kElementsNotNormalized", + "mode" : "thrpt", + "threads" : 1, + "forks" : 1, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + "-Xms8192m", + "-Xmx8192m", + "-Dtlc2.tool.ModuleOverwritesBenchmark.base=/home/markus/src/TLA/tla/tlatools/test-model" + ], + "jdkVersion" : "1.8.0_181", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.181-b13", + "warmupIterations" : 1, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 1, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "params" : { + "numOfElements" : "0" + }, + "primaryMetric" : { + "score" : 3.916296940504498E7, + "scoreError" : "NaN", + "scoreConfidence" : [ + "NaN", + "NaN" + ], + "scorePercentiles" : { + "0.0" : 3.916296940504498E7, + "50.0" : 3.916296940504498E7, + "90.0" : 3.916296940504498E7, + "95.0" : 3.916296940504498E7, + "99.0" : 3.916296940504498E7, + "99.9" : 3.916296940504498E7, + "99.99" : 3.916296940504498E7, + "99.999" : 3.916296940504498E7, + "99.9999" : 3.916296940504498E7, + "100.0" : 3.916296940504498E7 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 3.916296940504498E7 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.EnumerateSubsetBenchmark.kElementsNotNormalized", + "mode" : "thrpt", + "threads" : 1, + "forks" : 1, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + "-Xms8192m", + "-Xmx8192m", + "-Dtlc2.tool.ModuleOverwritesBenchmark.base=/home/markus/src/TLA/tla/tlatools/test-model" + ], + "jdkVersion" : "1.8.0_181", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.181-b13", + "warmupIterations" : 1, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 1, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "params" : { + "numOfElements" : "1" + }, + "primaryMetric" : { + "score" : 1.0229672606085628E7, + "scoreError" : "NaN", + "scoreConfidence" : [ + "NaN", + "NaN" + ], + "scorePercentiles" : { + "0.0" : 1.0229672606085628E7, + "50.0" : 1.0229672606085628E7, + "90.0" : 1.0229672606085628E7, + "95.0" : 1.0229672606085628E7, + "99.0" : 1.0229672606085628E7, + "99.9" : 1.0229672606085628E7, + "99.99" : 1.0229672606085628E7, + "99.999" : 1.0229672606085628E7, + "99.9999" : 1.0229672606085628E7, + "100.0" : 1.0229672606085628E7 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 1.0229672606085628E7 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.EnumerateSubsetBenchmark.kElementsNotNormalized", + "mode" : "thrpt", + "threads" : 1, + "forks" : 1, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + "-Xms8192m", + "-Xmx8192m", + "-Dtlc2.tool.ModuleOverwritesBenchmark.base=/home/markus/src/TLA/tla/tlatools/test-model" + ], + "jdkVersion" : "1.8.0_181", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.181-b13", + "warmupIterations" : 1, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 1, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "params" : { + "numOfElements" : "2" + }, + "primaryMetric" : { + "score" : 5187949.167063834, + "scoreError" : "NaN", + "scoreConfidence" : [ + "NaN", + "NaN" + ], + "scorePercentiles" : { + "0.0" : 5187949.167063834, + "50.0" : 5187949.167063834, + "90.0" : 5187949.167063834, + "95.0" : 5187949.167063834, + "99.0" : 5187949.167063834, + "99.9" : 5187949.167063834, + "99.99" : 5187949.167063834, + "99.999" : 5187949.167063834, + "99.9999" : 5187949.167063834, + "100.0" : 5187949.167063834 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 5187949.167063834 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.EnumerateSubsetBenchmark.kElementsNotNormalized", + "mode" : "thrpt", + "threads" : 1, + "forks" : 1, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + "-Xms8192m", + "-Xmx8192m", + "-Dtlc2.tool.ModuleOverwritesBenchmark.base=/home/markus/src/TLA/tla/tlatools/test-model" + ], + "jdkVersion" : "1.8.0_181", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.181-b13", + "warmupIterations" : 1, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 1, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "params" : { + "numOfElements" : "3" + }, + "primaryMetric" : { + "score" : 2615973.0534431874, + "scoreError" : "NaN", + "scoreConfidence" : [ + "NaN", + "NaN" + ], + "scorePercentiles" : { + "0.0" : 2615973.0534431874, + "50.0" : 2615973.0534431874, + "90.0" : 2615973.0534431874, + "95.0" : 2615973.0534431874, + "99.0" : 2615973.0534431874, + "99.9" : 2615973.0534431874, + "99.99" : 2615973.0534431874, + "99.999" : 2615973.0534431874, + "99.9999" : 2615973.0534431874, + "100.0" : 2615973.0534431874 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 2615973.0534431874 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.EnumerateSubsetBenchmark.kElementsNotNormalized", + "mode" : "thrpt", + "threads" : 1, + "forks" : 1, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + "-Xms8192m", + "-Xmx8192m", + "-Dtlc2.tool.ModuleOverwritesBenchmark.base=/home/markus/src/TLA/tla/tlatools/test-model" + ], + "jdkVersion" : "1.8.0_181", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.181-b13", + "warmupIterations" : 1, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 1, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "params" : { + "numOfElements" : "4" + }, + "primaryMetric" : { + "score" : 1488347.4408349597, + "scoreError" : "NaN", + "scoreConfidence" : [ + "NaN", + "NaN" + ], + "scorePercentiles" : { + "0.0" : 1488347.4408349597, + "50.0" : 1488347.4408349597, + "90.0" : 1488347.4408349597, + "95.0" : 1488347.4408349597, + "99.0" : 1488347.4408349597, + "99.9" : 1488347.4408349597, + "99.99" : 1488347.4408349597, + "99.999" : 1488347.4408349597, + "99.9999" : 1488347.4408349597, + "100.0" : 1488347.4408349597 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 1488347.4408349597 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.EnumerateSubsetBenchmark.kElementsNotNormalized", + "mode" : "thrpt", + "threads" : 1, + "forks" : 1, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + "-Xms8192m", + "-Xmx8192m", + "-Dtlc2.tool.ModuleOverwritesBenchmark.base=/home/markus/src/TLA/tla/tlatools/test-model" + ], + "jdkVersion" : "1.8.0_181", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.181-b13", + "warmupIterations" : 1, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 1, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "params" : { + "numOfElements" : "8" + }, + "primaryMetric" : { + "score" : 88358.67738981682, + "scoreError" : "NaN", + "scoreConfidence" : [ + "NaN", + "NaN" + ], + "scorePercentiles" : { + "0.0" : 88358.67738981682, + "50.0" : 88358.67738981682, + "90.0" : 88358.67738981682, + "95.0" : 88358.67738981682, + "99.0" : 88358.67738981682, + "99.9" : 88358.67738981682, + "99.99" : 88358.67738981682, + "99.999" : 88358.67738981682, + "99.9999" : 88358.67738981682, + "100.0" : 88358.67738981682 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 88358.67738981682 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.EnumerateSubsetBenchmark.kElementsNotNormalized", + "mode" : "thrpt", + "threads" : 1, + "forks" : 1, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + "-Xms8192m", + "-Xmx8192m", + "-Dtlc2.tool.ModuleOverwritesBenchmark.base=/home/markus/src/TLA/tla/tlatools/test-model" + ], + "jdkVersion" : "1.8.0_181", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.181-b13", + "warmupIterations" : 1, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 1, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "params" : { + "numOfElements" : "10" + }, + "primaryMetric" : { + "score" : 16036.52761637113, + "scoreError" : "NaN", + "scoreConfidence" : [ + "NaN", + "NaN" + ], + "scorePercentiles" : { + "0.0" : 16036.52761637113, + "50.0" : 16036.52761637113, + "90.0" : 16036.52761637113, + "95.0" : 16036.52761637113, + "99.0" : 16036.52761637113, + "99.9" : 16036.52761637113, + "99.99" : 16036.52761637113, + "99.999" : 16036.52761637113, + "99.9999" : 16036.52761637113, + "100.0" : 16036.52761637113 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 16036.52761637113 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.EnumerateSubsetBenchmark.kElementsNotNormalized", + "mode" : "thrpt", + "threads" : 1, + "forks" : 1, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + "-Xms8192m", + "-Xmx8192m", + "-Dtlc2.tool.ModuleOverwritesBenchmark.base=/home/markus/src/TLA/tla/tlatools/test-model" + ], + "jdkVersion" : "1.8.0_181", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.181-b13", + "warmupIterations" : 1, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 1, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "params" : { + "numOfElements" : "12" + }, + "primaryMetric" : { + "score" : 3750.424490260002, + "scoreError" : "NaN", + "scoreConfidence" : [ + "NaN", + "NaN" + ], + "scorePercentiles" : { + "0.0" : 3750.424490260002, + "50.0" : 3750.424490260002, + "90.0" : 3750.424490260002, + "95.0" : 3750.424490260002, + "99.0" : 3750.424490260002, + "99.9" : 3750.424490260002, + "99.99" : 3750.424490260002, + "99.999" : 3750.424490260002, + "99.9999" : 3750.424490260002, + "100.0" : 3750.424490260002 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 3750.424490260002 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.EnumerateSubsetBenchmark.kElementsNotNormalized", + "mode" : "thrpt", + "threads" : 1, + "forks" : 1, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + "-Xms8192m", + "-Xmx8192m", + "-Dtlc2.tool.ModuleOverwritesBenchmark.base=/home/markus/src/TLA/tla/tlatools/test-model" + ], + "jdkVersion" : "1.8.0_181", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.181-b13", + "warmupIterations" : 1, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 1, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "params" : { + "numOfElements" : "14" + }, + "primaryMetric" : { + "score" : 825.1117433250652, + "scoreError" : "NaN", + "scoreConfidence" : [ + "NaN", + "NaN" + ], + "scorePercentiles" : { + "0.0" : 825.1117433250652, + "50.0" : 825.1117433250652, + "90.0" : 825.1117433250652, + "95.0" : 825.1117433250652, + "99.0" : 825.1117433250652, + "99.9" : 825.1117433250652, + "99.99" : 825.1117433250652, + "99.999" : 825.1117433250652, + "99.9999" : 825.1117433250652, + "100.0" : 825.1117433250652 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 825.1117433250652 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.EnumerateSubsetBenchmark.kElementsNotNormalized", + "mode" : "thrpt", + "threads" : 1, + "forks" : 1, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + "-Xms8192m", + "-Xmx8192m", + "-Dtlc2.tool.ModuleOverwritesBenchmark.base=/home/markus/src/TLA/tla/tlatools/test-model" + ], + "jdkVersion" : "1.8.0_181", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.181-b13", + "warmupIterations" : 1, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 1, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "params" : { + "numOfElements" : "16" + }, + "primaryMetric" : { + "score" : 212.8694242448053, + "scoreError" : "NaN", + "scoreConfidence" : [ + "NaN", + "NaN" + ], + "scorePercentiles" : { + "0.0" : 212.8694242448053, + "50.0" : 212.8694242448053, + "90.0" : 212.8694242448053, + "95.0" : 212.8694242448053, + "99.0" : 212.8694242448053, + "99.9" : 212.8694242448053, + "99.99" : 212.8694242448053, + "99.999" : 212.8694242448053, + "99.9999" : 212.8694242448053, + "100.0" : 212.8694242448053 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 212.8694242448053 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.EnumerateSubsetBenchmark.kElementsNotNormalized", + "mode" : "thrpt", + "threads" : 1, + "forks" : 1, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + "-Xms8192m", + "-Xmx8192m", + "-Dtlc2.tool.ModuleOverwritesBenchmark.base=/home/markus/src/TLA/tla/tlatools/test-model" + ], + "jdkVersion" : "1.8.0_181", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.181-b13", + "warmupIterations" : 1, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 1, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "params" : { + "numOfElements" : "18" + }, + "primaryMetric" : { + "score" : 51.088303317628935, + "scoreError" : "NaN", + "scoreConfidence" : [ + "NaN", + "NaN" + ], + "scorePercentiles" : { + "0.0" : 51.088303317628935, + "50.0" : 51.088303317628935, + "90.0" : 51.088303317628935, + "95.0" : 51.088303317628935, + "99.0" : 51.088303317628935, + "99.9" : 51.088303317628935, + "99.99" : 51.088303317628935, + "99.999" : 51.088303317628935, + "99.9999" : 51.088303317628935, + "100.0" : 51.088303317628935 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 51.088303317628935 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.EnumerateSubsetBenchmark.kElementsNotNormalized", + "mode" : "thrpt", + "threads" : 1, + "forks" : 1, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + "-Xms8192m", + "-Xmx8192m", + "-Dtlc2.tool.ModuleOverwritesBenchmark.base=/home/markus/src/TLA/tla/tlatools/test-model" + ], + "jdkVersion" : "1.8.0_181", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.181-b13", + "warmupIterations" : 1, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 1, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "params" : { + "numOfElements" : "19" + }, + "primaryMetric" : { + "score" : 24.809251093862, + "scoreError" : "NaN", + "scoreConfidence" : [ + "NaN", + "NaN" + ], + "scorePercentiles" : { + "0.0" : 24.809251093862, + "50.0" : 24.809251093862, + "90.0" : 24.809251093862, + "95.0" : 24.809251093862, + "99.0" : 24.809251093862, + "99.9" : 24.809251093862, + "99.99" : 24.809251093862, + "99.999" : 24.809251093862, + "99.9999" : 24.809251093862, + "100.0" : 24.809251093862 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 24.809251093862 + ] + ] + }, + "secondaryMetrics" : { + } + } +] + + diff --git a/tlatools/test-benchmark/tlc2/value/Randomization-1530025487-ebb8802.json b/tlatools/test-benchmark/tlc2/value/Randomization-1530025487-ebb8802.json new file mode 100644 index 0000000000000000000000000000000000000000..d9aaedad0627acc2d8cef1befdcdb0508f6508a8 --- /dev/null +++ b/tlatools/test-benchmark/tlc2/value/Randomization-1530025487-ebb8802.json @@ -0,0 +1,1540 @@ +[ + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.SubsetValueBenchmark.probabilisticN035k16d01", + "mode" : "thrpt", + "threads" : 32, + "forks" : 5, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 2, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 2, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "primaryMetric" : { + "score" : 9.814265639856911, + "scoreError" : 3.5519226084921507, + "scoreConfidence" : [ + 6.262343031364761, + 13.366188248349061 + ], + "scorePercentiles" : { + "0.0" : 6.753607668439613, + "50.0" : 10.470070397652428, + "90.0" : 12.827941145274211, + "95.0" : 12.858649761728246, + "99.0" : 12.858649761728246, + "99.9" : 12.858649761728246, + "99.99" : 12.858649761728246, + "99.999" : 12.858649761728246, + "99.9999" : 12.858649761728246, + "100.0" : 12.858649761728246 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 12.858649761728246, + 6.911478184305761 + ], + [ + 11.208217963617345, + 12.551563597187895 + ], + [ + 10.767867782062131, + 11.546268236993155 + ], + [ + 7.480349528344792, + 7.892380662647449 + ], + [ + 6.753607668439613, + 10.172273013242725 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.SubsetValueBenchmark.probabilisticN035k16d02", + "mode" : "thrpt", + "threads" : 32, + "forks" : 5, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 2, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 2, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "primaryMetric" : { + "score" : 2.7932446205666954, + "scoreError" : 0.7212536165365129, + "scoreConfidence" : [ + 2.0719910040301825, + 3.5144982371032083 + ], + "scorePercentiles" : { + "0.0" : 1.9673773629805331, + "50.0" : 3.088890563191904, + "90.0" : 3.201938518939384, + "95.0" : 3.2048232297325607, + "99.0" : 3.2048232297325607, + "99.9" : 3.2048232297325607, + "99.99" : 3.2048232297325607, + "99.999" : 3.2048232297325607, + "99.9999" : 3.2048232297325607, + "100.0" : 3.2048232297325607 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 3.1759761218007947, + 3.1546063327804683 + ], + [ + 3.136413968282235, + 2.5629672227690783 + ], + [ + 1.9673773629805331, + 2.313233836993042 + ], + [ + 3.1541066773395916, + 3.2048232297325607 + ], + [ + 3.0413671581015724, + 2.221574294887079 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.SubsetValueBenchmark.probabilisticN035k80d01", + "mode" : "thrpt", + "threads" : 32, + "forks" : 5, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 2, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 2, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "primaryMetric" : { + "score" : 28.38809904359423, + "scoreError" : 9.202418157455796, + "scoreConfidence" : [ + 19.185680886138435, + 37.59051720105003 + ], + "scorePercentiles" : { + "0.0" : 19.36331095829827, + "50.0" : 28.60973170258842, + "90.0" : 36.29196537233252, + "95.0" : 36.29504934912339, + "99.0" : 36.29504934912339, + "99.9" : 36.29504934912339, + "99.99" : 36.29504934912339, + "99.999" : 36.29504934912339, + "99.9999" : 36.29504934912339, + "100.0" : 36.29504934912339 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 29.685271545719385, + 24.61858183441453 + ], + [ + 33.0776564207316, + 20.534132532710004 + ], + [ + 24.572440645545356, + 27.53419185945745 + ], + [ + 31.93614570872759, + 19.36331095829827 + ], + [ + 36.26420958121468, + 36.29504934912339 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.SubsetValueBenchmark.probabilisticN035k80d02", + "mode" : "thrpt", + "threads" : 32, + "forks" : 5, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 2, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 2, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "primaryMetric" : { + "score" : 10.52227709850522, + "scoreError" : 1.6101589911941476, + "scoreConfidence" : [ + 8.912118107311073, + 12.132436089699368 + ], + "scorePercentiles" : { + "0.0" : 8.491378801933548, + "50.0" : 11.247893814845149, + "90.0" : 11.420671041101858, + "95.0" : 11.432291287500947, + "99.0" : 11.432291287500947, + "99.9" : 11.432291287500947, + "99.99" : 11.432291287500947, + "99.999" : 11.432291287500947, + "99.9999" : 11.432291287500947, + "100.0" : 11.432291287500947 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 8.491378801933548, + 9.657996752431464 + ], + [ + 9.574077581775065, + 9.66664321520015 + ], + [ + 11.2822253177283, + 11.31608882351006 + ], + [ + 11.214644306366141, + 11.432291287500947 + ], + [ + 11.281143323324155, + 11.306281575282378 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.SubsetValueBenchmark.probabilisticN060k16d01", + "mode" : "thrpt", + "threads" : 32, + "forks" : 5, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 2, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 2, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "primaryMetric" : { + "score" : 2.7945558732575426, + "scoreError" : 0.3736130106734233, + "scoreConfidence" : [ + 2.4209428625841194, + 3.168168883930966 + ], + "scorePercentiles" : { + "0.0" : 2.221249810954392, + "50.0" : 2.8431194315606305, + "90.0" : 3.0276341790221912, + "95.0" : 3.0276871486165895, + "99.0" : 3.0276871486165895, + "99.9" : 3.0276871486165895, + "99.99" : 3.0276871486165895, + "99.999" : 3.0276871486165895, + "99.9999" : 3.0276871486165895, + "100.0" : 3.0276871486165895 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 2.8392579355058656, + 3.027157452672608 + ], + [ + 2.571440923836749, + 3.0276871486165895 + ], + [ + 2.221249810954392, + 2.8485126236395106 + ], + [ + 2.846980927615396, + 3.0254151539107506 + ], + [ + 2.7837884068656784, + 2.7540683489578917 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.SubsetValueBenchmark.probabilisticN060k16d02", + "mode" : "thrpt", + "threads" : 32, + "forks" : 5, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 2, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 2, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "primaryMetric" : { + "score" : 2.645667431385532, + "scoreError" : 0.16743465996444615, + "scoreConfidence" : [ + 2.478232771421086, + 2.8131020913499785 + ], + "scorePercentiles" : { + "0.0" : 2.4741351571772863, + "50.0" : 2.625042773404063, + "90.0" : 2.8393349288272796, + "95.0" : 2.8481004996723, + "99.0" : 2.8481004996723, + "99.9" : 2.8481004996723, + "99.99" : 2.8481004996723, + "99.999" : 2.8481004996723, + "99.9999" : 2.8481004996723, + "100.0" : 2.8481004996723 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 2.5924849093010445, + 2.5767759287385683 + ], + [ + 2.7243545950925228, + 2.760444791222093 + ], + [ + 2.657600637507082, + 2.4741351571772863 + ], + [ + 2.5567216005732463, + 2.5847076500537622 + ], + [ + 2.68134854451742, + 2.8481004996723 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.SubsetValueBenchmark.probabilisticN060k80d01", + "mode" : "thrpt", + "threads" : 32, + "forks" : 5, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 2, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 2, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "primaryMetric" : { + "score" : 11.298601269411964, + "scoreError" : 0.720388691798619, + "scoreConfidence" : [ + 10.578212577613346, + 12.018989961210583 + ], + "scorePercentiles" : { + "0.0" : 10.287617624566224, + "50.0" : 11.423558384797065, + "90.0" : 11.721178226261209, + "95.0" : 11.729644309762948, + "99.0" : 11.729644309762948, + "99.9" : 11.729644309762948, + "99.99" : 11.729644309762948, + "99.999" : 11.729644309762948, + "99.9999" : 11.729644309762948, + "100.0" : 11.729644309762948 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 11.45647631886964, + 11.644983474745558 + ], + [ + 11.403544772058899, + 11.415097940763632 + ], + [ + 11.729644309762948, + 11.642029824041085 + ], + [ + 10.577135428368546, + 10.287617624566224 + ], + [ + 11.432018828830499, + 11.397464172112608 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.SubsetValueBenchmark.probabilisticN060k80d02", + "mode" : "thrpt", + "threads" : 32, + "forks" : 5, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 2, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 2, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "primaryMetric" : { + "score" : 8.69862560268995, + "scoreError" : 2.850730402828029, + "scoreConfidence" : [ + 5.84789519986192, + 11.549356005517978 + ], + "scorePercentiles" : { + "0.0" : 5.770652554442691, + "50.0" : 9.55560067282565, + "90.0" : 10.610404276107895, + "95.0" : 10.648561829452847, + "99.0" : 10.648561829452847, + "99.9" : 10.648561829452847, + "99.99" : 10.648561829452847, + "99.999" : 10.648561829452847, + "99.9999" : 10.648561829452847, + "100.0" : 10.648561829452847 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 10.648561829452847, + 10.266986296003333 + ], + [ + 10.212611163875144, + 9.078390005156265 + ], + [ + 10.032811340495034, + 6.596330980326417 + ], + [ + 6.265218422598946, + 10.141051281441365 + ], + [ + 7.973642153107459, + 5.770652554442691 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.SubsetValueBenchmark.probabilisticN100k16d01", + "mode" : "thrpt", + "threads" : 32, + "forks" : 5, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 2, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 2, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "primaryMetric" : { + "score" : 1.5248786293018326, + "scoreError" : 0.17380994878025358, + "scoreConfidence" : [ + 1.351068680521579, + 1.6986885780820862 + ], + "scorePercentiles" : { + "0.0" : 1.4303491635625107, + "50.0" : 1.4914262953464568, + "90.0" : 1.7974093389527392, + "95.0" : 1.8189693047549174, + "99.0" : 1.8189693047549174, + "99.9" : 1.8189693047549174, + "99.99" : 1.8189693047549174, + "99.999" : 1.8189693047549174, + "99.9999" : 1.8189693047549174, + "100.0" : 1.8189693047549174 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 1.4598484244159358, + 1.4624449978364078 + ], + [ + 1.4303491635625107, + 1.4416282783013532 + ], + [ + 1.4885810066800946, + 1.6033696467331364 + ], + [ + 1.4942715840128191, + 1.5194815860617874 + ], + [ + 1.5298423006593602, + 1.8189693047549174 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.SubsetValueBenchmark.probabilisticN100k16d02", + "mode" : "thrpt", + "threads" : 32, + "forks" : 5, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 2, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 2, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "primaryMetric" : { + "score" : 1.5171087604788753, + "scoreError" : 0.137741029417015, + "scoreConfidence" : [ + 1.3793677310618604, + 1.6548497898958903 + ], + "scorePercentiles" : { + "0.0" : 1.374140686846801, + "50.0" : 1.5061976180530554, + "90.0" : 1.6719182167808382, + "95.0" : 1.6755564936470366, + "99.0" : 1.6755564936470366, + "99.9" : 1.6755564936470366, + "99.99" : 1.6755564936470366, + "99.999" : 1.6755564936470366, + "99.9999" : 1.6755564936470366, + "100.0" : 1.6755564936470366 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 1.4550262673134087, + 1.4716255018554139 + ], + [ + 1.54464295541126, + 1.4655570993307343 + ], + [ + 1.6391737249850518, + 1.540769734250697 + ], + [ + 1.374140686846801, + 1.4580145430017348 + ], + [ + 1.5465805981466152, + 1.6755564936470366 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.SubsetValueBenchmark.probabilisticN100k80d01", + "mode" : "thrpt", + "threads" : 32, + "forks" : 5, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 2, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 2, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "primaryMetric" : { + "score" : 5.522708358140621, + "scoreError" : 0.8619263972210056, + "scoreConfidence" : [ + 4.660781960919615, + 6.384634755361627 + ], + "scorePercentiles" : { + "0.0" : 4.557760347176983, + "50.0" : 5.562722302155111, + "90.0" : 6.1258120806090774, + "95.0" : 6.133386755058857, + "99.0" : 6.133386755058857, + "99.9" : 6.133386755058857, + "99.99" : 6.133386755058857, + "99.999" : 6.133386755058857, + "99.9999" : 6.133386755058857, + "100.0" : 6.133386755058857 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 4.557760347176983, + 5.943804937625557 + ], + [ + 4.61878359597353, + 6.054253757090362 + ], + [ + 5.426317144240321, + 5.573249938573027 + ], + [ + 6.057640010561063, + 5.309692429369306 + ], + [ + 6.133386755058857, + 5.552194665737194 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.SubsetValueBenchmark.probabilisticN100k80d02", + "mode" : "thrpt", + "threads" : 32, + "forks" : 5, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 2, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 2, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "primaryMetric" : { + "score" : 5.2034486056520395, + "scoreError" : 0.8396356557754167, + "scoreConfidence" : [ + 4.363812949876623, + 6.043084261427456 + ], + "scorePercentiles" : { + "0.0" : 4.2453851147076325, + "50.0" : 5.279500987356893, + "90.0" : 5.983987887291637, + "95.0" : 6.010975853830928, + "99.0" : 6.010975853830928, + "99.9" : 6.010975853830928, + "99.99" : 6.010975853830928, + "99.999" : 6.010975853830928, + "99.9999" : 6.010975853830928, + "100.0" : 6.010975853830928 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 5.243868701914112, + 5.741096188438025 + ], + [ + 5.315133272799674, + 5.184673423835484 + ], + [ + 5.546667181331337, + 5.371833327105703 + ], + [ + 5.021845325531628, + 6.010975853830928 + ], + [ + 4.353007667025872, + 4.2453851147076325 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.SubsetValueBenchmark.probabilisticN200k16d01", + "mode" : "thrpt", + "threads" : 32, + "forks" : 5, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 2, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 2, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "primaryMetric" : { + "score" : 1.339924631382442, + "scoreError" : 0.04479747613250227, + "scoreConfidence" : [ + 1.2951271552499397, + 1.3847221075149443 + ], + "scorePercentiles" : { + "0.0" : 1.2974725063142822, + "50.0" : 1.3317294576636773, + "90.0" : 1.405961752460973, + "95.0" : 1.4112371767231358, + "99.0" : 1.4112371767231358, + "99.9" : 1.4112371767231358, + "99.99" : 1.4112371767231358, + "99.999" : 1.4112371767231358, + "99.9999" : 1.4112371767231358, + "100.0" : 1.4112371767231358 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 1.2974725063142822, + 1.3317783160699779 + ], + [ + 1.330023220063289, + 1.3217067595557936 + ], + [ + 1.3312044039027107, + 1.331680599257377 + ], + [ + 1.345338260664422, + 1.3584829341015097 + ], + [ + 1.3403221371719218, + 1.4112371767231358 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.SubsetValueBenchmark.probabilisticN200k16d02", + "mode" : "thrpt", + "threads" : 32, + "forks" : 5, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 2, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 2, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "primaryMetric" : { + "score" : 1.2928807628469392, + "scoreError" : 0.09300984990006785, + "scoreConfidence" : [ + 1.1998709129468714, + 1.385890612747007 + ], + "scorePercentiles" : { + "0.0" : 1.23105772545197, + "50.0" : 1.2758400061388142, + "90.0" : 1.4029274412912995, + "95.0" : 1.4031941394477145, + "99.0" : 1.4031941394477145, + "99.9" : 1.4031941394477145, + "99.99" : 1.4031941394477145, + "99.999" : 1.4031941394477145, + "99.9999" : 1.4031941394477145, + "100.0" : 1.4031941394477145 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 1.23105772545197, + 1.2546742096555419 + ], + [ + 1.4031941394477145, + 1.291465109010951 + ], + [ + 1.2563893499415755, + 1.2967143565966486 + ], + [ + 1.2602149032666774, + 1.2927650207517143 + ], + [ + 1.2418056564630344, + 1.4005271578835636 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.SubsetValueBenchmark.probabilisticN200k80d01", + "mode" : "thrpt", + "threads" : 32, + "forks" : 5, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 2, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 2, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "primaryMetric" : { + "score" : 4.5465162346852726, + "scoreError" : 0.44026456747307174, + "scoreConfidence" : [ + 4.1062516672122005, + 4.986780802158345 + ], + "scorePercentiles" : { + "0.0" : 4.079849518094677, + "50.0" : 4.5407630971316895, + "90.0" : 5.044907925189187, + "95.0" : 5.065693011936493, + "99.0" : 5.065693011936493, + "99.9" : 5.065693011936493, + "99.99" : 5.065693011936493, + "99.999" : 5.065693011936493, + "99.9999" : 5.065693011936493, + "100.0" : 5.065693011936493 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 4.079849518094677, + 4.638800409435509 + ], + [ + 4.204652426820363, + 4.857842144463429 + ], + [ + 4.691995094785729, + 4.415265131211205 + ], + [ + 5.065693011936493, + 4.567625992390603 + ], + [ + 4.429538415841941, + 4.513900201872776 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.SubsetValueBenchmark.probabilisticN200k80d02", + "mode" : "thrpt", + "threads" : 32, + "forks" : 5, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 2, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 2, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "primaryMetric" : { + "score" : 4.3364906992767995, + "scoreError" : 0.6361600103822059, + "scoreConfidence" : [ + 3.7003306888945935, + 4.972650709659005 + ], + "scorePercentiles" : { + "0.0" : 3.4850481939473967, + "50.0" : 4.4695550132572155, + "90.0" : 4.878136508784269, + "95.0" : 4.8966769796925815, + "99.0" : 4.8966769796925815, + "99.9" : 4.8966769796925815, + "99.99" : 4.8966769796925815, + "99.999" : 4.8966769796925815, + "99.9999" : 4.8966769796925815, + "100.0" : 4.8966769796925815 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 4.8966769796925815, + 4.711272270609456 + ], + [ + 4.1235368739389076, + 4.476218983648658 + ], + [ + 4.644397477491868, + 4.5275343861681225 + ], + [ + 4.462891042865774, + 4.0115585617605545 + ], + [ + 3.4850481939473967, + 4.025772222644671 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.SubsetValueBenchmark.probabilisticN300k16d01", + "mode" : "thrpt", + "threads" : 32, + "forks" : 5, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 2, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 2, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "primaryMetric" : { + "score" : 1.6148478717291934, + "scoreError" : 0.7446877881103863, + "scoreConfidence" : [ + 0.8701600836188071, + 2.3595356598395796 + ], + "scorePercentiles" : { + "0.0" : 1.1656347467176837, + "50.0" : 1.315218831808714, + "90.0" : 2.2482497486098527, + "95.0" : 2.2518752253276304, + "99.0" : 2.2518752253276304, + "99.9" : 2.2518752253276304, + "99.99" : 2.2518752253276304, + "99.999" : 2.2518752253276304, + "99.9999" : 2.2518752253276304, + "100.0" : 2.2518752253276304 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 1.21743252963011, + 1.1656347467176837 + ], + [ + 1.175409735645137, + 1.2585759163329866 + ], + [ + 1.231843853772198, + 1.3718617472844412 + ], + [ + 2.11681396227486, + 2.14341054215703 + ], + [ + 2.215620458149855, + 2.2518752253276304 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.SubsetValueBenchmark.probabilisticN300k16d02", + "mode" : "thrpt", + "threads" : 32, + "forks" : 5, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 2, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 2, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "primaryMetric" : { + "score" : 1.8602219960318038, + "scoreError" : 0.39115633236720815, + "scoreConfidence" : [ + 1.4690656636645958, + 2.251378328399012 + ], + "scorePercentiles" : { + "0.0" : 1.4062697603214702, + "50.0" : 1.964762284880499, + "90.0" : 2.0888910844309247, + "95.0" : 2.0920717403135463, + "99.0" : 2.0920717403135463, + "99.9" : 2.0920717403135463, + "99.99" : 2.0920717403135463, + "99.999" : 2.0920717403135463, + "99.9999" : 2.0920717403135463, + "100.0" : 2.0920717403135463 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 1.7989008575828505, + 1.8112093511172844 + ], + [ + 2.0361709586331793, + 2.056975849771783 + ], + [ + 2.060265181487331, + 2.0920717403135463 + ], + [ + 1.4108316913295962, + 1.4062697603214702 + ], + [ + 1.9262588615098382, + 2.00326570825116 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.SubsetValueBenchmark.probabilisticN300k80d01", + "mode" : "thrpt", + "threads" : 32, + "forks" : 5, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 2, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 2, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "primaryMetric" : { + "score" : 6.55685280713317, + "scoreError" : 1.521062607592989, + "scoreConfidence" : [ + 5.035790199540181, + 8.07791541472616 + ], + "scorePercentiles" : { + "0.0" : 3.722375583789737, + "50.0" : 6.866447622580452, + "90.0" : 7.070923082701012, + "95.0" : 7.07416375512963, + "99.0" : 7.07416375512963, + "99.9" : 7.07416375512963, + "99.99" : 7.07416375512963, + "99.999" : 7.07416375512963, + "99.9999" : 7.07416375512963, + "100.0" : 7.07416375512963 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 6.8806062455577495, + 6.883754498127009 + ], + [ + 7.07416375512963, + 6.641330464691329 + ], + [ + 6.8522889996031555, + 7.007776420526916 + ], + [ + 7.041757030843449, + 6.763664651435106 + ], + [ + 3.722375583789737, + 6.700810421627625 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.SubsetValueBenchmark.probabilisticN300k80d02", + "mode" : "thrpt", + "threads" : 32, + "forks" : 5, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 2, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 2, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "primaryMetric" : { + "score" : 5.301216427827282, + "scoreError" : 1.1289294375629328, + "scoreConfidence" : [ + 4.172286990264349, + 6.430145865390215 + ], + "scorePercentiles" : { + "0.0" : 3.542477847530514, + "50.0" : 5.271405080419002, + "90.0" : 6.102467200471888, + "95.0" : 6.106821842803865, + "99.0" : 6.106821842803865, + "99.9" : 6.106821842803865, + "99.99" : 6.106821842803865, + "99.999" : 6.106821842803865, + "99.9999" : 6.106821842803865, + "100.0" : 6.106821842803865 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 5.486593720751928, + 6.0632754194840945 + ], + [ + 5.223263080702427, + 3.542477847530514 + ], + [ + 5.018855922335199, + 5.100012670505602 + ], + [ + 6.106821842803865, + 5.319547080135576 + ], + [ + 5.137619833973987, + 6.0136968600496274 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.SubsetValueBenchmark.probabilisticN400k16d01", + "mode" : "thrpt", + "threads" : 32, + "forks" : 5, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 2, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 2, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "primaryMetric" : { + "score" : 1.5860227730491998, + "scoreError" : 0.30379023334378413, + "scoreConfidence" : [ + 1.2822325397054157, + 1.8898130063929839 + ], + "scorePercentiles" : { + "0.0" : 1.0920813832707281, + "50.0" : 1.5994173400063993, + "90.0" : 1.791811943594711, + "95.0" : 1.7958322769866282, + "99.0" : 1.7958322769866282, + "99.9" : 1.7958322769866282, + "99.99" : 1.7958322769866282, + "99.999" : 1.7958322769866282, + "99.9999" : 1.7958322769866282, + "100.0" : 1.7958322769866282 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 1.0920813832707281, + 1.7958322769866282 + ], + [ + 1.5932804092121582, + 1.7556289430674565 + ], + [ + 1.52308380137161, + 1.715720305433191 + ], + [ + 1.7223091241348114, + 1.5188029344158969 + ], + [ + 1.6055542708006407, + 1.537934281798876 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.SubsetValueBenchmark.probabilisticN400k16d02", + "mode" : "thrpt", + "threads" : 32, + "forks" : 5, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 2, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 2, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "primaryMetric" : { + "score" : 1.1599289382358806, + "scoreError" : 0.22612698801505537, + "scoreConfidence" : [ + 0.9338019502208252, + 1.386055926250936 + ], + "scorePercentiles" : { + "0.0" : 0.9825177739479852, + "50.0" : 1.077451286867186, + "90.0" : 1.4022996800130283, + "95.0" : 1.410049661563603, + "99.0" : 1.410049661563603, + "99.9" : 1.410049661563603, + "99.99" : 1.410049661563603, + "99.999" : 1.410049661563603, + "99.9999" : 1.410049661563603, + "100.0" : 1.410049661563603 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 1.0763690618814368, + 1.0651796680221743 + ], + [ + 1.070265094440761, + 1.0249632943993892 + ], + [ + 0.9825177739479852, + 1.0785335118529353 + ], + [ + 1.3325498460578566, + 1.410049661563603 + ], + [ + 1.286618213721185, + 1.2722432564714805 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.SubsetValueBenchmark.probabilisticN400k80d01", + "mode" : "thrpt", + "threads" : 32, + "forks" : 5, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 2, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 2, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "primaryMetric" : { + "score" : 6.027975265926844, + "scoreError" : 0.35112510651249884, + "scoreConfidence" : [ + 5.676850159414345, + 6.379100372439343 + ], + "scorePercentiles" : { + "0.0" : 5.644475565004447, + "50.0" : 6.0550531061834345, + "90.0" : 6.354428372291083, + "95.0" : 6.3564761807971415, + "99.0" : 6.3564761807971415, + "99.9" : 6.3564761807971415, + "99.99" : 6.3564761807971415, + "99.999" : 6.3564761807971415, + "99.9999" : 6.3564761807971415, + "100.0" : 6.3564761807971415 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 5.97900037940529, + 6.335998095736555 + ], + [ + 5.8384965728694365, + 6.025064868648608 + ], + [ + 5.644475565004447, + 6.0918640144917955 + ], + [ + 6.3564761807971415, + 5.7602399161218445 + ], + [ + 6.163095722475063, + 6.08504134371826 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.SubsetValueBenchmark.probabilisticN400k80d02", + "mode" : "thrpt", + "threads" : 32, + "forks" : 5, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 2, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 2, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "primaryMetric" : { + "score" : 4.627633904221076, + "scoreError" : 0.7331295121689335, + "scoreConfidence" : [ + 3.8945043920521427, + 5.36076341639001 + ], + "scorePercentiles" : { + "0.0" : 3.821230691559164, + "50.0" : 4.559809491822633, + "90.0" : 5.374791544313783, + "95.0" : 5.413207484888207, + "99.0" : 5.413207484888207, + "99.9" : 5.413207484888207, + "99.99" : 5.413207484888207, + "99.999" : 5.413207484888207, + "99.9999" : 5.413207484888207, + "100.0" : 5.413207484888207 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 5.0290480791439665, + 5.016734041159645 + ], + [ + 4.268812624543834, + 4.620526293998292 + ], + [ + 4.499092689646973, + 4.426721770032627 + ], + [ + 4.173392584090863, + 5.007572783147188 + ], + [ + 3.821230691559164, + 5.413207484888207 + ] + ] + }, + "secondaryMetrics" : { + } + } +] + + diff --git a/tlatools/test-benchmark/tlc2/value/Randomization-1530034012-4a34ace.json b/tlatools/test-benchmark/tlc2/value/Randomization-1530034012-4a34ace.json new file mode 100644 index 0000000000000000000000000000000000000000..a156c7da7c271207631634dd5169d6fd2187962f --- /dev/null +++ b/tlatools/test-benchmark/tlc2/value/Randomization-1530034012-4a34ace.json @@ -0,0 +1,1540 @@ +[ + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.SubsetValueBenchmark.probabilisticN035k16d01", + "mode" : "thrpt", + "threads" : 32, + "forks" : 5, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 2, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 2, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "primaryMetric" : { + "score" : 104.7011087104809, + "scoreError" : 3.7423761914164815, + "scoreConfidence" : [ + 100.95873251906441, + 108.44348490189738 + ], + "scorePercentiles" : { + "0.0" : 100.88954035368401, + "50.0" : 105.882966608304, + "90.0" : 107.34104031428244, + "95.0" : 107.42153923641156, + "99.0" : 107.42153923641156, + "99.9" : 107.42153923641156, + "99.99" : 107.42153923641156, + "99.999" : 107.42153923641156, + "99.9999" : 107.42153923641156, + "100.0" : 107.42153923641156 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 101.44416419113956, + 100.88954035368401 + ], + [ + 106.13510124450892, + 106.0740992343222 + ], + [ + 106.61655001512035, + 107.42153923641156 + ], + [ + 106.02129553873436, + 105.38379400794531 + ], + [ + 101.28036560506887, + 105.74463767787363 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.SubsetValueBenchmark.probabilisticN035k16d02", + "mode" : "thrpt", + "threads" : 32, + "forks" : 5, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 2, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 2, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "primaryMetric" : { + "score" : 83.24147104170652, + "scoreError" : 4.554176259789341, + "scoreConfidence" : [ + 78.68729478191717, + 87.79564730149586 + ], + "scorePercentiles" : { + "0.0" : 78.55079784872044, + "50.0" : 83.91370138812889, + "90.0" : 86.58980075188902, + "95.0" : 86.64764393677703, + "99.0" : 86.64764393677703, + "99.9" : 86.64764393677703, + "99.99" : 86.64764393677703, + "99.999" : 86.64764393677703, + "99.9999" : 86.64764393677703, + "100.0" : 86.64764393677703 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 78.55079784872044, + 79.51303312164389 + ], + [ + 79.74896736709258, + 82.565057137537 + ], + [ + 83.75757469808822, + 86.06921208789687 + ], + [ + 85.80992118205909, + 86.64764393677703 + ], + [ + 84.06982807816954, + 85.6826749590806 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.SubsetValueBenchmark.probabilisticN035k80d01", + "mode" : "thrpt", + "threads" : 32, + "forks" : 5, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 2, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 2, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "primaryMetric" : { + "score" : 224.85663488295086, + "scoreError" : 6.028591856716745, + "scoreConfidence" : [ + 218.8280430262341, + 230.88522673966762 + ], + "scorePercentiles" : { + "0.0" : 218.36459658547616, + "50.0" : 226.62847510687035, + "90.0" : 229.33631048633563, + "95.0" : 229.38790988428408, + "99.0" : 229.38790988428408, + "99.9" : 229.38790988428408, + "99.99" : 229.38790988428408, + "99.999" : 229.38790988428408, + "99.9999" : 229.38790988428408, + "100.0" : 229.38790988428408 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 226.63656062437886, + 226.7640288893431 + ], + [ + 219.78519797409433, + 219.94064929540707 + ], + [ + 229.38790988428408, + 228.87191590479955 + ], + [ + 218.36459658547616, + 226.62038958936185 + ], + [ + 225.25506764156407, + 226.94003244079963 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.SubsetValueBenchmark.probabilisticN035k80d02", + "mode" : "thrpt", + "threads" : 32, + "forks" : 5, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 2, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 2, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "primaryMetric" : { + "score" : 185.97813137612835, + "scoreError" : 19.18711039638145, + "scoreConfidence" : [ + 166.7910209797469, + 205.1652417725098 + ], + "scorePercentiles" : { + "0.0" : 150.78714247391278, + "50.0" : 190.58386393172543, + "90.0" : 192.086769900789, + "95.0" : 192.1063873271388, + "99.0" : 192.1063873271388, + "99.9" : 192.1063873271388, + "99.99" : 192.1063873271388, + "99.999" : 192.1063873271388, + "99.9999" : 192.1063873271388, + "100.0" : 192.1063873271388 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 192.1063873271388, + 191.2646953076457 + ], + [ + 191.9102130636406, + 191.31943951811584 + ], + [ + 191.15971111652445, + 189.79898769959755 + ], + [ + 190.0080167469264, + 189.18588301321768 + ], + [ + 150.78714247391278, + 182.2408374945636 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.SubsetValueBenchmark.probabilisticN060k16d01", + "mode" : "thrpt", + "threads" : 32, + "forks" : 5, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 2, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 2, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "primaryMetric" : { + "score" : 42.32871200796817, + "scoreError" : 17.81179967979969, + "scoreConfidence" : [ + 24.51691232816848, + 60.14051168776786 + ], + "scorePercentiles" : { + "0.0" : 24.31185136900342, + "50.0" : 44.95980125392232, + "90.0" : 57.23931162819738, + "95.0" : 57.50164238101311, + "99.0" : 57.50164238101311, + "99.9" : 57.50164238101311, + "99.99" : 57.50164238101311, + "99.999" : 57.50164238101311, + "99.9999" : 57.50164238101311, + "100.0" : 57.50164238101311 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 50.88292893960396, + 49.21247544702237 + ], + [ + 54.87833485285576, + 39.086245900476555 + ], + [ + 24.31185136900342, + 29.59827317841755 + ], + [ + 48.24622995596074, + 27.895765503444338 + ], + [ + 41.6733725518839, + 57.50164238101311 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.SubsetValueBenchmark.probabilisticN060k16d02", + "mode" : "thrpt", + "threads" : 32, + "forks" : 5, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 2, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 2, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "primaryMetric" : { + "score" : 49.43683507637419, + "scoreError" : 8.920714718785595, + "scoreConfidence" : [ + 40.51612035758859, + 58.357549795159784 + ], + "scorePercentiles" : { + "0.0" : 35.51213429538773, + "50.0" : 51.44721321118388, + "90.0" : 53.52767867950377, + "95.0" : 53.54939167959375, + "99.0" : 53.54939167959375, + "99.9" : 53.54939167959375, + "99.99" : 53.54939167959375, + "99.999" : 53.54939167959375, + "99.9999" : 53.54939167959375, + "100.0" : 53.54939167959375 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 50.90779977793198, + 53.54939167959375 + ], + [ + 42.09399366036272, + 50.619658203534115 + ], + [ + 53.332261678693996, + 52.79084790579731 + ], + [ + 51.677694661744894, + 35.51213429538773 + ], + [ + 51.216731760622864, + 52.66783714007247 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.SubsetValueBenchmark.probabilisticN060k80d01", + "mode" : "thrpt", + "threads" : 32, + "forks" : 5, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 2, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 2, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "primaryMetric" : { + "score" : 93.05935428685841, + "scoreError" : 38.50167771555803, + "scoreConfidence" : [ + 54.55767657130038, + 131.56103200241643 + ], + "scorePercentiles" : { + "0.0" : 57.93386263113723, + "50.0" : 94.70200646800936, + "90.0" : 123.68899208986174, + "95.0" : 124.27879912212194, + "99.0" : 124.27879912212194, + "99.9" : 124.27879912212194, + "99.99" : 124.27879912212194, + "99.999" : 124.27879912212194, + "99.9999" : 124.27879912212194, + "100.0" : 124.27879912212194 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 78.1520920575015, + 124.27879912212194 + ], + [ + 116.16801584966383, + 85.16604351364255 + ], + [ + 57.93386263113723, + 73.94587810450388 + ], + [ + 118.38072879951989, + 104.23796942237615 + ], + [ + 58.02829412796092, + 114.30185924015635 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.SubsetValueBenchmark.probabilisticN060k80d02", + "mode" : "thrpt", + "threads" : 32, + "forks" : 5, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 2, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 2, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "primaryMetric" : { + "score" : 106.43974868838147, + "scoreError" : 23.394593630033878, + "scoreConfidence" : [ + 83.04515505834759, + 129.83434231841534 + ], + "scorePercentiles" : { + "0.0" : 74.25661377870344, + "50.0" : 114.07122657351444, + "90.0" : 120.05072448321653, + "95.0" : 120.2156072166627, + "99.0" : 120.2156072166627, + "99.9" : 120.2156072166627, + "99.99" : 120.2156072166627, + "99.999" : 120.2156072166627, + "99.9999" : 120.2156072166627, + "100.0" : 120.2156072166627 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 118.5667798822009, + 120.2156072166627 + ], + [ + 117.79734487632652, + 117.82734911263601 + ], + [ + 114.96353532253951, + 90.04572984768308 + ], + [ + 95.661695758385, + 113.17891782448936 + ], + [ + 101.88391326418818, + 74.25661377870344 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.SubsetValueBenchmark.probabilisticN100k16d01", + "mode" : "thrpt", + "threads" : 32, + "forks" : 5, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 2, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 2, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "primaryMetric" : { + "score" : 21.38813297153077, + "scoreError" : 7.568160681308476, + "scoreConfidence" : [ + 13.819972290222292, + 28.956293652839246 + ], + "scorePercentiles" : { + "0.0" : 13.58606924078156, + "50.0" : 23.914049859153785, + "90.0" : 26.255547300671388, + "95.0" : 26.302995416083153, + "99.0" : 26.302995416083153, + "99.9" : 26.302995416083153, + "99.99" : 26.302995416083153, + "99.999" : 26.302995416083153, + "99.9999" : 26.302995416083153, + "100.0" : 26.302995416083153 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 13.811099062641686, + 17.941382206144834 + ], + [ + 13.58606924078156, + 18.262562513874666 + ], + [ + 25.337148269403375, + 25.828514261965516 + ], + [ + 24.983459026105365, + 26.302995416083153 + ], + [ + 24.38372946115993, + 23.44437025714764 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.SubsetValueBenchmark.probabilisticN100k16d02", + "mode" : "thrpt", + "threads" : 32, + "forks" : 5, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 2, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 2, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "primaryMetric" : { + "score" : 19.622156265483028, + "scoreError" : 4.987868775642429, + "scoreConfidence" : [ + 14.634287489840599, + 24.610025041125457 + ], + "scorePercentiles" : { + "0.0" : 14.93138023958173, + "50.0" : 20.246976801211183, + "90.0" : 24.27346949430521, + "95.0" : 24.427045698424894, + "99.0" : 24.427045698424894, + "99.9" : 24.427045698424894, + "99.99" : 24.427045698424894, + "99.999" : 24.427045698424894, + "99.9999" : 24.427045698424894, + "100.0" : 24.427045698424894 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 22.89128365722806, + 24.427045698424894 + ], + [ + 19.844680591528572, + 20.649273010893797 + ], + [ + 15.873171889829425, + 18.1962620552237 + ], + [ + 14.93138023958173, + 15.739929179229202 + ], + [ + 21.57751635813752, + 22.091019974753376 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.SubsetValueBenchmark.probabilisticN100k80d01", + "mode" : "thrpt", + "threads" : 32, + "forks" : 5, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 2, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 2, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "primaryMetric" : { + "score" : 74.26454714978158, + "scoreError" : 15.183553853622525, + "scoreConfidence" : [ + 59.08099329615905, + 89.4481010034041 + ], + "scorePercentiles" : { + "0.0" : 51.674450673992695, + "50.0" : 78.40778804239744, + "90.0" : 81.63045236364282, + "95.0" : 81.6569849685138, + "99.0" : 81.6569849685138, + "99.9" : 81.6569849685138, + "99.99" : 81.6569849685138, + "99.999" : 81.6569849685138, + "99.9999" : 81.6569849685138, + "100.0" : 81.6569849685138 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 80.39172776653989, + 81.6569849685138 + ], + [ + 81.39165891980406, + 81.14393282427004 + ], + [ + 78.82191071568474, + 51.674450673992695 + ], + [ + 76.76071269165135, + 61.78549587027839 + ], + [ + 71.02493169797056, + 77.99366536911016 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.SubsetValueBenchmark.probabilisticN100k80d02", + "mode" : "thrpt", + "threads" : 32, + "forks" : 5, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 2, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 2, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "primaryMetric" : { + "score" : 60.853839105773126, + "scoreError" : 22.12483509675573, + "scoreConfidence" : [ + 38.729004009017395, + 82.97867420252885 + ], + "scorePercentiles" : { + "0.0" : 38.26110300104155, + "50.0" : 68.39288235960099, + "90.0" : 74.81830147083643, + "95.0" : 74.92553079057613, + "99.0" : 74.92553079057613, + "99.9" : 74.92553079057613, + "99.99" : 74.92553079057613, + "99.999" : 74.92553079057613, + "99.9999" : 74.92553079057613, + "100.0" : 74.92553079057613 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 50.71748836211502, + 48.6232060078198 + ], + [ + 40.686946926075855, + 38.26110300104155 + ], + [ + 66.04801192655289, + 71.26506936711934 + ], + [ + 73.42004429060252, + 70.73775279264909 + ], + [ + 73.85323759317912, + 74.92553079057613 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.SubsetValueBenchmark.probabilisticN200k16d01", + "mode" : "thrpt", + "threads" : 32, + "forks" : 5, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 2, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 2, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "primaryMetric" : { + "score" : 10.639470911792866, + "scoreError" : 3.088289738509205, + "scoreConfidence" : [ + 7.551181173283661, + 13.727760650302072 + ], + "scorePercentiles" : { + "0.0" : 6.993385262401218, + "50.0" : 10.729512830841635, + "90.0" : 13.677033176490005, + "95.0" : 13.81168335274697, + "99.0" : 13.81168335274697, + "99.9" : 13.81168335274697, + "99.99" : 13.81168335274697, + "99.999" : 13.81168335274697, + "99.9999" : 13.81168335274697, + "100.0" : 13.81168335274697 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 11.409030310947868, + 10.525541923680748 + ], + [ + 8.482542765083899, + 10.631500751171094 + ], + [ + 10.827524910512178, + 8.955246572761785 + ], + [ + 6.993385262401218, + 12.293071678445585 + ], + [ + 12.465181590177318, + 13.81168335274697 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.SubsetValueBenchmark.probabilisticN200k16d02", + "mode" : "thrpt", + "threads" : 32, + "forks" : 5, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 2, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 2, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "primaryMetric" : { + "score" : 10.456715599876055, + "scoreError" : 1.9028778570206695, + "scoreConfidence" : [ + 8.553837742855386, + 12.359593456896725 + ], + "scorePercentiles" : { + "0.0" : 7.864941352835479, + "50.0" : 10.939755490790464, + "90.0" : 11.714816996362217, + "95.0" : 11.753691593959166, + "99.0" : 11.753691593959166, + "99.9" : 11.753691593959166, + "99.99" : 11.753691593959166, + "99.999" : 11.753691593959166, + "99.9999" : 11.753691593959166, + "100.0" : 11.753691593959166 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 10.952660748060072, + 11.364945617989674 + ], + [ + 10.769860190657486, + 7.864941352835479 + ], + [ + 8.47649876887637, + 10.500627774438772 + ], + [ + 11.753691593959166, + 10.96587977575521 + ], + [ + 10.991199942667485, + 10.926850233520856 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.SubsetValueBenchmark.probabilisticN200k80d01", + "mode" : "thrpt", + "threads" : 32, + "forks" : 5, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 2, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 2, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "primaryMetric" : { + "score" : 31.969197873061233, + "scoreError" : 1.6677155997623578, + "scoreConfidence" : [ + 30.301482273298877, + 33.63691347282359 + ], + "scorePercentiles" : { + "0.0" : 30.056920293183016, + "50.0" : 32.21477295504585, + "90.0" : 33.33626563901803, + "95.0" : 33.34763314031518, + "99.0" : 33.34763314031518, + "99.9" : 33.34763314031518, + "99.99" : 33.34763314031518, + "99.999" : 33.34763314031518, + "99.9999" : 33.34763314031518, + "100.0" : 33.34763314031518 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 32.419857477883355, + 30.91815408954798 + ], + [ + 32.64844059293107, + 30.056920293183016 + ], + [ + 32.760711407669184, + 32.00968843220835 + ], + [ + 33.34763314031518, + 30.990574339821343 + ], + [ + 31.30604082970921, + 33.23395812734369 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.SubsetValueBenchmark.probabilisticN200k80d02", + "mode" : "thrpt", + "threads" : 32, + "forks" : 5, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 2, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 2, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "primaryMetric" : { + "score" : 28.20474698855128, + "scoreError" : 3.205925122939175, + "scoreConfidence" : [ + 24.998821865612104, + 31.410672111490452 + ], + "scorePercentiles" : { + "0.0" : 22.814486172933808, + "50.0" : 28.542282298254698, + "90.0" : 30.383993715660303, + "95.0" : 30.416778077286274, + "99.0" : 30.416778077286274, + "99.9" : 30.416778077286274, + "99.99" : 30.416778077286274, + "99.999" : 30.416778077286274, + "99.9999" : 30.416778077286274, + "100.0" : 30.416778077286274 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 27.921304255505774, + 28.829394237761125 + ], + [ + 28.255170358748266, + 27.439633953256468 + ], + [ + 22.814486172933808, + 30.416778077286274 + ], + [ + 29.197701087165743, + 30.088934461026557 + ], + [ + 29.133379644045366, + 27.950687637783403 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.SubsetValueBenchmark.probabilisticN300k16d01", + "mode" : "thrpt", + "threads" : 32, + "forks" : 5, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 2, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 2, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "primaryMetric" : { + "score" : 8.203667020100887, + "scoreError" : 0.5884166824514152, + "scoreConfidence" : [ + 7.6152503376494725, + 8.792083702552302 + ], + "scorePercentiles" : { + "0.0" : 7.360515269435855, + "50.0" : 8.338306550242326, + "90.0" : 8.592865318321673, + "95.0" : 8.594258152249708, + "99.0" : 8.594258152249708, + "99.9" : 8.594258152249708, + "99.99" : 8.594258152249708, + "99.999" : 8.594258152249708, + "99.9999" : 8.594258152249708, + "100.0" : 8.594258152249708 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 7.898286271236425, + 8.076259860587378 + ], + [ + 8.594258152249708, + 7.360515269435855 + ], + [ + 7.930906266852778, + 8.459445698375168 + ], + [ + 8.395991372547563, + 8.46005576881755 + ], + [ + 8.28062172793709, + 8.580329812969351 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.SubsetValueBenchmark.probabilisticN300k16d02", + "mode" : "thrpt", + "threads" : 32, + "forks" : 5, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 2, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 2, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "primaryMetric" : { + "score" : 5.763911744882545, + "scoreError" : 1.5043550652048963, + "scoreConfidence" : [ + 4.259556679677649, + 7.268266810087441 + ], + "scorePercentiles" : { + "0.0" : 4.429803153294701, + "50.0" : 5.873212023012337, + "90.0" : 6.966232203393936, + "95.0" : 6.966942875753675, + "99.0" : 6.966942875753675, + "99.9" : 6.966942875753675, + "99.99" : 6.966942875753675, + "99.999" : 6.966942875753675, + "99.9999" : 6.966942875753675, + "100.0" : 6.966942875753675 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 6.9598361521562815, + 5.064587996073755 + ], + [ + 4.946417436382128, + 6.4726554457373355 + ], + [ + 4.575986438005888, + 6.966942875753675 + ], + [ + 5.302652006486136, + 6.476463905397021 + ], + [ + 4.429803153294701, + 6.443772039538537 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.SubsetValueBenchmark.probabilisticN300k80d01", + "mode" : "thrpt", + "threads" : 32, + "forks" : 5, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 2, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 2, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "primaryMetric" : { + "score" : 18.801957044765565, + "scoreError" : 1.1768612520141342, + "scoreConfidence" : [ + 17.62509579275143, + 19.9788182967797 + ], + "scorePercentiles" : { + "0.0" : 17.140027541125153, + "50.0" : 18.882789178775543, + "90.0" : 19.570854300260155, + "95.0" : 19.571867386585367, + "99.0" : 19.571867386585367, + "99.9" : 19.571867386585367, + "99.99" : 19.571867386585367, + "99.999" : 19.571867386585367, + "99.9999" : 19.571867386585367, + "100.0" : 19.571867386585367 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 19.42278989517915, + 18.714642574717033 + ], + [ + 17.140027541125153, + 18.975405204474427 + ], + [ + 19.571867386585367, + 18.601652618233114 + ], + [ + 19.325002434758275, + 17.916273116173247 + ], + [ + 18.79017315307666, + 19.561736523333238 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.SubsetValueBenchmark.probabilisticN300k80d02", + "mode" : "thrpt", + "threads" : 32, + "forks" : 5, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 2, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 2, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "primaryMetric" : { + "score" : 14.422596923700885, + "scoreError" : 3.137380248280707, + "scoreConfidence" : [ + 11.285216675420179, + 17.559977171981593 + ], + "scorePercentiles" : { + "0.0" : 11.383432415016602, + "50.0" : 14.75604222776579, + "90.0" : 17.026921267980402, + "95.0" : 17.04323687245957, + "99.0" : 17.04323687245957, + "99.9" : 17.04323687245957, + "99.99" : 17.04323687245957, + "99.999" : 17.04323687245957, + "99.9999" : 17.04323687245957, + "100.0" : 17.04323687245957 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 15.99247577392052, + 15.73714255929863 + ], + [ + 14.611301908761718, + 11.383432415016602 + ], + [ + 17.04323687245957, + 11.547055656321096 + ], + [ + 16.880080827667893, + 12.746244623874427 + ], + [ + 14.90078254676986, + 13.384216052918532 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.SubsetValueBenchmark.probabilisticN400k16d01", + "mode" : "thrpt", + "threads" : 32, + "forks" : 5, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 2, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 2, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "primaryMetric" : { + "score" : 5.211916463832229, + "scoreError" : 0.2878694029810478, + "scoreConfidence" : [ + 4.924047060851182, + 5.499785866813276 + ], + "scorePercentiles" : { + "0.0" : 4.853473806617481, + "50.0" : 5.1995205258016615, + "90.0" : 5.471484334583664, + "95.0" : 5.474007785023183, + "99.0" : 5.474007785023183, + "99.9" : 5.474007785023183, + "99.99" : 5.474007785023183, + "99.999" : 5.474007785023183, + "99.9999" : 5.474007785023183, + "100.0" : 5.474007785023183 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 5.354418757268614, + 5.194312458985413 + ], + [ + 5.131591504423528, + 5.474007785023183 + ], + [ + 5.448773280627998, + 5.305461276121833 + ], + [ + 4.853473806617481, + 5.20472859261791 + ], + [ + 5.088690394104383, + 5.0637067825319475 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.SubsetValueBenchmark.probabilisticN400k16d02", + "mode" : "thrpt", + "threads" : 32, + "forks" : 5, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 2, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 2, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "primaryMetric" : { + "score" : 3.629304527595204, + "scoreError" : 1.132757347058442, + "scoreConfidence" : [ + 2.496547180536762, + 4.762061874653646 + ], + "scorePercentiles" : { + "0.0" : 2.915559309920613, + "50.0" : 3.364772640443146, + "90.0" : 4.990571914894521, + "95.0" : 5.0300565133233714, + "99.0" : 5.0300565133233714, + "99.9" : 5.0300565133233714, + "99.99" : 5.0300565133233714, + "99.999" : 5.0300565133233714, + "99.9999" : 5.0300565133233714, + "100.0" : 5.0300565133233714 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 3.3260879136804467, + 3.403457367205845 + ], + [ + 3.2699226571259983, + 4.635210529034868 + ], + [ + 3.6596373110352705, + 2.915559309920613 + ], + [ + 5.0300565133233714, + 4.193119686256376 + ], + [ + 2.92811952830071, + 2.9318744600685447 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.SubsetValueBenchmark.probabilisticN400k80d01", + "mode" : "thrpt", + "threads" : 32, + "forks" : 5, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 2, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 2, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "primaryMetric" : { + "score" : 11.919790589132187, + "scoreError" : 2.3040130587387164, + "scoreConfidence" : [ + 9.615777530393471, + 14.223803647870904 + ], + "scorePercentiles" : { + "0.0" : 9.277005723195174, + "50.0" : 12.138283342462927, + "90.0" : 13.607971145445818, + "95.0" : 13.623840845084036, + "99.0" : 13.623840845084036, + "99.9" : 13.623840845084036, + "99.99" : 13.623840845084036, + "99.999" : 13.623840845084036, + "99.9999" : 13.623840845084036, + "100.0" : 13.623840845084036 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 12.70028229987442, + 13.465143848701851 + ], + [ + 9.277005723195174, + 11.576284385051434 + ], + [ + 10.71207001794642, + 13.3336465184469 + ], + [ + 12.93772688522609, + 13.623840845084036 + ], + [ + 10.140269780101741, + 11.431635587693814 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.SubsetValueBenchmark.probabilisticN400k80d02", + "mode" : "thrpt", + "threads" : 32, + "forks" : 5, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 2, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 2, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "primaryMetric" : { + "score" : 10.814951479430821, + "scoreError" : 1.6082721225126038, + "scoreConfidence" : [ + 9.206679356918217, + 12.423223601943425 + ], + "scorePercentiles" : { + "0.0" : 8.09518224087975, + "50.0" : 11.009162887282663, + "90.0" : 11.986930428051336, + "95.0" : 12.040319596025027, + "99.0" : 12.040319596025027, + "99.9" : 12.040319596025027, + "99.99" : 12.040319596025027, + "99.999" : 12.040319596025027, + "99.9999" : 12.040319596025027, + "100.0" : 12.040319596025027 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 11.409715740306465, + 10.62887792068522 + ], + [ + 11.220771101438357, + 12.040319596025027 + ], + [ + 10.549688373453217, + 8.09518224087975 + ], + [ + 10.797554673126971, + 11.506427916288116 + ], + [ + 11.221808921245392, + 10.679168310859689 + ] + ] + }, + "secondaryMetrics" : { + } + } +] + + diff --git a/tlatools/test-benchmark/tlc2/value/Randomization-1530090015-39e5e3b02.json b/tlatools/test-benchmark/tlc2/value/Randomization-1530090015-39e5e3b02.json new file mode 100644 index 0000000000000000000000000000000000000000..51e87eb6534dffee27aa39b6e1715157afc2df0c --- /dev/null +++ b/tlatools/test-benchmark/tlc2/value/Randomization-1530090015-39e5e3b02.json @@ -0,0 +1,2764 @@ +[ + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.SubsetValueBenchmark.exactN035K08", + "mode" : "thrpt", + "threads" : 32, + "forks" : 5, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 2, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 3, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "primaryMetric" : { + "score" : 1097.4100006265485, + "scoreError" : 18.746172610480603, + "scoreConfidence" : [ + 1078.6638280160678, + 1116.1561732370292 + ], + "scorePercentiles" : { + "0.0" : 1070.9852942694877, + "50.0" : 1097.1172671563727, + "90.0" : 1117.9515997728565, + "95.0" : 1118.5004689143204, + "99.0" : 1118.5004689143204, + "99.9" : 1118.5004689143204, + "99.99" : 1118.5004689143204, + "99.999" : 1118.5004689143204, + "99.9999" : 1118.5004689143204, + "100.0" : 1118.5004689143204 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 1113.7826718183462, + 1113.2646305679596, + 1114.9188538486044 + ], + [ + 1084.917793753047, + 1084.299986018495, + 1079.464182047453 + ], + [ + 1070.9852942694877, + 1075.1616840185263, + 1078.1363993743855 + ], + [ + 1096.1524577709054, + 1097.1172671563727, + 1099.277463166022 + ], + [ + 1117.585169662422, + 1118.5004689143204, + 1117.5856870118807 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.SubsetValueBenchmark.exactN035K13", + "mode" : "thrpt", + "threads" : 32, + "forks" : 5, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 2, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 3, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "primaryMetric" : { + "score" : 1034.2552716486327, + "scoreError" : 27.35169469851862, + "scoreConfidence" : [ + 1006.9035769501141, + 1061.6069663471515 + ], + "scorePercentiles" : { + "0.0" : 1005.3538384051334, + "50.0" : 1022.6534180179567, + "90.0" : 1067.0820977406106, + "95.0" : 1068.458879600175, + "99.0" : 1068.458879600175, + "99.9" : 1068.458879600175, + "99.99" : 1068.458879600175, + "99.999" : 1068.458879600175, + "99.9999" : 1068.458879600175, + "100.0" : 1068.458879600175 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 1066.1642431675677, + 1063.3273758617393, + 1068.458879600175 + ], + [ + 1062.2765573564798, + 1061.573347389188, + 1061.5603793273897 + ], + [ + 1010.1720467924506, + 1015.693698660016, + 1005.3538384051334 + ], + [ + 1015.2201321834501, + 1025.303876739694, + 1022.6534180179567 + ], + [ + 1015.886233836108, + 1007.6578509270264, + 1012.5271964651157 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.SubsetValueBenchmark.exactN035K208", + "mode" : "thrpt", + "threads" : 32, + "forks" : 5, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 2, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 3, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "primaryMetric" : { + "score" : 502.66124243720077, + "scoreError" : 10.614974423466029, + "scoreConfidence" : [ + 492.04626801373473, + 513.2762168606668 + ], + "scorePercentiles" : { + "0.0" : 489.5148473340847, + "50.0" : 500.4178505896733, + "90.0" : 518.1309761542769, + "95.0" : 520.3806131370512, + "99.0" : 520.3806131370512, + "99.9" : 520.3806131370512, + "99.99" : 520.3806131370512, + "99.999" : 520.3806131370512, + "99.9999" : 520.3806131370512, + "100.0" : 520.3806131370512 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 510.77485406637885, + 505.81162738513854, + 509.07757893513616 + ], + [ + 493.85353690685673, + 504.07730401905195, + 500.4178505896733 + ], + [ + 493.85940376784185, + 489.5148473340847, + 493.74139475743834 + ], + [ + 516.6312181657607, + 515.8472703403316, + 520.3806131370512 + ], + [ + 495.2043172055357, + 493.9980281156843, + 496.7287918320494 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.SubsetValueBenchmark.exactN035K213", + "mode" : "thrpt", + "threads" : 32, + "forks" : 5, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 2, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 3, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "primaryMetric" : { + "score" : 462.0258729311534, + "scoreError" : 7.877473468365682, + "scoreConfidence" : [ + 454.1483994627877, + 469.9033463995191 + ], + "scorePercentiles" : { + "0.0" : 449.2511741938684, + "50.0" : 460.48813319259403, + "90.0" : 474.1827113566221, + "95.0" : 476.19767792691187, + "99.0" : 476.19767792691187, + "99.9" : 476.19767792691187, + "99.99" : 476.19767792691187, + "99.999" : 476.19767792691187, + "99.9999" : 476.19767792691187, + "100.0" : 476.19767792691187 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 464.9046948760132, + 458.8498608215445, + 460.6953931107954 + ], + [ + 460.48813319259403, + 449.2511741938684, + 459.6366661583392 + ], + [ + 472.8394003097623, + 470.8250313809336, + 476.19767792691187 + ], + [ + 456.7541738807125, + 453.99886375051915, + 456.1469952817119 + ], + [ + 466.8236790826741, + 458.0855305153289, + 464.89081948559203 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.SubsetValueBenchmark.exactN060K08", + "mode" : "thrpt", + "threads" : 32, + "forks" : 5, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 2, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 3, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "primaryMetric" : { + "score" : 1076.5566631329848, + "scoreError" : 22.991060050267603, + "scoreConfidence" : [ + 1053.5656030827172, + 1099.5477231832524 + ], + "scorePercentiles" : { + "0.0" : 1045.0955162635062, + "50.0" : 1075.3019088738981, + "90.0" : 1113.2633951944288, + "95.0" : 1113.6691955910264, + "99.0" : 1113.6691955910264, + "99.9" : 1113.6691955910264, + "99.99" : 1113.6691955910264, + "99.999" : 1113.6691955910264, + "99.9999" : 1113.6691955910264, + "100.0" : 1113.6691955910264 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 1112.992861596697, + 1113.6691955910264, + 1111.3041072898486 + ], + [ + 1045.0955162635062, + 1051.3772405221043, + 1048.4291300952746 + ], + [ + 1076.000070587678, + 1076.0625748128045, + 1071.5587553529267 + ], + [ + 1071.6859312258673, + 1068.3009730999265, + 1071.4440678427177 + ], + [ + 1075.3019088738981, + 1077.0246306759236, + 1078.1029831645724 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.SubsetValueBenchmark.exactN060K208", + "mode" : "thrpt", + "threads" : 32, + "forks" : 5, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 2, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 3, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "primaryMetric" : { + "score" : 496.1030631646705, + "scoreError" : 3.512699094351389, + "scoreConfidence" : [ + 492.59036407031914, + 499.6157622590219 + ], + "scorePercentiles" : { + "0.0" : 491.43330033311906, + "50.0" : 494.94021411353697, + "90.0" : 501.88168312327156, + "95.0" : 502.1685014930431, + "99.0" : 502.1685014930431, + "99.9" : 502.1685014930431, + "99.99" : 502.1685014930431, + "99.999" : 502.1685014930431, + "99.9999" : 502.1685014930431, + "100.0" : 502.1685014930431 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 494.622303651207, + 495.90058307689736, + 492.580756602375 + ], + [ + 494.1116037743665, + 495.01071450868614, + 494.94021411353697 + ], + [ + 501.69047087675716, + 494.5425609152021, + 494.7138223319291 + ], + [ + 497.9790836860548, + 493.42693781498247, + 502.1685014930431 + ], + [ + 497.5099091980131, + 500.915185093889, + 491.43330033311906 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.SubsetValueBenchmark.exactN100K08", + "mode" : "thrpt", + "threads" : 32, + "forks" : 5, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 2, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 3, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "primaryMetric" : { + "score" : 1006.1274789066429, + "scoreError" : 8.632240073484734, + "scoreConfidence" : [ + 997.4952388331582, + 1014.7597189801276 + ], + "scorePercentiles" : { + "0.0" : 994.5489507073092, + "50.0" : 1008.2208529988354, + "90.0" : 1016.7314568936902, + "95.0" : 1017.9323197373135, + "99.0" : 1017.9323197373135, + "99.9" : 1017.9323197373135, + "99.99" : 1017.9323197373135, + "99.999" : 1017.9323197373135, + "99.9999" : 1017.9323197373135, + "100.0" : 1017.9323197373135 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 1011.4325473447026, + 1012.3943441117173, + 1012.0558392628138 + ], + [ + 999.2841855514707, + 994.5489507073092, + 996.9796561999148 + ], + [ + 997.8109211825629, + 999.1303362912394, + 995.8581509199988 + ], + [ + 1008.7552140583837, + 1006.7978351857138, + 1008.2208529988354 + ], + [ + 1015.930881664608, + 1017.9323197373135, + 1014.7801483830583 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.SubsetValueBenchmark.exactN100K10", + "mode" : "thrpt", + "threads" : 32, + "forks" : 5, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 2, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 3, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "primaryMetric" : { + "score" : 970.6587710396896, + "scoreError" : 16.366794132058363, + "scoreConfidence" : [ + 954.2919769076312, + 987.0255651717479 + ], + "scorePercentiles" : { + "0.0" : 952.0599711325602, + "50.0" : 975.4598238934245, + "90.0" : 993.2203947074937, + "95.0" : 993.9927256858603, + "99.0" : 993.9927256858603, + "99.9" : 993.9927256858603, + "99.99" : 993.9927256858603, + "99.999" : 993.9927256858603, + "99.9999" : 993.9927256858603, + "100.0" : 993.9927256858603 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 978.8544718524862, + 975.9496048189004, + 976.2882003954288 + ], + [ + 955.7626937786133, + 953.4614389039157, + 952.0599711325602 + ], + [ + 989.752017782688, + 993.9927256858603, + 992.7055073885828 + ], + [ + 975.4598238934245, + 974.8941100428519, + 977.6110691282292 + ], + [ + 952.6560927866482, + 953.5554874785408, + 956.8783505266105 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.SubsetValueBenchmark.exactN100K208", + "mode" : "thrpt", + "threads" : 32, + "forks" : 5, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 2, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 3, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "primaryMetric" : { + "score" : 471.55749301741713, + "scoreError" : 18.06646722720142, + "scoreConfidence" : [ + 453.4910257902157, + 489.62396024461856 + ], + "scorePercentiles" : { + "0.0" : 444.2461877774847, + "50.0" : 478.64697777245993, + "90.0" : 488.6964600347632, + "95.0" : 489.07127104849866, + "99.0" : 489.07127104849866, + "99.9" : 489.07127104849866, + "99.99" : 489.07127104849866, + "99.999" : 489.07127104849866, + "99.9999" : 489.07127104849866, + "100.0" : 489.07127104849866 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 462.6847861327774, + 461.33662119031106, + 461.9008743593246 + ], + [ + 485.7371925332991, + 487.93614870638663, + 487.1728144213904 + ], + [ + 478.7600319803987, + 478.64697777245993, + 475.73798730951023 + ], + [ + 444.4951556999079, + 444.99416499519276, + 444.2461877774847 + ], + [ + 488.4465860256062, + 489.07127104849866, + 482.19559530870873 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.SubsetValueBenchmark.exactN100K210", + "mode" : "thrpt", + "threads" : 32, + "forks" : 5, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 2, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 3, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "primaryMetric" : { + "score" : 463.27979318017736, + "scoreError" : 2.738923148411572, + "scoreConfidence" : [ + 460.5408700317658, + 466.01871632858894 + ], + "scorePercentiles" : { + "0.0" : 455.0503335926937, + "50.0" : 463.59813051170687, + "90.0" : 465.5939719156776, + "95.0" : 465.6217761333669, + "99.0" : 465.6217761333669, + "99.9" : 465.6217761333669, + "99.99" : 465.6217761333669, + "99.999" : 465.6217761333669, + "99.9999" : 465.6217761333669, + "100.0" : 465.6217761333669 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 462.9965879231482, + 464.76841742491064, + 464.03749327877694 + ], + [ + 464.54349294814807, + 464.7117517675539, + 463.3785963327552 + ], + [ + 463.59813051170687, + 461.82233064366426, + 463.4625924577529 + ], + [ + 461.94882832573154, + 462.7852470345038, + 455.0503335926937 + ], + [ + 464.8958835573962, + 465.6217761333669, + 465.57543577055145 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.SubsetValueBenchmark.exactN200K10", + "mode" : "thrpt", + "threads" : 32, + "forks" : 5, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 2, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 3, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "primaryMetric" : { + "score" : 989.5388634227542, + "scoreError" : 14.134596639039657, + "scoreConfidence" : [ + 975.4042667837145, + 1003.6734600617938 + ], + "scorePercentiles" : { + "0.0" : 961.1299197289487, + "50.0" : 994.9023644719024, + "90.0" : 1001.4687219332199, + "95.0" : 1002.1899328794412, + "99.0" : 1002.1899328794412, + "99.9" : 1002.1899328794412, + "99.99" : 1002.1899328794412, + "99.999" : 1002.1899328794412, + "99.9999" : 1002.1899328794412, + "100.0" : 1002.1899328794412 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 1002.1899328794412, + 1000.987914635739, + 999.3257493472246 + ], + [ + 994.0756147113772, + 992.0096401293727, + 983.5593316535548 + ], + [ + 969.9189180491841, + 961.1299197289487, + 965.7159429222877 + ], + [ + 997.4301681073473, + 997.2109925387504, + 993.5771423426628 + ], + [ + 996.0496049913994, + 994.9023644719024, + 994.9997148321199 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.SubsetValueBenchmark.exactN200K210", + "mode" : "thrpt", + "threads" : 32, + "forks" : 5, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 2, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 3, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "primaryMetric" : { + "score" : 446.7645512891926, + "scoreError" : 5.442536243476984, + "scoreConfidence" : [ + 441.3220150457156, + 452.20708753266956 + ], + "scorePercentiles" : { + "0.0" : 439.7788610749823, + "50.0" : 444.6941886476842, + "90.0" : 454.3681767298469, + "95.0" : 455.1577951726488, + "99.0" : 455.1577951726488, + "99.9" : 455.1577951726488, + "99.99" : 455.1577951726488, + "99.999" : 455.1577951726488, + "99.9999" : 455.1577951726488, + "100.0" : 455.1577951726488 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 442.536439675751, + 442.1295516755243, + 443.61230124071636 + ], + [ + 441.93263323460167, + 443.15368820445184, + 439.7788610749823 + ], + [ + 451.37913040998745, + 453.119744620769, + 455.1577951726488 + ], + [ + 445.3849374919964, + 443.67885909223224, + 444.6941886476842 + ], + [ + 449.5295117305883, + 453.8417644346456, + 451.5388626313101 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.SubsetValueBenchmark.exactN300K09", + "mode" : "thrpt", + "threads" : 32, + "forks" : 5, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 2, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 3, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "primaryMetric" : { + "score" : 954.5732192493725, + "scoreError" : 5.551621199499133, + "scoreConfidence" : [ + 949.0215980498734, + 960.1248404488716 + ], + "scorePercentiles" : { + "0.0" : 945.3720405703026, + "50.0" : 953.6524108444851, + "90.0" : 962.1200566980764, + "95.0" : 962.5669852462835, + "99.0" : 962.5669852462835, + "99.9" : 962.5669852462835, + "99.99" : 962.5669852462835, + "99.999" : 962.5669852462835, + "99.9999" : 962.5669852462835, + "100.0" : 962.5669852462835 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 954.1688926211596, + 945.3720405703026, + 950.2428738759257 + ], + [ + 962.5669852462835, + 957.7397409724297, + 961.2017714264199 + ], + [ + 953.6524108444851, + 947.3565151947821, + 951.9325767720046 + ], + [ + 952.9053707672446, + 952.9197918501438, + 951.4168492777303 + ], + [ + 955.5011769209501, + 961.822104332605, + 959.7991880681216 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.SubsetValueBenchmark.exactN300K209", + "mode" : "thrpt", + "threads" : 32, + "forks" : 5, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 2, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 3, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "primaryMetric" : { + "score" : 450.89503297404093, + "scoreError" : 12.138825735933237, + "scoreConfidence" : [ + 438.7562072381077, + 463.03385870997414 + ], + "scorePercentiles" : { + "0.0" : 435.72432879783145, + "50.0" : 454.37173618852, + "90.0" : 462.3558539284659, + "95.0" : 462.8919887974014, + "99.0" : 462.8919887974014, + "99.9" : 462.8919887974014, + "99.99" : 462.8919887974014, + "99.999" : 462.8919887974014, + "99.9999" : 462.8919887974014, + "100.0" : 462.8919887974014 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 462.8919887974014, + 461.99843068250885, + 460.4487856098874 + ], + [ + 436.09229699373617, + 435.73679679316433, + 435.72432879783145 + ], + [ + 457.9076168075443, + 454.37173618852, + 453.84531942745616 + ], + [ + 461.18430422993384, + 461.55135982783224, + 461.21794818436797 + ], + [ + 439.49132061780773, + 436.9753218138549, + 443.98793983876783 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.SubsetValueBenchmark.exactN400K09", + "mode" : "thrpt", + "threads" : 32, + "forks" : 5, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 2, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 3, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "primaryMetric" : { + "score" : 983.4090697829313, + "scoreError" : 20.353875245485536, + "scoreConfidence" : [ + 963.0551945374458, + 1003.7629450284168 + ], + "scorePercentiles" : { + "0.0" : 961.3544835692653, + "50.0" : 986.8900359941699, + "90.0" : 1014.0635900707392, + "95.0" : 1015.3218017992034, + "99.0" : 1015.3218017992034, + "99.9" : 1015.3218017992034, + "99.99" : 1015.3218017992034, + "99.999" : 1015.3218017992034, + "99.9999" : 1015.3218017992034, + "100.0" : 1015.3218017992034 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 1013.2247822517631, + 1008.5224693030892, + 1015.3218017992034 + ], + [ + 963.6600003262133, + 961.3544835692653, + 964.2604725503714 + ], + [ + 964.255848325267, + 963.6935738628946, + 965.7504960556753 + ], + [ + 982.4583162785699, + 986.8900359941699, + 990.323392540617 + ], + [ + 987.3693459858683, + 991.2407062635492, + 992.8103216374556 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.SubsetValueBenchmark.exactN400K209", + "mode" : "thrpt", + "threads" : 32, + "forks" : 5, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 2, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 3, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "primaryMetric" : { + "score" : 451.1764672222263, + "scoreError" : 2.545263432265774, + "scoreConfidence" : [ + 448.6312037899605, + 453.72173065449203 + ], + "scorePercentiles" : { + "0.0" : 446.6593536735987, + "50.0" : 452.20506456629676, + "90.0" : 453.91965046617594, + "95.0" : 454.35371900812015, + "99.0" : 454.35371900812015, + "99.9" : 454.35371900812015, + "99.99" : 454.35371900812015, + "99.999" : 454.35371900812015, + "99.9999" : 454.35371900812015, + "100.0" : 454.35371900812015 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 450.57287995912594, + 446.6593536735987, + 447.7813070872143 + ], + [ + 453.2936817739289, + 454.35371900812015, + 452.64494170279477 + ], + [ + 453.63027143821313, + 453.2989723622714, + 452.2870188343068 + ], + [ + 450.5136392458904, + 452.76369198433946, + 452.20506456629676 + ], + [ + 450.47627812762016, + 449.13553895483466, + 448.03064961483847 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.SubsetValueBenchmark.probabilisticN035k16d01", + "mode" : "thrpt", + "threads" : 32, + "forks" : 5, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 2, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 3, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "primaryMetric" : { + "score" : 111.79662970943086, + "scoreError" : 0.7890127048293907, + "scoreConfidence" : [ + 111.00761700460147, + 112.58564241426025 + ], + "scorePercentiles" : { + "0.0" : 110.81792123675102, + "50.0" : 111.63634201501539, + "90.0" : 113.06924508737265, + "95.0" : 113.08224436332766, + "99.0" : 113.08224436332766, + "99.9" : 113.08224436332766, + "99.99" : 113.08224436332766, + "99.999" : 113.08224436332766, + "99.9999" : 113.08224436332766, + "100.0" : 113.08224436332766 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 111.29903017089258, + 111.42436747681585, + 111.63634201501539 + ], + [ + 111.00989580513003, + 110.93670869534073, + 110.81792123675102 + ], + [ + 111.52833563609899, + 112.1591021102084, + 111.1764979636141 + ], + [ + 112.36085370900122, + 112.05007826200827, + 111.78819153003603 + ], + [ + 113.06057890340264, + 112.61929776381965, + 113.08224436332766 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.SubsetValueBenchmark.probabilisticN035k16d02", + "mode" : "thrpt", + "threads" : 32, + "forks" : 5, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 2, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 3, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "primaryMetric" : { + "score" : 89.7182022702618, + "scoreError" : 2.172963251231951, + "scoreConfidence" : [ + 87.54523901902985, + 91.89116552149375 + ], + "scorePercentiles" : { + "0.0" : 85.84200393117202, + "50.0" : 90.27090741911687, + "90.0" : 92.3810979004838, + "95.0" : 93.11756140139963, + "99.0" : 93.11756140139963, + "99.9" : 93.11756140139963, + "99.99" : 93.11756140139963, + "99.999" : 93.11756140139963, + "99.9999" : 93.11756140139963, + "100.0" : 93.11756140139963 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 85.84200393117202, + 89.1807854408291, + 90.27090741911687 + ], + [ + 87.3556755615599, + 88.79771050809072, + 88.51046955362463 + ], + [ + 91.55413764147796, + 91.89012223320658, + 93.11756140139963 + ], + [ + 86.51916283413281, + 90.98701469226555, + 90.52543821910433 + ], + [ + 90.98478201115844, + 89.75155479071478, + 90.48570781607357 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.SubsetValueBenchmark.probabilisticN035k80d01", + "mode" : "thrpt", + "threads" : 32, + "forks" : 5, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 2, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 3, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "primaryMetric" : { + "score" : 202.3949374176122, + "scoreError" : 76.820246452902, + "scoreConfidence" : [ + 125.5746909647102, + 279.2151838705142 + ], + "scorePercentiles" : { + "0.0" : 62.533078280440755, + "50.0" : 236.98298162260488, + "90.0" : 238.04756155357592, + "95.0" : 238.44544251126433, + "99.0" : 238.44544251126433, + "99.9" : 238.44544251126433, + "99.99" : 238.44544251126433, + "99.999" : 238.44544251126433, + "99.9999" : 238.44544251126433, + "100.0" : 238.44544251126433 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 65.28482459982169, + 62.533078280440755, + 62.86501075965584 + ], + [ + 236.34452072668992, + 237.04304386424084, + 237.24788684662477 + ], + [ + 237.14014300559694, + 236.98298162260488, + 236.96093410427136 + ], + [ + 237.29981291785649, + 237.78230758178364, + 238.44544251126433 + ], + [ + 236.06974469885435, + 237.18346967861726, + 236.74086006585983 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.SubsetValueBenchmark.probabilisticN035k80d02", + "mode" : "thrpt", + "threads" : 32, + "forks" : 5, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 2, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 3, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "primaryMetric" : { + "score" : 168.757445724798, + "scoreError" : 59.96352310536157, + "scoreConfidence" : [ + 108.79392261943644, + 228.72096883015956 + ], + "scorePercentiles" : { + "0.0" : 59.64677121868498, + "50.0" : 198.8785907076378, + "90.0" : 200.62793021114447, + "95.0" : 201.44893232158273, + "99.0" : 201.44893232158273, + "99.9" : 201.44893232158273, + "99.99" : 201.44893232158273, + "99.999" : 201.44893232158273, + "99.9999" : 201.44893232158273, + "100.0" : 201.44893232158273 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 199.59465968359842, + 198.8785907076378, + 201.44893232158273 + ], + [ + 185.05094006181417, + 181.90098450811843, + 184.69705621029397 + ], + [ + 198.1725657833017, + 199.40604617925544, + 200.0805954708523 + ], + [ + 200.07847647167017, + 199.12662121145598, + 199.65956416851796 + ], + [ + 61.03111129545997, + 62.58877057972579, + 59.64677121868498 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.SubsetValueBenchmark.probabilisticN060k16d01", + "mode" : "thrpt", + "threads" : 32, + "forks" : 5, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 2, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 3, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "primaryMetric" : { + "score" : 41.88173804156052, + "scoreError" : 21.641052708911293, + "scoreConfidence" : [ + 20.240685332649225, + 63.52279075047181 + ], + "scorePercentiles" : { + "0.0" : 17.314578239882202, + "50.0" : 53.43711652682356, + "90.0" : 60.884092184012786, + "95.0" : 61.260559842110936, + "99.0" : 61.260559842110936, + "99.9" : 61.260559842110936, + "99.99" : 61.260559842110936, + "99.999" : 61.260559842110936, + "99.9999" : 61.260559842110936, + "100.0" : 61.260559842110936 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 59.33104684307764, + 59.53181275631804, + 53.43711652682356 + ], + [ + 17.80064132900073, + 18.55071640995103, + 18.58120889565767 + ], + [ + 58.03999543510948, + 53.4100942386382, + 58.052406438944026 + ], + [ + 17.85164062383314, + 18.333252045960474, + 17.314578239882202 + ], + [ + 60.63311374528069, + 56.097887252819845, + 61.260559842110936 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.SubsetValueBenchmark.probabilisticN060k16d02", + "mode" : "thrpt", + "threads" : 32, + "forks" : 5, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 2, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 3, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "primaryMetric" : { + "score" : 36.56665003083814, + "scoreError" : 18.66373381114973, + "scoreConfidence" : [ + 17.90291621968841, + 55.23038384198787 + ], + "scorePercentiles" : { + "0.0" : 15.056264334102831, + "50.0" : 48.329605821660785, + "90.0" : 53.08653654903394, + "95.0" : 54.25752497444918, + "99.0" : 54.25752497444918, + "99.9" : 54.25752497444918, + "99.99" : 54.25752497444918, + "99.999" : 54.25752497444918, + "99.9999" : 54.25752497444918, + "100.0" : 54.25752497444918 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 52.30587759875711, + 50.89740808812191, + 48.490769953699825 + ], + [ + 16.81083935546008, + 15.564007832258609, + 16.39521871779643 + ], + [ + 16.733797362232355, + 15.516230446715122, + 15.056264334102831 + ], + [ + 50.11015442617881, + 48.329605821660785, + 48.684902971532644 + ], + [ + 47.545755585789614, + 54.25752497444918, + 51.80139299381688 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.SubsetValueBenchmark.probabilisticN060k80d01", + "mode" : "thrpt", + "threads" : 32, + "forks" : 5, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 2, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 3, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "primaryMetric" : { + "score" : 78.2768276326478, + "scoreError" : 54.24124305658098, + "scoreConfidence" : [ + 24.035584576066817, + 132.51807068922878 + ], + "scorePercentiles" : { + "0.0" : 37.250187654772, + "50.0" : 38.96014368934481, + "90.0" : 139.48222343003533, + "95.0" : 140.34307206731017, + "99.0" : 140.34307206731017, + "99.9" : 140.34307206731017, + "99.99" : 140.34307206731017, + "99.999" : 140.34307206731017, + "99.9999" : 140.34307206731017, + "100.0" : 140.34307206731017 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 138.22101883517513, + 138.90832433851875, + 140.34307206731017 + ], + [ + 38.96014368934481, + 38.924566981836946, + 39.00455037162455 + ], + [ + 37.59438997098785, + 38.523063640560814, + 38.392137576295916 + ], + [ + 37.76841497596615, + 37.250187654772, + 37.936969239249734 + ], + [ + 136.21572250509894, + 137.9734908501482, + 138.13636179282656 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.SubsetValueBenchmark.probabilisticN060k80d02", + "mode" : "thrpt", + "threads" : 32, + "forks" : 5, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 2, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 3, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "primaryMetric" : { + "score" : 124.03155340277755, + "scoreError" : 1.7041628682686132, + "scoreConfidence" : [ + 122.32739053450894, + 125.73571627104616 + ], + "scorePercentiles" : { + "0.0" : 121.03892315668844, + "50.0" : 123.98575598368575, + "90.0" : 126.25927433354428, + "95.0" : 127.84501521488984, + "99.0" : 127.84501521488984, + "99.9" : 127.84501521488984, + "99.99" : 127.84501521488984, + "99.999" : 127.84501521488984, + "99.9999" : 127.84501521488984, + "100.0" : 127.84501521488984 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 121.03892315668844, + 123.34968751497817, + 124.61003817070844 + ], + [ + 121.64343181005583, + 122.66636174264978, + 123.61521861057459 + ], + [ + 124.82761951845447, + 127.84501521488984, + 125.14647894863876 + ], + [ + 123.82364098353128, + 123.98575598368575, + 125.20211374598057 + ], + [ + 123.9664162877595, + 124.33974867593705, + 124.4128506771307 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.SubsetValueBenchmark.probabilisticN100k16d01", + "mode" : "thrpt", + "threads" : 32, + "forks" : 5, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 2, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 3, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "primaryMetric" : { + "score" : 17.21948547671312, + "scoreError" : 9.768385836056055, + "scoreConfidence" : [ + 7.451099640657066, + 26.987871312769176 + ], + "scorePercentiles" : { + "0.0" : 9.531255528831748, + "50.0" : 10.471563319670624, + "90.0" : 28.820521988121587, + "95.0" : 29.828117035381403, + "99.0" : 29.828117035381403, + "99.9" : 29.828117035381403, + "99.99" : 29.828117035381403, + "99.999" : 29.828117035381403, + "99.9999" : 29.828117035381403, + "100.0" : 29.828117035381403 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 10.483362539127844, + 9.531255528831748, + 10.094349529554583 + ], + [ + 27.621117396416544, + 29.828117035381403, + 27.994409180320012 + ], + [ + 10.471563319670624, + 10.1331220370504, + 10.318472291948353 + ], + [ + 9.595252849157111, + 9.769539562772682, + 9.869845681337562 + ], + [ + 27.205193467975715, + 27.227889774537143, + 28.148791956615042 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.SubsetValueBenchmark.probabilisticN100k16d02", + "mode" : "thrpt", + "threads" : 32, + "forks" : 5, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 2, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 3, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "primaryMetric" : { + "score" : 18.464359867970856, + "scoreError" : 8.311327421577667, + "scoreConfidence" : [ + 10.153032446393189, + 26.77568728954852 + ], + "scorePercentiles" : { + "0.0" : 8.229146693738992, + "50.0" : 22.49167287956409, + "90.0" : 25.804821887916685, + "95.0" : 25.829650222260966, + "99.0" : 25.829650222260966, + "99.9" : 25.829650222260966, + "99.99" : 25.829650222260966, + "99.999" : 25.829650222260966, + "99.9999" : 25.829650222260966, + "100.0" : 25.829650222260966 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 9.527894173773607, + 9.818854530996784, + 9.154779599286208 + ], + [ + 25.654878365292156, + 24.834351265024267, + 24.59512253294589 + ], + [ + 22.156172311251122, + 25.788269665020497, + 25.360154295341367 + ], + [ + 9.595702277507995, + 8.229146693738992, + 9.828474154678585 + ], + [ + 22.49167287956409, + 24.100275052880303, + 25.829650222260966 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.SubsetValueBenchmark.probabilisticN100k80d01", + "mode" : "thrpt", + "threads" : 32, + "forks" : 5, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 2, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 3, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "primaryMetric" : { + "score" : 72.49527604120003, + "scoreError" : 28.035411213750177, + "scoreConfidence" : [ + 44.45986482744985, + 100.53068725495021 + ], + "scorePercentiles" : { + "0.0" : 21.47265569784971, + "50.0" : 84.75581903533245, + "90.0" : 86.00899397831984, + "95.0" : 86.22436713282667, + "99.0" : 86.22436713282667, + "99.9" : 86.22436713282667, + "99.99" : 86.22436713282667, + "99.999" : 86.22436713282667, + "99.9999" : 86.22436713282667, + "100.0" : 86.22436713282667 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 86.22436713282667, + 84.75581903533245, + 85.7171555039946 + ], + [ + 85.26646896110239, + 84.6158681141584, + 84.5742491024965 + ], + [ + 21.47265569784971, + 22.16855145680661, + 21.906505925320847 + ], + [ + 85.72688693918556, + 85.86541187531529, + 85.72165739057459 + ], + [ + 82.97022991080364, + 85.83572145510848, + 84.60759211712481 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.SubsetValueBenchmark.probabilisticN100k80d02", + "mode" : "thrpt", + "threads" : 32, + "forks" : 5, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 2, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 3, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "primaryMetric" : { + "score" : 75.76316076457995, + "scoreError" : 1.08734402016124, + "scoreConfidence" : [ + 74.67581674441871, + 76.85050478474119 + ], + "scorePercentiles" : { + "0.0" : 74.6818791597644, + "50.0" : 75.48095324431024, + "90.0" : 77.58887233782316, + "95.0" : 77.83464678924264, + "99.0" : 77.83464678924264, + "99.9" : 77.83464678924264, + "99.99" : 77.83464678924264, + "99.999" : 77.83464678924264, + "99.9999" : 77.83464678924264, + "100.0" : 77.83464678924264 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 75.48095324431024, + 77.09733152820617, + 75.17226414701386 + ], + [ + 75.31666545428482, + 77.4250227035435, + 77.83464678924264 + ], + [ + 74.6818791597644, + 75.11506337121622, + 74.77331452668341 + ], + [ + 75.4896030608472, + 75.8044221987372, + 76.70930150903817 + ], + [ + 74.81801152854665, + 74.9970337339526, + 75.7318985133122 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.SubsetValueBenchmark.probabilisticN200k16d01", + "mode" : "thrpt", + "threads" : 32, + "forks" : 5, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 2, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 3, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "primaryMetric" : { + "score" : 13.476450163931311, + "scoreError" : 1.2896892083531966, + "scoreConfidence" : [ + 12.186760955578114, + 14.766139372284508 + ], + "scorePercentiles" : { + "0.0" : 11.671607189080921, + "50.0" : 13.469265986649162, + "90.0" : 15.118259520342173, + "95.0" : 15.163268753626065, + "99.0" : 15.163268753626065, + "99.9" : 15.163268753626065, + "99.99" : 15.163268753626065, + "99.999" : 15.163268753626065, + "99.9999" : 15.163268753626065, + "100.0" : 15.163268753626065 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 12.058133062196317, + 15.163268753626065, + 13.960561231856634 + ], + [ + 13.469265986649162, + 11.671607189080921, + 14.865003799454195 + ], + [ + 14.064668471493432, + 13.87572764864681, + 15.088253364819579 + ], + [ + 13.346198109882147, + 12.974736022371665, + 11.874707898705099 + ], + [ + 12.066633708048501, + 14.81302882386872, + 12.85495838827039 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.SubsetValueBenchmark.probabilisticN200k16d02", + "mode" : "thrpt", + "threads" : 32, + "forks" : 5, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 2, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 3, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "primaryMetric" : { + "score" : 8.792444933870911, + "scoreError" : 3.804202494942238, + "scoreConfidence" : [ + 4.988242438928673, + 12.59664742881315 + ], + "scorePercentiles" : { + "0.0" : 4.149665279641244, + "50.0" : 10.400189052702512, + "90.0" : 12.909168961085117, + "95.0" : 13.298437179178826, + "99.0" : 13.298437179178826, + "99.9" : 13.298437179178826, + "99.99" : 13.298437179178826, + "99.999" : 13.298437179178826, + "99.9999" : 13.298437179178826, + "100.0" : 13.298437179178826 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 10.997988780122506, + 12.649656815689308, + 10.9454950321249 + ], + [ + 11.912332925901623, + 10.922879908100002, + 10.400189052702512 + ], + [ + 5.078954968203786, + 4.995431022633059, + 4.759604131263156 + ], + [ + 12.264762086331977, + 10.292533184425917, + 13.298437179178826 + ], + [ + 4.149665279641244, + 4.158323207461708, + 5.060420434283126 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.SubsetValueBenchmark.probabilisticN200k80d01", + "mode" : "thrpt", + "threads" : 32, + "forks" : 5, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 2, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 3, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "primaryMetric" : { + "score" : 33.37684670457286, + "scoreError" : 1.1565852491337936, + "scoreConfidence" : [ + 32.22026145543907, + 34.53343195370665 + ], + "scorePercentiles" : { + "0.0" : 31.472270538496055, + "50.0" : 33.10015327046203, + "90.0" : 35.2133249446947, + "95.0" : 35.863345731220264, + "99.0" : 35.863345731220264, + "99.9" : 35.863345731220264, + "99.99" : 35.863345731220264, + "99.999" : 35.863345731220264, + "99.9999" : 35.863345731220264, + "100.0" : 35.863345731220264 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 33.10015327046203, + 32.89648344373738, + 33.39307641332626 + ], + [ + 32.867732424665064, + 32.68752414839513, + 34.38295008974919 + ], + [ + 32.74220812187432, + 32.84528659957435, + 33.70332732200391 + ], + [ + 32.280178487772815, + 33.52977072059713, + 35.863345731220264 + ], + [ + 31.472270538496055, + 34.779977753677656, + 34.10841550304143 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.SubsetValueBenchmark.probabilisticN200k80d02", + "mode" : "thrpt", + "threads" : 32, + "forks" : 5, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 2, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 3, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "primaryMetric" : { + "score" : 30.151335851401438, + "scoreError" : 1.378331339199239, + "scoreConfidence" : [ + 28.7730045122022, + 31.529667190600676 + ], + "scorePercentiles" : { + "0.0" : 27.54284438702816, + "50.0" : 30.65998212492608, + "90.0" : 31.717771399770445, + "95.0" : 31.923578454605742, + "99.0" : 31.923578454605742, + "99.9" : 31.923578454605742, + "99.99" : 31.923578454605742, + "99.999" : 31.923578454605742, + "99.9999" : 31.923578454605742, + "100.0" : 31.923578454605742 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 29.596739267682036, + 30.941198236266363, + 31.011414237193954 + ], + [ + 30.65998212492608, + 28.831923022843988, + 31.580566696546914 + ], + [ + 27.54284438702816, + 31.105304347233353, + 31.923578454605742 + ], + [ + 30.77385159749443, + 31.16622091259212, + 30.311370773497615 + ], + [ + 28.27503088694388, + 28.95587964045146, + 29.594133185715393 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.SubsetValueBenchmark.probabilisticN300k16d01", + "mode" : "thrpt", + "threads" : 32, + "forks" : 5, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 2, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 3, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "primaryMetric" : { + "score" : 7.61875429536946, + "scoreError" : 2.4566164405898365, + "scoreConfidence" : [ + 5.162137854779623, + 10.075370735959297 + ], + "scorePercentiles" : { + "0.0" : 3.144677802002079, + "50.0" : 8.745904272573547, + "90.0" : 8.89636638544586, + "95.0" : 8.903701335626923, + "99.0" : 8.903701335626923, + "99.9" : 8.903701335626923, + "99.99" : 8.903701335626923, + "99.999" : 8.903701335626923, + "99.9999" : 8.903701335626923, + "100.0" : 8.903701335626923 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 8.503088573991551, + 8.773988239024698, + 8.76367514142568 + ], + [ + 8.600631378190974, + 8.745904272573547, + 8.76158497674196 + ], + [ + 3.1969390106546505, + 3.144677802002079, + 3.2170094933240767 + ], + [ + 8.891476418658485, + 8.720486419629784, + 8.418913150443379 + ], + [ + 8.863072431677601, + 8.776165786576518, + 8.903701335626923 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.SubsetValueBenchmark.probabilisticN300k16d02", + "mode" : "thrpt", + "threads" : 32, + "forks" : 5, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 2, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 3, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "primaryMetric" : { + "score" : 6.365052802025193, + "scoreError" : 1.930732054854557, + "scoreConfidence" : [ + 4.434320747170636, + 8.29578485687975 + ], + "scorePercentiles" : { + "0.0" : 2.7971553338224524, + "50.0" : 7.135663135428521, + "90.0" : 7.683754720951823, + "95.0" : 7.7332392258994584, + "99.0" : 7.7332392258994584, + "99.9" : 7.7332392258994584, + "99.99" : 7.7332392258994584, + "99.999" : 7.7332392258994584, + "99.9999" : 7.7332392258994584, + "100.0" : 7.7332392258994584 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 2.9216825985770014, + 3.040284362761938, + 2.7971553338224524 + ], + [ + 7.341871035609219, + 6.498861914849152, + 6.985421065283115 + ], + [ + 7.135663135428521, + 7.27932326531456, + 7.650765050986733 + ], + [ + 7.145284940127866, + 7.3495945036029475, + 7.1173658579880925 + ], + [ + 7.7332392258994584, + 7.103607107321174, + 7.375672632805673 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.SubsetValueBenchmark.probabilisticN300k80d01", + "mode" : "thrpt", + "threads" : 32, + "forks" : 5, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 2, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 3, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "primaryMetric" : { + "score" : 14.785894114222918, + "scoreError" : 7.075300736265591, + "scoreConfidence" : [ + 7.710593377957327, + 21.86119485048851 + ], + "scorePercentiles" : { + "0.0" : 6.6238101234689175, + "50.0" : 18.63405522824858, + "90.0" : 22.137903119219704, + "95.0" : 23.443478066734833, + "99.0" : 23.443478066734833, + "99.9" : 23.443478066734833, + "99.99" : 23.443478066734833, + "99.999" : 23.443478066734833, + "99.9999" : 23.443478066734833, + "100.0" : 23.443478066734833 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 20.81463482193428, + 21.26751982087628, + 18.63405522824858 + ], + [ + 6.6238101234689175, + 6.75658101722185, + 7.051271053910943 + ], + [ + 19.0767889149469, + 19.13286549864879, + 20.00862757999917 + ], + [ + 8.333342952466932, + 6.795686318602955, + 7.148169626132261 + ], + [ + 17.922511600530722, + 23.443478066734833, + 18.77906908962039 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.SubsetValueBenchmark.probabilisticN300k80d02", + "mode" : "thrpt", + "threads" : 32, + "forks" : 5, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 2, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 3, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "primaryMetric" : { + "score" : 15.069645555892203, + "scoreError" : 4.995764307788415, + "scoreConfidence" : [ + 10.073881248103788, + 20.065409863680618 + ], + "scorePercentiles" : { + "0.0" : 6.525438308782814, + "50.0" : 15.836747387967138, + "90.0" : 19.861826612604133, + "95.0" : 21.353181774527926, + "99.0" : 21.353181774527926, + "99.9" : 21.353181774527926, + "99.99" : 21.353181774527926, + "99.999" : 21.353181774527926, + "99.9999" : 21.353181774527926, + "100.0" : 21.353181774527926 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 18.4972820712625, + 15.836747387967138, + 14.398722570118466 + ], + [ + 18.19260235832646, + 18.867589837988273, + 21.353181774527926 + ], + [ + 15.637367010115758, + 15.44904856502463, + 17.03452840674345 + ], + [ + 14.766921479727264, + 18.15860626837125, + 17.654420461003873 + ], + [ + 6.681110290356621, + 6.525438308782814, + 6.991116548066628 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.SubsetValueBenchmark.probabilisticN400k16d01", + "mode" : "thrpt", + "threads" : 32, + "forks" : 5, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 2, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 3, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "primaryMetric" : { + "score" : 5.9353103682546475, + "scoreError" : 0.5290397619836832, + "scoreConfidence" : [ + 5.406270606270964, + 6.464350130238331 + ], + "scorePercentiles" : { + "0.0" : 4.878922326878594, + "50.0" : 6.054419632488941, + "90.0" : 6.527640940958293, + "95.0" : 6.597539744033696, + "99.0" : 6.597539744033696, + "99.9" : 6.597539744033696, + "99.99" : 6.597539744033696, + "99.999" : 6.597539744033696, + "99.9999" : 6.597539744033696, + "100.0" : 6.597539744033696 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 6.054419632488941, + 6.4116396040170525, + 5.328226475687357 + ], + [ + 6.144454845608136, + 6.481041738908025, + 6.004152066541914 + ], + [ + 4.878922326878594, + 6.3474334083378565, + 6.2586295143444 + ], + [ + 5.515362412224871, + 5.55586134418077, + 6.14224253754926 + ], + [ + 5.903052458531737, + 5.406677414487111, + 6.597539744033696 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.SubsetValueBenchmark.probabilisticN400k16d02", + "mode" : "thrpt", + "threads" : 32, + "forks" : 5, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 2, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 3, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "primaryMetric" : { + "score" : 4.673432502837802, + "scoreError" : 1.552878455432352, + "scoreConfidence" : [ + 3.1205540474054496, + 6.226310958270154 + ], + "scorePercentiles" : { + "0.0" : 2.181545141685607, + "50.0" : 5.214889853823411, + "90.0" : 6.017274704916607, + "95.0" : 6.1166478430723, + "99.0" : 6.1166478430723, + "99.9" : 6.1166478430723, + "99.99" : 6.1166478430723, + "99.999" : 6.1166478430723, + "99.9999" : 6.1166478430723, + "100.0" : 6.1166478430723 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 5.856509945420771, + 5.737826324730627, + 5.771332586433917 + ], + [ + 2.181545141685607, + 2.229206724090047, + 2.3084518606427173 + ], + [ + 5.951025946146145, + 5.214889853823411, + 5.886929566087091 + ], + [ + 4.427686276216721, + 3.8137970569954778, + 4.275272285428256 + ], + [ + 6.1166478430723, + 4.422922211435889, + 5.907443920358064 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.SubsetValueBenchmark.probabilisticN400k80d01", + "mode" : "thrpt", + "threads" : 32, + "forks" : 5, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 2, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 3, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "primaryMetric" : { + "score" : 14.191716644777708, + "scoreError" : 1.1430000694377032, + "scoreConfidence" : [ + 13.048716575340006, + 15.33471671421541 + ], + "scorePercentiles" : { + "0.0" : 11.917408845733258, + "50.0" : 13.958179722702921, + "90.0" : 15.837807319547421, + "95.0" : 17.105871806738772, + "99.0" : 17.105871806738772, + "99.9" : 17.105871806738772, + "99.99" : 17.105871806738772, + "99.999" : 17.105871806738772, + "99.9999" : 17.105871806738772, + "100.0" : 17.105871806738772 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 14.992430994753187, + 14.017956171834632, + 13.770044082590667 + ], + [ + 14.736605508795641, + 14.292851076013473, + 13.791605869593202 + ], + [ + 14.608433896935244, + 17.105871806738772, + 13.643092918848009 + ], + [ + 14.530654082597914, + 13.85593344020735, + 13.958179722702921 + ], + [ + 11.917408845733258, + 13.822748519920893, + 13.83193273440047 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.SubsetValueBenchmark.probabilisticN400k80d02", + "mode" : "thrpt", + "threads" : 32, + "forks" : 5, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 2, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 3, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "primaryMetric" : { + "score" : 11.445924020087164, + "scoreError" : 0.7562865417460121, + "scoreConfidence" : [ + 10.689637478341151, + 12.202210561833176 + ], + "scorePercentiles" : { + "0.0" : 10.115188136208515, + "50.0" : 11.625739262267807, + "90.0" : 12.254490517068911, + "95.0" : 12.274847040996331, + "99.0" : 12.274847040996331, + "99.9" : 12.274847040996331, + "99.99" : 12.274847040996331, + "99.999" : 12.274847040996331, + "99.9999" : 12.274847040996331, + "100.0" : 12.274847040996331 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 11.041682048939798, + 10.72140891631031, + 11.713493275401389 + ], + [ + 12.274847040996331, + 11.99574212912567, + 11.424147706282449 + ], + [ + 12.227950081408531, + 12.15024855716867, + 10.115188136208515 + ], + [ + 11.625739262267807, + 12.240919501117297, + 11.246913457541167 + ], + [ + 10.355403568190786, + 10.814855595486414, + 11.740321024862308 + ] + ] + }, + "secondaryMetrics" : { + } + } +] + + diff --git a/tlatools/test-benchmark/tlc2/value/Randomization-1530276206-f9351808e.json b/tlatools/test-benchmark/tlc2/value/Randomization-1530276206-f9351808e.json new file mode 100644 index 0000000000000000000000000000000000000000..620024cfc1c2b143d7f3f404519e535020256760 --- /dev/null +++ b/tlatools/test-benchmark/tlc2/value/Randomization-1530276206-f9351808e.json @@ -0,0 +1,5480 @@ +[ + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.IntervalValueBenchmark.randomSubset", + "mode" : "thrpt", + "threads" : 1, + "forks" : 3, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + "-Xms32768m", + "-Xmx32768m" + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 3, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 3, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "params" : { + "numOfElements" : "10", + "size" : "16" + }, + "primaryMetric" : { + "score" : 49640.96317575774, + "scoreError" : 5313.823551389228, + "scoreConfidence" : [ + 44327.13962436851, + 54954.786727146966 + ], + "scorePercentiles" : { + "0.0" : 45299.63022275939, + "50.0" : 51707.01294679341, + "90.0" : 51792.0525170781, + "95.0" : 51792.0525170781, + "99.0" : 51792.0525170781, + "99.9" : 51792.0525170781, + "99.99" : 51792.0525170781, + "99.999" : 51792.0525170781, + "99.9999" : 51792.0525170781, + "100.0" : 51792.0525170781 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 45530.88567814011, + 51764.47020165688, + 51757.65739578748 + ], + [ + 45446.34432145206, + 51792.0525170781, + 51766.0839096055 + ], + [ + 45299.63022275939, + 51707.01294679341, + 51704.53138854678 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.IntervalValueBenchmark.randomSubset", + "mode" : "thrpt", + "threads" : 1, + "forks" : 3, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + "-Xms32768m", + "-Xmx32768m" + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 3, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 3, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "params" : { + "numOfElements" : "10", + "size" : "18" + }, + "primaryMetric" : { + "score" : 50092.47797235212, + "scoreError" : 5392.976153483466, + "scoreConfidence" : [ + 44699.50181886865, + 55485.45412583559 + ], + "scorePercentiles" : { + "0.0" : 45806.15583093199, + "50.0" : 52212.23315720261, + "90.0" : 52291.239598679516, + "95.0" : 52291.239598679516, + "99.0" : 52291.239598679516, + "99.9" : 52291.239598679516, + "99.99" : 52291.239598679516, + "99.999" : 52291.239598679516, + "99.9999" : 52291.239598679516, + "100.0" : 52291.239598679516 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 45818.19205297116, + 52231.680868925316, + 52221.134396231224 + ], + [ + 45806.15583093199, + 52291.239598679516, + 52237.194933707266 + ], + [ + 45816.40300074469, + 52212.23315720261, + 52198.06791177533 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.IntervalValueBenchmark.randomSubset", + "mode" : "thrpt", + "threads" : 1, + "forks" : 3, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + "-Xms32768m", + "-Xmx32768m" + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 3, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 3, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "params" : { + "numOfElements" : "10", + "size" : "20" + }, + "primaryMetric" : { + "score" : 49990.28972514267, + "scoreError" : 5502.797921246911, + "scoreConfidence" : [ + 44487.491803895755, + 55493.087646389584 + ], + "scorePercentiles" : { + "0.0" : 45511.00241759965, + "50.0" : 52126.89606655572, + "90.0" : 52233.40400921543, + "95.0" : 52233.40400921543, + "99.0" : 52233.40400921543, + "99.9" : 52233.40400921543, + "99.99" : 52233.40400921543, + "99.999" : 52233.40400921543, + "99.9999" : 52233.40400921543, + "100.0" : 52233.40400921543 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 45511.00241759965, + 52233.40400921543, + 52205.53649296292 + ], + [ + 45667.504109107984, + 52124.212991879795, + 52126.89606655572 + ], + [ + 45696.077246698456, + 52170.13722017491, + 52177.83697208921 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.IntervalValueBenchmark.randomSubset", + "mode" : "thrpt", + "threads" : 1, + "forks" : 3, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + "-Xms32768m", + "-Xmx32768m" + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 3, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 3, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "params" : { + "numOfElements" : "10", + "size" : "22" + }, + "primaryMetric" : { + "score" : 50193.049249178745, + "scoreError" : 5503.959207285861, + "scoreConfidence" : [ + 44689.09004189288, + 55697.00845646461 + ], + "scorePercentiles" : { + "0.0" : 45643.817888990605, + "50.0" : 52301.39674013717, + "90.0" : 52462.18236414704, + "95.0" : 52462.18236414704, + "99.0" : 52462.18236414704, + "99.9" : 52462.18236414704, + "99.99" : 52462.18236414704, + "99.999" : 52462.18236414704, + "99.9999" : 52462.18236414704, + "100.0" : 52462.18236414704 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 45892.976137266036, + 52462.18236414704, + 52453.20770855828 + ], + [ + 45643.817888990605, + 52291.433577618795, + 52301.39674013717 + ], + [ + 45947.118771708076, + 52390.05153155216, + 52355.258522630495 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.IntervalValueBenchmark.randomSubset", + "mode" : "thrpt", + "threads" : 1, + "forks" : 3, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + "-Xms32768m", + "-Xmx32768m" + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 3, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 3, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "params" : { + "numOfElements" : "12", + "size" : "16" + }, + "primaryMetric" : { + "score" : 12388.472482181454, + "scoreError" : 1310.6534167783188, + "scoreConfidence" : [ + 11077.819065403135, + 13699.125898959774 + ], + "scorePercentiles" : { + "0.0" : 11285.266335573237, + "50.0" : 12834.514397250188, + "90.0" : 13044.637481341451, + "95.0" : 13044.637481341451, + "99.0" : 13044.637481341451, + "99.9" : 13044.637481341451, + "99.99" : 13044.637481341451, + "99.999" : 13044.637481341451, + "99.9999" : 13044.637481341451, + "100.0" : 13044.637481341451 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 11285.266335573237, + 12834.514397250188, + 12829.567077442836 + ], + [ + 11314.962513672499, + 12837.195838018031, + 12834.883987297198 + ], + [ + 11470.809031188168, + 13044.41567784948, + 13044.637481341451 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.IntervalValueBenchmark.randomSubset", + "mode" : "thrpt", + "threads" : 1, + "forks" : 3, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + "-Xms32768m", + "-Xmx32768m" + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 3, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 3, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "params" : { + "numOfElements" : "12", + "size" : "18" + }, + "primaryMetric" : { + "score" : 12613.63279333138, + "scoreError" : 1320.2113558877807, + "scoreConfidence" : [ + 11293.4214374436, + 13933.844149219161 + ], + "scorePercentiles" : { + "0.0" : 11558.576256589442, + "50.0" : 13136.284044044995, + "90.0" : 13145.815445587406, + "95.0" : 13145.815445587406, + "99.0" : 13145.815445587406, + "99.9" : 13145.815445587406, + "99.99" : 13145.815445587406, + "99.999" : 13145.815445587406, + "99.9999" : 13145.815445587406, + "100.0" : 13145.815445587406 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 11562.562122602727, + 13121.216969485053, + 13142.682578357562 + ], + [ + 11577.397173511414, + 13136.564495516533, + 13145.815445587406 + ], + [ + 11558.576256589442, + 13136.284044044995, + 13141.596054287289 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.IntervalValueBenchmark.randomSubset", + "mode" : "thrpt", + "threads" : 1, + "forks" : 3, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + "-Xms32768m", + "-Xmx32768m" + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 3, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 3, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "params" : { + "numOfElements" : "12", + "size" : "20" + }, + "primaryMetric" : { + "score" : 12767.277008962783, + "scoreError" : 1350.7430977066049, + "scoreConfidence" : [ + 11416.533911256178, + 14118.020106669388 + ], + "scorePercentiles" : { + "0.0" : 11673.125520204727, + "50.0" : 13293.226993436885, + "90.0" : 13322.824248316305, + "95.0" : 13322.824248316305, + "99.0" : 13322.824248316305, + "99.9" : 13322.824248316305, + "99.99" : 13322.824248316305, + "99.999" : 13322.824248316305, + "99.9999" : 13322.824248316305, + "100.0" : 13322.824248316305 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 11682.42453794474, + 13322.824248316305, + 13305.14901056783 + ], + [ + 11731.931211941232, + 13310.804851354042, + 13298.118294577689 + ], + [ + 11673.125520204727, + 13293.226993436885, + 13287.888412321618 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.IntervalValueBenchmark.randomSubset", + "mode" : "thrpt", + "threads" : 1, + "forks" : 3, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + "-Xms32768m", + "-Xmx32768m" + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 3, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 3, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "params" : { + "numOfElements" : "12", + "size" : "22" + }, + "primaryMetric" : { + "score" : 12782.30988288717, + "scoreError" : 1349.2729930773378, + "scoreConfidence" : [ + 11433.036889809831, + 14131.582875964508 + ], + "scorePercentiles" : { + "0.0" : 11696.55425677517, + "50.0" : 13306.127044938077, + "90.0" : 13329.692162687668, + "95.0" : 13329.692162687668, + "99.0" : 13329.692162687668, + "99.9" : 13329.692162687668, + "99.99" : 13329.692162687668, + "99.999" : 13329.692162687668, + "99.9999" : 13329.692162687668, + "100.0" : 13329.692162687668 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 11708.654623917859, + 13323.82703106391, + 13314.724906111427 + ], + [ + 11730.355758753562, + 13329.692162687668, + 13326.110985122861 + ], + [ + 11696.55425677517, + 13304.742176614005, + 13306.127044938077 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.IntervalValueBenchmark.randomSubset", + "mode" : "thrpt", + "threads" : 1, + "forks" : 3, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + "-Xms32768m", + "-Xmx32768m" + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 3, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 3, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "params" : { + "numOfElements" : "14", + "size" : "16" + }, + "primaryMetric" : { + "score" : 3093.1495929008024, + "scoreError" : 346.74835334135383, + "scoreConfidence" : [ + 2746.4012395594486, + 3439.897946242156 + ], + "scorePercentiles" : { + "0.0" : 2811.640231155538, + "50.0" : 3196.7520849762427, + "90.0" : 3250.545388604313, + "95.0" : 3250.545388604313, + "99.0" : 3250.545388604313, + "99.9" : 3250.545388604313, + "99.99" : 3250.545388604313, + "99.999" : 3250.545388604313, + "99.9999" : 3250.545388604313, + "100.0" : 3250.545388604313 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 2826.116998686077, + 3250.545388604313, + 3247.768020740325 + ], + [ + 2820.5627690430083, + 3196.28472878314, + 3196.7520849762427 + ], + [ + 2811.640231155538, + 3244.312320960192, + 3244.3637931583835 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.IntervalValueBenchmark.randomSubset", + "mode" : "thrpt", + "threads" : 1, + "forks" : 3, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + "-Xms32768m", + "-Xmx32768m" + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 3, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 3, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "params" : { + "numOfElements" : "14", + "size" : "18" + }, + "primaryMetric" : { + "score" : 3122.0276611038216, + "scoreError" : 348.9807966037056, + "scoreConfidence" : [ + 2773.046864500116, + 3471.008457707527 + ], + "scorePercentiles" : { + "0.0" : 2843.861197248272, + "50.0" : 3225.4945702961986, + "90.0" : 3280.0974416640975, + "95.0" : 3280.0974416640975, + "99.0" : 3280.0974416640975, + "99.9" : 3280.0974416640975, + "99.99" : 3280.0974416640975, + "99.999" : 3280.0974416640975, + "99.9999" : 3280.0974416640975, + "100.0" : 3280.0974416640975 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 2843.861197248272, + 3225.4945702961986, + 3224.285841135945 + ], + [ + 2851.2312950983455, + 3280.0974416640975, + 3279.2880277145277 + ], + [ + 2844.767137452957, + 3275.2140152716156, + 3274.009424052435 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.IntervalValueBenchmark.randomSubset", + "mode" : "thrpt", + "threads" : 1, + "forks" : 3, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + "-Xms32768m", + "-Xmx32768m" + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 3, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 3, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "params" : { + "numOfElements" : "14", + "size" : "20" + }, + "primaryMetric" : { + "score" : 3128.182505713841, + "scoreError" : 352.70588064925437, + "scoreConfidence" : [ + 2775.476625064586, + 3480.8883863630954 + ], + "scorePercentiles" : { + "0.0" : 2847.3938357080215, + "50.0" : 3266.4757342822227, + "90.0" : 3270.3027372924234, + "95.0" : 3270.3027372924234, + "99.0" : 3270.3027372924234, + "99.9" : 3270.3027372924234, + "99.99" : 3270.3027372924234, + "99.999" : 3270.3027372924234, + "99.9999" : 3270.3027372924234, + "100.0" : 3270.3027372924234 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 2849.4282134610653, + 3268.9208437052844, + 3268.7159804614453 + ], + [ + 2848.1879022834896, + 3268.6771978438255, + 3270.3027372924234 + ], + [ + 2847.3938357080215, + 3266.4757342822227, + 3265.5401063867894 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.IntervalValueBenchmark.randomSubset", + "mode" : "thrpt", + "threads" : 1, + "forks" : 3, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + "-Xms32768m", + "-Xmx32768m" + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 3, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 3, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "params" : { + "numOfElements" : "14", + "size" : "22" + }, + "primaryMetric" : { + "score" : 3153.7636821728956, + "scoreError" : 348.28080992188865, + "scoreConfidence" : [ + 2805.482872251007, + 3502.0444920947843 + ], + "scorePercentiles" : { + "0.0" : 2872.890876914191, + "50.0" : 3291.3750004584786, + "90.0" : 3293.583354708205, + "95.0" : 3293.583354708205, + "99.0" : 3293.583354708205, + "99.9" : 3293.583354708205, + "99.99" : 3293.583354708205, + "99.999" : 3293.583354708205, + "99.9999" : 3293.583354708205, + "100.0" : 3293.583354708205 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 2881.1285187344015, + 3293.447094293651, + 3293.583354708205 + ], + [ + 2878.301292488309, + 3291.590790172244, + 3291.8708447988733 + ], + [ + 2872.890876914191, + 3289.685366987708, + 3291.3750004584786 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.IntervalValueBenchmark.randomSubset", + "mode" : "thrpt", + "threads" : 1, + "forks" : 3, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + "-Xms32768m", + "-Xmx32768m" + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 3, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 3, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "params" : { + "numOfElements" : "16", + "size" : "16" + }, + "primaryMetric" : { + "score" : 754.1516892747499, + "scoreError" : 79.62590658677267, + "scoreConfidence" : [ + 674.5257826879772, + 833.7775958615225 + ], + "scorePercentiles" : { + "0.0" : 689.815108835357, + "50.0" : 785.5121091682423, + "90.0" : 786.1593997389693, + "95.0" : 786.1593997389693, + "99.0" : 786.1593997389693, + "99.9" : 786.1593997389693, + "99.99" : 786.1593997389693, + "99.999" : 786.1593997389693, + "99.9999" : 786.1593997389693, + "100.0" : 786.1593997389693 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 689.815108835357, + 785.5741258481343, + 785.7560041624466 + ], + [ + 691.9066761852858, + 785.3832547314797, + 785.5121091682423 + ], + [ + 691.2113743557514, + 786.0471504470823, + 786.1593997389693 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.IntervalValueBenchmark.randomSubset", + "mode" : "thrpt", + "threads" : 1, + "forks" : 3, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + "-Xms32768m", + "-Xmx32768m" + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 3, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 3, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "params" : { + "numOfElements" : "16", + "size" : "18" + }, + "primaryMetric" : { + "score" : 755.2970915553086, + "scoreError" : 80.71289326564478, + "scoreConfidence" : [ + 674.5841982896638, + 836.0099848209534 + ], + "scorePercentiles" : { + "0.0" : 689.3261645820087, + "50.0" : 787.1481893418177, + "90.0" : 787.4757133907947, + "95.0" : 787.4757133907947, + "99.0" : 787.4757133907947, + "99.9" : 787.4757133907947, + "99.99" : 787.4757133907947, + "99.999" : 787.4757133907947, + "99.9999" : 787.4757133907947, + "100.0" : 787.4757133907947 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 692.9483094744046, + 787.3657829481007, + 787.4757133907947 + ], + [ + 689.3261645820087, + 787.3677945014489, + 787.4191934017078 + ], + [ + 691.5283032251681, + 787.094373132326, + 787.1481893418177 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.IntervalValueBenchmark.randomSubset", + "mode" : "thrpt", + "threads" : 1, + "forks" : 3, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + "-Xms32768m", + "-Xmx32768m" + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 3, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 3, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "params" : { + "numOfElements" : "16", + "size" : "20" + }, + "primaryMetric" : { + "score" : 762.6279992742304, + "scoreError" : 80.34724687095452, + "scoreConfidence" : [ + 682.2807524032759, + 842.975246145185 + ], + "scorePercentiles" : { + "0.0" : 697.3980478833844, + "50.0" : 794.3445633019713, + "90.0" : 795.0085959260741, + "95.0" : 795.0085959260741, + "99.0" : 795.0085959260741, + "99.9" : 795.0085959260741, + "99.99" : 795.0085959260741, + "99.999" : 795.0085959260741, + "99.9999" : 795.0085959260741, + "100.0" : 795.0085959260741 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 698.4229637307766, + 794.3445633019713, + 793.9489816526121 + ], + [ + 700.8456003388544, + 794.5945236436294, + 794.4315156786275 + ], + [ + 697.3980478833844, + 795.0085959260741, + 794.6572013121432 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.IntervalValueBenchmark.randomSubset", + "mode" : "thrpt", + "threads" : 1, + "forks" : 3, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + "-Xms32768m", + "-Xmx32768m" + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 3, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 3, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "params" : { + "numOfElements" : "16", + "size" : "22" + }, + "primaryMetric" : { + "score" : 764.3750602448353, + "scoreError" : 80.68894396184207, + "scoreConfidence" : [ + 683.6861162829932, + 845.0640042066773 + ], + "scorePercentiles" : { + "0.0" : 699.7014110614892, + "50.0" : 796.2602155288497, + "90.0" : 796.5952973580177, + "95.0" : 796.5952973580177, + "99.0" : 796.5952973580177, + "99.9" : 796.5952973580177, + "99.99" : 796.5952973580177, + "99.999" : 796.5952973580177, + "99.9999" : 796.5952973580177, + "100.0" : 796.5952973580177 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 701.0430727080461, + 796.5131224765947, + 796.2602155288497 + ], + [ + 699.7014110614892, + 796.5952973580177, + 796.4591521020396 + ], + [ + 700.3195823264297, + 796.4214650040959, + 796.0622236379558 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.SetEnumValueBenchmark.randomSubset", + "mode" : "thrpt", + "threads" : 1, + "forks" : 3, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + "-Xms32768m", + "-Xmx32768m" + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 3, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 3, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "params" : { + "numOfElements" : "10", + "size" : "16" + }, + "primaryMetric" : { + "score" : 49502.210903830506, + "scoreError" : 2180.6384847775116, + "scoreConfidence" : [ + 47321.572419052995, + 51682.84938860802 + ], + "scorePercentiles" : { + "0.0" : 47655.33428821115, + "50.0" : 50230.826123322026, + "90.0" : 50501.60093043969, + "95.0" : 50501.60093043969, + "99.0" : 50501.60093043969, + "99.9" : 50501.60093043969, + "99.99" : 50501.60093043969, + "99.999" : 50501.60093043969, + "99.9999" : 50501.60093043969, + "100.0" : 50501.60093043969 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 50391.905166447745, + 47844.68146107775, + 50442.44941352523 + ], + [ + 50478.94781912993, + 47841.85382307288, + 50501.60093043969 + ], + [ + 50132.29910924824, + 47655.33428821115, + 50230.826123322026 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.SetEnumValueBenchmark.randomSubset", + "mode" : "thrpt", + "threads" : 1, + "forks" : 3, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + "-Xms32768m", + "-Xmx32768m" + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 3, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 3, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "params" : { + "numOfElements" : "10", + "size" : "18" + }, + "primaryMetric" : { + "score" : 43388.646770714564, + "scoreError" : 1824.0070133783458, + "scoreConfidence" : [ + 41564.63975733622, + 45212.65378409291 + ], + "scorePercentiles" : { + "0.0" : 41908.68208838806, + "50.0" : 44109.81981657809, + "90.0" : 44426.17983992118, + "95.0" : 44426.17983992118, + "99.0" : 44426.17983992118, + "99.9" : 44426.17983992118, + "99.99" : 44426.17983992118, + "99.999" : 44426.17983992118, + "99.9999" : 44426.17983992118, + "100.0" : 44426.17983992118 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 42959.98334772449, + 41908.68208838806, + 44426.17983992118 + ], + [ + 44146.3158770916, + 41945.847090831056, + 44231.11069648557 + ], + [ + 44109.81981657809, + 42362.86628906258, + 44407.0158903484 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.SetEnumValueBenchmark.randomSubset", + "mode" : "thrpt", + "threads" : 1, + "forks" : 3, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + "-Xms32768m", + "-Xmx32768m" + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 3, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 3, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "params" : { + "numOfElements" : "12", + "size" : "16" + }, + "primaryMetric" : { + "score" : 13073.928567644358, + "scoreError" : 455.0289758555767, + "scoreConfidence" : [ + 12618.89959178878, + 13528.957543499935 + ], + "scorePercentiles" : { + "0.0" : 12698.65628972352, + "50.0" : 13248.105996963992, + "90.0" : 13266.864886797623, + "95.0" : 13266.864886797623, + "99.0" : 13266.864886797623, + "99.9" : 13266.864886797623, + "99.99" : 13266.864886797623, + "99.999" : 13266.864886797623, + "99.9999" : 13266.864886797623, + "100.0" : 13266.864886797623 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 13249.94147626679, + 12718.420804415366, + 13246.593618380437 + ], + [ + 13264.846389306727, + 12698.65628972352, + 13266.864886797623 + ], + [ + 13248.105996963992, + 12722.25928141193, + 13249.668365532834 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.SetEnumValueBenchmark.randomSubset", + "mode" : "thrpt", + "threads" : 1, + "forks" : 3, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + "-Xms32768m", + "-Xmx32768m" + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 3, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 3, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "params" : { + "numOfElements" : "12", + "size" : "18" + }, + "primaryMetric" : { + "score" : 12501.577464061656, + "scoreError" : 444.6915074792037, + "scoreConfidence" : [ + 12056.885956582453, + 12946.26897154086 + ], + "scorePercentiles" : { + "0.0" : 12037.42229550993, + "50.0" : 12638.508815275294, + "90.0" : 12718.100722495303, + "95.0" : 12718.100722495303, + "99.0" : 12718.100722495303, + "99.9" : 12718.100722495303, + "99.99" : 12718.100722495303, + "99.999" : 12718.100722495303, + "99.9999" : 12718.100722495303, + "100.0" : 12718.100722495303 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 12718.100722495303, + 12228.434844498648, + 12710.659628481108 + ], + [ + 12689.742493400452, + 12224.186682821746, + 12716.142434180676 + ], + [ + 12550.999259891745, + 12037.42229550993, + 12638.508815275294 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.SetEnumValueBenchmark.randomSubset", + "mode" : "thrpt", + "threads" : 1, + "forks" : 3, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + "-Xms32768m", + "-Xmx32768m" + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 3, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 3, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "params" : { + "numOfElements" : "14", + "size" : "16" + }, + "primaryMetric" : { + "score" : 3284.772083671136, + "scoreError" : 81.60408630341242, + "scoreConfidence" : [ + 3203.1679973677237, + 3366.3761699745482 + ], + "scorePercentiles" : { + "0.0" : 3189.1372114368223, + "50.0" : 3277.284569032633, + "90.0" : 3329.641950984068, + "95.0" : 3329.641950984068, + "99.0" : 3329.641950984068, + "99.9" : 3329.641950984068, + "99.99" : 3329.641950984068, + "99.999" : 3329.641950984068, + "99.9999" : 3329.641950984068, + "100.0" : 3329.641950984068 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 3327.848346895691, + 3275.004005396584, + 3255.6325331662183 + ], + [ + 3329.641950984068, + 3277.284569032633, + 3251.9750289858857 + ], + [ + 3327.825256708278, + 3328.599850434049, + 3189.1372114368223 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.SetEnumValueBenchmark.randomSubset", + "mode" : "thrpt", + "threads" : 1, + "forks" : 3, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + "-Xms32768m", + "-Xmx32768m" + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 3, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 3, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "params" : { + "numOfElements" : "14", + "size" : "18" + }, + "primaryMetric" : { + "score" : 3230.6479624325852, + "scoreError" : 94.90664496969116, + "scoreConfidence" : [ + 3135.741317462894, + 3325.5546074022764 + ], + "scorePercentiles" : { + "0.0" : 3153.754348280746, + "50.0" : 3267.453613803697, + "90.0" : 3273.684632912715, + "95.0" : 3273.684632912715, + "99.0" : 3273.684632912715, + "99.9" : 3273.684632912715, + "99.99" : 3273.684632912715, + "99.999" : 3273.684632912715, + "99.9999" : 3273.684632912715, + "100.0" : 3273.684632912715 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 3273.684632912715, + 3153.754348280746, + 3267.453613803697 + ], + [ + 3258.1025910932767, + 3156.428497056323, + 3271.285152033579 + ], + [ + 3267.8386497664164, + 3156.5328019876342, + 3270.7513749588757 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.SetEnumValueBenchmark.randomSubset", + "mode" : "thrpt", + "threads" : 1, + "forks" : 3, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + "-Xms32768m", + "-Xmx32768m" + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 3, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 3, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "params" : { + "numOfElements" : "16", + "size" : "16" + }, + "primaryMetric" : { + "score" : 823.4316998112048, + "scoreError" : 25.939794171726785, + "scoreConfidence" : [ + 797.491905639478, + 849.3714939829316 + ], + "scorePercentiles" : { + "0.0" : 802.397710235852, + "50.0" : 833.5045147381396, + "90.0" : 834.2711558096636, + "95.0" : 834.2711558096636, + "99.0" : 834.2711558096636, + "99.9" : 834.2711558096636, + "99.99" : 834.2711558096636, + "99.999" : 834.2711558096636, + "99.9999" : 834.2711558096636, + "100.0" : 834.2711558096636 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 833.3595645394191, + 833.7893620355246, + 803.4402352532005 + ], + [ + 833.5045147381396, + 833.6484430823392, + 802.7288730250546 + ], + [ + 833.7454395816497, + 834.2711558096636, + 802.397710235852 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.SetEnumValueBenchmark.randomSubset", + "mode" : "thrpt", + "threads" : 1, + "forks" : 3, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + "-Xms32768m", + "-Xmx32768m" + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 3, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 3, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "params" : { + "numOfElements" : "16", + "size" : "18" + }, + "primaryMetric" : { + "score" : 809.6322316499339, + "scoreError" : 16.253938423035617, + "scoreConfidence" : [ + 793.3782932268983, + 825.8861700729695 + ], + "scorePercentiles" : { + "0.0" : 796.0190033616673, + "50.0" : 811.8755776600925, + "90.0" : 819.4075214305818, + "95.0" : 819.4075214305818, + "99.0" : 819.4075214305818, + "99.9" : 819.4075214305818, + "99.99" : 819.4075214305818, + "99.999" : 819.4075214305818, + "99.9999" : 819.4075214305818, + "100.0" : 819.4075214305818 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 818.5031196447524, + 798.4081991109515, + 810.7415320198564 + ], + [ + 819.4075214305818, + 797.8467244104413, + 811.8755776600925 + ], + [ + 819.2693405867176, + 796.0190033616673, + 814.619066624345 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.SetOfFcnsBenchmark.randomSubset", + "mode" : "thrpt", + "threads" : 1, + "forks" : 3, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + "-Xms32768m", + "-Xmx32768m" + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 3, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 3, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "params" : { + "numOfElements" : "10", + "sizeS" : "8", + "sizeT" : "16" + }, + "primaryMetric" : { + "score" : 647.1867941761544, + "scoreError" : 3.7818910980181824, + "scoreConfidence" : [ + 643.4049030781363, + 650.9686852741726 + ], + "scorePercentiles" : { + "0.0" : 644.213016835251, + "50.0" : 647.9407506823107, + "90.0" : 649.5076908495989, + "95.0" : 649.5076908495989, + "99.0" : 649.5076908495989, + "99.9" : 649.5076908495989, + "99.99" : 649.5076908495989, + "99.999" : 649.5076908495989, + "99.9999" : 649.5076908495989, + "100.0" : 649.5076908495989 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 647.9407506823107, + 647.9116977621215, + 648.0067141135914 + ], + [ + 644.3013790717916, + 644.213016835251, + 644.3666218206417 + ], + [ + 649.2607806796373, + 649.1724957704467, + 649.5076908495989 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.SetOfFcnsBenchmark.randomSubset", + "mode" : "thrpt", + "threads" : 1, + "forks" : 3, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + "-Xms32768m", + "-Xmx32768m" + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 3, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 3, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "params" : { + "numOfElements" : "10", + "sizeS" : "16", + "sizeT" : "16" + }, + "primaryMetric" : { + "score" : 221.44655051938983, + "scoreError" : 13.909079062854891, + "scoreConfidence" : [ + 207.53747145653494, + 235.35562958224472 + ], + "scorePercentiles" : { + "0.0" : 204.8240311023435, + "50.0" : 224.0518031742482, + "90.0" : 228.35683319745365, + "95.0" : 228.35683319745365, + "99.0" : 228.35683319745365, + "99.9" : 228.35683319745365, + "99.99" : 228.35683319745365, + "99.999" : 228.35683319745365, + "99.9999" : 228.35683319745365, + "100.0" : 228.35683319745365 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 204.8240311023435, + 224.0518031742482, + 224.01327724454094 + ], + [ + 213.56829048978287, + 227.33864065781717, + 227.26232787518387 + ], + [ + 215.4858636393008, + 228.11788729383744, + 228.35683319745365 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.SetOfFcnsBenchmark.randomSubset", + "mode" : "thrpt", + "threads" : 1, + "forks" : 3, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + "-Xms32768m", + "-Xmx32768m" + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 3, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 3, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "params" : { + "numOfElements" : "10", + "sizeS" : "16", + "sizeT" : "32" + }, + "primaryMetric" : { + "score" : 222.37137540024008, + "scoreError" : 5.559984450616226, + "scoreConfidence" : [ + 216.81139094962387, + 227.9313598508563 + ], + "scorePercentiles" : { + "0.0" : 216.58675683600657, + "50.0" : 222.70165156756354, + "90.0" : 226.16307043469672, + "95.0" : 226.16307043469672, + "99.0" : 226.16307043469672, + "99.9" : 226.16307043469672, + "99.99" : 226.16307043469672, + "99.999" : 226.16307043469672, + "99.9999" : 226.16307043469672, + "100.0" : 226.16307043469672 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 216.58675683600657, + 224.1371309829792, + 224.10075995959227 + ], + [ + 220.35363962879737, + 226.1492340225272, + 226.16307043469672 + ], + [ + 218.4864923788299, + 222.66364279116806, + 222.70165156756354 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.SetOfFcnsBenchmark.randomSubset", + "mode" : "thrpt", + "threads" : 1, + "forks" : 3, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + "-Xms32768m", + "-Xmx32768m" + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 3, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 3, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "params" : { + "numOfElements" : "10", + "sizeS" : "46", + "sizeT" : "46" + }, + "primaryMetric" : { + "score" : 75.72740097592362, + "scoreError" : 3.4132048806359965, + "scoreConfidence" : [ + 72.31419609528761, + 79.14060585655962 + ], + "scorePercentiles" : { + "0.0" : 72.72645314188827, + "50.0" : 76.95022373203598, + "90.0" : 77.27206641844197, + "95.0" : 77.27206641844197, + "99.0" : 77.27206641844197, + "99.9" : 77.27206641844197, + "99.99" : 77.27206641844197, + "99.999" : 77.27206641844197, + "99.9999" : 77.27206641844197, + "100.0" : 77.27206641844197 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 72.94548866083792, + 76.9410767018892, + 76.98245623710076 + ], + [ + 73.43212452775425, + 76.95022373203598, + 77.03967211843509 + ], + [ + 72.72645314188827, + 77.27206641844197, + 77.25704724492914 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.SetOfFcnsBenchmark.randomSubset", + "mode" : "thrpt", + "threads" : 1, + "forks" : 3, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + "-Xms32768m", + "-Xmx32768m" + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 3, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 3, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "params" : { + "numOfElements" : "12", + "sizeS" : "8", + "sizeT" : "16" + }, + "primaryMetric" : { + "score" : 161.53980160918428, + "scoreError" : 1.870134697144832, + "scoreConfidence" : [ + 159.66966691203945, + 163.40993630632912 + ], + "scorePercentiles" : { + "0.0" : 159.03524580466686, + "50.0" : 161.70340128551297, + "90.0" : 162.49031781942435, + "95.0" : 162.49031781942435, + "99.0" : 162.49031781942435, + "99.9" : 162.49031781942435, + "99.99" : 162.49031781942435, + "99.999" : 162.49031781942435, + "99.9999" : 162.49031781942435, + "100.0" : 162.49031781942435 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 162.07445600022487, + 160.57583425222728, + 159.03524580466686 + ], + [ + 161.70340128551297, + 161.68507671480938, + 161.50123217365086 + ], + [ + 162.49031781942435, + 162.38764340457288, + 162.40500702756916 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.SetOfFcnsBenchmark.randomSubset", + "mode" : "thrpt", + "threads" : 1, + "forks" : 3, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + "-Xms32768m", + "-Xmx32768m" + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 3, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 3, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "params" : { + "numOfElements" : "12", + "sizeS" : "16", + "sizeT" : "16" + }, + "primaryMetric" : { + "score" : 53.46499347299082, + "scoreError" : 5.867414373810976, + "scoreConfidence" : [ + 47.597579099179846, + 59.33240784680179 + ], + "scorePercentiles" : { + "0.0" : 45.94204023414223, + "50.0" : 53.479703035485905, + "90.0" : 56.75676236356654, + "95.0" : 56.75676236356654, + "99.0" : 56.75676236356654, + "99.9" : 56.75676236356654, + "99.99" : 56.75676236356654, + "99.999" : 56.75676236356654, + "99.9999" : 56.75676236356654, + "100.0" : 56.75676236356654 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 45.94204023414223, + 53.36608236984878, + 53.34870923323005 + ], + [ + 53.479703035485905, + 56.75676236356654, + 56.74956202778669 + ], + [ + 50.30251209236314, + 55.63350741616807, + 55.60606248432599 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.SetOfFcnsBenchmark.randomSubset", + "mode" : "thrpt", + "threads" : 1, + "forks" : 3, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + "-Xms32768m", + "-Xmx32768m" + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 3, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 3, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "params" : { + "numOfElements" : "12", + "sizeS" : "16", + "sizeT" : "32" + }, + "primaryMetric" : { + "score" : 51.8439407388088, + "scoreError" : 4.484671845858238, + "scoreConfidence" : [ + 47.35926889295056, + 56.328612584667034 + ], + "scorePercentiles" : { + "0.0" : 47.27989289225459, + "50.0" : 53.492635674195995, + "90.0" : 53.757214799388535, + "95.0" : 53.757214799388535, + "99.0" : 53.757214799388535, + "99.9" : 53.757214799388535, + "99.99" : 53.757214799388535, + "99.999" : 53.757214799388535, + "99.9999" : 53.757214799388535, + "100.0" : 53.757214799388535 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 47.27989289225459, + 53.74038598329412, + 53.757214799388535 + ], + [ + 49.09660954034463, + 53.492635674195995, + 53.4736155993692 + ], + [ + 48.6573067020978, + 53.55710707161856, + 53.54069838671571 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.SetOfFcnsBenchmark.randomSubset", + "mode" : "thrpt", + "threads" : 1, + "forks" : 3, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + "-Xms32768m", + "-Xmx32768m" + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 3, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 3, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "params" : { + "numOfElements" : "12", + "sizeS" : "46", + "sizeT" : "46" + }, + "primaryMetric" : { + "score" : 18.90725915615425, + "scoreError" : 0.7804465567066122, + "scoreConfidence" : [ + 18.12681259944764, + 19.68770571286086 + ], + "scorePercentiles" : { + "0.0" : 18.083064307930712, + "50.0" : 19.14917876700788, + "90.0" : 19.249811875079445, + "95.0" : 19.249811875079445, + "99.0" : 19.249811875079445, + "99.9" : 19.249811875079445, + "99.99" : 19.249811875079445, + "99.999" : 19.249811875079445, + "99.9999" : 19.249811875079445, + "100.0" : 19.249811875079445 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 18.411711432236366, + 19.248074461825105, + 19.249811875079445 + ], + [ + 18.083064307930712, + 19.144228606307806, + 19.14917876700788 + ], + [ + 18.415538642092212, + 19.23014612115009, + 19.233578191758667 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.SetOfFcnsBenchmark.randomSubset", + "mode" : "thrpt", + "threads" : 1, + "forks" : 3, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + "-Xms32768m", + "-Xmx32768m" + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 3, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 3, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "params" : { + "numOfElements" : "14", + "sizeS" : "8", + "sizeT" : "16" + }, + "primaryMetric" : { + "score" : 39.624521240283165, + "scoreError" : 1.0488684170365838, + "scoreConfidence" : [ + 38.57565282324658, + 40.67338965731975 + ], + "scorePercentiles" : { + "0.0" : 39.125842021507026, + "50.0" : 39.28708732835848, + "90.0" : 40.46096128601025, + "95.0" : 40.46096128601025, + "99.0" : 40.46096128601025, + "99.9" : 40.46096128601025, + "99.99" : 40.46096128601025, + "99.999" : 40.46096128601025, + "99.9999" : 40.46096128601025, + "100.0" : 40.46096128601025 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 39.28708732835848, + 39.257636656375865, + 39.33546447082681 + ], + [ + 40.46096128601025, + 40.45441613380897, + 40.43723948355265 + ], + [ + 39.125842021507026, + 39.13170218795689, + 39.13034159415156 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.SetOfFcnsBenchmark.randomSubset", + "mode" : "thrpt", + "threads" : 1, + "forks" : 3, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + "-Xms32768m", + "-Xmx32768m" + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 3, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 3, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "params" : { + "numOfElements" : "14", + "sizeS" : "16", + "sizeT" : "16" + }, + "primaryMetric" : { + "score" : 13.274726190126739, + "scoreError" : 1.3039809195863328, + "scoreConfidence" : [ + 11.970745270540405, + 14.578707109713072 + ], + "scorePercentiles" : { + "0.0" : 11.624137750149469, + "50.0" : 13.414896159016187, + "90.0" : 13.918947078802928, + "95.0" : 13.918947078802928, + "99.0" : 13.918947078802928, + "99.9" : 13.918947078802928, + "99.99" : 13.918947078802928, + "99.999" : 13.918947078802928, + "99.9999" : 13.918947078802928, + "100.0" : 13.918947078802928 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 11.624137750149469, + 13.405266857198068, + 13.414896159016187 + ], + [ + 12.854352745998838, + 13.914432007900592, + 13.918947078802928 + ], + [ + 12.6445060460198, + 13.830851172745358, + 13.86514589330942 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.SetOfFcnsBenchmark.randomSubset", + "mode" : "thrpt", + "threads" : 1, + "forks" : 3, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + "-Xms32768m", + "-Xmx32768m" + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 3, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 3, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "params" : { + "numOfElements" : "14", + "sizeS" : "16", + "sizeT" : "32" + }, + "primaryMetric" : { + "score" : 13.461700151375673, + "scoreError" : 0.8943109617012777, + "scoreConfidence" : [ + 12.567389189674396, + 14.356011113076951 + ], + "scorePercentiles" : { + "0.0" : 12.216580937652516, + "50.0" : 13.474822827150453, + "90.0" : 13.885334874438747, + "95.0" : 13.885334874438747, + "99.0" : 13.885334874438747, + "99.9" : 13.885334874438747, + "99.99" : 13.885334874438747, + "99.999" : 13.885334874438747, + "99.9999" : 13.885334874438747, + "100.0" : 13.885334874438747 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 13.474822827150453, + 13.885334874438747, + 13.880361830891577 + ], + [ + 12.216580937652516, + 13.34324755612668, + 13.333659592726146 + ], + [ + 13.312995951652352, + 13.855167548603434, + 13.85313024313915 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.SetOfFcnsBenchmark.randomSubset", + "mode" : "thrpt", + "threads" : 1, + "forks" : 3, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + "-Xms32768m", + "-Xmx32768m" + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 3, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 3, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "params" : { + "numOfElements" : "14", + "sizeS" : "46", + "sizeT" : "46" + }, + "primaryMetric" : { + "score" : 4.785473823060737, + "scoreError" : 0.09157201524952041, + "scoreConfidence" : [ + 4.693901807811217, + 4.8770458383102575 + ], + "scorePercentiles" : { + "0.0" : 4.680392565751563, + "50.0" : 4.814195849833611, + "90.0" : 4.826405956632901, + "95.0" : 4.826405956632901, + "99.0" : 4.826405956632901, + "99.9" : 4.826405956632901, + "99.99" : 4.826405956632901, + "99.999" : 4.826405956632901, + "99.9999" : 4.826405956632901, + "100.0" : 4.826405956632901 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 4.825325890186347, + 4.826405956632901, + 4.824177954164531 + ], + [ + 4.7068701547087, + 4.816073932095836, + 4.814195849833611 + ], + [ + 4.680392565751563, + 4.788402722758373, + 4.787419381414775 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.SetOfFcnsBenchmark.randomSubset", + "mode" : "thrpt", + "threads" : 1, + "forks" : 3, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + "-Xms32768m", + "-Xmx32768m" + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 3, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 3, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "params" : { + "numOfElements" : "16", + "sizeS" : "8", + "sizeT" : "16" + }, + "primaryMetric" : { + "score" : 9.89982000722811, + "scoreError" : 0.33280867548870424, + "scoreConfidence" : [ + 9.567011331739405, + 10.232628682716815 + ], + "scorePercentiles" : { + "0.0" : 9.734914127138898, + "50.0" : 9.791439108813481, + "90.0" : 10.16822709776698, + "95.0" : 10.16822709776698, + "99.0" : 10.16822709776698, + "99.9" : 10.16822709776698, + "99.99" : 10.16822709776698, + "99.999" : 10.16822709776698, + "99.9999" : 10.16822709776698, + "100.0" : 10.16822709776698 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 9.734914127138898, + 9.7470755601293, + 9.758007599891789 + ], + [ + 9.784627853561423, + 9.791439108813481, + 9.79476878246331 + ], + [ + 10.163168560261175, + 10.156151375026647, + 10.16822709776698 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.SetOfFcnsBenchmark.randomSubset", + "mode" : "thrpt", + "threads" : 1, + "forks" : 3, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + "-Xms32768m", + "-Xmx32768m" + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 3, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 3, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "params" : { + "numOfElements" : "16", + "sizeS" : "16", + "sizeT" : "16" + }, + "primaryMetric" : { + "score" : 3.46851120656168, + "scoreError" : 0.15187211446914156, + "scoreConfidence" : [ + 3.3166390920925384, + 3.620383321030822 + ], + "scorePercentiles" : { + "0.0" : 3.3073638224575204, + "50.0" : 3.493397740973071, + "90.0" : 3.565974969542478, + "95.0" : 3.565974969542478, + "99.0" : 3.565974969542478, + "99.9" : 3.565974969542478, + "99.99" : 3.565974969542478, + "99.999" : 3.565974969542478, + "99.9999" : 3.565974969542478, + "100.0" : 3.565974969542478 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 3.347335173684363, + 3.4981533045812223, + 3.493397740973071 + ], + [ + 3.538081938209828, + 3.565974969542478, + 3.559943009691145 + ], + [ + 3.3073638224575204, + 3.4566397768670205, + 3.4497111230484716 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.SetOfFcnsBenchmark.randomSubset", + "mode" : "thrpt", + "threads" : 1, + "forks" : 3, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + "-Xms32768m", + "-Xmx32768m" + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 3, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 3, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "params" : { + "numOfElements" : "16", + "sizeS" : "16", + "sizeT" : "32" + }, + "primaryMetric" : { + "score" : 3.320243517610311, + "scoreError" : 0.2033555773696232, + "scoreConfidence" : [ + 3.1168879402406877, + 3.5235990949799345 + ], + "scorePercentiles" : { + "0.0" : 3.128674595620351, + "50.0" : 3.320454421215615, + "90.0" : 3.4454338235067277, + "95.0" : 3.4454338235067277, + "99.0" : 3.4454338235067277, + "99.9" : 3.4454338235067277, + "99.99" : 3.4454338235067277, + "99.999" : 3.4454338235067277, + "99.9999" : 3.4454338235067277, + "100.0" : 3.4454338235067277 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 3.136340117029289, + 3.320454421215615, + 3.3238979868107763 + ], + [ + 3.444734865360478, + 3.444531840232602, + 3.4454338235067277 + ], + [ + 3.128674595620351, + 3.3184243644982026, + 3.3196996442187574 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.SetOfFcnsBenchmark.randomSubset", + "mode" : "thrpt", + "threads" : 1, + "forks" : 3, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + "-Xms32768m", + "-Xmx32768m" + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 3, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 3, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "params" : { + "numOfElements" : "16", + "sizeS" : "46", + "sizeT" : "46" + }, + "primaryMetric" : { + "score" : 1.2002835531139824, + "scoreError" : 0.00981363481252792, + "scoreConfidence" : [ + 1.1904699183014544, + 1.2100971879265103 + ], + "scorePercentiles" : { + "0.0" : 1.1888399240420688, + "50.0" : 1.201983800331197, + "90.0" : 1.2066745006827952, + "95.0" : 1.2066745006827952, + "99.0" : 1.2066745006827952, + "99.9" : 1.2066745006827952, + "99.99" : 1.2066745006827952, + "99.999" : 1.2066745006827952, + "99.9999" : 1.2066745006827952, + "100.0" : 1.2066745006827952 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 1.2041308148128782, + 1.201983800331197, + 1.1958477616169232 + ], + [ + 1.2063945371173965, + 1.2008293589671861, + 1.1888399240420688 + ], + [ + 1.2066745006827952, + 1.202267767625606, + 1.1955835128297898 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.SubsetBenchmark.randomSetOfSubsets", + "mode" : "thrpt", + "threads" : 1, + "forks" : 3, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + "-Xms32768m", + "-Xmx32768m" + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 3, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 3, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "params" : { + "numOfElements" : "10", + "size" : "32" + }, + "primaryMetric" : { + "score" : 940.8855661086935, + "scoreError" : 3.4223536604405456, + "scoreConfidence" : [ + 937.463212448253, + 944.3079197691341 + ], + "scorePercentiles" : { + "0.0" : 937.7496835501817, + "50.0" : 941.8177828251877, + "90.0" : 942.7714879849624, + "95.0" : 942.7714879849624, + "99.0" : 942.7714879849624, + "99.9" : 942.7714879849624, + "99.99" : 942.7714879849624, + "99.999" : 942.7714879849624, + "99.9999" : 942.7714879849624, + "100.0" : 942.7714879849624 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 941.7459055359798, + 937.7496835501817, + 941.8177828251877 + ], + [ + 942.5585323480059, + 938.2784667462907, + 942.1225315535781 + ], + [ + 942.2929621202793, + 938.6327423137767, + 942.7714879849624 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.SubsetBenchmark.randomSetOfSubsets", + "mode" : "thrpt", + "threads" : 1, + "forks" : 3, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + "-Xms32768m", + "-Xmx32768m" + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 3, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 3, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "params" : { + "numOfElements" : "10", + "size" : "64" + }, + "primaryMetric" : { + "score" : 488.3817704649295, + "scoreError" : 0.9632956295974225, + "scoreConfidence" : [ + 487.41847483533206, + 489.34506609452694 + ], + "scorePercentiles" : { + "0.0" : 487.5965761315435, + "50.0" : 488.38102095368896, + "90.0" : 489.3325381106533, + "95.0" : 489.3325381106533, + "99.0" : 489.3325381106533, + "99.9" : 489.3325381106533, + "99.99" : 489.3325381106533, + "99.999" : 489.3325381106533, + "99.9999" : 489.3325381106533, + "100.0" : 489.3325381106533 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 489.3325381106533, + 488.25101439577145, + 488.452599571124 + ], + [ + 488.9109274511842, + 487.812757100369, + 487.5965761315435 + ], + [ + 488.83932517783984, + 488.38102095368896, + 487.85917529219154 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.SubsetBenchmark.randomSetOfSubsets", + "mode" : "thrpt", + "threads" : 1, + "forks" : 3, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + "-Xms32768m", + "-Xmx32768m" + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 3, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 3, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "params" : { + "numOfElements" : "10", + "size" : "128" + }, + "primaryMetric" : { + "score" : 242.61365637195175, + "scoreError" : 1.9134005293665914, + "scoreConfidence" : [ + 240.70025584258516, + 244.52705690131833 + ], + "scorePercentiles" : { + "0.0" : 241.26479708971638, + "50.0" : 242.34398252470044, + "90.0" : 244.24675229669347, + "95.0" : 244.24675229669347, + "99.0" : 244.24675229669347, + "99.9" : 244.24675229669347, + "99.99" : 244.24675229669347, + "99.999" : 244.24675229669347, + "99.9999" : 244.24675229669347, + "100.0" : 244.24675229669347 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 241.4397517030553, + 244.01334660280847, + 242.34398252470044 + ], + [ + 241.26479708971638, + 243.7866250915524, + 242.12992466789876 + ], + [ + 241.68321929043304, + 244.24675229669347, + 242.61450808070728 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.SubsetBenchmark.randomSetOfSubsets", + "mode" : "thrpt", + "threads" : 1, + "forks" : 3, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + "-Xms32768m", + "-Xmx32768m" + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 3, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 3, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "params" : { + "numOfElements" : "10", + "size" : "256" + }, + "primaryMetric" : { + "score" : 120.85955941053479, + "scoreError" : 3.7280365091521346, + "scoreConfidence" : [ + 117.13152290138265, + 124.58759591968692 + ], + "scorePercentiles" : { + "0.0" : 116.6453650373679, + "50.0" : 120.06786377622636, + "90.0" : 122.98037514837691, + "95.0" : 122.98037514837691, + "99.0" : 122.98037514837691, + "99.9" : 122.98037514837691, + "99.99" : 122.98037514837691, + "99.999" : 122.98037514837691, + "99.9999" : 122.98037514837691, + "100.0" : 122.98037514837691 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 119.52903855196647, + 122.9665550276074, + 122.93574806155075 + ], + [ + 119.69657382921002, + 122.98037514837691, + 122.8541072830955 + ], + [ + 116.6453650373679, + 120.06040797941165, + 120.06786377622636 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.SubsetBenchmark.randomSetOfSubsets", + "mode" : "thrpt", + "threads" : 1, + "forks" : 3, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + "-Xms32768m", + "-Xmx32768m" + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 3, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 3, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "params" : { + "numOfElements" : "12", + "size" : "32" + }, + "primaryMetric" : { + "score" : 236.15568531499392, + "scoreError" : 0.9343184697856747, + "scoreConfidence" : [ + 235.22136684520825, + 237.09000378477958 + ], + "scorePercentiles" : { + "0.0" : 235.18235796901774, + "50.0" : 236.27009630502064, + "90.0" : 236.71074512210538, + "95.0" : 236.71074512210538, + "99.0" : 236.71074512210538, + "99.9" : 236.71074512210538, + "99.99" : 236.71074512210538, + "99.999" : 236.71074512210538, + "99.9999" : 236.71074512210538, + "100.0" : 236.71074512210538 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 236.69176980161902, + 235.72484818055145, + 236.71074512210538 + ], + [ + 236.5139670958129, + 235.52594275864817, + 236.60991270130964 + ], + [ + 236.27009630502064, + 235.18235796901774, + 236.17152790086047 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.SubsetBenchmark.randomSetOfSubsets", + "mode" : "thrpt", + "threads" : 1, + "forks" : 3, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + "-Xms32768m", + "-Xmx32768m" + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 3, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 3, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "params" : { + "numOfElements" : "12", + "size" : "64" + }, + "primaryMetric" : { + "score" : 120.86574418461974, + "scoreError" : 0.5189869088755038, + "scoreConfidence" : [ + 120.34675727574424, + 121.38473109349525 + ], + "scorePercentiles" : { + "0.0" : 120.43954658603056, + "50.0" : 121.00004093431384, + "90.0" : 121.18853293499319, + "95.0" : 121.18853293499319, + "99.0" : 121.18853293499319, + "99.9" : 121.18853293499319, + "99.99" : 121.18853293499319, + "99.999" : 121.18853293499319, + "99.9999" : 121.18853293499319, + "100.0" : 121.18853293499319 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 120.4515814273751, + 120.43954658603056, + 120.51591497018322 + ], + [ + 121.11987061106079, + 121.03536572279732, + 120.91070696956106 + ], + [ + 121.18853293499319, + 121.13013750526268, + 121.00004093431384 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.SubsetBenchmark.randomSetOfSubsets", + "mode" : "thrpt", + "threads" : 1, + "forks" : 3, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + "-Xms32768m", + "-Xmx32768m" + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 3, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 3, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "params" : { + "numOfElements" : "12", + "size" : "128" + }, + "primaryMetric" : { + "score" : 59.50769061781115, + "scoreError" : 2.491321344548971, + "scoreConfidence" : [ + 57.01636927326218, + 61.99901196236012 + ], + "scorePercentiles" : { + "0.0" : 55.55904745390125, + "50.0" : 59.99867807312548, + "90.0" : 60.12785914176523, + "95.0" : 60.12785914176523, + "99.0" : 60.12785914176523, + "99.9" : 60.12785914176523, + "99.99" : 60.12785914176523, + "99.999" : 60.12785914176523, + "99.9999" : 60.12785914176523, + "100.0" : 60.12785914176523 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 59.95975139329583, + 59.99867807312548, + 55.55904745390125 + ], + [ + 59.9533037865786, + 59.86773438632764, + 60.034152404427 + ], + [ + 60.062748602814544, + 60.00594031806476, + 60.12785914176523 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.SubsetBenchmark.randomSetOfSubsets", + "mode" : "thrpt", + "threads" : 1, + "forks" : 3, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + "-Xms32768m", + "-Xmx32768m" + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 3, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 3, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "params" : { + "numOfElements" : "12", + "size" : "256" + }, + "primaryMetric" : { + "score" : 30.035431778783398, + "scoreError" : 0.7720115667275985, + "scoreConfidence" : [ + 29.2634202120558, + 30.807443345510997 + ], + "scorePercentiles" : { + "0.0" : 29.36866906331322, + "50.0" : 30.259863504658576, + "90.0" : 30.38089095732961, + "95.0" : 30.38089095732961, + "99.0" : 30.38089095732961, + "99.9" : 30.38089095732961, + "99.99" : 30.38089095732961, + "99.999" : 30.38089095732961, + "99.9999" : 30.38089095732961, + "100.0" : 30.38089095732961 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 29.36866906331322, + 30.257560790621415, + 30.259863504658576 + ], + [ + 29.524440119872914, + 30.377688781433207, + 30.378318426064762 + ], + [ + 29.393690138840228, + 30.377764226916675, + 30.38089095732961 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.SubsetBenchmark.randomSetOfSubsets", + "mode" : "thrpt", + "threads" : 1, + "forks" : 3, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + "-Xms32768m", + "-Xmx32768m" + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 3, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 3, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "params" : { + "numOfElements" : "14", + "size" : "32" + }, + "primaryMetric" : { + "score" : 57.78449169282468, + "scoreError" : 0.25174915581279583, + "scoreConfidence" : [ + 57.532742537011885, + 58.036240848637476 + ], + "scorePercentiles" : { + "0.0" : 57.514251611175986, + "50.0" : 57.76518675310032, + "90.0" : 57.942565521247296, + "95.0" : 57.942565521247296, + "99.0" : 57.942565521247296, + "99.9" : 57.942565521247296, + "99.99" : 57.942565521247296, + "99.999" : 57.942565521247296, + "99.9999" : 57.942565521247296, + "100.0" : 57.942565521247296 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 57.76518675310032, + 57.514251611175986, + 57.74584132456393 + ], + [ + 57.92848099338778, + 57.693013889485805, + 57.942565521247296 + ], + [ + 57.89925314595382, + 57.64918655421162, + 57.92264544229567 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.SubsetBenchmark.randomSetOfSubsets", + "mode" : "thrpt", + "threads" : 1, + "forks" : 3, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + "-Xms32768m", + "-Xmx32768m" + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 3, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 3, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "params" : { + "numOfElements" : "14", + "size" : "64" + }, + "primaryMetric" : { + "score" : 29.651491243464402, + "scoreError" : 0.12291674794846726, + "scoreConfidence" : [ + 29.528574495515937, + 29.77440799141287 + ], + "scorePercentiles" : { + "0.0" : 29.564685568469237, + "50.0" : 29.628667539981386, + "90.0" : 29.750422375906208, + "95.0" : 29.750422375906208, + "99.0" : 29.750422375906208, + "99.9" : 29.750422375906208, + "99.99" : 29.750422375906208, + "99.999" : 29.750422375906208, + "99.9999" : 29.750422375906208, + "100.0" : 29.750422375906208 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 29.564685568469237, + 29.571865226878806, + 29.583432053296068 + ], + [ + 29.712168387905113, + 29.750422375906208, + 29.628667539981386 + ], + [ + 29.70578937525705, + 29.72982764429422, + 29.61656301919153 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.SubsetBenchmark.randomSetOfSubsets", + "mode" : "thrpt", + "threads" : 1, + "forks" : 3, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + "-Xms32768m", + "-Xmx32768m" + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 3, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 3, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "params" : { + "numOfElements" : "14", + "size" : "128" + }, + "primaryMetric" : { + "score" : 14.966346077321589, + "scoreError" : 0.14083917460810721, + "scoreConfidence" : [ + 14.825506902713482, + 15.107185251929696 + ], + "scorePercentiles" : { + "0.0" : 14.870153350883559, + "50.0" : 14.965133247853384, + "90.0" : 15.071977759947723, + "95.0" : 15.071977759947723, + "99.0" : 15.071977759947723, + "99.9" : 15.071977759947723, + "99.99" : 15.071977759947723, + "99.999" : 15.071977759947723, + "99.9999" : 15.071977759947723, + "100.0" : 15.071977759947723 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 14.872909128391527, + 15.07088173326791, + 14.966466458528696 + ], + [ + 14.876996820055009, + 15.054670031738755, + 14.947926165227734 + ], + [ + 14.870153350883559, + 15.071977759947723, + 14.965133247853384 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.SubsetBenchmark.randomSetOfSubsets", + "mode" : "thrpt", + "threads" : 1, + "forks" : 3, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + "-Xms32768m", + "-Xmx32768m" + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 3, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 3, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "params" : { + "numOfElements" : "14", + "size" : "256" + }, + "primaryMetric" : { + "score" : 7.4929123396556365, + "scoreError" : 0.17711661607330295, + "scoreConfidence" : [ + 7.315795723582333, + 7.67002895572894 + ], + "scorePercentiles" : { + "0.0" : 7.344177741488789, + "50.0" : 7.560633453264906, + "90.0" : 7.5680383531645585, + "95.0" : 7.5680383531645585, + "99.0" : 7.5680383531645585, + "99.9" : 7.5680383531645585, + "99.99" : 7.5680383531645585, + "99.999" : 7.5680383531645585, + "99.9999" : 7.5680383531645585, + "100.0" : 7.5680383531645585 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 7.352927179314497, + 7.5680383531645585, + 7.568031280437031 + ], + [ + 7.344177741488789, + 7.558938968199607, + 7.561108925864961 + ], + [ + 7.360537627959725, + 7.560633453264906, + 7.561817527206654 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.SubsetBenchmark.randomSetOfSubsets", + "mode" : "thrpt", + "threads" : 1, + "forks" : 3, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + "-Xms32768m", + "-Xmx32768m" + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 3, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 3, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "params" : { + "numOfElements" : "16", + "size" : "32" + }, + "primaryMetric" : { + "score" : 14.12707487278992, + "scoreError" : 0.0705701121612911, + "scoreConfidence" : [ + 14.056504760628629, + 14.197644984951213 + ], + "scorePercentiles" : { + "0.0" : 14.045219248776148, + "50.0" : 14.12659745374599, + "90.0" : 14.174116369702018, + "95.0" : 14.174116369702018, + "99.0" : 14.174116369702018, + "99.9" : 14.174116369702018, + "99.99" : 14.174116369702018, + "99.999" : 14.174116369702018, + "99.9999" : 14.174116369702018, + "100.0" : 14.174116369702018 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 14.12659745374599, + 14.045219248776148, + 14.118116581295373 + ], + [ + 14.160373672620183, + 14.094305865194697, + 14.159211580439047 + ], + [ + 14.174116369702018, + 14.102596604618341, + 14.163136478717492 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.SubsetBenchmark.randomSetOfSubsets", + "mode" : "thrpt", + "threads" : 1, + "forks" : 3, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + "-Xms32768m", + "-Xmx32768m" + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 3, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 3, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "params" : { + "numOfElements" : "16", + "size" : "64" + }, + "primaryMetric" : { + "score" : 7.266381680744571, + "scoreError" : 0.0314834507050696, + "scoreConfidence" : [ + 7.2348982300395015, + 7.29786513144964 + ], + "scorePercentiles" : { + "0.0" : 7.242444363446909, + "50.0" : 7.267648124694403, + "90.0" : 7.288481059319786, + "95.0" : 7.288481059319786, + "99.0" : 7.288481059319786, + "99.9" : 7.288481059319786, + "99.99" : 7.288481059319786, + "99.999" : 7.288481059319786, + "99.9999" : 7.288481059319786, + "100.0" : 7.288481059319786 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 7.267648124694403, + 7.285342164764824, + 7.242444363446909 + ], + [ + 7.255021652408882, + 7.285224559232886, + 7.24294226646453 + ], + [ + 7.278377968993735, + 7.288481059319786, + 7.2519529673751855 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.SubsetBenchmark.randomSetOfSubsets", + "mode" : "thrpt", + "threads" : 1, + "forks" : 3, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + "-Xms32768m", + "-Xmx32768m" + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 3, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 3, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "params" : { + "numOfElements" : "16", + "size" : "128" + }, + "primaryMetric" : { + "score" : 3.6497554428776358, + "scoreError" : 0.03841688365133788, + "scoreConfidence" : [ + 3.611338559226298, + 3.6881723265289734 + ], + "scorePercentiles" : { + "0.0" : 3.6183155091861385, + "50.0" : 3.648096864564528, + "90.0" : 3.6841018017113205, + "95.0" : 3.6841018017113205, + "99.0" : 3.6841018017113205, + "99.9" : 3.6841018017113205, + "99.99" : 3.6841018017113205, + "99.999" : 3.6841018017113205, + "99.9999" : 3.6841018017113205, + "100.0" : 3.6841018017113205 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 3.634059338089468, + 3.6788480653647095, + 3.648096864564528 + ], + [ + 3.6183155091861385, + 3.6691475217581515, + 3.632623610621194 + ], + [ + 3.6340565733165495, + 3.6841018017113205, + 3.6485497012866586 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.SubsetBenchmark.randomSetOfSubsets", + "mode" : "thrpt", + "threads" : 1, + "forks" : 3, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + "-Xms32768m", + "-Xmx32768m" + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 3, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 3, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "params" : { + "numOfElements" : "16", + "size" : "256" + }, + "primaryMetric" : { + "score" : 1.8523095561784801, + "scoreError" : 0.030323853785498622, + "scoreConfidence" : [ + 1.8219857023929815, + 1.8826334099639788 + ], + "scorePercentiles" : { + "0.0" : 1.8239117503947395, + "50.0" : 1.8621334722675167, + "90.0" : 1.8673452589640651, + "95.0" : 1.8673452589640651, + "99.0" : 1.8673452589640651, + "99.9" : 1.8673452589640651, + "99.99" : 1.8673452589640651, + "99.999" : 1.8673452589640651, + "99.9999" : 1.8673452589640651, + "100.0" : 1.8673452589640651 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 1.8375132948596045, + 1.8673452589640651, + 1.8606371045545045 + ], + [ + 1.8253859071644025, + 1.8650899516618138, + 1.8663739030912672 + ], + [ + 1.8239117503947395, + 1.8621334722675167, + 1.8623953626484073 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.SubsetBenchmark.randomSetOfSubsets", + "mode" : "thrpt", + "threads" : 1, + "forks" : 3, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + "-Xms32768m", + "-Xmx32768m" + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 3, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 3, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "params" : { + "numOfElements" : "20", + "size" : "32" + }, + "primaryMetric" : { + "score" : 0.815405606395385, + "scoreError" : 0.007376600213618425, + "scoreConfidence" : [ + 0.8080290061817665, + 0.8227822066090034 + ], + "scorePercentiles" : { + "0.0" : 0.8100211392269732, + "50.0" : 0.8157161091242544, + "90.0" : 0.8222992984714158, + "95.0" : 0.8222992984714158, + "99.0" : 0.8222992984714158, + "99.9" : 0.8222992984714158, + "99.99" : 0.8222992984714158, + "99.999" : 0.8222992984714158, + "99.9999" : 0.8222992984714158, + "100.0" : 0.8222992984714158 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 0.8117869388228873, + 0.8145968267194525, + 0.8102262997624171 + ], + [ + 0.8100211392269732, + 0.8157161091242544, + 0.8162979185742621 + ], + [ + 0.8163625083929198, + 0.8213434184638821, + 0.8222992984714158 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.SubsetBenchmark.randomSetOfSubsets", + "mode" : "thrpt", + "threads" : 1, + "forks" : 3, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + "-Xms32768m", + "-Xmx32768m" + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 3, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 3, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "params" : { + "numOfElements" : "20", + "size" : "64" + }, + "primaryMetric" : { + "score" : 0.40328804844292354, + "scoreError" : 0.0028630303370206483, + "scoreConfidence" : [ + 0.4004250181059029, + 0.40615107877994416 + ], + "scorePercentiles" : { + "0.0" : 0.40163443030059487, + "50.0" : 0.40217276377360556, + "90.0" : 0.4061534001859155, + "95.0" : 0.4061534001859155, + "99.0" : 0.4061534001859155, + "99.9" : 0.4061534001859155, + "99.99" : 0.4061534001859155, + "99.999" : 0.4061534001859155, + "99.9999" : 0.4061534001859155, + "100.0" : 0.4061534001859155 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 0.40476771290873986, + 0.4037297178244125, + 0.4061534001859155 + ], + [ + 0.40215063632598, + 0.40163443030059487, + 0.4051522304851012 + ], + [ + 0.4021542415673392, + 0.4016773026146231, + 0.40217276377360556 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.SubsetBenchmark.randomSetOfSubsets", + "mode" : "thrpt", + "threads" : 1, + "forks" : 3, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + "-Xms32768m", + "-Xmx32768m" + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 3, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 3, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "params" : { + "numOfElements" : "20", + "size" : "128" + }, + "primaryMetric" : { + "score" : 0.21530835237739487, + "scoreError" : 0.00316882904311516, + "scoreConfidence" : [ + 0.2121395233342797, + 0.21847718142051004 + ], + "scorePercentiles" : { + "0.0" : 0.213179245449477, + "50.0" : 0.21498614336373545, + "90.0" : 0.21804435382366047, + "95.0" : 0.21804435382366047, + "99.0" : 0.21804435382366047, + "99.9" : 0.21804435382366047, + "99.99" : 0.21804435382366047, + "99.999" : 0.21804435382366047, + "99.9999" : 0.21804435382366047, + "100.0" : 0.21804435382366047 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 0.21361651479341479, + 0.21804435382366047, + 0.2153415105318766 + ], + [ + 0.213179245449477, + 0.21728117821674925, + 0.21455915572280176 + ], + [ + 0.21326258869209858, + 0.21750448080274012, + 0.21498614336373545 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.SubsetBenchmark.randomSetOfSubsets", + "mode" : "thrpt", + "threads" : 1, + "forks" : 3, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + "-Xms32768m", + "-Xmx32768m" + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 3, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 3, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "params" : { + "numOfElements" : "20", + "size" : "256" + }, + "primaryMetric" : { + "score" : 0.11238928761614583, + "scoreError" : 0.001728818027193669, + "scoreConfidence" : [ + 0.11066046958895216, + 0.1141181056433395 + ], + "scorePercentiles" : { + "0.0" : 0.11096554243148343, + "50.0" : 0.11236778839587218, + "90.0" : 0.11371225952719105, + "95.0" : 0.11371225952719105, + "99.0" : 0.11371225952719105, + "99.9" : 0.11371225952719105, + "99.99" : 0.11371225952719105, + "99.999" : 0.11371225952719105, + "99.9999" : 0.11371225952719105, + "100.0" : 0.11371225952719105 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 0.11096554243148343, + 0.11331316219066813, + 0.11236778839587218 + ], + [ + 0.11132446045043788, + 0.11371225952719105, + 0.11260766904717413 + ], + [ + 0.11141079827926309, + 0.11366691638640296, + 0.11213499183681985 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.SubsetBenchmark.randomSetOfSubsets", + "mode" : "thrpt", + "threads" : 1, + "forks" : 3, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + "-Xms32768m", + "-Xmx32768m" + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 3, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 3, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "params" : { + "numOfElements" : "24", + "size" : "32" + }, + "primaryMetric" : { + "score" : 0.04910733411972115, + "scoreError" : 9.919993036906951E-4, + "scoreConfidence" : [ + 0.048115334816030454, + 0.05009933342341185 + ], + "scorePercentiles" : { + "0.0" : 0.04785069270165027, + "50.0" : 0.04920083794793364, + "90.0" : 0.04974845748136814, + "95.0" : 0.04974845748136814, + "99.0" : 0.04974845748136814, + "99.9" : 0.04974845748136814, + "99.99" : 0.04974845748136814, + "99.999" : 0.04974845748136814, + "99.9999" : 0.04974845748136814, + "100.0" : 0.04974845748136814 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 0.04920083794793364, + 0.049013302202522674, + 0.04863697352455684 + ], + [ + 0.049574854456079974, + 0.04942860585264381, + 0.04974845748136814 + ], + [ + 0.04956659261874403, + 0.04785069270165027, + 0.04894569029199095 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.SubsetBenchmark.randomSetOfSubsets", + "mode" : "thrpt", + "threads" : 1, + "forks" : 3, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + "-Xms32768m", + "-Xmx32768m" + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 3, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 3, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "params" : { + "numOfElements" : "24", + "size" : "64" + }, + "primaryMetric" : { + "score" : 0.021996942226223695, + "scoreError" : 0.001223011827842711, + "scoreConfidence" : [ + 0.020773930398380984, + 0.023219954054066405 + ], + "scorePercentiles" : { + "0.0" : 0.021206605100633595, + "50.0" : 0.022204038901540282, + "90.0" : 0.02285143457296508, + "95.0" : 0.02285143457296508, + "99.0" : 0.02285143457296508, + "99.9" : 0.02285143457296508, + "99.99" : 0.02285143457296508, + "99.999" : 0.02285143457296508, + "99.9999" : 0.02285143457296508, + "100.0" : 0.02285143457296508 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 0.02285143457296508, + 0.021271412124217133, + 0.0222529103969887 + ], + [ + 0.02277063641256935, + 0.021206605100633595, + 0.021370956424092393 + ], + [ + 0.02281475408024271, + 0.021229732022763986, + 0.022204038901540282 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.SubsetBenchmark.randomSetOfSubsetsExact", + "mode" : "thrpt", + "threads" : 1, + "forks" : 3, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + "-Xms32768m", + "-Xmx32768m" + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 3, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 3, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "params" : { + "numOfElements" : "10", + "size" : "32" + }, + "primaryMetric" : { + "score" : 6386.565795976956, + "scoreError" : 792.1663122224614, + "scoreConfidence" : [ + 5594.399483754494, + 7178.732108199418 + ], + "scorePercentiles" : { + "0.0" : 5713.694702288845, + "50.0" : 6655.358764129061, + "90.0" : 6745.727252434273, + "95.0" : 6745.727252434273, + "99.0" : 6745.727252434273, + "99.9" : 6745.727252434273, + "99.99" : 6745.727252434273, + "99.999" : 6745.727252434273, + "99.9999" : 6745.727252434273, + "100.0" : 6745.727252434273 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 6699.219916941565, + 5768.823450309422, + 6703.286814783046 + ], + [ + 6741.156445050042, + 5797.684182713598, + 6745.727252434273 + ], + [ + 6655.358764129061, + 5713.694702288845, + 6654.140635142748 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.SubsetBenchmark.randomSetOfSubsetsExact", + "mode" : "thrpt", + "threads" : 1, + "forks" : 3, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + "-Xms32768m", + "-Xmx32768m" + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 3, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 3, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "params" : { + "numOfElements" : "10", + "size" : "64" + }, + "primaryMetric" : { + "score" : 5751.799729776909, + "scoreError" : 703.7751119214772, + "scoreConfidence" : [ + 5048.024617855432, + 6455.574841698386 + ], + "scorePercentiles" : { + "0.0" : 5188.826682026075, + "50.0" : 6027.003718632205, + "90.0" : 6041.565670269849, + "95.0" : 6041.565670269849, + "99.0" : 6041.565670269849, + "99.9" : 6041.565670269849, + "99.99" : 6041.565670269849, + "99.999" : 6041.565670269849, + "99.9999" : 6041.565670269849, + "100.0" : 6041.565670269849 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 6035.290118817631, + 5197.995356856098, + 6041.565670269849 + ], + [ + 6027.6365465635, + 5188.826682026075, + 6026.93874641699 + ], + [ + 6027.448315558002, + 5193.4924128518305, + 6027.003718632205 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.SubsetBenchmark.randomSetOfSubsetsExact", + "mode" : "thrpt", + "threads" : 1, + "forks" : 3, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + "-Xms32768m", + "-Xmx32768m" + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 3, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 3, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "params" : { + "numOfElements" : "10", + "size" : "128" + }, + "primaryMetric" : { + "score" : 5396.41858036665, + "scoreError" : 692.4767567843886, + "scoreConfidence" : [ + 4703.941823582261, + 6088.895337151038 + ], + "scorePercentiles" : { + "0.0" : 4717.085732529054, + "50.0" : 5449.325363271581, + "90.0" : 5804.387234518538, + "95.0" : 5804.387234518538, + "99.0" : 5804.387234518538, + "99.9" : 5804.387234518538, + "99.99" : 5804.387234518538, + "99.999" : 5804.387234518538, + "99.9999" : 5804.387234518538, + "100.0" : 5804.387234518538 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 5710.726353968836, + 4936.6454282045515, + 5710.563537849235 + ], + [ + 5443.704529230653, + 4717.085732529054, + 5449.325363271581 + ], + [ + 5795.56767079827, + 4999.761372929123, + 5804.387234518538 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.SubsetBenchmark.randomSetOfSubsetsExact", + "mode" : "thrpt", + "threads" : 1, + "forks" : 3, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + "-Xms32768m", + "-Xmx32768m" + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 3, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 3, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "params" : { + "numOfElements" : "10", + "size" : "256" + }, + "primaryMetric" : { + "score" : 5338.528833690778, + "scoreError" : 642.6865323262485, + "scoreConfidence" : [ + 4695.84230136453, + 5981.215366017026 + ], + "scorePercentiles" : { + "0.0" : 4816.211036391922, + "50.0" : 5592.290362015759, + "90.0" : 5598.237388965811, + "95.0" : 5598.237388965811, + "99.0" : 5598.237388965811, + "99.9" : 5598.237388965811, + "99.99" : 5598.237388965811, + "99.999" : 5598.237388965811, + "99.9999" : 5598.237388965811, + "100.0" : 5598.237388965811 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 5589.214694677729, + 4836.893728784345, + 5593.741175525523 + ], + [ + 5592.290362015759, + 4832.858876227758, + 5594.915771067337 + ], + [ + 5592.396469560819, + 4816.211036391922, + 5598.237388965811 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.SubsetBenchmark.randomSetOfSubsetsExact", + "mode" : "thrpt", + "threads" : 1, + "forks" : 3, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + "-Xms32768m", + "-Xmx32768m" + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 3, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 3, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "params" : { + "numOfElements" : "12", + "size" : "32" + }, + "primaryMetric" : { + "score" : 1445.2994799478993, + "scoreError" : 175.00246806454027, + "scoreConfidence" : [ + 1270.2970118833591, + 1620.3019480124394 + ], + "scorePercentiles" : { + "0.0" : 1304.453222626917, + "50.0" : 1512.8888905018887, + "90.0" : 1517.6364934622381, + "95.0" : 1517.6364934622381, + "99.0" : 1517.6364934622381, + "99.9" : 1517.6364934622381, + "99.99" : 1517.6364934622381, + "99.999" : 1517.6364934622381, + "99.9999" : 1517.6364934622381, + "100.0" : 1517.6364934622381 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 1512.8548350143578, + 1513.0402455594708, + 1304.453222626917 + ], + [ + 1512.8888905018887, + 1515.3646737654515, + 1305.1104620235824 + ], + [ + 1516.482476628759, + 1517.6364934622381, + 1309.8640199484262 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.SubsetBenchmark.randomSetOfSubsetsExact", + "mode" : "thrpt", + "threads" : 1, + "forks" : 3, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + "-Xms32768m", + "-Xmx32768m" + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 3, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 3, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "params" : { + "numOfElements" : "12", + "size" : "64" + }, + "primaryMetric" : { + "score" : 1315.0872776573538, + "scoreError" : 159.24200916012424, + "scoreConfidence" : [ + 1155.8452684972294, + 1474.329286817478 + ], + "scorePercentiles" : { + "0.0" : 1177.8433003668486, + "50.0" : 1366.6742876812843, + "90.0" : 1385.2593526169985, + "95.0" : 1385.2593526169985, + "99.0" : 1385.2593526169985, + "99.9" : 1385.2593526169985, + "99.99" : 1385.2593526169985, + "99.999" : 1385.2593526169985, + "99.9999" : 1385.2593526169985, + "100.0" : 1385.2593526169985 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 1382.8361977329685, + 1382.6820468439241, + 1195.6796392562026 + ], + [ + 1365.612489963466, + 1366.6742876812843, + 1177.8433003668486 + ], + [ + 1384.8586134304985, + 1385.2593526169985, + 1194.3395710239927 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.SubsetBenchmark.randomSetOfSubsetsExact", + "mode" : "thrpt", + "threads" : 1, + "forks" : 3, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + "-Xms32768m", + "-Xmx32768m" + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 3, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 3, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "params" : { + "numOfElements" : "12", + "size" : "128" + }, + "primaryMetric" : { + "score" : 1283.678005805807, + "scoreError" : 157.03079680154033, + "scoreConfidence" : [ + 1126.6472090042666, + 1440.7088026073475 + ], + "scorePercentiles" : { + "0.0" : 1153.4418641546772, + "50.0" : 1343.8744918106247, + "90.0" : 1347.442856430225, + "95.0" : 1347.442856430225, + "99.0" : 1347.442856430225, + "99.9" : 1347.442856430225, + "99.99" : 1347.442856430225, + "99.999" : 1347.442856430225, + "99.9999" : 1347.442856430225, + "100.0" : 1347.442856430225 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 1346.9373529042812, + 1347.442856430225, + 1164.7204691032007 + ], + [ + 1343.8744918106247, + 1342.9954214911315, + 1153.4418641546772 + ], + [ + 1347.0783197578448, + 1347.303553607001, + 1159.3077229932755 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.SubsetBenchmark.randomSetOfSubsetsExact", + "mode" : "thrpt", + "threads" : 1, + "forks" : 3, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + "-Xms32768m", + "-Xmx32768m" + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 3, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 3, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "params" : { + "numOfElements" : "12", + "size" : "256" + }, + "primaryMetric" : { + "score" : 1269.9493588394673, + "scoreError" : 152.09842493843254, + "scoreConfidence" : [ + 1117.8509339010348, + 1422.0477837779 + ], + "scorePercentiles" : { + "0.0" : 1147.258945682416, + "50.0" : 1326.3094960282574, + "90.0" : 1333.9773182234367, + "95.0" : 1333.9773182234367, + "99.0" : 1333.9773182234367, + "99.9" : 1333.9773182234367, + "99.99" : 1333.9773182234367, + "99.999" : 1333.9773182234367, + "99.9999" : 1333.9773182234367, + "100.0" : 1333.9773182234367 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 1330.937017988604, + 1331.781013712139, + 1151.3025128066724 + ], + [ + 1325.092552687089, + 1326.3094960282574, + 1147.258945682416 + ], + [ + 1333.430930831772, + 1333.9773182234367, + 1149.4544415948185 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.SubsetBenchmark.randomSetOfSubsetsExact", + "mode" : "thrpt", + "threads" : 1, + "forks" : 3, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + "-Xms32768m", + "-Xmx32768m" + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 3, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 3, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "params" : { + "numOfElements" : "14", + "size" : "32" + }, + "primaryMetric" : { + "score" : 331.4211691811984, + "scoreError" : 39.72591885175523, + "scoreConfidence" : [ + 291.6952503294432, + 371.14708803295366 + ], + "scorePercentiles" : { + "0.0" : 299.70654954457757, + "50.0" : 346.81718517560597, + "90.0" : 347.5654842338588, + "95.0" : 347.5654842338588, + "99.0" : 347.5654842338588, + "99.9" : 347.5654842338588, + "99.99" : 347.5654842338588, + "99.999" : 347.5654842338588, + "99.9999" : 347.5654842338588, + "100.0" : 347.5654842338588 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 347.5654842338588, + 347.3078636522035, + 299.70654954457757 + ], + [ + 346.81718517560597, + 347.5600492130343, + 300.24013083607304 + ], + [ + 347.02901204498386, + 346.8002041858344, + 299.7640437446145 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.SubsetBenchmark.randomSetOfSubsetsExact", + "mode" : "thrpt", + "threads" : 1, + "forks" : 3, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + "-Xms32768m", + "-Xmx32768m" + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 3, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 3, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "params" : { + "numOfElements" : "14", + "size" : "64" + }, + "primaryMetric" : { + "score" : 311.3392998224587, + "scoreError" : 8.092880027858497, + "scoreConfidence" : [ + 303.24641979460023, + 319.4321798503172 + ], + "scorePercentiles" : { + "0.0" : 303.864281154577, + "50.0" : 313.53343870279156, + "90.0" : 315.28847407610476, + "95.0" : 315.28847407610476, + "99.0" : 315.28847407610476, + "99.9" : 315.28847407610476, + "99.99" : 315.28847407610476, + "99.999" : 315.28847407610476, + "99.9999" : 315.28847407610476, + "100.0" : 315.28847407610476 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 313.53343870279156, + 313.52494012757575, + 306.9042783090912 + ], + [ + 314.64847282876036, + 314.63972158920654, + 304.42205790234715 + ], + [ + 315.228033711674, + 315.28847407610476, + 303.864281154577 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.SubsetBenchmark.randomSetOfSubsetsExact", + "mode" : "thrpt", + "threads" : 1, + "forks" : 3, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + "-Xms32768m", + "-Xmx32768m" + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 3, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 3, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "params" : { + "numOfElements" : "14", + "size" : "128" + }, + "primaryMetric" : { + "score" : 302.2836732171543, + "scoreError" : 4.149706655572463, + "scoreConfidence" : [ + 298.1339665615818, + 306.43337987272673 + ], + "scorePercentiles" : { + "0.0" : 299.0505978738112, + "50.0" : 303.12956903610376, + "90.0" : 304.64899373895605, + "95.0" : 304.64899373895605, + "99.0" : 304.64899373895605, + "99.9" : 304.64899373895605, + "99.99" : 304.64899373895605, + "99.999" : 304.64899373895605, + "99.9999" : 304.64899373895605, + "100.0" : 304.64899373895605 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 299.10203001349043, + 299.0505978738112, + 299.15578250184234 + ], + [ + 303.1047767742677, + 303.12956903610376, + 303.1836065480273 + ], + [ + 304.64899373895605, + 304.54986000188603, + 304.62784246600364 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.SubsetBenchmark.randomSetOfSubsetsExact", + "mode" : "thrpt", + "threads" : 1, + "forks" : 3, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + "-Xms32768m", + "-Xmx32768m" + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 3, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 3, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "params" : { + "numOfElements" : "14", + "size" : "256" + }, + "primaryMetric" : { + "score" : 300.95692329813187, + "scoreError" : 3.927290388673294, + "scoreConfidence" : [ + 297.0296329094586, + 304.88421368680514 + ], + "scorePercentiles" : { + "0.0" : 297.82178632724117, + "50.0" : 302.460585005198, + "90.0" : 302.6934983893305, + "95.0" : 302.6934983893305, + "99.0" : 302.6934983893305, + "99.9" : 302.6934983893305, + "99.99" : 302.6934983893305, + "99.999" : 302.6934983893305, + "99.9999" : 302.6934983893305, + "100.0" : 302.6934983893305 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 302.667066530584, + 302.6934983893305, + 302.6823762496941 + ], + [ + 302.460585005198, + 302.08539343860014, + 302.47158139038163 + ], + [ + 297.86992487307157, + 297.82178632724117, + 297.8600974790862 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.SubsetBenchmark.randomSetOfSubsetsExact", + "mode" : "thrpt", + "threads" : 1, + "forks" : 3, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + "-Xms32768m", + "-Xmx32768m" + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 3, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 3, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "params" : { + "numOfElements" : "16", + "size" : "32" + }, + "primaryMetric" : { + "score" : 74.50878265101892, + "scoreError" : 1.5659955259011837, + "scoreConfidence" : [ + 72.94278712511773, + 76.0747781769201 + ], + "scorePercentiles" : { + "0.0" : 73.18420589697276, + "50.0" : 75.0801265833059, + "90.0" : 75.19920151231858, + "95.0" : 75.19920151231858, + "99.0" : 75.19920151231858, + "99.9" : 75.19920151231858, + "99.99" : 75.19920151231858, + "99.999" : 75.19920151231858, + "99.9999" : 75.19920151231858, + "100.0" : 75.19920151231858 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 75.07250433493931, + 75.0801265833059, + 75.08953871319032 + ], + [ + 73.18420589697276, + 73.30250562624731, + 73.31900441517166 + ], + [ + 75.19920151231858, + 75.15335166079, + 75.17860511623435 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.SubsetBenchmark.randomSetOfSubsetsExact", + "mode" : "thrpt", + "threads" : 1, + "forks" : 3, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + "-Xms32768m", + "-Xmx32768m" + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 3, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 3, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "params" : { + "numOfElements" : "16", + "size" : "64" + }, + "primaryMetric" : { + "score" : 75.52429503392334, + "scoreError" : 0.5517480024585578, + "scoreConfidence" : [ + 74.97254703146479, + 76.07604303638189 + ], + "scorePercentiles" : { + "0.0" : 75.19908655669559, + "50.0" : 75.44663150488897, + "90.0" : 75.9517135006675, + "95.0" : 75.9517135006675, + "99.0" : 75.9517135006675, + "99.9" : 75.9517135006675, + "99.99" : 75.9517135006675, + "99.999" : 75.9517135006675, + "99.9999" : 75.9517135006675, + "100.0" : 75.9517135006675 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 75.35367838336924, + 75.4596740558918, + 75.44663150488897 + ], + [ + 75.93801711229243, + 75.93950246123045, + 75.9517135006675 + ], + [ + 75.19908655669559, + 75.21442172604338, + 75.21593000423054 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.SubsetBenchmark.randomSetOfSubsetsExact", + "mode" : "thrpt", + "threads" : 1, + "forks" : 3, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + "-Xms32768m", + "-Xmx32768m" + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 3, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 3, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "params" : { + "numOfElements" : "16", + "size" : "128" + }, + "primaryMetric" : { + "score" : 67.47125972839588, + "scoreError" : 0.9761457967212661, + "scoreConfidence" : [ + 66.49511393167461, + 68.44740552511715 + ], + "scorePercentiles" : { + "0.0" : 66.9589801118839, + "50.0" : 67.17805812799128, + "90.0" : 68.25316810917784, + "95.0" : 68.25316810917784, + "99.0" : 68.25316810917784, + "99.9" : 68.25316810917784, + "99.99" : 68.25316810917784, + "99.999" : 68.25316810917784, + "99.9999" : 68.25316810917784, + "100.0" : 68.25316810917784 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 68.23832315328102, + 68.22025948080903, + 68.25316810917784 + ], + [ + 67.20470000773832, + 67.17224019849884, + 67.17805812799128 + ], + [ + 66.99686263071861, + 66.9589801118839, + 67.01874573546412 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.SubsetBenchmark.randomSetOfSubsetsExact", + "mode" : "thrpt", + "threads" : 1, + "forks" : 3, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + "-Xms32768m", + "-Xmx32768m" + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 3, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 3, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "params" : { + "numOfElements" : "16", + "size" : "256" + }, + "primaryMetric" : { + "score" : 67.32137339767219, + "scoreError" : 0.6819868243441913, + "scoreConfidence" : [ + 66.639386573328, + 68.00336022201638 + ], + "scorePercentiles" : { + "0.0" : 66.89781446847066, + "50.0" : 67.22587345785756, + "90.0" : 67.83559662001014, + "95.0" : 67.83559662001014, + "99.0" : 67.83559662001014, + "99.9" : 67.83559662001014, + "99.99" : 67.83559662001014, + "99.999" : 67.83559662001014, + "99.9999" : 67.83559662001014, + "100.0" : 67.83559662001014 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 67.83559662001014, + 67.8322057260434, + 67.82536987354793 + ], + [ + 67.23765444299026, + 67.20703272536338, + 67.22587345785756 + ], + [ + 66.91126212224623, + 66.89781446847066, + 66.9195511425202 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.SubsetBenchmark.randomSetOfSubsetsExact", + "mode" : "thrpt", + "threads" : 1, + "forks" : 3, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + "-Xms32768m", + "-Xmx32768m" + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 3, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 3, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "params" : { + "numOfElements" : "20", + "size" : "32" + }, + "primaryMetric" : { + "score" : 3.9488767206617124, + "scoreError" : 0.01271426348096421, + "scoreConfidence" : [ + 3.9361624571807483, + 3.9615909841426764 + ], + "scorePercentiles" : { + "0.0" : 3.9395433532608974, + "50.0" : 3.9479157994483596, + "90.0" : 3.961011353696486, + "95.0" : 3.961011353696486, + "99.0" : 3.961011353696486, + "99.9" : 3.961011353696486, + "99.99" : 3.961011353696486, + "99.999" : 3.961011353696486, + "99.9999" : 3.961011353696486, + "100.0" : 3.961011353696486 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 3.9474670664367095, + 3.9407630752460774, + 3.941580998321083 + ], + [ + 3.951314216557007, + 3.9479157994483596, + 3.9395433532608974 + ], + [ + 3.961011353696486, + 3.9580669899343075, + 3.9522276330544845 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.SubsetBenchmark.randomSetOfSubsetsExact", + "mode" : "thrpt", + "threads" : 1, + "forks" : 3, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + "-Xms32768m", + "-Xmx32768m" + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 3, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 3, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "params" : { + "numOfElements" : "20", + "size" : "64" + }, + "primaryMetric" : { + "score" : 3.712183075627399, + "scoreError" : 0.004411977283529826, + "scoreConfidence" : [ + 3.707771098343869, + 3.716595052910929 + ], + "scorePercentiles" : { + "0.0" : 3.7088991397404043, + "50.0" : 3.71197990044313, + "90.0" : 3.716693076529153, + "95.0" : 3.716693076529153, + "99.0" : 3.716693076529153, + "99.9" : 3.716693076529153, + "99.99" : 3.716693076529153, + "99.999" : 3.716693076529153, + "99.9999" : 3.716693076529153, + "100.0" : 3.716693076529153 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 3.712300697056719, + 3.7088991397404043, + 3.712866800949536 + ], + [ + 3.7156716434094634, + 3.709451487937914, + 3.7103748198468636 + ], + [ + 3.716693076529153, + 3.711410114733411, + 3.71197990044313 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.SubsetBenchmark.randomSetOfSubsetsExact", + "mode" : "thrpt", + "threads" : 1, + "forks" : 3, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + "-Xms32768m", + "-Xmx32768m" + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 3, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 3, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "params" : { + "numOfElements" : "20", + "size" : "128" + }, + "primaryMetric" : { + "score" : 3.6510588192982802, + "scoreError" : 0.010158835052119179, + "scoreConfidence" : [ + 3.640899984246161, + 3.6612176543503994 + ], + "scorePercentiles" : { + "0.0" : 3.6417932096613206, + "50.0" : 3.652773141901331, + "90.0" : 3.659304341428677, + "95.0" : 3.659304341428677, + "99.0" : 3.659304341428677, + "99.9" : 3.659304341428677, + "99.99" : 3.659304341428677, + "99.999" : 3.659304341428677, + "99.9999" : 3.659304341428677, + "100.0" : 3.659304341428677 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 3.6417932096613206, + 3.6484140187033773, + 3.6427744353372957 + ], + [ + 3.6490259698619028, + 3.656541227931445, + 3.6532776508863405 + ], + [ + 3.652773141901331, + 3.659304341428677, + 3.65562537797283 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.SubsetBenchmark.randomSetOfSubsetsExact", + "mode" : "thrpt", + "threads" : 1, + "forks" : 3, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + "-Xms32768m", + "-Xmx32768m" + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 3, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 3, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "params" : { + "numOfElements" : "20", + "size" : "256" + }, + "primaryMetric" : { + "score" : 3.585564475367141, + "scoreError" : 0.05905549153815459, + "scoreConfidence" : [ + 3.5265089838289865, + 3.6446199669052954 + ], + "scorePercentiles" : { + "0.0" : 3.533524149742564, + "50.0" : 3.6065639777483764, + "90.0" : 3.6158764022769976, + "95.0" : 3.6158764022769976, + "99.0" : 3.6158764022769976, + "99.9" : 3.6158764022769976, + "99.99" : 3.6158764022769976, + "99.999" : 3.6158764022769976, + "99.9999" : 3.6158764022769976, + "100.0" : 3.6158764022769976 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 3.6065639777483764, + 3.6045966516377965, + 3.6119839861983194 + ], + [ + 3.6071137983733683, + 3.6067334754515645, + 3.6158764022769976 + ], + [ + 3.5447209034309646, + 3.5389669334443195, + 3.533524149742564 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.SubsetBenchmark.randomSetOfSubsetsExact", + "mode" : "thrpt", + "threads" : 1, + "forks" : 3, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + "-Xms32768m", + "-Xmx32768m" + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 3, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 3, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "params" : { + "numOfElements" : "24", + "size" : "32" + }, + "primaryMetric" : { + "score" : 0.20908264283795241, + "scoreError" : 0.005521587867919933, + "scoreConfidence" : [ + 0.2035610549700325, + 0.21460423070587234 + ], + "scorePercentiles" : { + "0.0" : 0.20496353683409937, + "50.0" : 0.20922435885392085, + "90.0" : 0.21325690252957974, + "95.0" : 0.21325690252957974, + "99.0" : 0.21325690252957974, + "99.9" : 0.21325690252957974, + "99.99" : 0.21325690252957974, + "99.999" : 0.21325690252957974, + "99.9999" : 0.21325690252957974, + "100.0" : 0.21325690252957974 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 0.20542256226005204, + 0.21325690252957974, + 0.20935781859286504 + ], + [ + 0.20496353683409937, + 0.21256935316767595, + 0.2086984266652402 + ], + [ + 0.20550737583398893, + 0.21274345080414955, + 0.20922435885392085 + ] + ] + }, + "secondaryMetrics" : { + } + }, + { + "jmhVersion" : "1.21", + "benchmark" : "tlc2.value.SubsetBenchmark.randomSetOfSubsetsExact", + "mode" : "thrpt", + "threads" : 1, + "forks" : 3, + "jvm" : "/usr/lib/jvm/java-8-oracle/jre/bin/java", + "jvmArgs" : [ + "-Xms32768m", + "-Xmx32768m" + ], + "jdkVersion" : "1.8.0_171", + "vmName" : "Java HotSpot(TM) 64-Bit Server VM", + "vmVersion" : "25.171-b11", + "warmupIterations" : 3, + "warmupTime" : "10 s", + "warmupBatchSize" : 1, + "measurementIterations" : 3, + "measurementTime" : "10 s", + "measurementBatchSize" : 1, + "params" : { + "numOfElements" : "24", + "size" : "64" + }, + "primaryMetric" : { + "score" : 0.1974683037696797, + "scoreError" : 0.010640093913706952, + "scoreConfidence" : [ + 0.18682820985597273, + 0.20810839768338665 + ], + "scorePercentiles" : { + "0.0" : 0.19000299496590883, + "50.0" : 0.19685709664617682, + "90.0" : 0.20514302078860988, + "95.0" : 0.20514302078860988, + "99.0" : 0.20514302078860988, + "99.9" : 0.20514302078860988, + "99.99" : 0.20514302078860988, + "99.999" : 0.20514302078860988, + "99.9999" : 0.20514302078860988, + "100.0" : 0.20514302078860988 + }, + "scoreUnit" : "ops/s", + "rawData" : [ + [ + 0.2050238255850441, + 0.19685709664617682, + 0.19055863675929088 + ], + [ + 0.20514302078860988, + 0.19650966439452058, + 0.19000299496590883 + ], + [ + 0.20504185082390858, + 0.19712856235839904, + 0.19094908160525834 + ] + ] + }, + "secondaryMetrics" : { + } + } +] + + diff --git a/tlatools/test-benchmark/tlc2/value/impl/EnumerateSubsetBenchmark.java b/tlatools/test-benchmark/tlc2/value/impl/EnumerateSubsetBenchmark.java new file mode 100644 index 0000000000000000000000000000000000000000..78995d3d950b6a33e8337988fe9ed0826a662003 --- /dev/null +++ b/tlatools/test-benchmark/tlc2/value/impl/EnumerateSubsetBenchmark.java @@ -0,0 +1,122 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.value.impl; + +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.Param; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.State; + +import tlc2.util.FP64; +import tlc2.value.RandomEnumerableValues; + +@State(Scope.Benchmark) +public class EnumerateSubsetBenchmark { + + static { + RandomEnumerableValues.setSeed(15041980L); + RandomEnumerableValues.reset(); + + FP64.Init(); + } + + @Param({"0", "1", "2", "3", "4", "8", "10", "12", "14", "16", "18", "19"}) + public int numOfElements; + + @Benchmark + public Enumerable elementsAlwaysNormalized() { + final IntervalValue inner = new IntervalValue(1, numOfElements); + final SubsetValue subset = new SubsetValue(inner); + + final ValueVec vals = new ValueVec(subset.size()); + final ValueEnumeration Enum = subset.elementsNormalized(); + Value elem; + while ((elem = Enum.nextElement()) != null) { + vals.addElement(elem); + } + return (Enumerable) new SetEnumValue(vals, true).normalize(); + } + + @Benchmark + public Enumerable kElementsNotNormalized() { + final IntervalValue inner = new IntervalValue(1, numOfElements); + final SubsetValue subset = new SubsetValue(inner); + + final ValueVec vec = new ValueVec(subset.size()); + for (int i = 0; i <= inner.size(); i++) { + final ValueEnumeration Enum = subset.kElements(i); + Value elem; + while ((elem = Enum.nextElement()) != null) { + vec.addElement(elem); + } + } + return (Enumerable) new SetEnumValue(vec, false); + } + + @Benchmark + public Enumerable kElementsNormalized() { + final IntervalValue inner = new IntervalValue(1, numOfElements); + final SubsetValue subset = new SubsetValue(inner); + + final ValueVec vec = new ValueVec(subset.size()); + for (int i = 0; i <= inner.size(); i++) { + final ValueEnumeration Enum = subset.kElements(i); + Value elem; + while ((elem = Enum.nextElement()) != null) { + vec.addElement(elem); + } + } + return (Enumerable) new SetEnumValue(vec, false).normalize(); + } + + @Benchmark + public Enumerable elementsNotNormalized() { + final IntervalValue inner = new IntervalValue(1, numOfElements); + final SubsetValue subset = new SubsetValue(inner); + + final ValueVec vals = new ValueVec(subset.size()); + final ValueEnumeration Enum = subset.elementsLexicographic(); + Value elem; + while ((elem = Enum.nextElement()) != null) { + vals.addElement(elem); + } + return (Enumerable) new SetEnumValue(vals, false); + } + + @Benchmark + public Enumerable elementsNormalized() { + final IntervalValue inner = new IntervalValue(1, numOfElements); + final SubsetValue subset = new SubsetValue(inner); + + final ValueVec vals = new ValueVec(subset.size()); + final ValueEnumeration Enum = subset.elementsLexicographic(); + Value elem; + while ((elem = Enum.nextElement()) != null) { + vals.addElement(elem); + } + return (Enumerable) new SetEnumValue(vals, false).normalize(); + } +} diff --git a/tlatools/test-benchmark/tlc2/value/impl/IntervalValueBenchmark.java b/tlatools/test-benchmark/tlc2/value/impl/IntervalValueBenchmark.java new file mode 100644 index 0000000000000000000000000000000000000000..6f7bd8f2de99ededd742fbef27ce3597228b80f0 --- /dev/null +++ b/tlatools/test-benchmark/tlc2/value/impl/IntervalValueBenchmark.java @@ -0,0 +1,67 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.value.impl; + +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.Level; +import org.openjdk.jmh.annotations.Param; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; + +import tlc2.util.FP64; +import tlc2.value.RandomEnumerableValues; +import tlc2.value.impl.Enumerable; +import tlc2.value.impl.IntervalValue; + +@State(Scope.Benchmark) +public class IntervalValueBenchmark { + + static { + RandomEnumerableValues.setSeed(15041980L); + RandomEnumerableValues.reset(); + + FP64.Init(); + } + + @Param({"16", "18", "20", "22"}) + public int size; + + @Param({"10", "12", "14", "16"}) + public int numOfElements; + + public Enumerable intervalValue; + + @Setup(Level.Invocation) + public void setup() { + intervalValue = (Enumerable) new IntervalValue(1, 1 << size).normalize(); + } + + @Benchmark + public Enumerable randomSubset() { + return intervalValue.getRandomSubset(1 << numOfElements); + } +} diff --git a/tlatools/test-benchmark/tlc2/value/impl/RandomizationBenchmark.java b/tlatools/test-benchmark/tlc2/value/impl/RandomizationBenchmark.java new file mode 100644 index 0000000000000000000000000000000000000000..5e168f4d590469ef0494387aef4148f409225c07 --- /dev/null +++ b/tlatools/test-benchmark/tlc2/value/impl/RandomizationBenchmark.java @@ -0,0 +1,522 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.value.impl; + +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.State; + +import tlc2.TLCGlobals; +import tlc2.util.FP64; +import tlc2.value.RandomEnumerableValues; +import tlc2.value.impl.Enumerable; +import tlc2.value.impl.IntValue; +import tlc2.value.impl.IntervalValue; +import tlc2.value.impl.SetEnumValue; +import tlc2.value.impl.SetOfFcnsValue; +import tlc2.value.impl.SubsetValue; + +@State(Scope.Benchmark) +public class RandomizationBenchmark { + + private static final Enumerable enum016; + private static final Enumerable enum032; + private static final Enumerable enum064; + private static final Enumerable enum128; + private static final Enumerable enum256; + private static final Enumerable enum512; + private static final Enumerable enum1024; + private static final Enumerable enum2048; + private static final Enumerable enum4096; + private static final Enumerable enum8192; + private static final Enumerable enum16384; + private static final Enumerable enum32768; + private static final Enumerable enumTLCBound; + + private static final Enumerable interval16; + private static final Enumerable interval20; + private static final Enumerable interval24; + private static final Enumerable interval28; + private static final Enumerable interval31; + + private static final int twoPow12 = (int) (Math.pow(2, 12) - 1); + private static final int twoPow08 = (int) (Math.pow(2, 8) - 1); + private static final int twoPow16 = (int) (Math.pow(2, 16) - 1); + private static final Enumerable fcns008x008; // 2^24 + private static final Enumerable fcns011x011; // ~2^31 + private static final Enumerable fcns016x008; + private static final Enumerable fcns016x016; // ~2^65 + private static final Enumerable fcns032x016; + private static final Enumerable fcns032x032; // ~2^150 + private static final Enumerable fcns048x048; // ~2^268 + + private static final SubsetValue subset2pow24; // 2^24 + private static final SubsetValue subset2pow31; // ~2^31 + private static final SubsetValue subset2pow65; // ~2^65 + private static final SubsetValue subset2pow150; // ~2^150 + private static final SubsetValue subset2pow268; // ~2^268 + + private static ValueVec getValues(int from, int to) { + final ValueVec vec = new ValueVec(to - from); + for (int i = from; i <= to; i++) { + vec.addElement(IntValue.gen(i)); + } + return vec; + } + + static { + RandomEnumerableValues.setSeed(15041980L); + RandomEnumerableValues.reset(); + + FP64.Init(); + + enum016 = new SetEnumValue(getValues(1, 16), true); + enum032 = new SetEnumValue(getValues(1, 32), true); + enum064 = new SetEnumValue(getValues(1, 64), true); + enum128 = new SetEnumValue(getValues(1, 128), true); + enum256 = new SetEnumValue(getValues(1, 256), true); + enum512 = new SetEnumValue(getValues(1, 512), true); + enum1024 = new SetEnumValue(getValues(1, 1024), true); + enum2048 = new SetEnumValue(getValues(1, 2048), true); + enum4096 = new SetEnumValue(getValues(1, 4096), true); + enum8192 = new SetEnumValue(getValues(1, 8192), true); + enum16384 = new SetEnumValue(getValues(1, 16384), true); + enum32768 = new SetEnumValue(getValues(1, 32768), true); + enumTLCBound = new SetEnumValue(getValues(1, TLCGlobals.setBound), true); + + interval16 = new IntervalValue(0, 2 << 16); + interval20 = new IntervalValue(0, 2 << 20); + interval24 = new IntervalValue(0, 2 << 24); + interval28 = new IntervalValue(0, 2 << 28); + interval31 = new IntervalValue(0, Integer.MAX_VALUE); // maximum possible value for internal ValueVec + + fcns008x008 = new SetOfFcnsValue(new SetEnumValue(getValues(1, 8), true), + new SetEnumValue(getValues(1, 8), true)); + fcns011x011 = new SetOfFcnsValue(new SetEnumValue(getValues(1, 11), true), + new SetEnumValue(getValues(1, 11), true)); + fcns016x008 = new SetOfFcnsValue(new SetEnumValue(getValues(1, 16), true), + new SetEnumValue(getValues(1, 8), true)); + fcns016x016 = new SetOfFcnsValue(new SetEnumValue(getValues(1, 16), true), + new SetEnumValue(getValues(1, 16), true)); + fcns032x016 = new SetOfFcnsValue(new SetEnumValue(getValues(1, 32), true), + new SetEnumValue(getValues(1, 16), true)); + fcns032x032 = new SetOfFcnsValue(new SetEnumValue(getValues(1, 32), true), + new SetEnumValue(getValues(1, 32), true)); + fcns048x048 = new SetOfFcnsValue(new SetEnumValue(getValues(1, 48), true), + new SetEnumValue(getValues(1, 48), true)); + + subset2pow24 = new SubsetValue(new IntervalValue(1, 24)); + subset2pow31 = new SubsetValue(new IntervalValue(1, 31)); + subset2pow65 = new SubsetValue(new IntervalValue(1, 65)); + subset2pow150 = new SubsetValue(new IntervalValue(1, 150)); + subset2pow268 = new SubsetValue(new IntervalValue(1, 268)); + } + + /* exact */ + + @Benchmark + public Enumerable randomSetOfSubsetExact024k008() { + return subset2pow24.getRandomSetOfSubsets(twoPow08, 10); + } + + @Benchmark + public Enumerable randomSetOfSubsetExact024k012() { + return subset2pow24.getRandomSetOfSubsets(twoPow12, 10); + } + + @Benchmark + public Enumerable randomSetOfSubsetExact024k016() { + return subset2pow24.getRandomSetOfSubsets(twoPow16, 10); + } + + @Benchmark + public Enumerable randomSetOfSubsetExact031k008() { + return subset2pow31.getRandomSetOfSubsets(twoPow08, 10); + } + + @Benchmark + public Enumerable randomSetOfSubsetExact031k012() { + return subset2pow31.getRandomSetOfSubsets(twoPow12, 10); + } + + @Benchmark + public Enumerable randomSetOfSubsetExact031k016() { + return subset2pow31.getRandomSetOfSubsets(twoPow16, 10); + } + + @Benchmark + public Enumerable randomSetOfSubsetExact65k008() { + return subset2pow65.getRandomSetOfSubsets(twoPow08, 10); + } + + @Benchmark + public Enumerable randomSetOfSubsetExact065k012() { + return subset2pow65.getRandomSetOfSubsets(twoPow12, 10); + } + + @Benchmark + public Enumerable randomSetOfSubsetExact065k016() { + return subset2pow65.getRandomSetOfSubsets(twoPow16, 10); + } + + @Benchmark + public Enumerable randomSetOfSubsetExact150k008() { + return subset2pow150.getRandomSetOfSubsets(twoPow08, 10); + } + + @Benchmark + public Enumerable randomSetOfSubsetExact150k012() { + return subset2pow150.getRandomSetOfSubsets(twoPow12, 10); + } + + @Benchmark + public Enumerable randomSetOfSubsetExact150k016() { + return subset2pow150.getRandomSetOfSubsets(twoPow16, 10); + } + + @Benchmark + public Enumerable randomSetOfSubsetExact268k008() { + return subset2pow268.getRandomSetOfSubsets(twoPow08, 10); + } + + @Benchmark + public Enumerable randomSetOfSubsetExact268k012() { + return subset2pow268.getRandomSetOfSubsets(twoPow12, 10); + } + + @Benchmark + public Enumerable randomSetOfSubsetExact268k016() { + return subset2pow268.getRandomSetOfSubsets(twoPow16, 10); + } + + /* probabilistic */ + + @Benchmark + public Enumerable randomSetOfSubset024k008() { + return subset2pow24.getRandomSetOfSubsets(twoPow08, .1d); + } + + @Benchmark + public Enumerable randomSetOfSubset024k012() { + return subset2pow24.getRandomSetOfSubsets(twoPow12, .1d); + } + + @Benchmark + public Enumerable randomSetOfSubset024k016() { + return subset2pow24.getRandomSetOfSubsets(twoPow16, .1d); + } + + @Benchmark + public Enumerable randomSetOfSubset031k008() { + return subset2pow31.getRandomSetOfSubsets(twoPow08, .1d); + } + + @Benchmark + public Enumerable randomSetOfSubset031k012() { + return subset2pow31.getRandomSetOfSubsets(twoPow12, .1d); + } + + @Benchmark + public Enumerable randomSetOfSubset031k016() { + return subset2pow31.getRandomSetOfSubsets(twoPow16, .1d); + } + + @Benchmark + public Enumerable randomSetOfSubset065k008() { + return subset2pow65.getRandomSetOfSubsets(twoPow08, .1d); + } + + @Benchmark + public Enumerable randomSetOfSubset065k012() { + return subset2pow65.getRandomSetOfSubsets(twoPow12, .1d); + } + + @Benchmark + public Enumerable randomSetOfSubset065k016() { + return subset2pow65.getRandomSetOfSubsets(twoPow16, .1d); + } + + @Benchmark + public Enumerable randomSetOfSubset150k008() { + return subset2pow150.getRandomSetOfSubsets(twoPow08, .1d); + } + + @Benchmark + public Enumerable randomSetOfSubset150k012() { + return subset2pow150.getRandomSetOfSubsets(twoPow12, .1d); + } + + @Benchmark + public Enumerable randomSetOfSubset150k016() { + return subset2pow150.getRandomSetOfSubsets(twoPow16, .1d); + } + + @Benchmark + public Enumerable randomSetOfSubset268k008() { + return subset2pow268.getRandomSetOfSubsets(twoPow08, .1d); + } + + @Benchmark + public Enumerable randomSetOfSubset268k012() { + return subset2pow268.getRandomSetOfSubsets(twoPow12, .1d); + } + + @Benchmark + public Enumerable randomSetOfSubset268k016() { + return subset2pow268.getRandomSetOfSubsets(twoPow16, .1d); + } + + /* IntervalValue */ + + @Benchmark + public Enumerable randomInterval016setBound() { + return interval16.getRandomSubset(TLCGlobals.setBound); + } + + @Benchmark + public Enumerable randomInterval020setBound() { + return interval20.getRandomSubset(TLCGlobals.setBound); + } + + @Benchmark + public Enumerable randomInterval024setBound() { + return interval24.getRandomSubset(TLCGlobals.setBound); + } + + @Benchmark + public Enumerable randomInterval028setBound() { + return interval28.getRandomSubset(TLCGlobals.setBound); + } + + @Benchmark + public Enumerable randomInterval031setBound() { + return interval31.getRandomSubset(TLCGlobals.setBound); + } + + @Benchmark + public Enumerable randomIntervalt016all() { + return interval16.getRandomSubset(interval16.size() - 1); + } + + @Benchmark + public Enumerable randomInterval020all() { + return interval20.getRandomSubset(interval20.size() - 1); + } + + @Benchmark + public Enumerable randomInterval024all() { + return interval24.getRandomSubset(interval24.size() - 1); + } + + @Benchmark + public Enumerable randomIntervalt016half() { + return interval16.getRandomSubset(interval16.size() / 2); + } + + @Benchmark + public Enumerable randomInterval020half() { + return interval20.getRandomSubset(interval20.size() / 2); + } + + @Benchmark + public Enumerable randomInterval024half() { + return interval24.getRandomSubset(interval24.size() / 2); + } + + /* SetEnumValue */ + + @Benchmark + public Enumerable randomSubset016() { + return enum016.getRandomSubset(enum016.size() - 1); + } + + @Benchmark + public Enumerable randomSubset032() { + return enum032.getRandomSubset(enum032.size() - 1); + } + + @Benchmark + public Enumerable randomSubset064() { + return enum064.getRandomSubset(enum064.size() - 1); + } + + @Benchmark + public Enumerable randomSubset128() { + return enum128.getRandomSubset(enum128.size() - 1); + } + + @Benchmark + public Enumerable randomSubset256() { + return enum256.getRandomSubset(enum256.size() - 1); + } + + @Benchmark + public Enumerable randomSubset512() { + return enum512.getRandomSubset(enum512.size() - 1); + } + + @Benchmark + public Enumerable randomSubset1024() { + return enum1024.getRandomSubset(enum1024.size() - 1); + } + + @Benchmark + public Enumerable randomSubset2048() { + return enum2048.getRandomSubset(enum2048.size() - 1); + } + + @Benchmark + public Enumerable randomSubset4096() { + return enum4096.getRandomSubset(enum4096.size() - 1); + } + + @Benchmark + public Enumerable randomSubset8192() { + return enum8192.getRandomSubset(enum8192.size() - 1); + } + + @Benchmark + public Enumerable randomSubset16384() { + return enum16384.getRandomSubset(enum16384.size() - 1); + } + + @Benchmark + public Enumerable randomSubset32768() { + return enum32768.getRandomSubset(enum32768.size() - 1); + } + + @Benchmark + public Enumerable randomSubsetTLCBound() { + return enumTLCBound.getRandomSubset(enumTLCBound.size() - 1); + } + + /* randomSubset with SetOfFcns */ + + @Benchmark + public Enumerable randomSubsetFcns008x008p16() { + return fcns008x008.getRandomSubset(twoPow16); + } + + @Benchmark + public Enumerable randomSubsetFcns008x008p08() { + return fcns008x008.getRandomSubset(twoPow08); + } + + @Benchmark + public Enumerable randomSubsetFcns008x008p12() { + return fcns008x008.getRandomSubset(twoPow12); + } + + @Benchmark + public Enumerable randomSubsetFcns011x011p16() { + return fcns008x008.getRandomSubset(twoPow16); + } + + @Benchmark + public Enumerable randomSubsetFcns011x011p08() { + return fcns011x011.getRandomSubset(twoPow08); + } + + @Benchmark + public Enumerable randomSubsetFcns011x011p12() { + return fcns011x011.getRandomSubset(twoPow12); + } + + @Benchmark + public Enumerable randomSubsetFcns016x008p16() { + return fcns011x011.getRandomSubset(twoPow16); + } + + @Benchmark + public Enumerable randomSubsetFcns016x008p08() { + return fcns016x008.getRandomSubset(twoPow08); + } + + @Benchmark + public Enumerable randomSubsetFcns016x008p12() { + return fcns016x008.getRandomSubset(twoPow12); + } + + @Benchmark + public Enumerable randomSubsetFcns016x016p16() { + return fcns016x016.getRandomSubset(twoPow16); + } + + @Benchmark + public Enumerable randomSubsetFcns016x016p08() { + return fcns016x016.getRandomSubset(twoPow08); + } + + @Benchmark + public Enumerable randomSubsetFcns016x016p12() { + return fcns016x016.getRandomSubset(twoPow12); + } + + @Benchmark + public Enumerable randomSubsetFcns032x016p16() { + return fcns032x016.getRandomSubset(twoPow16); + } + + @Benchmark + public Enumerable randomSubsetFcns032x016p08() { + return fcns032x016.getRandomSubset(twoPow08); + } + + @Benchmark + public Enumerable randomSubsetFcns032x016p12() { + return fcns032x016.getRandomSubset(twoPow12); + } + + @Benchmark + public Enumerable randomSubsetFcns032x032p16() { + return fcns032x032.getRandomSubset(twoPow16); + } + + @Benchmark + public Enumerable randomSubsetFcns032x032p08() { + return fcns032x032.getRandomSubset(twoPow08); + } + + @Benchmark + public Enumerable randomSubsetFcns032x032p12() { + return fcns032x032.getRandomSubset(twoPow12); + } + + @Benchmark + public Enumerable randomSubsetFcns048x048p16() { + return fcns048x048.getRandomSubset(twoPow16); + } + + @Benchmark + public Enumerable randomSubsetFcns048x048p08() { + return fcns048x048.getRandomSubset(twoPow08); + } + + @Benchmark + public Enumerable randomSubsetFcns048x048p12() { + return fcns048x048.getRandomSubset(twoPow12); + } +} diff --git a/tlatools/test-benchmark/tlc2/value/impl/SetEnumValueBenchmark.java b/tlatools/test-benchmark/tlc2/value/impl/SetEnumValueBenchmark.java new file mode 100644 index 0000000000000000000000000000000000000000..e0b853c4585f1fe58ec2e8e0b373a635bb1933be --- /dev/null +++ b/tlatools/test-benchmark/tlc2/value/impl/SetEnumValueBenchmark.java @@ -0,0 +1,76 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.value.impl; + +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.Level; +import org.openjdk.jmh.annotations.Param; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; + +import tlc2.util.FP64; +import tlc2.value.RandomEnumerableValues; +import tlc2.value.impl.Enumerable; +import tlc2.value.impl.IntValue; +import tlc2.value.impl.SetEnumValue; + +@State(Scope.Benchmark) +public class SetEnumValueBenchmark { + + static { + RandomEnumerableValues.setSeed(15041980L); + RandomEnumerableValues.reset(); + + FP64.Init(); + } + + private static ValueVec getValues(int from, int to) { + final ValueVec vec = new ValueVec(to - from); + for (int i = from; i <= to; i++) { + vec.addElement(IntValue.gen(i)); + } + return vec; + } + + @Param({"10", "12", "14", "16"}) + public int numOfElements; + + @Param({"16", "18", "20", "22"}) + public int size; + + public Enumerable setEnumValue; + + @Setup(Level.Invocation) + public void setup() { + setEnumValue = (Enumerable) new SetEnumValue(getValues(1, 1 << size), false).normalize(); + } + + @Benchmark + public Enumerable randomSubset() { + return setEnumValue.getRandomSubset(1 << numOfElements); + } +} diff --git a/tlatools/test-benchmark/tlc2/value/impl/SetOfFcnsBenchmark.java b/tlatools/test-benchmark/tlc2/value/impl/SetOfFcnsBenchmark.java new file mode 100644 index 0000000000000000000000000000000000000000..09ef16cd5b415dce356bf724bb3c8ed7d81a04ff --- /dev/null +++ b/tlatools/test-benchmark/tlc2/value/impl/SetOfFcnsBenchmark.java @@ -0,0 +1,84 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.value.impl; + +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.Level; +import org.openjdk.jmh.annotations.Param; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; + +import tlc2.util.FP64; +import tlc2.value.RandomEnumerableValues; + +@State(Scope.Benchmark) +public class SetOfFcnsBenchmark { + + static { + RandomEnumerableValues.setSeed(15041980L); + RandomEnumerableValues.reset(); + + FP64.Init(); + } + + @Param({"10", "12", "14", "16"}) + public int numOfElements; + + @Param({"16", "32", "46"}) + public int sizeT; + + @Param({"8", "16", "46"}) + public int sizeS; + + // 08x16 ~= 2^32 = 4294967296 + // 16x16 = 2^64 = 10^19 + // 16x32 ~= 2^128 ~= 10^38 + // 46x46 ~= 2^256 ~= 10^77 + + public Enumerable setOfFcns; + + @Setup(Level.Invocation) + public void setup() { + if ((sizeS == 8 && sizeT == 16) + || (sizeS == 16 && sizeT == 16) + || (sizeS == 16 && sizeT == 32) + || (sizeS == 46 && sizeT == 46)) { + final Value domain = new IntervalValue(1, sizeS); + final Value range = new IntervalValue(1, sizeT); + setOfFcns = (Enumerable) new SetOfFcnsValue(domain, range).normalize(); + } else { + // This appears to be the only way to skip permutations from the parameter space + // sizeS X sizeT X numOfElements. + System.exit(0); + } + } + + @Benchmark + public Enumerable randomSubset() { + return setOfFcns.getRandomSubset(1 << numOfElements); + } +} diff --git a/tlatools/test-benchmark/tlc2/value/impl/SubsetBenchmark.java b/tlatools/test-benchmark/tlc2/value/impl/SubsetBenchmark.java new file mode 100644 index 0000000000000000000000000000000000000000..54aa0060ae703eacdb29f86141dd141cf80b9abf --- /dev/null +++ b/tlatools/test-benchmark/tlc2/value/impl/SubsetBenchmark.java @@ -0,0 +1,80 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.value.impl; + +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.Level; +import org.openjdk.jmh.annotations.Param; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; + +import tlc2.util.FP64; +import tlc2.value.RandomEnumerableValues; +import tlc2.value.impl.Enumerable; +import tlc2.value.impl.IntervalValue; +import tlc2.value.impl.SubsetValue; + +@State(Scope.Benchmark) +public class SubsetBenchmark { + + static { + RandomEnumerableValues.setSeed(15041980L); + RandomEnumerableValues.reset(); + + FP64.Init(); + } + + @Param({"10", "12", "14", "16", "20", "24"}) + public int numOfElements; + + @Param({"32", "64", "128", "256"}) + public int size; + + public SubsetValue subset; + + @Setup(Level.Invocation) + public void setup() { + if (size < 128 || (size >= 128 && numOfElements <= 20)) { + subset = (SubsetValue) new SubsetValue(new IntervalValue(1, size)).normalize(); + } else { + // This appears to be the only way to skip permutations from the parameter space + // size X numOfElements. These permutations will go OOM or reach GC overhad + // limit anyway. + System.exit(0); + } + } + + @Benchmark + public Enumerable randomSetOfSubsets() { + return subset.getRandomSetOfSubsets(1 << numOfElements, .1d); + } + + @Benchmark + public Enumerable randomSetOfSubsetsExact() { + return subset.getRandomSetOfSubsets(1 << numOfElements, 10); + } +} diff --git a/tlatools/test-benchmark/tlc2/value/impl/SubsetValueBenchmark.java b/tlatools/test-benchmark/tlc2/value/impl/SubsetValueBenchmark.java new file mode 100644 index 0000000000000000000000000000000000000000..c2aecd266ce7a24a607468ffa8007b69adf12d40 --- /dev/null +++ b/tlatools/test-benchmark/tlc2/value/impl/SubsetValueBenchmark.java @@ -0,0 +1,315 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.value.impl; + +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.State; + +import tlc2.util.FP64; +import tlc2.value.RandomEnumerableValues; +import tlc2.value.impl.Enumerable; +import tlc2.value.impl.IntervalValue; +import tlc2.value.impl.SubsetValue; + +@State(Scope.Benchmark) +public class SubsetValueBenchmark { + + private static final SubsetValue subset35; + private static final SubsetValue subset60; + private static final SubsetValue subset100; + private static final SubsetValue subset200; + private static final SubsetValue subset300; + private static final SubsetValue subset400; + + private static final int k = 80000; + private static final double d = 0.1d; + private static final int k2 = 160000; + private static final double d2 = 0.2d; + + static { + RandomEnumerableValues.setSeed(15041980L); + RandomEnumerableValues.reset(); + + FP64.Init(); + + subset35 = new SubsetValue(new IntervalValue(1, 35)); + subset35.normalize(); + subset60 = new SubsetValue(new IntervalValue(1, 60)); + subset60.normalize(); + subset100 = new SubsetValue(new IntervalValue(1, 100)); + subset100.normalize(); + subset200 = new SubsetValue(new IntervalValue(1, 200)); + subset200.normalize(); + subset300 = new SubsetValue(new IntervalValue(1, 300)); + subset300.normalize(); + subset400 = new SubsetValue(new IntervalValue(1, 400)); + subset400.normalize(); + } + + @Benchmark + public Enumerable probabilisticN035k80d01() { + // ~49k subsets + return subset35.getRandomSetOfSubsets(k, d); + } + + @Benchmark + public Enumerable probabilisticN060k80d01() { + // ~76500 subsets + return subset60.getRandomSetOfSubsets(k, d); + } + + @Benchmark + public Enumerable probabilisticN100k80d01() { + // ~79900 + return subset100.getRandomSetOfSubsets(k, d); + } + + @Benchmark + public Enumerable probabilisticN200k80d01() { + // ~80k + return subset200.getRandomSetOfSubsets(k, d); + } + + @Benchmark + public Enumerable probabilisticN300k80d01() { + // ~80k + return subset300.getRandomSetOfSubsets(k, d); + } + + @Benchmark + public Enumerable probabilisticN400k80d01() { + // ~80k + return subset400.getRandomSetOfSubsets(k, d); + } + + @Benchmark + public Enumerable probabilisticN035k16d01() { + // ~73500 + return subset35.getRandomSetOfSubsets(k2, d); + } + + @Benchmark + public Enumerable probabilisticN060k16d01() { + // ~150k + return subset60.getRandomSetOfSubsets(k2, d); + } + + @Benchmark + public Enumerable probabilisticN100k16d01() { + // ~160k + return subset100.getRandomSetOfSubsets(k2, d); + } + + @Benchmark + public Enumerable probabilisticN200k16d01() { + // 160k + return subset200.getRandomSetOfSubsets(k2, d); + } + + @Benchmark + public Enumerable probabilisticN300k16d01() { + // 160k + return subset300.getRandomSetOfSubsets(k2, d); + } + + @Benchmark + public Enumerable probabilisticN400k16d01() { + // 160k + return subset400.getRandomSetOfSubsets(k2, d); + } + + /* d2 */ + + @Benchmark + public Enumerable probabilisticN035k80d02() { + // ~77600 + return subset35.getRandomSetOfSubsets(k, d2); + } + + @Benchmark + public Enumerable probabilisticN060k80d02() { + // 80k + return subset60.getRandomSetOfSubsets(k, d2); + } + + @Benchmark + public Enumerable probabilisticN100k80d02() { + // 80k + return subset100.getRandomSetOfSubsets(k, d2); + } + + @Benchmark + public Enumerable probabilisticN200k80d02() { + // 80k + return subset200.getRandomSetOfSubsets(k, d2); + } + + @Benchmark + public Enumerable probabilisticN300k80d02() { + // 80k + return subset300.getRandomSetOfSubsets(k, d2); + } + + @Benchmark + public Enumerable probabilisticN400k80d02() { + // 80k + return subset400.getRandomSetOfSubsets(k, d2); + } + + @Benchmark + public Enumerable probabilisticN035k16d02() { + // ~15200 + return subset35.getRandomSetOfSubsets(k2, d2); + } + + @Benchmark + public Enumerable probabilisticN060k16d02() { + // 160k + return subset60.getRandomSetOfSubsets(k2, d2); + } + + @Benchmark + public Enumerable probabilisticN100k16d02() { + // 160k + return subset100.getRandomSetOfSubsets(k2, d2); + } + + @Benchmark + public Enumerable probabilisticN200k16d02() { + // 160k + return subset200.getRandomSetOfSubsets(k2, d2); + } + + @Benchmark + public Enumerable probabilisticN300k16d02() { + // 160k + return subset300.getRandomSetOfSubsets(k2, d2); + } + + @Benchmark + public Enumerable probabilisticN400k16d02() { + // 160k + return subset400.getRandomSetOfSubsets(k2, d2); + } + + /* Exact getRandomSetOfSubsets */ + + @Benchmark + public Enumerable exactN035K08() { + return subset35.getRandomSetOfSubsets(k, 8); + } + + @Benchmark + public Enumerable exactN035K13() { + return subset35.getRandomSetOfSubsets(k, 13); + } + + @Benchmark + public Enumerable exactN060K08() { + return subset60.getRandomSetOfSubsets(k, 8); + } + + @Benchmark + public Enumerable exactN100K08() { + return subset100.getRandomSetOfSubsets(k, 8); + } + + @Benchmark + public Enumerable exactN100K10() { + return subset100.getRandomSetOfSubsets(k, 10); + } + + @Benchmark + public Enumerable exactN200K10() { + return subset200.getRandomSetOfSubsets(k, 10); + } + + @Benchmark + public Enumerable exactN300K09() { + return subset300.getRandomSetOfSubsets(k, 9); + } + + @Benchmark + public Enumerable exactN400K09() { + return subset400.getRandomSetOfSubsets(k, 9); + } + + /* k2 */ + + @Benchmark + public Enumerable exactN035K208() { + return subset35.getRandomSetOfSubsets(k2, 8); + } + + @Benchmark + public Enumerable exactN035K213() { + return subset35.getRandomSetOfSubsets(k2, 13); + } + + @Benchmark + public Enumerable exactN060K208() { + return subset60.getRandomSetOfSubsets(k2, 8); + } + + @Benchmark + public Enumerable exactN100K208() { + return subset100.getRandomSetOfSubsets(k2, 8); + } + + @Benchmark + public Enumerable exactN100K210() { + return subset100.getRandomSetOfSubsets(k2, 10); + } + + @Benchmark + public Enumerable exactN200K210() { + return subset200.getRandomSetOfSubsets(k2, 10); + } + + @Benchmark + public Enumerable exactN300K209() { + return subset300.getRandomSetOfSubsets(k2, 9); + } + + @Benchmark + public Enumerable exactN400K209() { + return subset400.getRandomSetOfSubsets(k2, 9); + } +} + +/* + * Mode.Throughput: Calculate number of operations in a time unit. (Higher score + * better) Mode.AverageTime: Calculate an average running time. (Lower score + * better) Mode.SampleTime: Calculate how long does it take for a method to run + * (including percentiles). Mode.SingleShotTime: Just run a method once (useful + * for cold-testing mode). Or more than once if you have specified a batch size + * for your iterations (see @Measurement annotation below) - in this case JMH + * will calculate the batch running time (total time for all invocations in a + * batch). Any set of these modes You can specify any set of these modes - the + * test will be run several times (depending on number of requested modes). + * Mode.All: All these modes one after another. + */ \ No newline at end of file diff --git a/tlatools/test-concurrent/tlc2/tool/fp/ConcurrentWriteTest.java b/tlatools/test-concurrent/tlc2/tool/fp/ConcurrentWriteTest.java new file mode 100644 index 0000000000000000000000000000000000000000..804f6c81c18e13b610a378cbb67835f3ab4b3408 --- /dev/null +++ b/tlatools/test-concurrent/tlc2/tool/fp/ConcurrentWriteTest.java @@ -0,0 +1,227 @@ +// Copyright (c) 2016 Markus Alexander Kuppe. All rights reserved. +package tlc2.tool.fp; + +import static org.junit.Assert.assertEquals; + +import java.io.File; +import java.io.IOException; +import java.io.RandomAccessFile; +import java.nio.ByteBuffer; +import java.nio.channels.FileChannel; +import java.util.ArrayList; +import java.util.Collection; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +import org.junit.Test; + +import tlc2.util.BufferedRandomAccessFile; + +public class ConcurrentWriteTest { + @Test + public void test() throws IOException { + final File tempFile = File.createTempFile("ConcurrentWriteTest_test", ".bin"); + + RandomAccessFile tmpRAF0 = new BufferedRandomAccessFile(tempFile, "rw"); + tmpRAF0.setLength(4000L * Long.BYTES); + for(long i = 0L; i < 1000; i++) { + tmpRAF0.writeLong(i); + } + + for(long i = 1000L; i < 2000; i++) { + tmpRAF0.writeLong(i); + } + + for(long i = 2000L; i < 3000; i++) { + tmpRAF0.writeLong(i); + } + + for(long i = 3000L; i < 4000; i++) { + tmpRAF0.writeLong(i); + } + + tmpRAF0.close(); + + final RandomAccessFile tmpRAF = new BufferedRandomAccessFile(tempFile, "r"); + for(long i = 0L; i < 4000; i++) { + assertEquals(i, tmpRAF.readLong()); + } + tmpRAF.close(); + } + + @Test + public void test1() throws IOException { + final File tempFile = File.createTempFile("ConcurrentWriteTest_test1", ".bin"); + final long limit = 4000000L; + final long partition = limit / 4L; + + final RandomAccessFile tmpRAF0 = new BufferedRandomAccessFile(tempFile, "rw"); + tmpRAF0.setLength(limit * Long.BYTES); + for(long i = 0L; i < partition; i++) { + tmpRAF0.writeLong(i); + } + + final RandomAccessFile tmpRAF1 = new BufferedRandomAccessFile(tempFile, "rw"); + tmpRAF1.setLength(limit * Long.BYTES); + tmpRAF1.seek(partition * Long.BYTES); + for (long i = partition; i < (2L * partition); i++) { + tmpRAF1.writeLong(i); + } + + final RandomAccessFile tmpRAF2 = new BufferedRandomAccessFile(tempFile, "rw"); + tmpRAF2.setLength(limit * Long.BYTES); + tmpRAF2.seek((2L * partition) * Long.BYTES); + for (long i = (2L * partition); i < (3L * partition); i++) { + tmpRAF2.writeLong(i); + } + + final RandomAccessFile tmpRAF3 = new BufferedRandomAccessFile(tempFile, "rw"); + tmpRAF3.setLength(limit * Long.BYTES); + tmpRAF3.seek((3L * partition) * Long.BYTES); + for (long i = (3L * partition); i < (4L * partition); i++) { + tmpRAF3.writeLong(i); + } + + tmpRAF0.close(); + tmpRAF1.close(); + tmpRAF2.close(); + tmpRAF3.close(); + + final RandomAccessFile tmpRAF = new BufferedRandomAccessFile(tempFile, "r"); + for(long i = 0L; i < limit; i++) { + assertEquals(i, tmpRAF.readLong()); + } + tmpRAF.close(); + } + + @Test + public void test2() throws IOException { + final File tempFile = File.createTempFile("ConcurrentWriteTest_test2", ".bin"); + final long limit = 4000000L; + final long partition = limit / 4L; + + final RandomAccessFile tmpRAF0 = new BufferedRandomAccessFile(tempFile, "rw"); + tmpRAF0.setLength(limit * Long.BYTES); + + final RandomAccessFile tmpRAF1 = new BufferedRandomAccessFile(tempFile, "rw"); + tmpRAF1.setLength(limit * Long.BYTES); + tmpRAF1.seek(partition * Long.BYTES); + + final RandomAccessFile tmpRAF2 = new BufferedRandomAccessFile(tempFile, "rw"); + tmpRAF2.setLength(limit * Long.BYTES); + tmpRAF2.seek((2L * partition) * Long.BYTES); + + final RandomAccessFile tmpRAF3 = new BufferedRandomAccessFile(tempFile, "rw"); + tmpRAF3.setLength(limit * Long.BYTES); + tmpRAF3.seek((3L * partition) * Long.BYTES); + + for(long i = 0L; i < (1L * partition); i++) { + tmpRAF0.writeLong(i); + } + + for(long i = (1L * partition); i < (2L * partition); i++) { + tmpRAF1.writeLong(i); + } + + for(long i = (2L * partition); i < (3L * partition); i++) { + tmpRAF2.writeLong(i); + } + + for(long i = (3L * partition); i < (4L * partition); i++) { + tmpRAF3.writeLong(i); + } + + tmpRAF0.close(); + tmpRAF1.close(); + tmpRAF2.close(); + tmpRAF3.close(); + + final RandomAccessFile tmpRAF = new BufferedRandomAccessFile(tempFile, "r"); + for(long i = 0L; i < limit; i++) { + assertEquals(i, tmpRAF.readLong()); + } + tmpRAF.close(); + } + + @Test + public void test3() throws IOException, InterruptedException { + final File tempFile = File.createTempFile("ConcurrentWriteTest_test3", ".bin"); + final long limit = 400000000L; + final long writers = 8L; + final long partition = limit / writers; + + final Collection<Callable<Void>> tasks = new ArrayList<Callable<Void>>((int) writers); + for (long i = 0L; i < writers; i++) { + final long id = i; + tasks.add(new Callable<Void>() { + public Void call() throws Exception { + final RandomAccessFile tmpRAF = new BufferedRandomAccessFile(tempFile, "rw"); + tmpRAF.setLength(limit * Long.BYTES); + tmpRAF.seek(id * partition * Long.BYTES); + + for (long j = (id * partition); j < ((id + 1) * partition); j++) { + tmpRAF.writeLong(j); + } + + tmpRAF.close(); + return null; + } + }); + } + + final ExecutorService executorService = Executors.newFixedThreadPool((int) writers); + executorService.invokeAll(tasks); + executorService.shutdown(); + + final RandomAccessFile tmpRAF = new BufferedRandomAccessFile(tempFile, "r"); + for(long i = 0L; i < limit; i++) { + assertEquals(i, tmpRAF.readLong()); + } + tmpRAF.close(); + } + + @Test + public void test4() throws IOException, InterruptedException { + final File tempFile = File.createTempFile("ConcurrentWriteTest_test4", ".bin"); + final long limit = 400000000L; + final long writers = 8L; + final long partition = limit / writers; + final RandomAccessFile tmpRAF = new BufferedRandomAccessFile(tempFile, "rw"); + tmpRAF.setLength(limit * Long.BYTES); + final FileChannel channel = tmpRAF.getChannel(); + + final Collection<Callable<Void>> tasks = new ArrayList<Callable<Void>>((int) writers); + for (long i = 0L; i < writers; i++) { + final long id = i; + tasks.add(new Callable<Void>() { + public Void call() throws Exception { + long position = id * partition * Long.BYTES; + final ByteBuffer buffer = ByteBuffer.allocate(Long.BYTES/* * 1024*/); + for (long j = (id * partition); j < ((id + 1) * partition); j++/*j+=1024L*/) { +// for (int i = 0; i < buffer.capacity(); i++) { + buffer.putLong(j/* + i*/); + buffer.flip(); +// } + channel.write(buffer, position + (j * Long.BYTES)); + buffer.clear(); + } + channel.force(false); + return null; + } + }); + }; + + final ExecutorService executorService = Executors.newFixedThreadPool((int) writers); + executorService.invokeAll(tasks); + executorService.shutdown(); + + tmpRAF.close(); + + final BufferedRandomAccessFile checkRaf = new BufferedRandomAccessFile(tempFile, "r"); + for(long i = 0L; i < limit; i++) { + assertEquals(i, checkRaf.readLong()); + } + checkRaf.close(); + } +} diff --git a/tlatools/test-concurrent/tlc2/tool/fp/MultiThreadedFPSetTest.java b/tlatools/test-concurrent/tlc2/tool/fp/MultiThreadedFPSetTest.java index d7d5a294223e33529afdb81c1dd05669f5f921a3..2959b88da91ea032e54c32b346187a2a32459125 100644 --- a/tlatools/test-concurrent/tlc2/tool/fp/MultiThreadedFPSetTest.java +++ b/tlatools/test-concurrent/tlc2/tool/fp/MultiThreadedFPSetTest.java @@ -1,28 +1,41 @@ // Copyright (c) 2012 Markus Alexander Kuppe. All rights reserved. package tlc2.tool.fp; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + import java.io.IOException; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.util.Date; +import java.util.Timer; +import java.util.TimerTask; import java.util.concurrent.CountDownLatch; +import java.util.concurrent.CyclicBarrier; + +import org.junit.Assume; +import org.junit.Before; +import org.junit.Test; +import tlc2.TLCGlobals; import tlc2.tool.fp.generator.BatchedFingerPrintGenerator; import tlc2.tool.fp.generator.FingerPrintGenerator; import tlc2.tool.fp.generator.LongVecFingerPrintGenerator; +import tlc2.tool.fp.generator.PartitionedFingerPrintGenerator; +import tlc2.util.IdThread; public abstract class MultiThreadedFPSetTest extends AbstractFPSetTest { private static final int NUM_THREADS = Integer.getInteger(MultiThreadedFPSetTest.class.getName() + ".numThreads", - 2); + Runtime.getRuntime().availableProcessors()); private static final long INSERTIONS = Long.getLong(MultiThreadedFPSetTest.class.getName() + ".insertions", Integer.MAX_VALUE + 2L); /* (non-Javadoc) * @see junit.framework.TestCase#setUp() */ - protected void setUp() throws Exception { - super.setUp(); + @Before + public void printStats() throws Exception { System.out.println("Insertions: " + df.format(INSERTIONS) + " (approx: " + df.format(INSERTIONS * FPSet.LongSize >> 20) + " GiB)"); System.out.println("Thread count: " + NUM_THREADS); @@ -32,6 +45,7 @@ public abstract class MultiThreadedFPSetTest extends AbstractFPSetTest { * Test filling a {@link FPSet} with random fingerprints using multiple * threads in ordered batches */ + @Test public void testMaxFPSetSizeRndBatched() throws IOException, InterruptedException, NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException { doTest(BatchedFingerPrintGenerator.class); } @@ -40,6 +54,7 @@ public abstract class MultiThreadedFPSetTest extends AbstractFPSetTest { * Test filling a {@link FPSet} with random fingerprints using multiple * threads in ordered LongVecs using putBlock/containsBlock */ + @Test public void testMaxFPSetSizeRndBlock() throws IOException, InterruptedException, NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException { doTest(LongVecFingerPrintGenerator.class); } @@ -48,10 +63,22 @@ public abstract class MultiThreadedFPSetTest extends AbstractFPSetTest { * Test filling a {@link FPSet} with max int + 2L random using multiple * threads */ + @Test public void testMaxFPSetSizeRnd() throws IOException, InterruptedException, NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException { doTest(FingerPrintGenerator.class); } + /** + * Test filling a {@link FPSet} with multiple threads. Each thread accesses + * a disjunct partition of the key space and fills it up linearly. This + * prevents hash collisions as well as lock contention. Essentially, this is + * the best case scenario. It ignores INSERTIONS for now. + */ + @Test + public void testMaxFPSetSizePartitioned() throws IOException, InterruptedException, NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException { + doTest(PartitionedFingerPrintGenerator.class); + } + /** * @param fpgClass * @throws IOException @@ -63,37 +90,86 @@ public abstract class MultiThreadedFPSetTest extends AbstractFPSetTest { * @throws IllegalArgumentException * @throws InvocationTargetException */ - private void doTest(Class<? extends FingerPrintGenerator> fpgClass) throws IOException, InterruptedException, NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException { + protected FPSet doTest(Class<? extends FingerPrintGenerator> fpgClass) throws IOException, InterruptedException, NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException { + // Skip the test if the property + // -Dtlc2.tool.fp.MultiThreadedFPSetTest.excludes contains the simple + // name of the test. I.e. + // ...excludes=_BatchedFingerPrintGenerator_LongVecFingerPrintGenerator_PartitionedFingerPrintGenerator + // to skip all but FingerPrintGenerator test. Note that the name has to + // be prefixed with "_" to make it possible to skip + // "FingerPrintGenerator" itself. + Assume.assumeFalse(System.getProperty(MultiThreadedFPSetTest.class.getName() + ".excludes", "") + .contains("_" + fpgClass.getSimpleName())); + System.out.println("Running test: " + fpgClass.getSimpleName()); + + TLCGlobals.setNumWorkers(NUM_THREADS); final FPSet fpSet = getFPSetInitialized(NUM_THREADS); + fpSet.incWorkers(NUM_THREADS); final CountDownLatch latch = new CountDownLatch(NUM_THREADS); - final Constructor<?> constructor = fpgClass - .getConstructor(new Class[] { MultiThreadedFPSetTest.class, int.class, FPSet.class, CountDownLatch.class, long.class, long.class }); + final Constructor<?> constructor = fpgClass.getConstructor(new Class[] { MultiThreadedFPSetTest.class, + int.class, int.class, FPSet.class, CountDownLatch.class, long.class, long.class, CyclicBarrier.class }); + + // Take timestamp after instantiating FPSet to not measure zero'ing/initializing FPSet. + startTimestamp = System.currentTimeMillis(); + + final Timer timer = new Timer(); + final CyclicBarrier barrier = new CyclicBarrier(NUM_THREADS, new Runnable() { + public void run() { + // Start a periodic task to report progress. Do not do it as part of the + // FPGs below. It can drastically slow down an FPG selected to be the + // reporter. + final TimerTask reporter = new TimerTask() { + public void run() { + final long currentSize = fpSet.size(); + final long insertions = currentSize - previousSize; + if (fpSet instanceof FPSetStatistic) { + FPSetStatistic fpSetStatistics = (FPSetStatistic) fpSet; + System.out.println(System.currentTimeMillis() + " s (epoch); " + df.format(insertions) + " insertions/min; " + pf.format(fpSetStatistics.getLoadFactor()) + " load factor"); + } else { + System.out.println(System.currentTimeMillis() + " s (epoch); " + df.format(insertions) + " insertions/min"); + } + previousSize = currentSize; + } + }; + // Take timestamp after instantiating FPSet to not measure zero'ing/initializing FPSet. + startTimestamp = System.currentTimeMillis(); + timer.scheduleAtFixedRate(reporter, 1L, 60 * 1000); + } + }); long seed = RNG_SEED; final FingerPrintGenerator[] fpgs = new FingerPrintGenerator[NUM_THREADS]; for (int i = 0; i < fpgs.length; i++) { fpgs[i] = (FingerPrintGenerator) constructor.newInstance( - this, i, fpSet, latch, seed++, INSERTIONS); - Thread thread = new Thread(fpgs[i], "Producer#" + i); + this, i, fpgs.length, fpSet, latch, seed++, INSERTIONS, barrier); + Thread thread = new IdThread(fpgs[i], "Producer#" + i, i); thread.start(); } // wait for runnables/fpg to tear down the latch latch.await(); - endTimeStamp = new Date(); + // Cancel reporting task. + timer.cancel(); + long overallPuts = 0L; + long overallCollisions = 0L; // print stats for (int i = 0; i < fpgs.length; i++) { final FingerPrintGenerator fpg = fpgs[i]; long puts = fpg.getPuts(); - System.out.println("Producer: " + fpg.getId() + " puts: " + puts); - System.out.println("puts/collisions: " + (double) (puts / fpg.getCollisions())); + long collisions = fpg.getCollisions(); + System.out.println(String.format("Producer: %s, puts: %s, puts/collisions: %s", fpg.getId(), puts, + (collisions == 0 ? "none" : (double) (puts / collisions)))); overallPuts += puts; + overallCollisions += collisions; } + System.out.println(String.format("Total puts: %s, total collisions: %s, total load factor: %s, duration: %s ms.", overallPuts, + overallCollisions, df.format(((FPSetStatistic) fpSet).getLoadFactor()), endTimeStamp.getTime() - startTimestamp)); + printInsertionSpeed(fpSet, startTimestamp, endTimeStamp.getTime()); // Do not compare fpSet.size() to insertions as several FPGs might race // with the FPG that inserts the INSERTIONS element. Hence we count the @@ -108,5 +184,7 @@ public abstract class MultiThreadedFPSetTest extends AbstractFPSetTest { // Check a DiskFPSet's invariant that after flush all fingerprints in // the file are a) monotonically increasing and b) there are no duplicates. assertTrue(fpSet.checkInvariant()); + + return fpSet; } } diff --git a/tlatools/test-concurrent/tlc2/tool/fp/MultiThreadedLSBDiskFPSetTest.java b/tlatools/test-concurrent/tlc2/tool/fp/MultiThreadedLSBDiskFPSetTest.java new file mode 100644 index 0000000000000000000000000000000000000000..3b806e2fe6afd0b241621ffc54732c001c64cbef --- /dev/null +++ b/tlatools/test-concurrent/tlc2/tool/fp/MultiThreadedLSBDiskFPSetTest.java @@ -0,0 +1,16 @@ +// Copyright (c) 2016 Markus Alexander Kuppe. All rights reserved. +package tlc2.tool.fp; + +import java.io.IOException; + +public class MultiThreadedLSBDiskFPSetTest extends MultiThreadedFPSetTest { + + /* (non-Javadoc) + * @see tlc2.tool.fp.AbstractFPSetTest#getFPSet(long) + */ + @Override + protected FPSet getFPSet(final FPSetConfiguration fpSetConfig) throws IOException { + fpSetConfig.setRatio(1.0d); // Tests can consume all of the available VM memory + return new LSBDiskFPSet(fpSetConfig); + } +} diff --git a/tlatools/test-concurrent/tlc2/tool/fp/MultiThreadedMSBDiskFPSetTest.java b/tlatools/test-concurrent/tlc2/tool/fp/MultiThreadedMSBDiskFPSetTest.java index 41701ea7e675d71085a55c193e593e18d76916dc..7d34c1eb752338da7a3eb0285b7a95fbecf53373 100644 --- a/tlatools/test-concurrent/tlc2/tool/fp/MultiThreadedMSBDiskFPSetTest.java +++ b/tlatools/test-concurrent/tlc2/tool/fp/MultiThreadedMSBDiskFPSetTest.java @@ -10,6 +10,7 @@ public class MultiThreadedMSBDiskFPSetTest extends MultiThreadedFPSetTest { */ @Override protected FPSet getFPSet(final FPSetConfiguration fpSetConfig) throws IOException { + fpSetConfig.setRatio(1.0d); // Tests can consume all of the available VM memory return new MSBDiskFPSet(fpSetConfig); } } diff --git a/tlatools/test-concurrent/tlc2/tool/fp/MultiThreadedOffHeapDiskFPSetTest.java b/tlatools/test-concurrent/tlc2/tool/fp/MultiThreadedOffHeapDiskFPSetTest.java index 452b01cb093a9552383c3d755e23ce5377dabaf4..a6dc15bdf0b46cac444b26b822073c4b12872872 100644 --- a/tlatools/test-concurrent/tlc2/tool/fp/MultiThreadedOffHeapDiskFPSetTest.java +++ b/tlatools/test-concurrent/tlc2/tool/fp/MultiThreadedOffHeapDiskFPSetTest.java @@ -2,6 +2,11 @@ package tlc2.tool.fp; import java.io.IOException; +import java.lang.reflect.InvocationTargetException; + +import org.junit.Assert; + +import tlc2.tool.fp.generator.PartitionedFingerPrintGenerator; public class MultiThreadedOffHeapDiskFPSetTest extends MultiThreadedFPSetTest { @@ -10,6 +15,17 @@ public class MultiThreadedOffHeapDiskFPSetTest extends MultiThreadedFPSetTest { */ @Override protected FPSet getFPSet(final FPSetConfiguration fpSetConfig) throws IOException { - return new OffHeapDiskFPSet(new FPSetConfiguration(1.0d)); + return new OffHeapDiskFPSet(new FPSetConfiguration(1.0d, OffHeapDiskFPSet.class.getName())); + } + + /* (non-Javadoc) + * @see tlc2.tool.fp.MultiThreadedFPSetTest#testMaxFPSetSizePartitioned() + */ + @Override + public void testMaxFPSetSizePartitioned() + throws IOException, InterruptedException, NoSuchMethodException, SecurityException, InstantiationException, + IllegalAccessException, IllegalArgumentException, InvocationTargetException { + final OffHeapDiskFPSet fpSet = (OffHeapDiskFPSet) doTest(PartitionedFingerPrintGenerator.class); + Assert.assertEquals(0, fpSet.getBucketCapacity()); // bucket capacity is actually reprobe which expected to be zero. } } diff --git a/tlatools/test-concurrent/tlc2/tool/fp/generator/BatchedFingerPrintGenerator.java b/tlatools/test-concurrent/tlc2/tool/fp/generator/BatchedFingerPrintGenerator.java index 9ebb8bf255fd76b9c40f0f24ae52ef0d208f665c..dd2b536878ef475095576f3d2d98b45a7d092ff5 100644 --- a/tlatools/test-concurrent/tlc2/tool/fp/generator/BatchedFingerPrintGenerator.java +++ b/tlatools/test-concurrent/tlc2/tool/fp/generator/BatchedFingerPrintGenerator.java @@ -4,6 +4,9 @@ package tlc2.tool.fp.generator; import java.io.IOException; import java.util.Arrays; import java.util.concurrent.CountDownLatch; +import java.util.concurrent.CyclicBarrier; + +import org.junit.Assert; import tlc2.tool.fp.FPSet; import tlc2.tool.fp.MultiThreadedFPSetTest; @@ -12,23 +15,28 @@ public class BatchedFingerPrintGenerator extends FingerPrintGenerator { private static final int batch = 1024; - public BatchedFingerPrintGenerator(MultiThreadedFPSetTest test, int id, FPSet fpSet, CountDownLatch latch, long seed, long insertions) { - super(test, id, fpSet, latch, seed, insertions); + public BatchedFingerPrintGenerator(MultiThreadedFPSetTest test, int id, int numThreads, FPSet fpSet, CountDownLatch latch, long seed, long insertions, final CyclicBarrier barrier) { + super(test, id, numThreads, fpSet, latch, seed, insertions, barrier); } /* (non-Javadoc) * @see java.lang.Runnable#run() */ public void run() { + waitForAllThreadsStarted(); + long predecessors[] = new long[batch]; boolean initialized = false; - while (fpSet.size() < insertions) { + // Reduce number of FPSet#size invocation by counting puts/collisions. + // FPSet#size can cause an FPSet to synchronize all its writers slowing + // down execution. + while (puts + collisions < perThreadInsertions || fpSet.size() < totalInsertions) { try { // Make sure set still contains predecessors if (initialized) { for (int i = 0; i < predecessors.length; i++) { long predecessor = predecessors[i]; - MultiThreadedFPSetTest.assertTrue(fpSet.contains(predecessor)); + Assert.assertTrue(fpSet.contains(predecessor)); } } @@ -49,15 +57,9 @@ public class BatchedFingerPrintGenerator extends FingerPrintGenerator { collisions++; } } - - // First producer prints stats - if (id == 0) { - test.printInsertionSpeed(fpSet.size()); - } - } catch (IOException e) { e.printStackTrace(); - MultiThreadedFPSetTest.fail("Unexpected"); + Assert.fail("Unexpected"); } } latch.countDown(); diff --git a/tlatools/test-concurrent/tlc2/tool/fp/generator/FingerPrintGenerator.java b/tlatools/test-concurrent/tlc2/tool/fp/generator/FingerPrintGenerator.java index 728c0bfdbf164c54e5f80b5d48345a46766a0d53..aa0db19ed87e79b675dca8252f54e86e0e3d6f58 100644 --- a/tlatools/test-concurrent/tlc2/tool/fp/generator/FingerPrintGenerator.java +++ b/tlatools/test-concurrent/tlc2/tool/fp/generator/FingerPrintGenerator.java @@ -3,60 +3,83 @@ package tlc2.tool.fp.generator; import java.io.IOException; import java.util.Random; +import java.util.concurrent.BrokenBarrierException; import java.util.concurrent.CountDownLatch; +import java.util.concurrent.CyclicBarrier; + +import org.junit.Assert; import tlc2.tool.fp.FPSet; import tlc2.tool.fp.MultiThreadedFPSetTest; public class FingerPrintGenerator implements Runnable { - protected final long insertions; + protected final long totalInsertions; + protected final long perThreadInsertions; + protected final long seed; protected final Random rnd; protected final FPSet fpSet; protected final CountDownLatch latch; + protected final CyclicBarrier barrier; protected final int id; + protected final int numThreads; protected final MultiThreadedFPSetTest test; protected long puts = 0L; protected long collisions = 0L; - public FingerPrintGenerator(MultiThreadedFPSetTest test, int id, FPSet fpSet, CountDownLatch latch, long seed, long insertions) { + public FingerPrintGenerator(MultiThreadedFPSetTest test, int id, int numThreads, FPSet fpSet, CountDownLatch latch, + long seed, long totalInsertions, final CyclicBarrier barrier) { this.test = test; this.id = id; + this.numThreads = numThreads; this.fpSet = fpSet; this.latch = latch; + this.barrier = barrier; + this.seed = seed; this.rnd = new Random(seed); - this.insertions = insertions; + this.totalInsertions = totalInsertions; + this.perThreadInsertions = (long) Math.floor(totalInsertions / numThreads); } /* (non-Javadoc) * @see java.lang.Runnable#run() */ public void run() { + waitForAllThreadsStarted(); + long predecessor = 0L; - while (fpSet.size() < insertions) { + // Reduce number of FPSet#size invocation by counting puts/collisions. + // FPSet#size can cause an FPSet to synchronize all its writers slowing + // down execution. + while (puts + collisions < perThreadInsertions || fpSet.size() < totalInsertions) { try { // make sure set still contains predecessor if (predecessor != 0L) { - MultiThreadedFPSetTest.assertTrue(fpSet.contains(predecessor)); + Assert.assertTrue(fpSet.contains(predecessor)); } predecessor = rnd.nextLong(); + // Periodically verify the FPSet's content. This causes a + // drastic slow down. +// if (fpSet.size() % 10000 == 0) { +// final Random verify = new Random(seed); +// long fp = verify.nextLong(); +// while (fp != predecessor) { +// Assert.assertTrue(fpSet.contains(fp)); +// fp = verify.nextLong(); +// } +// } +// boolean put = fpSet.put(predecessor); if (put == false) { puts++; } else { collisions++; } - - // First producer prints stats - if (id == 0) { - test.printInsertionSpeed(fpSet.size()); - } - } catch (IOException e) { e.printStackTrace(); - MultiThreadedFPSetTest.fail("Unexpected"); + Assert.fail("Unexpected"); } } latch.countDown(); @@ -77,6 +100,16 @@ public class FingerPrintGenerator implements Runnable { * @return the collisions */ public long getCollisions() { - return collisions == 0 ? 1 : collisions; + return collisions; + } + + protected void waitForAllThreadsStarted() { + try { + barrier.await(); + } catch (InterruptedException e1) { + e1.printStackTrace(); + } catch (BrokenBarrierException e1) { + e1.printStackTrace(); + } } } \ No newline at end of file diff --git a/tlatools/test-concurrent/tlc2/tool/fp/generator/LongVecFingerPrintGenerator.java b/tlatools/test-concurrent/tlc2/tool/fp/generator/LongVecFingerPrintGenerator.java index 851a5a45ec627dee62401113e7ef03f8504a5f0d..fa38a0210b98938152daed9bc799b999c663029c 100644 --- a/tlatools/test-concurrent/tlc2/tool/fp/generator/LongVecFingerPrintGenerator.java +++ b/tlatools/test-concurrent/tlc2/tool/fp/generator/LongVecFingerPrintGenerator.java @@ -4,6 +4,9 @@ package tlc2.tool.fp.generator; import java.io.IOException; import java.util.Arrays; import java.util.concurrent.CountDownLatch; +import java.util.concurrent.CyclicBarrier; + +import org.junit.Assert; import tlc2.tool.fp.FPSet; import tlc2.tool.fp.MultiThreadedFPSetTest; @@ -14,22 +17,27 @@ public class LongVecFingerPrintGenerator extends FingerPrintGenerator { private static final int batch = 1024; - public LongVecFingerPrintGenerator(MultiThreadedFPSetTest test, int id, FPSet fpSet, CountDownLatch latch, long seed, long insertions) { - super(test, id, fpSet, latch, seed, insertions); + public LongVecFingerPrintGenerator(MultiThreadedFPSetTest test, int id, int numThreads, FPSet fpSet, CountDownLatch latch, long seed, long insertions, final CyclicBarrier barrier) { + super(test, id, numThreads, fpSet, latch, seed, insertions, barrier); } /* (non-Javadoc) * @see java.lang.Runnable#run() */ public void run() { + waitForAllThreadsStarted(); + TestLongVec predecessors = new TestLongVec(batch); boolean initialized = false; - while (fpSet.size() < insertions) { + // Reduce number of FPSet#size invocation by counting puts/collisions. + // FPSet#size can cause an FPSet to synchronize all its writers slowing + // down execution. + while (puts + collisions < perThreadInsertions || fpSet.size() < totalInsertions) { try { // Make sure set still contains predecessors if (initialized) { final BitVector bitVector = fpSet.containsBlock(predecessors); - MultiThreadedFPSetTest.assertTrue(bitVector.trueCnt() == batch); + Assert.assertEquals(batch, batch - bitVector.trueCnt()); } // Fill new fingerprints and sort them @@ -43,15 +51,9 @@ public class LongVecFingerPrintGenerator extends FingerPrintGenerator { final BitVector bitVector = fpSet.putBlock(predecessors); puts += bitVector.trueCnt(); collisions += (batch - bitVector.trueCnt()); - - // First producer prints stats - if (id == 0) { - test.printInsertionSpeed(fpSet.size()); - } - } catch (IOException e) { e.printStackTrace(); - MultiThreadedFPSetTest.fail("Unexpected"); + Assert.fail("Unexpected"); } } latch.countDown(); diff --git a/tlatools/test-concurrent/tlc2/tool/fp/generator/PartitionedFingerPrintGenerator.java b/tlatools/test-concurrent/tlc2/tool/fp/generator/PartitionedFingerPrintGenerator.java new file mode 100644 index 0000000000000000000000000000000000000000..c4deb32cc7aadfb9a23c11f4d78fa0428082b122 --- /dev/null +++ b/tlatools/test-concurrent/tlc2/tool/fp/generator/PartitionedFingerPrintGenerator.java @@ -0,0 +1,87 @@ +/******************************************************************************* + * Copyright (c) 2016 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ + +package tlc2.tool.fp.generator; + +import java.io.IOException; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.CyclicBarrier; + +import org.junit.Assert; + +import tlc2.tool.fp.FPSet; +import tlc2.tool.fp.MultiThreadedFPSetTest; + +public class PartitionedFingerPrintGenerator extends FingerPrintGenerator { + + private long fp; + + private final long numOfPerThreadBuckets; + private final long increment; + + public PartitionedFingerPrintGenerator(MultiThreadedFPSetTest test, int id, int numThreads, FPSet fpSet, CountDownLatch latch, + long seed, long insertions, final CyclicBarrier barrier) { + super(test, id, numThreads, fpSet, latch, seed, insertions, barrier); + + final long numOfTotalBuckets = fpSet.getConfiguration().getMemoryInFingerprintCnt(); + numOfPerThreadBuckets = numOfTotalBuckets / (1L * numThreads); + + final long perThreadStartBucket = numOfPerThreadBuckets * (1L * id); + increment = (long) Math.ceil((Long.MAX_VALUE - 1L) / (numOfTotalBuckets * 1d)); + fp = increment * perThreadStartBucket; + } + + /* (non-Javadoc) + * @see java.lang.Runnable#run() + */ + public void run() { + waitForAllThreadsStarted(); + + long insertions = 0L; + + while (insertions++ < numOfPerThreadBuckets) { + try { + if (fp != 0L) { + if (fpSet.put(fp) != false) { + Assert.fail("Linear fill-up should not cause a collision"); + } + // In case PartitionedFingerPrintGenerator and + // FingerPrintGenerator are used in performance tests, burn + // the same amount of cycles to obtain the next random like + // FPG does. puts is meaningless in the scope of PFPG + // anyway. It inserts up to a load factor of 1. + //puts += rnd.nextLong(); + puts++; + } + fp += increment; + } catch (IOException e) { + e.printStackTrace(); + Assert.fail("Unexpected"); + } + } + latch.countDown(); + } +} diff --git a/tlatools/test-long/tlc2/tool/fp/FPSetTest.java b/tlatools/test-long/tlc2/tool/fp/FPSetTest.java index 8d758ddfe87384d9a811f682ee6dc5e6910ba488..fd48e05b5e8da50d417c38f75f7b5a1ee0d89cf5 100644 --- a/tlatools/test-long/tlc2/tool/fp/FPSetTest.java +++ b/tlatools/test-long/tlc2/tool/fp/FPSetTest.java @@ -1,15 +1,22 @@ package tlc2.tool.fp; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + import java.io.IOException; import java.util.Date; import java.util.Random; +import org.junit.Test; + public abstract class FPSetTest extends AbstractFPSetTest { /** * Test filling a {@link FPSet} with four linearly incrementing values * @throws IOException */ + @Test public void testSimpleFill() throws IOException { final FPSet fpSet = getFPSet(new FPSetConfiguration()); fpSet.init(1, tmpdir, filename); @@ -29,6 +36,7 @@ public abstract class FPSetTest extends AbstractFPSetTest { * Test filling a {@link FPSet} with max int + 1 random * @throws IOException */ + @Test public void testMaxFPSetSizeRnd() throws IOException { Random rnd = new Random(RNG_SEED); @@ -50,7 +58,7 @@ public abstract class FPSetTest extends AbstractFPSetTest { long currentSize = fpSet.size(); assertTrue(i == currentSize); - printInsertionSpeed(currentSize); + printInsertionSpeed(fpSet); } // try creating a check point @@ -69,6 +77,7 @@ public abstract class FPSetTest extends AbstractFPSetTest { * Test filling a {@link FPSet} with max int + 1 * @throws IOException */ + @Test public void testMaxFPSetSize() throws IOException { // @@ -92,7 +101,7 @@ public abstract class FPSetTest extends AbstractFPSetTest { long currentSize = fpSet.size(); assertTrue(++counter == currentSize); - printInsertionSpeed(currentSize); + printInsertionSpeed(fpSet); } // try creating a check point diff --git a/tlatools/test-long/tlc2/tool/fp/OffHeapDiskFPSetTest.java b/tlatools/test-long/tlc2/tool/fp/OffHeapDiskFPSetLongTest.java similarity index 81% rename from tlatools/test-long/tlc2/tool/fp/OffHeapDiskFPSetTest.java rename to tlatools/test-long/tlc2/tool/fp/OffHeapDiskFPSetLongTest.java index a619498f84f614c16e37c4be4a6a82d9f5b8947e..dd0667435682a0e3152c7069c833c537e7a432aa 100644 --- a/tlatools/test-long/tlc2/tool/fp/OffHeapDiskFPSetTest.java +++ b/tlatools/test-long/tlc2/tool/fp/OffHeapDiskFPSetLongTest.java @@ -1,15 +1,21 @@ // Copyright (c) 2012 Markus Alexander Kuppe. All rights reserved. package tlc2.tool.fp; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + import java.io.IOException; import java.util.Random; +import org.junit.Test; + import util.TLCRuntime; -public class OffHeapDiskFPSetTest extends FPSetTest { +public class OffHeapDiskFPSetLongTest extends FPSetTest { private static final int FLUSHES = 4; + @Test public void testCollisionBucket() throws IOException { final FPSet fpSet = getFPSet(new FPSetConfiguration()); fpSet.init(1, tmpdir, filename); @@ -20,6 +26,7 @@ public class OffHeapDiskFPSetTest extends FPSetTest { } } + @Test public void testPosition() throws IOException { final FPSet fpSet = getFPSet(new FPSetConfiguration()); fpSet.init(1, tmpdir, filename); @@ -36,6 +43,7 @@ public class OffHeapDiskFPSetTest extends FPSetTest { /** * */ + @Test public void testMultipleFlushes() throws IOException { final FPSet fpSet = getFPSet(new FPSetConfiguration()); fpSet.init(1, tmpdir, filename); @@ -61,7 +69,8 @@ public class OffHeapDiskFPSetTest extends FPSetTest { * @see tlc2.tool.fp.AbstractFPSetTest#getFPSet(long) */ @Override - protected FPSet getFPSet(final FPSetConfiguration fpSetConfig) throws IOException { - return new OffHeapDiskFPSet(new FPSetConfiguration(1.0d)); + protected FPSet getFPSet(final FPSetConfiguration ignored) throws IOException { + final FPSetConfiguration config = new FPSetConfiguration(1.0d, OffHeapDiskFPSet.class.getName()); + return new OffHeapDiskFPSet(config); } } diff --git a/tlatools/test-long/tlc2/tool/liveness/MultiThreadedSpecTest.java b/tlatools/test-long/tlc2/tool/liveness/MultiThreadedSpecTest.java index f2e0f4dcf5db43857d0b3340afadda9b23bfae0a..095bd66b36d783f62f48b6e41ba0eb30a17bed65 100644 --- a/tlatools/test-long/tlc2/tool/liveness/MultiThreadedSpecTest.java +++ b/tlatools/test-long/tlc2/tool/liveness/MultiThreadedSpecTest.java @@ -26,6 +26,9 @@ package tlc2.tool.liveness; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + import java.lang.management.ManagementFactory; import java.lang.management.ThreadInfo; import java.lang.management.ThreadMXBean; @@ -36,6 +39,9 @@ import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; +import org.junit.Before; +import org.junit.Test; + import tlc2.TLCGlobals; import tlc2.output.EC; import tlc2.tool.WorkerMonitor; @@ -74,6 +80,7 @@ public abstract class MultiThreadedSpecTest extends ModelCheckerTestCase { this.waitedRatio = waitedRatio; } + @Test public void testSpec() throws BrokenBarrierException, InterruptedException, TimeoutException { // ModelChecker has finished and generated the expected amount of states assertTrue(recorder.recorded(EC.TLC_FINISHED)); @@ -128,6 +135,7 @@ public abstract class MultiThreadedSpecTest extends ModelCheckerTestCase { } } + @Before public void setUp() { // Set the threshold before TLC (periodically) checks liveness to // the largest possible value. This essentially stops TLC from checking @@ -155,8 +163,6 @@ public abstract class MultiThreadedSpecTest extends ModelCheckerTestCase { latch.countDown(); } }); - - super.setUp(); } protected int getNumberOfThreads() { diff --git a/tlatools/test-long/tlc2/tool/liveness/NoTableauSpecTest.java b/tlatools/test-long/tlc2/tool/liveness/NoTableauSpecTest.java index 87779edbc369c33844c2c7f8da33fa437a98d23f..c4dd39a04c4bf1fc388abb9ea32d7c1c113a2ccb 100644 --- a/tlatools/test-long/tlc2/tool/liveness/NoTableauSpecTest.java +++ b/tlatools/test-long/tlc2/tool/liveness/NoTableauSpecTest.java @@ -25,9 +25,11 @@ ******************************************************************************/ package tlc2.tool.liveness; +import util.TLAConstants; + public class NoTableauSpecTest extends MultiThreadedSpecTest { public NoTableauSpecTest() { - super("MC", "VoteProof", "137297983", "693930", 0.25d, 0.25d); + super(TLAConstants.Files.MODEL_CHECK_FILE_BASENAME, "VoteProof", "137297983", "693930", 0.25d, 0.25d); } } diff --git a/tlatools/test-long/tlc2/tool/liveness/TableauSpecTest.java b/tlatools/test-long/tlc2/tool/liveness/TableauSpecTest.java index a2789e1a5b8e91e542adddf8f6ac64fa0c3b58d5..332b7f4205975224cd3ad3525d44a244203b7ae7 100644 --- a/tlatools/test-long/tlc2/tool/liveness/TableauSpecTest.java +++ b/tlatools/test-long/tlc2/tool/liveness/TableauSpecTest.java @@ -25,9 +25,11 @@ ******************************************************************************/ package tlc2.tool.liveness; +import util.TLAConstants; + public class TableauSpecTest extends MultiThreadedSpecTest { public TableauSpecTest() { - super("MC", "EWD840", new String[] {"-maxSetSize", "9000000"}, "540765192", "10487806", 0.3d, 0.3d); + super(TLAConstants.Files.MODEL_CHECK_FILE_BASENAME, "EWD840", new String[] {"-maxSetSize", "9000000"}, "540765192", "10487806", 0.3d, 0.3d); } } diff --git a/tlatools/test-long/tlc2/tool/queue/DiskStateQueueTest.java b/tlatools/test-long/tlc2/tool/queue/DiskStateQueueTest.java index f5fccba4fa918552c46cc34617cb80a11aa54b9c..7722fccb64b69d5b5a05044514d0b8dcd223602c 100644 --- a/tlatools/test-long/tlc2/tool/queue/DiskStateQueueTest.java +++ b/tlatools/test-long/tlc2/tool/queue/DiskStateQueueTest.java @@ -1,7 +1,13 @@ package tlc2.tool.queue; +import static org.junit.Assert.assertTrue; + import java.io.File; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + import tlc2.tool.TLCState; public class DiskStateQueueTest extends StateQueueTest { @@ -11,9 +17,8 @@ public class DiskStateQueueTest extends StateQueueTest { /* (non-Javadoc) * @see tlc2.tool.queue.StateQueueTest#setUp() */ - protected void setUp() throws Exception { - super.setUp(); - + @Before + public void setUp() throws Exception { // create a temp folder in java.io.tmpdir and have it deleted on VM exit final String diskdir = System.getProperty("java.io.tmpdir") + File.separator + "MultiDiskStateQueueTest_" + System.currentTimeMillis(); @@ -27,6 +32,7 @@ public class DiskStateQueueTest extends StateQueueTest { /* (non-Javadoc) * @see junit.framework.TestCase#tearDown() */ + @After public void tearDown() { // delete all nested files final File[] listFiles = file.listFiles(); @@ -39,6 +45,7 @@ public class DiskStateQueueTest extends StateQueueTest { // add Integer.MAX_VALUE states and check growth of MultiStateQueue. // Reuse the same state to speed up instantiation and space requirements + @Test public void testGrowBeyondIntMaxValue() { final TLCState state = new DummyTLCState(); diff --git a/tlatools/test-model/.gitattributes b/tlatools/test-model/.gitattributes new file mode 100644 index 0000000000000000000000000000000000000000..8b8a4987707a99a70444d60d1bd5911155574261 --- /dev/null +++ b/tlatools/test-model/.gitattributes @@ -0,0 +1,2 @@ +* text eol=lf +*.zip -text \ No newline at end of file diff --git a/tlatools/test-model/.gitignore b/tlatools/test-model/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..2f100884b03c579eea1de3e29a003ea94ac86545 --- /dev/null +++ b/tlatools/test-model/.gitignore @@ -0,0 +1,2 @@ +/CallGotoTest.old +/CallGotoUnlabeledTest.old diff --git a/tlatools/test-model/AS/AS.cfg b/tlatools/test-model/AS/AS.cfg new file mode 100644 index 0000000000000000000000000000000000000000..2aacadae1f61d029d84eab28111b6caae7aabb1c --- /dev/null +++ b/tlatools/test-model/AS/AS.cfg @@ -0,0 +1,6 @@ +\* CONSTANT definitions +CONSTANT +c = 4 +\* SPECIFICATION definition +SPECIFICATION +ASInit diff --git a/tlatools/test-model/AS/AS.tla b/tlatools/test-model/AS/AS.tla new file mode 100644 index 0000000000000000000000000000000000000000..20482a157b7c15884ae910d82a2ecdfe3539d682 --- /dev/null +++ b/tlatools/test-model/AS/AS.tla @@ -0,0 +1,30 @@ +--------------------------------- MODULE AS --------------------------------- +EXTENDS Integers, FiniteSets + +CONSTANT c +ASSUME c \in Nat \ {0} + +R == -42 + + +VARIABLES x, z + +ASTypeOK == /\ x \in 1..c \cup {R} + /\ z \subseteq 1..c + +ASInit == /\ x = R + /\ z = {} + +ASChoose == /\ Cardinality(z) # c + /\ \E n \in 1..c \ z : /\ x' = n + /\ z' = z \cup {n} + +ASRest == /\ Cardinality(z) = c + /\ x' = R + /\ z' = {} + +ASNext == ASChoose \/ ASRest + +AS == ASInit /\ [][ASNext]_<<x, z>> + +============================================================================= diff --git a/tlatools/test-model/AssertExpressionStack.cfg b/tlatools/test-model/AssertExpressionStack.cfg new file mode 100644 index 0000000000000000000000000000000000000000..bc224b9def594b0819de3f0b45ede63ea8213c7b --- /dev/null +++ b/tlatools/test-model/AssertExpressionStack.cfg @@ -0,0 +1,2 @@ +SPECIFICATION +Spec diff --git a/tlatools/test-model/AssertExpressionStack.tla b/tlatools/test-model/AssertExpressionStack.tla new file mode 100644 index 0000000000000000000000000000000000000000..5d344919bb01af72f67c5accf6653042d1524969 --- /dev/null +++ b/tlatools/test-model/AssertExpressionStack.tla @@ -0,0 +1,7 @@ +------------------------------ MODULE AssertExpressionStack ------------------------------ +EXTENDS Naturals, TLC + +VARIABLES x + +Spec == x = 0 /\ [][x'=1/\Assert(x=0, "Fails after initial state")]_x +============================================================================= diff --git a/tlatools/test-model/AssignmentInit.cfg b/tlatools/test-model/AssignmentInit.cfg new file mode 100644 index 0000000000000000000000000000000000000000..cea554fa6375ea5fbb9074b946f867a1bd02ac99 --- /dev/null +++ b/tlatools/test-model/AssignmentInit.cfg @@ -0,0 +1,7 @@ +SPECIFICATION +Spec +CONSTANT +N = 42 +M = 42 +\*INVARIANT +\*Inv \ No newline at end of file diff --git a/tlatools/test-model/AssignmentInit.tla b/tlatools/test-model/AssignmentInit.tla new file mode 100644 index 0000000000000000000000000000000000000000..54c495e3c4a08c9665a035e5cc9b769c5bceced5 --- /dev/null +++ b/tlatools/test-model/AssignmentInit.tla @@ -0,0 +1,30 @@ +--------------------------- MODULE AssignmentInit --------------------------- +EXTENDS Integers, TLC +VARIABLE s + +min(S) == CHOOSE e \in S: \A a \in S: e <= a + +InitExit(var, S) == \E val \in S: (var = val /\ var > min(S)) + +InitAll(var, S) == \A val \in S: (var = val /\ var \in S) + +InitIn(var, S) == var \in S /\ var > min(S) + +InitEq(var, S, val) == var \in S /\ var + 1 = val + +isOdd(n) == n % 2 = 1 +InitEven(var, S) == var \in S /\ isOdd(var) + +\* With this Init(var), the test does not fail without its fix. +\*Init(ignored) == \E val \in {0,1}: (s = val /\ s > 0) + +\* Init1 one state + Init3 one state +Spec == /\ \/ InitExit(s, {0,1}) \* 1 unique state + \/ InitAll(s, {2}) \* 1 unique state + \/ InitIn(s, {4,5}) \* 1 unique state + \/ InitAll(s, {6,7}) \* 0 unique states + \/ InitEq(s, {8,9}, 10) \* 1 unique states + \/ InitEven(s, {10,11}) \* 1 unique states + /\ [][InitExit(s, {0,1})/\UNCHANGED s]_s + +============================================================================= diff --git a/tlatools/test-model/AssignmentInitExpensive.cfg b/tlatools/test-model/AssignmentInitExpensive.cfg new file mode 100644 index 0000000000000000000000000000000000000000..cea554fa6375ea5fbb9074b946f867a1bd02ac99 --- /dev/null +++ b/tlatools/test-model/AssignmentInitExpensive.cfg @@ -0,0 +1,7 @@ +SPECIFICATION +Spec +CONSTANT +N = 42 +M = 42 +\*INVARIANT +\*Inv \ No newline at end of file diff --git a/tlatools/test-model/AssignmentInitExpensive.tla b/tlatools/test-model/AssignmentInitExpensive.tla new file mode 100644 index 0000000000000000000000000000000000000000..dc4091521a568de4fa2872183761c4392cdedc83 --- /dev/null +++ b/tlatools/test-model/AssignmentInitExpensive.tla @@ -0,0 +1,17 @@ +--------------------------- MODULE AssignmentInitExpensive --------------------------- +EXTENDS Integers +VARIABLE s + +\* expensiveOp takes ~10 seconds wallclock time on a modern day machine. +\* The point is, that doing 10k times will make the test time out. +expensiveOp == CHOOSE e \in SUBSET (1..18) : TRUE + +\* This variant (with an unused parameter) causes TLC to reevaluate +\* CHOOSE ... SUBSET 10k times. +\*expensiveOpParam(ignored) == CHOOSE e \in SUBSET (1..18) : TRUE + +Init(var) == \E val \in 0..10000: var = expensiveOp + +Spec == Init(s) /\ [][UNCHANGED s]_s + +============================================================================= diff --git a/tlatools/test-model/AssignmentInitNeg.cfg b/tlatools/test-model/AssignmentInitNeg.cfg new file mode 100644 index 0000000000000000000000000000000000000000..c8cffbe64c3dcc8bce148218f2e7b66a7537f573 --- /dev/null +++ b/tlatools/test-model/AssignmentInitNeg.cfg @@ -0,0 +1,4 @@ +SPECIFICATION +Spec +INVARIANT +Inv \ No newline at end of file diff --git a/tlatools/test-model/AssignmentInitNeg.tla b/tlatools/test-model/AssignmentInitNeg.tla new file mode 100644 index 0000000000000000000000000000000000000000..8269be1a94fed8c333f1d2ee3e9bdb95ac0545b6 --- /dev/null +++ b/tlatools/test-model/AssignmentInitNeg.tla @@ -0,0 +1,10 @@ +--------------------------- MODULE AssignmentInitNeg --------------------------- +EXTENDS Integers +VARIABLE s + +Init(var) == \E val \in 0..1: (var = val /\ var < 1) + +Spec == Init(s) /\ [][UNCHANGED s]_s + +Inv == (s < 1) +============================================================================= diff --git a/tlatools/test-model/AssignmentNext.cfg b/tlatools/test-model/AssignmentNext.cfg new file mode 100644 index 0000000000000000000000000000000000000000..c8cffbe64c3dcc8bce148218f2e7b66a7537f573 --- /dev/null +++ b/tlatools/test-model/AssignmentNext.cfg @@ -0,0 +1,4 @@ +SPECIFICATION +Spec +INVARIANT +Inv \ No newline at end of file diff --git a/tlatools/test-model/AssignmentNext.tla b/tlatools/test-model/AssignmentNext.tla new file mode 100644 index 0000000000000000000000000000000000000000..75450d5ab5cfd5bb62dde3346e63959f5ecd0690 --- /dev/null +++ b/tlatools/test-model/AssignmentNext.tla @@ -0,0 +1,10 @@ +--------------------------- MODULE AssignmentNext --------------------------- +EXTENDS Integers +VARIABLE s + +Next(var) == \E val \in 0..1: (var' = val /\ var' > 0) + +Spec == s = 23 /\ [][Next(s)]_s + +Inv == s # 0 +============================================================================= diff --git a/tlatools/test-model/AssignmentNext2.cfg b/tlatools/test-model/AssignmentNext2.cfg new file mode 100644 index 0000000000000000000000000000000000000000..bc224b9def594b0819de3f0b45ede63ea8213c7b --- /dev/null +++ b/tlatools/test-model/AssignmentNext2.cfg @@ -0,0 +1,2 @@ +SPECIFICATION +Spec diff --git a/tlatools/test-model/AssignmentNext2.tla b/tlatools/test-model/AssignmentNext2.tla new file mode 100644 index 0000000000000000000000000000000000000000..fa337732111cdd97afea05adc986bdfb5ebdfd63 --- /dev/null +++ b/tlatools/test-model/AssignmentNext2.tla @@ -0,0 +1,9 @@ +--------------------------- MODULE AssignmentNext2 --------------------------- +EXTENDS Integers +VARIABLE s + +Next(var) == \E val \in 0..1: (var = val /\ var > 0) + +Spec == s = "" /\ [][Next(s')]_s + +============================================================================= diff --git a/tlatools/test-model/AssignmentNext3.cfg b/tlatools/test-model/AssignmentNext3.cfg new file mode 100644 index 0000000000000000000000000000000000000000..bc224b9def594b0819de3f0b45ede63ea8213c7b --- /dev/null +++ b/tlatools/test-model/AssignmentNext3.cfg @@ -0,0 +1,2 @@ +SPECIFICATION +Spec diff --git a/tlatools/test-model/AssignmentNext3.tla b/tlatools/test-model/AssignmentNext3.tla new file mode 100644 index 0000000000000000000000000000000000000000..9843f1a755efb077eb20961bbd4b304583d2bef8 --- /dev/null +++ b/tlatools/test-model/AssignmentNext3.tla @@ -0,0 +1,9 @@ +--------------------------- MODULE AssignmentNext3 --------------------------- +EXTENDS Integers +VARIABLE s + +F(var) == (var \in 0..9 /\ (var % 2 = 0)) + +Spec == s=0 /\ [][F(s')]_s + +============================================================================= diff --git a/tlatools/test-model/BagsTest.cfg b/tlatools/test-model/BagsTest.cfg new file mode 100644 index 0000000000000000000000000000000000000000..c03e566aa685ca74cda77763f103772eca0b2097 --- /dev/null +++ b/tlatools/test-model/BagsTest.cfg @@ -0,0 +1,4 @@ +SPECIFICATION +Spec +CONSTANT +C = C \ No newline at end of file diff --git a/tlatools/test-model/BagsTest.tla b/tlatools/test-model/BagsTest.tla new file mode 100644 index 0000000000000000000000000000000000000000..05dd61b84d5f28d64767b0da9a2b81c84c0ad831 --- /dev/null +++ b/tlatools/test-model/BagsTest.tla @@ -0,0 +1,199 @@ +---------------------------- MODULE BagsTest ---------------------------- +EXTENDS Bags, TLC, Integers + +CONSTANT C + +strings == {"a","b","c","d","e","f","g","h","i","j","k","l","m","n","o"} + +s == {"a","b","c"} + +a == <<>> +b == <<1,2,3>> +c == <<2,3,4>> +d == <<1,1,1>> +e == 1 :> 1 +f == 2 :> 1 +g == [x \in {1,2,3} |-> x * x] +h == [a |-> 3, b |-> 2, c |-> 1] + +ASSUME(EmptyBag = <<>>) + +\* IsABag +ASSUME(IsABag(a)) +ASSUME(IsABag(b)) +ASSUME(IsABag(c)) +ASSUME(IsABag(d)) +ASSUME(IsABag(e)) +ASSUME(IsABag(f)) +ASSUME(IsABag(g)) +ASSUME(IsABag(h)) + +ASSUME(IsABag(SetToBag({}))) + +t == <<0>> +u == 2 :> -1 +v == <<1,0>> +w == <<0,1>> +x == <<0,0,0>> +y == <<-1,-2,-3>> +z == <<"a","b","c">> + +ASSUME(~IsABag(t)) +ASSUME(~IsABag(u)) +ASSUME(~IsABag(v)) +ASSUME(~IsABag(w)) +ASSUME(~IsABag(x)) +ASSUME(~IsABag(y)) +ASSUME(~IsABag(z)) \* With the standard module TLC fails with "Cannot decide if element "a" is element of Nat" + +ASSUME(\A bool \in BOOLEAN : ~IsABag(bool)) +ASSUME(~IsABag(C)) + +\* BagCardinality +ASSUME(BagCardinality(a) = 0) +ASSUME(BagCardinality(b) = 6) +ASSUME(BagCardinality(c) = 9) +ASSUME(BagCardinality(d) = 3) +ASSUME(BagCardinality(e) = 1) +ASSUME(BagCardinality(f) = 1) +ASSUME(BagCardinality(g) = (1*1+2*2+3*3)) +ASSUME(BagCardinality(h) = 6) + +ASSUME(BagCardinality(<<1>>) = 1) +ASSUME(BagCardinality(<<1,3>>) = 4) + +\* BagIn +ASSUME(\A elem \in 1..10: ~BagIn(elem, a)) +ASSUME(\A elem \in 1..3: BagIn(elem, b)) +ASSUME(\A elem \in 4..10: ~BagIn(elem, b)) +ASSUME(\A dom \in DOMAIN c: BagIn(dom, c)) +ASSUME(\A dom \in DOMAIN d: BagIn(dom, d)) +ASSUME(\A dom \in DOMAIN e: BagIn(dom, e)) +ASSUME(\A dom \in DOMAIN f: BagIn(dom, f)) +ASSUME(\A dom \in DOMAIN f: BagIn(dom, f)) +ASSUME(\A dom \in DOMAIN g: BagIn(dom, g)) +ASSUME(\A dom \in DOMAIN h: BagIn(dom, h)) +ASSUME(\A str \in (strings \ DOMAIN h): ~BagIn(str, h)) +ASSUME(\A elem \in s : BagIn(elem, SetToBag(s))) + + +\* SetToBag and BagToSet +Codomain(fun) == {fun[i] : i \in DOMAIN fun} +ASSUME(SetToBag(s) = [elem \in s |-> 1]) +ASSUME(DOMAIN SetToBag(s) = s) +ASSUME(Codomain(SetToBag(s)) = {1}) +ASSUME(BagToSet(SetToBag(s)) = s) + +ASSUME(BagToSet(a) = {}) +ASSUME(BagToSet(b) = DOMAIN b) +ASSUME(BagToSet(c) = DOMAIN c) +ASSUME(BagToSet(d) = DOMAIN d) +ASSUME(BagToSet(e) = {1}) +ASSUME(BagToSet(f) = {2}) +ASSUME(BagToSet(g) = DOMAIN g) +ASSUME(BagToSet(h) = s) + + +\* CopiesIn +ASSUME(\A str \in strings: CopiesIn(str, a) = 0) + +ASSUME(\A dom \in DOMAIN b: CopiesIn(dom, b) = b[dom]) +ASSUME(\A elem \in 5..10: CopiesIn(elem, b) = 0) +ASSUME(\A dom \in DOMAIN c: CopiesIn(dom, c) = c[dom]) +ASSUME(\A elem \in 5..10: CopiesIn(elem, c) = 0) +ASSUME(\A dom \in DOMAIN d: CopiesIn(dom, d) = d[dom]) +ASSUME(\A elem \in 5..10: CopiesIn(elem, d) = 0) + +ASSUME(CopiesIn(1, e) = 1) +ASSUME(CopiesIn(2, f) = 1) +ASSUME(CopiesIn(1, f) = 0) +ASSUME(CopiesIn(1, 1 :> 0) = 0) +ASSUME(CopiesIn(2, 2 :> -1) = -1) + +ASSUME(\A dom \in DOMAIN g: CopiesIn(dom, g) = dom * dom) + +ASSUME(CopiesIn("a", h) = 3) +ASSUME(CopiesIn("b", h) = 2) +ASSUME(CopiesIn("c", h) = 1) +ASSUME(\A str \in (strings \ DOMAIN h): CopiesIn(str, h) = 0) + +ASSUME(\A aelem \in s: CopiesIn(aelem, [elem \in s |-> 42]) = 42) +ASSUME(\A aelem \in s: CopiesIn(aelem, [elem \in s |-> 0]) = 0) + + +\* (+) +ASSUME(EmptyBag (+) EmptyBag = EmptyBag) +ASSUME(a (+) a = a) +ASSUME(b (+) b = <<2,4,6>>) +ASSUME(b (+) EmptyBag = b) +ASSUME(c (+) c = <<4,6,8>>) +ASSUME(d (+) d = <<2,2,2>>) +ASSUME(e (+) e = <<2>>) +ASSUME(b (+) c = c (+) b) +ASSUME(b (+) c = <<3,5,7>>) +ASSUME(h (+) h = [a |-> 6, b |-> 4, c |-> 2]) +ASSUME([c |-> 3, d |-> 2, e |-> 1] (+) h = [a |-> 3, b |-> 2, c |-> 4, d |-> 2, e |-> 1]) + + +\* (-) +ASSUME(a (-) a = a) +ASSUME(b (-) b = EmptyBag) +ASSUME(b (-) EmptyBag = b) +ASSUME(c (-) c = EmptyBag) +ASSUME(d (-) d = EmptyBag) +ASSUME(e (-) e = EmptyBag) +ASSUME(b (-) c = b (-) c) +ASSUME(b (-) c = EmptyBag) +ASSUME(c (-) b = <<1,1,1>>) +ASSUME([c |-> 3, d |-> 2, e |-> 1] (-) h = [c |-> 2, d |-> 2, e |-> 1]) +ASSUME((1:>1) (-) EmptyBag = <<1>>) + + +\* BagUnion +ASSUME(BagUnion({a,a}) = a) +ASSUME(BagUnion({b,b}) = b) +ASSUME(BagUnion({c,c}) = c) +ASSUME(BagUnion({d,d}) = d) +ASSUME(BagUnion({e,e}) = e) +ASSUME(BagUnion({f,f}) = f) +ASSUME(BagUnion({g,g}) = g) +ASSUME(BagUnion({h,h}) = h) +ASSUME(BagUnion({h,h,[c |-> 3, d |-> 2, e |-> 1]}) = h (+) [c |-> 3, d |-> 2, e |-> 1]) +ASSUME(BagUnion({a,b,c,d}) = <<4,6,8>>) +ASSUME(BagUnion({EmptyBag, EmptyBag}) = <<>>) +ASSUME(BagUnion({[a |-> 3, b |-> 2, c |-> 1],[c |-> 3, d |-> 2, e |-> 1]}) = [a |-> 3, b |-> 2, c |-> 4, d |-> 2, e |-> 1]) +ASSUME(BagUnion({1 :> 2, 2 :> 2, 3 :> 3}) = (1 :> 2) (+) (2 :> 2) (+) (3 :> 3)) +ASSUME(BagUnion({<<>>, <<1,1,1>>, <<1,2,3,4>>}) = (1:>2 @@ 2:>3 @@ 3:>4 @@ 4:>4)) + + +\* \sqsubseteq +ASSUME((a \sqsubseteq a) = TRUE) +ASSUME((a \sqsubseteq b) = TRUE) +ASSUME((a \sqsubseteq c) = TRUE) +ASSUME((a \sqsubseteq d) = TRUE) +ASSUME((b \sqsubseteq b) = TRUE) +ASSUME((c \sqsubseteq c) = TRUE) +ASSUME((h \sqsubseteq [a |-> 3]) = FALSE) +ASSUME((h \sqsubseteq [a |-> 3, b |-> 2]) = FALSE) +ASSUME(([a |-> 3] \sqsubseteq h) = TRUE) +ASSUME(([a |-> 3, b |-> 2] \sqsubseteq h) = TRUE) +ASSUME(([a |-> 3, c |-> 1] \sqsubseteq h) = TRUE) +ASSUME(([b |-> 2, c |-> 1] \sqsubseteq h) = TRUE) +ASSUME((h \sqsubseteq h) = TRUE) + + +\* BagOfAll +ASSUME(BagOfAll(LAMBDA elem : elem, a) = a) +ASSUME(BagOfAll(LAMBDA elem : TRUE, a) = a) +ASSUME(BagOfAll(LAMBDA elem : elem, h) = h) +ASSUME(BagOfAll(LAMBDA elem : TRUE, h) = TRUE :> 6) +ASSUME(BagOfAll(LAMBDA elem : IF elem = "a" THEN "b" ELSE elem, [a |-> 3, b |-> 2, c |-> 1]) = [b |-> 5, c |-> 1]) + + +\* SubBag +\* Module overwrite does not overwrite SubBag + +\* A dummy spec to make TLC run model checking as required by BagsTest.java. +Spec == [][TRUE]_<<>> + +============================================================================= diff --git a/tlatools/test-model/BidirectionalTransitions.tla b/tlatools/test-model/BidirectionalTransitions.tla new file mode 100644 index 0000000000000000000000000000000000000000..13692344091ab32bb16f8c9eddfa4340e1837986 --- /dev/null +++ b/tlatools/test-model/BidirectionalTransitions.tla @@ -0,0 +1,28 @@ +-------- MODULE BidirectionalTransitions -------- +EXTENDS Naturals +VARIABLE x + +------------------------------------------------- + +A == \/ x' = (x + 1) % 3 +B == x' \in 0..2 +Spec1A == (x=0) /\ [][A \/ B]_x/\ WF_x(A) +Prop1A == Spec1A /\ WF_x(A) /\ []<><<A>>_x + +Fair1B == WF_x(B) +Spec1B == (x=0) /\ [][A \/ B]_x/\ Fair1B +Prop1Bx == WF_x(A) +Prop1By == []<><<A>>_x + +------------------------------------------------- + +C == x' = (x + 1) % 4 +D == IF x = 0 THEN x' = 3 ELSE x' = x - 1 +Spec2 == (x=0) /\ [][C \/ D]_x/\ WF_x(D) +Prop2 == Spec2 /\ WF_x(D) /\ []<><<D>>_x + +Fair2C == WF_x(C) +Spec2C == (x=0) /\ [][C \/ D]_x/\ Fair2C +Prop2Cx == WF_x(D) +Prop2Cy == []<><<D>>_x +================================================= diff --git a/tlatools/test-model/BidirectionalTransitions1.cfg b/tlatools/test-model/BidirectionalTransitions1.cfg new file mode 100644 index 0000000000000000000000000000000000000000..4dfeb6f715ebb0c65e5dc6ea88e7e21bb111121c --- /dev/null +++ b/tlatools/test-model/BidirectionalTransitions1.cfg @@ -0,0 +1,2 @@ +SPECIFICATION Spec1A +PROPERTY Prop1A \ No newline at end of file diff --git a/tlatools/test-model/BidirectionalTransitions1Bx.cfg b/tlatools/test-model/BidirectionalTransitions1Bx.cfg new file mode 100644 index 0000000000000000000000000000000000000000..e367efe8156a93bef21e9f069e2762959c275273 --- /dev/null +++ b/tlatools/test-model/BidirectionalTransitions1Bx.cfg @@ -0,0 +1,2 @@ +SPECIFICATION Spec1B +PROPERTY Prop1Bx \ No newline at end of file diff --git a/tlatools/test-model/BidirectionalTransitions1By.cfg b/tlatools/test-model/BidirectionalTransitions1By.cfg new file mode 100644 index 0000000000000000000000000000000000000000..5ea4ab4b101fc5cbd4f3e4960ce7762fae4793a8 --- /dev/null +++ b/tlatools/test-model/BidirectionalTransitions1By.cfg @@ -0,0 +1,2 @@ +SPECIFICATION Spec1B +PROPERTY Prop1By \ No newline at end of file diff --git a/tlatools/test-model/BidirectionalTransitions2.cfg b/tlatools/test-model/BidirectionalTransitions2.cfg new file mode 100644 index 0000000000000000000000000000000000000000..cf19795244dfcd43754275e2226f72f98622b353 --- /dev/null +++ b/tlatools/test-model/BidirectionalTransitions2.cfg @@ -0,0 +1,2 @@ +SPECIFICATION Spec2 +PROPERTY Prop2 \ No newline at end of file diff --git a/tlatools/test-model/BidirectionalTransitions2Cx.cfg b/tlatools/test-model/BidirectionalTransitions2Cx.cfg new file mode 100644 index 0000000000000000000000000000000000000000..89d65691a97406e4ea87144e83ee11f266b746b0 --- /dev/null +++ b/tlatools/test-model/BidirectionalTransitions2Cx.cfg @@ -0,0 +1,2 @@ +SPECIFICATION Spec2C +PROPERTY Prop2Cx \ No newline at end of file diff --git a/tlatools/test-model/BidirectionalTransitions2Cy.cfg b/tlatools/test-model/BidirectionalTransitions2Cy.cfg new file mode 100644 index 0000000000000000000000000000000000000000..8fdea573a4265780e569292ab97742c5b3b99a7a --- /dev/null +++ b/tlatools/test-model/BidirectionalTransitions2Cy.cfg @@ -0,0 +1,2 @@ +SPECIFICATION Spec2C +PROPERTY Prop2Cy \ No newline at end of file diff --git a/tlatools/test-model/Bug156/FindOp.tla b/tlatools/test-model/Bug156/FindOp.tla index 565d6a4166ec9be74f15724a037f9d77c2f84aeb..04f2226f73061387a7dff9a1088478688237375a 100644 --- a/tlatools/test-model/Bug156/FindOp.tla +++ b/tlatools/test-model/Bug156/FindOp.tla @@ -1,2474 +1,2474 @@ -Known Bugs: -- The operator may not be found if you click on a space inside its name, as in - "Bar (x) ! y" - -Known Features: -- Detects the ">" and "<" in " <3> " as operators. - -- If at a label that is also the name of an operator, then that operator - will be found. - -- An operator appearing inside a comment or a string will be found. - -------------------------------- MODULE FindOpWithoutPrint ------------------------------- -EXTENDS Integers, Sequences, TLC ------------------------------------------------------------------------------ -(***************************************************************************) -(* Operations for doing things in Java coordinates. *) -(***************************************************************************) -JavaSeq(seq) == [i \in 0 .. (Len(seq)-1) |-> seq[i+1]] - \* Turns a TLA sequence into a Java array. - -JavaSubseq(seq, b, e) == [i \in 0 .. (e-1-b) |-> seq[i+b]] - \* Corresponds to Java's Subseq String method. - -JavaLen(seq) == IF DOMAIN seq = {} - THEN 0 - ELSE 1 + - CHOOSE i \in DOMAIN seq : \A j \in DOMAIN seq : i >= j - \* Corresponds to Java's seq.size() for strings or seq.len for - \* arrays. - -JavaIndex(jarray, elt, fromPos) == - \* For jarray a Java array, it equals the smallest i >= fromPos such that - \* jarray[i] = elt, or -1 if there is no such i - LET Found(i) == jarray[i] = elt - lastIdx == JavaLen(jarray) - 1 - IN IF \E i \in fromPos .. lastIdx : Found(i) - THEN CHOOSE i \in fromPos .. lastIdx : /\ Found(i) - /\ \A j \in fromPos .. (i-1): ~Found(j) - ELSE -1 - -JavaSeqSeq(seq) == [i \in 0 .. (Len(seq)-1) |-> JavaSeq(seq[i+1])] - \* Converts a TLA sequence of TLA sequences to the corresponding - \* array of Java arrays - -JavaAppend(seq, elt) == LET len == JavaLen(seq) - IN [i \in 0..len |-> IF i = len THEN elt ELSE seq[i]] - \* Appends an element to the end of a Java sequence. - -JavaConcatenate(seq1, seq2) == LET len1 == JavaLen(seq1) - len2 == JavaLen(seq2) - IN [i \in 0..(len1 + len2-1) |-> - IF i < len1 THEN seq1[i] - ELSE seq2[i-len1]] - \* Concatenates two Java arrays - -IsJavaArray(array, S) == array \in [0..(JavaLen(array)-1) -> S] - \* True iff array is a Java array of elements of "type" S - -\* NullJavaArray == (-1 :> {}) ------------------------------------------------------------------------------ -(***************************************************************************) -(* The input. *) -(***************************************************************************) - -(***************************************************************************) -(* The contents of the current line of the module. *) -(***************************************************************************) -lineInput == JavaSeq(<< "f", "(" , "b", ")", \* " ", "!", " ", "a", "!" , " ", "g" , \* , " ", " ", " ", "(", "[", "x", "]", ")", " ", -\* "(", "e", ")", " ", -\* "<", "3", "4", ">", "a", "." -" " - >>) - - -(***************************************************************************) -(* The current position (in Java coordinates) of the cursor in the module. *) -(***************************************************************************) -currentPosition == 1 \* 7 ------------------------------------------------------------------------------ -Padding == JavaSeq(<<";", ";", ";", ";", ";", ";", ";", ";", ";", ";">>) - \* In an implementation ";" is replaced by an untypable character - - -GoingLeft == FALSE - \* TRUE would mean that we continue searching for the symbol to the left if - \* find that we're inside a subexpression selector. - -XSymbols == JavaSeqSeq(<< <<"(", "\\", "X", ")">> >>) - \* These are the Operators that contain "X". - \* Note that "\\X" is a TeXSymbol, not an Operator. - -Operators == JavaSeqSeq(<< -\* "-+->", "<=>", "...", "::=", "(+)", "(-)", "(.)", "(/)", "(\\X)", -\* "--", "**", "++", "<:", "<=", "<", ">=", "..", "||", "[]", "<>", -\* "/\\", "\\/", "//", "/", "/=", "~>", "=>", "=<", "=|", "^^", "##", -\* "|-", "|=", "&&", "$$", "??", "%%", "@@", "!!", ":>", ":=", "~", "=", -\* "#", "^", "-", "*", ">", "<", "+", "|", "&", "$", "%", "\\" -<< "(", "+", ")" >>, -<< "-", "+", "-", ">" >>, -<< "<", "=", ">" >>, -<< ".", ".", "." >>, -<< ":", ":", "=" >>, -<< "(", "-", ")" >>, -<< "(", ".", ")" >>, -<< "(", "/", ")" >>, -<< "(", "\\", "X", ")" >>, -<< "-", "-" >>, -<< "*", "*" >>, -<< "+", "+" >>, -<< "<", ":" >>, -<< "<", "=" >>, -<< "<", " " >>, -<< ">", "=" >>, -<< ".", "." >>, -<< "|", "|" >>, -<< "[", "]" >>, -<< "<", ">" >>, -<< "/", "\\" >>, -<< "\\", "/" >>, -<< "/", "/" >>, -<< "/", "=" >>, -<< "~", ">" >>, -<< "=", ">" >>, -<< "=", "<" >>, -<< "=", "|" >>, -<< "^", "^" >>, -<< "#", "#" >>, -<< "|", "-" >>, -<< "|", "=" >>, -<< "&", "&" >>, -<< "$", "$" >>, -<< "?", "?" >>, -<< "%", "%" >>, -<< "@", "@" >>, -<< "!", "!" >>, -<< ":", ">" >>, -<< ":", "=" >>, -<<"^", "+">>, -<<"^", "*">>, -<<"^", "#">>, -<<"~">>, -<<"=">>, -<<"#">>, -<<"^">>, -<<"-">>, -<<"*">>, -<<">">>, -<<"<">>, -<<"+">>, -<<"|">>, -<<"&">>, -<<"$">>, -<<"%">>, -<<"\\">>, -<<"'">> ->>) - -NonOperators == JavaSeqSeq( << -<<"=", "=">>, -<<"-", "-", "-">>, -<<"-", ">">>, -<<"<", "-">>, -<<"*", "*", "*">>, -<<"<", "<">>, -<<">", ">">> ->> ) - -\* ParenOperators == JavaSeqSeq( << -\* << "(", "+", ")" >>, -\* << "(", "-", ")" >>, -\* << "(", ".", ")" >>, -\* << "(", "/", ")" >>, -\* << "(", "\\", "X", ")" >> -\* >> ) - -TeXSymbols == JavaSeqSeq(<< -\* "\\neg", "\\lnot", "\\approx", -\* "\\asymp", "\\bigcirc", "\\bullet", "\\cap", "\\cdot", "\\circ", -\* "\\cong", "\\cup", "\\div", "\\doteq", "\\equiv", "\\geq", "\\gg", -\* "\\in", "\\intersect", "\\union", "\\land", "\\leq", "\\ll", "\\lor", -\* "\\mod", "\\o", "\\odot", "\\ominus", "\\oplus", "\\oslash", -\* "\\otimes", "\\prec", "\\preceq", "\\propto", "\\sim", "\\simeq", -\* "\\sqcap", "\\sqcup", "\\sqsubset", "\\sqsupset", "\\sqsubseteq", -\* "\\sqsupseteq", "\\star", "\\subset", "\\subseteq", "\\succ", -\* "\\succeq", "\\supset", "\\supseteq", "\\uplus", "\\wr", "\\notin", -\* "\\times", "\\X" -<<"\\", "n", "e", "g">>, -<<"\\", "l", "n", "o", "t">>, -<<"\\", "a", "p", "p", "r", "o", "x">>, -<<"\\", "a", "s", "y", "m", "p">>, -<<"\\", "b", "i", "g", "c", "i", "r", "c">>, -<<"\\", "b", "u", "l", "l", "e", "t">>, -<<"\\", "c", "a", "p">>, -<<"\\", "c", "d", "o", "t">>, -<<"\\", "c", "i", "r", "c">>, -<<"\\", "c", "o", "n", "g">>, -<<"\\", "c", "u", "p">>, -<<"\\", "d", "i", "v">>, -<<"\\", "d", "o", "t", "e", "q">>, -<<"\\", "e", "q", "u", "i", "v">>, -<<"\\", "g", "e", "q">>, -<<"\\", "g", "g">>, -<<"\\", "i", "n", "t", "e", "r", "s", "e", "c", "t">>, -<<"\\", "u", "n", "i", "o", "n">>, -<<"\\", "l", "a", "n", "d">>, -<<"\\", "l", "e", "q">>, -<<"\\", "l", "l">>, -<<"\\", "l", "o", "r">>, -<<"\\", "m", "o", "d">>, -<<"\\", "o", "d", "o", "t">>, -<<"\\", "o", "m", "i", "n", "u", "s">>, -<<"\\", "o", "p", "l", "u", "s">>, -<<"\\", "o", "s", "l", "a", "s", "h">>, -<<"\\", "o", "t", "i", "m", "e", "s">>, -<<"\\", "p", "r", "e", "c">>, -<<"\\", "p", "r", "e", "c", "e", "q", "">>, -<<"\\", "p", "r", "o", "p", "t", "o">>, -<<"\\", "s", "i", "m">>, -<<"\\", "s", "i", "m", "e", "q">>, -<<"\\", "s", "q", "c", "a", "p">>, -<<"\\", "s", "q", "c", "u", "p">>, -<<"\\", "s", "q", "s", "u", "b", "s", "e", "t">>, -<<"\\", "s", "q", "s", "u", "p", "s", "e", "t">>, -<<"\\", "s", "q", "s", "u", "b", "s", "e", "t", "e", "q">>, -<<"\\", "s", "q", "s", "u", "p", "s", "e", "t", "e", "q">>, -<<"\\", "s", "t", "a", "r">>, -<<"\\", "s", "u", "b", "s", "e", "t", "">>, -<<"\\", "s", "u", "b", "s", "e", "t", "e", "q">>, -<<"\\", "s", "u", "c", "c">>, -<<"\\", "s", "u", "c", "c", "e", "q">>, -<<"\\", "s", "u", "p", "s", "e", "t", "">>, -<<"\\", "s", "u", "p", "s", "e", "t", "e", "q">>, -<<"\\", "u", "p", "l", "u", "s">>, -<<"\\", "w", "r">>, -<<"\\", "n", "o", "t", "i", "n">>, -<<"\\", "t", "i", "m", "e", "s">>, -<<"\\", "o">>, -<<"\\", "i", "n">>, -<<"\\", "X">> ->>) - -IsNumber(seq) == /\ \A i \in DOMAIN seq : - seq[i] \in {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9"} - /\ (DOMAIN seq) # {} -IdChar == {"a", "b", "c", "d", "e", "f", "g", "h", "i", "n", (* ... *) "X", "Y", "Z", - "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "_"} - -RightDelimiters == JavaSeqSeq(<< <<")">>, <<"]">>, <<"}">>, <<">", ">">> >>) -LeftDelimiters == JavaSeqSeq(<< <<"(">>, <<"[">>, <<"{">>, <<"<", "<">> >>) ------------------------------------------------------------------------------ -TokenSpec == [token : STRING, leftPos : Nat, rightPos : Nat] -NullTokenSpec == [null |-> "null"] -(**************************************************************************** - ---algorithm FindOp { -variables - line = JavaConcatenate (Padding, JavaConcatenate(lineInput, Padding)); - foundTokenSpecs = << >> ; - \* Array records of form [token |-> string of a token, - \* leftPos |-> location in line of left of token, - \* rightPos |-> location in line of just to the - \* right of token] - lastToken = FALSE; \* true if there can be no tokens to right of first - \* one found - notLastToken = FALSE; \* true if we found a "!" to the right of the - \* first token found. - curPos = currentPosition + JavaLen(Padding) ; - returnVal; \* Use for returning values from a procedure call. - tempVar1 ; - tempVar2 ; - tempVar3 ; - -define -{ - FixOrigin(tokarray) == - [i \in DOMAIN tokarray |-> - [token |->tokarray[i].token, rightPos |-> tokarray[i].rightPos - JavaLen(Padding), - leftPos |-> tokarray[i].leftPos - JavaLen(Padding)]] - -}; - -macro vreturn(val) -{ returnVal := val; - return; -} - -\* Returns the TokenSpec for the token in the array tokArray of -\* Java strings (Java array of 1-character strings) in the line -\* containing the character at position pos, or NullTokenSpec -\* if there is none. -procedure findTokenIn(pos, tokArray) - variables i ; tokIdx ; - { i := 0; - while (i < JavaLen(tokArray)) - { tokIdx := JavaIndex(tokArray[i], line[pos], 0); - while (tokIdx # -1) - { with (ftlft = pos - tokIdx, - ftrt = pos - tokIdx + JavaLen(tokArray[i])) - { if (tokArray[i] = JavaSubseq(line, ftlft, ftrt)) - { vreturn([token |-> tokArray[i], - leftPos |-> ftlft, rightPos |-> ftrt]) - }; - }; - tokIdx := JavaIndex(tokArray[i], line[pos], tokIdx+1); - }; - i := i+1; - }; - vreturn(NullTokenSpec); - } - - -\* Returns the TokenSpec of the largest token containing digits, -\* letters, and "_" characters containing position pos. Returns -\* NullTokenSpec if that would be an empty token. -procedure findMaximalIdCharSeq(pos) - variables token = << >>; left = pos; rt = pos; - { \* token := << >>; - \* left := pos; - while (line[left-1] \in IdChar) - { left := left-1; - }; - while (line[rt] \in IdChar) - { rt := rt+1 - }; - if (left = rt) - { vreturn(NullTokenSpec) } - else - { vreturn([token |-> JavaSubseq(line, left, rt), - leftPos |-> left, rightPos |-> rt]) - } - } - -\* Returns the position of the left paren matching a ")" at pos-1. -\* If there is none, it returns -1. -procedure findMatchingLeftParen(pos) -{ if (line[pos-1] # ")") {vreturn(pos)}; - call findMatchingLeftInner(pos, 0); - return; - } - -procedure findMatchingLeftInner(pos, delim) - variables ipos = pos; jdelim; delimLen; -{ delimLen := JavaLen(LeftDelimiters[delim]); - ipos := pos - delimLen; - while (JavaSubseq(line, ipos-delimLen, ipos) # LeftDelimiters[delim]) - { if (line[ipos-1] = ";") {vreturn(-1)}; - ipos := ipos-1; - jdelim := 0; - while (jdelim < JavaLen(LeftDelimiters)) - { if (JavaSubseq(line, ipos-delimLen, ipos) = RightDelimiters[jdelim]) - { call findMatchingLeftInner(ipos, jdelim); - ipos := returnVal; - if (ipos < 0) { vreturn(-1)}; - jdelim := 99 ; \* exit while loop - }; - jdelim := jdelim+1; - } - }; - vreturn(ipos-delimLen); - } - -\* Returns the position of the right paren matching a "(" at position pos. -\* If there is none, it returns -1. -procedure findMatchingRightParen(pos) -{ if (line[pos] # "(") {vreturn(pos)}; - call findMatchingRightInner(pos, 0); - return; - } - -procedure findMatchingRightInner(pos, delim) - variables ipos = pos; jdelim; delimLen; -{ delimLen := JavaLen(RightDelimiters[delim]); - ipos := pos + delimLen; - while (JavaSubseq(line, ipos, ipos+delimLen) # RightDelimiters[delim]) - { if (line[ipos] = ";") {vreturn(-1)}; - ipos := ipos+1; - jdelim := 0; - while (jdelim < JavaLen(RightDelimiters)) - { if (JavaSubseq(line, ipos, ipos+delimLen) = LeftDelimiters[jdelim]) - { call findMatchingRightInner(ipos, jdelim); - ipos := returnVal; - if (ipos < 0) { vreturn(-1)}; - jdelim := 99 ; \* exit while loop - }; - jdelim := jdelim+1; - } - }; - vreturn(ipos+delimLen); - } - -\* Assumes that leftTok and rightTok are two TokenSpecs, -\* possibly NullTokenSpecs, separated by a ">". Checks -\* if they form a step name. If so, it returns the -\* TokenSpec for that step name; otherwise it returns -\* NullTokenSpec. -procedure checkIfStepName(leftTok, rightTok) -{ if (leftTok = NullTokenSpec \/ rightTok = NullTokenSpec) - { vreturn(NullTokenSpec) }; - if ( /\ IsNumber(leftTok.token) - /\ line[leftTok.leftPos-1] = "<" - /\ line[leftTok.leftPos-2] # "<" ) - { vreturn( [token |-> JavaSubseq(line, - leftTok.leftPos-1, - rightTok.rightPos), - leftPos |-> leftTok.leftPos-1, - rightPos |-> rightTok.rightPos] ) - }; - else { vreturn(NullTokenSpec) }; -} -procedure skipLeftOverSpaces() -{ while (line[curPos-1] = " ") { curPos := curPos - 1; }; - return; -} - -procedure skipRightOverSpaces() -{ while (line[curPos] = " ") { curPos := curPos+1; }; - return; -} - -\* Returns a TokenSpec array of possible tokens to be selected -\* when the cursor is at position curPos of line. It -\* returns a zero-length array if there are none. -\* -\* Known Bug: This will not work right in some cases when curPos -\* are spaces inside a name, as in -\* -\* Foo (x, y)!bar -\* ^ -procedure findTokenSpecs() -variables currentToken; left; rt; notDone; foundBangToken; -temp1; temp2; temp3; -{ \* Try to find the current TokenSpec and put it in foundTokenSpecs, - \* leaving curPos to the left of that token. If the token is a - \* step name, we can return that single TokenSpec as the spec. - \* If it is an unnamed step, or if it is not a possible identifier - \* or operator symbol, we return << >> (a zero-length array). - - \* First, we must check if we may be inside a string of form (" ")^* "!" (" ")^* - \* and, if so, move to its left, setting notLastToken to TRUE - if (line[curPos] = " ") - { call skipLeftOverSpaces() ; - if (line[curPos-1] = "!" /\ line[curPos-2] # "!") - { notLastToken := TRUE; - curPos := curPos - 1; - call skipLeftOverSpaces(); - } - } - else if (line[curPos] = "!" /\ line[curPos-1] # "!" /\ line[curPos+1] # "!") - { notLastToken := TRUE; - call skipLeftOverSpaces() - }; - - \* If we're to the right of a ")", we could be inside something like - \* - \* Foo(42) ! bar - \* ^ - \* so we set notLastToken true and move to the left of the parentesized string. - if ( /\ \/ notLastToken - \/ line[curPos] = " " - /\ line[curPos-1] = ")") - { notLastToken := TRUE; - call findMatchingLeftParen(curPos); - if (returnVal < 0) {vreturn(<< >>)}; - curPos := returnVal; - call skipLeftOverSpaces(); - }; - \* If notLastToken is true, then we should be just to the right - \* of the token we are expecting to find to the left of the "!" or the - \* parenthesized expression. - \* Otherwise, if we're not at an IdChar or at an operator, then - \* let's try one character to the left. - \* Note: this happens to work if we're right before "<4>7" or "\in" - \* because both "<" and "\\" are operators. - - if (notLastToken) { curPos := curPos - 1; }; - else - { if (line[curPos] \notin IdChar) - { call findTokenIn(curPos, Operators); - if ( returnVal = NullTokenSpec ) - { curPos := curPos - 1 - } - } - - } ; -\* \* Following code replaced by preceding "else" clause -\* -\* \* If we didn't move left and we're at a "(" or "." that's not the beginning -\* \* of an operator, let's move left 1. -\* if (~ notLastToken /\ line[curPos] = "(") -\* { call findTokenIn(curPos, ParenOperators); -\* if (returnVal = NullTokenSpec) { curPos := curPos - 1 } -\* } -\* else if ( /\ ~ notLastToken -\* /\ line[curPos] = "." -\* /\ line[curPos+1] # "." -\* /\ line[curPos-1] # "." ) -\* { curPos := curPos - 1 }; -\* -\* \* If we're at a space (which means we haven't found a "!" or moved left -\* \* over a parenthesized string), then try as if the cursor were -\* \* one character to the left. -\* if (line[curPos] = " ") { curPos := curPos - 1}; - - if (line[curPos] \in IdChar) - { \* Check if we're at an "X" that is inside an "\X" or "\X". - \* Note: this works because there are no TeXSymbols besides - if (line[curPos] = "X") { call findTokenIn(curPos, XSymbols); } - else {returnVal := NullTokenSpec} ; - if (returnVal # NullTokenSpec) - { foundTokenSpecs := JavaSeq(<<returnVal>>); - curPos := returnVal.leftPos; - lastToken := TRUE; - } - else - { call findMaximalIdCharSeq(curPos); - currentToken := returnVal; - \* Check if token is part of step name. If it is, we set - \* First, see if currentToken could be the token after the "<...>". - if ( line[currentToken.leftPos-1] = ">" ) - { call findMaximalIdCharSeq(currentToken.leftPos - 1); - call checkIfStepName(returnVal, currentToken); - if (returnVal # NullTokenSpec) - { foundTokenSpecs := JavaSeq(<<returnVal>>); - vreturn(FixOrigin(foundTokenSpecs)); - } - }; - \* Next see if it's the token to the left of the ">" - if (line[currentToken.rightPos] = ">" ) - { call findMaximalIdCharSeq(currentToken.rightPos+1); - call checkIfStepName(currentToken, returnVal); - if (returnVal # NullTokenSpec) - { foundTokenSpecs := JavaSeq(<<returnVal>>); - vreturn(FixOrigin(foundTokenSpecs)); - } - }; - - \* Next, check if it's a number, and abort if it is. - if (IsNumber(currentToken.token)) - { vreturn(<< >>) - }; - - \* Next, check if the token appears as "\\"token, in - \* which case we adjust currentTok - left := currentToken.leftPos ; - rt := currentToken.rightPos ; - if ( /\ line[left-1] = "\\" - /\ line[left-2] # "\\" - /\ \E ii \in DOMAIN TeXSymbols : - JavaSubseq(line, left-1, rt) = TeXSymbols[ii] ) - { lastToken := TRUE; - currentToken.leftPos := left - 1 || - currentToken.token := JavaSubseq(line, left-1, rt); - }; - foundTokenSpecs := - JavaSeq( << currentToken >>); - curPos := left; - } - } - else - { call findTokenIn(curPos, Operators); - currentToken := returnVal; - if (currentToken = NullTokenSpec) - { vreturn(<< >>); - }; - call findTokenIn(curPos, NonOperators); - if (returnVal # NullTokenSpec) - { vreturn(<< >>); - }; - - \* Need to check if beginning or end of level specifier - \* of step number. - if ( currentToken.token = JavaSeq(<< "<" >>) ) - { call findMaximalIdCharSeq(currentToken.rightPos); - temp1 := returnVal; - if (/\ temp1 # NullTokenSpec - /\ line[temp1.rightPos] = ">") - { call findMaximalIdCharSeq(temp1.rightPos + 1); - temp2 := returnVal; - call checkIfStepName(temp1, temp2); - if (returnVal # NullTokenSpec) - { foundTokenSpecs := JavaSeq(<<returnVal>>); - vreturn(FixOrigin(foundTokenSpecs)); - } - } - } - else if (currentToken.token = JavaSeq(<< ">" >>)) - { call findMaximalIdCharSeq(currentToken.leftPos); - temp1 := returnVal; - call findMaximalIdCharSeq(currentToken.rightPos); - temp2 := returnVal; - call checkIfStepName(temp1, temp2); - if (returnVal # NullTokenSpec) - { foundTokenSpecs := JavaSeq(<<returnVal>>); - vreturn(FixOrigin(foundTokenSpecs)); - } - }; - - \* We check for the case of a "\\" starting a TeX token. - if (currentToken.token = JavaSeq(<<"\\">>)) - { call findMaximalIdCharSeq(currentToken.rightPos); - if ( /\ returnVal # NullTokenSpec - /\ \E ii \in DOMAIN TeXSymbols : - JavaSubseq(line, - currentToken.leftPos, - returnVal.rightPos) = TeXSymbols[ii] ) - { currentToken.rightPos := returnVal.rightPos || - currentToken.token := JavaSubseq(line, - currentToken.leftPos, - returnVal.rightPos) - } - }; - - foundTokenSpecs := JavaSeq(<<currentToken>>); - curPos := currentToken.leftPos; - lastToken := TRUE; - }; - - assert JavaLen(foundTokenSpecs) = 1; - -\*print <<"1", foundTokenSpecs[0]>>; - \** We have now found the base token. We now look to find extenders - \* to the left of the form idtoken "!". These must be part of the - \* name, so we prepend them to foundTokenSpecs[0]. - curPos := foundTokenSpecs[0].leftPos; - notDone := TRUE; - while (notDone) - { call skipLeftOverSpaces(); - if (curPos < 0 \/ line[curPos-1] = ";") - { notDone := FALSE; } - else - { if (line[curPos-1] = "!" /\ (line[curPos-2] # "!")) - { curPos := curPos - 1; - call skipLeftOverSpaces(); - call findMatchingLeftParen(curPos); - curPos := returnVal; - if (curPos < 0) { notDone := FALSE } - else - { call skipLeftOverSpaces(); - call findMaximalIdCharSeq(curPos); - currentToken := returnVal ; - if ( \/ currentToken = NullTokenSpec - \/ IsNumber(currentToken.token) ) - { notDone := FALSE } - else - { curPos := currentToken.leftPos; - foundTokenSpecs[0] := - [token |-> JavaConcatenate( - JavaAppend(currentToken.token, "!"), - foundTokenSpecs[0].token), - leftPos |-> curPos, - rightPos |-> foundTokenSpecs[0].rightPos]; - } - } - } - else {notDone := FALSE;} - }; - - }; - - assert foundTokenSpecs # << >> ; - - if (lastToken = TRUE) {vreturn(FixOrigin(foundTokenSpecs))}; - -\* print <<"2", foundTokenSpecs[0]>>; - - curPos := foundTokenSpecs[0].rightPos; - foundBangToken := FALSE; - notDone := TRUE; - while (notDone) - { call skipRightOverSpaces(); - call findMatchingRightParen(curPos); - curPos := returnVal; - if (curPos < 0) {notDone := FALSE} - else - { call skipRightOverSpaces(); - if (line[curPos] # "!" \/ line[curPos+1] = "!") - { notDone := FALSE } - else - { curPos := curPos + 1; - call skipRightOverSpaces(); - call findMaximalIdCharSeq(curPos); - currentToken := returnVal ; - if ( \/ currentToken = NullTokenSpec - \/ IsNumber(currentToken.token) ) - { notDone := FALSE } - else - { foundBangToken := TRUE; - foundTokenSpecs := - JavaConcatenate( - JavaSeq( - << [token |-> - JavaConcatenate(JavaAppend(foundTokenSpecs[0].token, - "!"), - currentToken.token), - leftPos |-> foundTokenSpecs[0].leftPos, - rightPos |-> currentToken.rightPos] - >> ) , - foundTokenSpecs); - curPos := currentToken.rightPos; - } - } - } - }; - - if (notLastToken /\ ~ foundBangToken) { vreturn (<< >>) }; - - vreturn(FixOrigin(foundTokenSpecs)); -} \* end findTokenSpecs - -\* Main Body: -{ curPos := JavaLen(Padding); - tempVar1 := (-1 :> ""); - tempVar2 := JavaLen(Padding); -\*curPos := 15; -\*call findTokenSpecs(); -\*print returnVal; -\* print line; - while (tempVar2 < JavaLen(lineInput) + JavaLen(Padding)) - { \* print <<"curPos: ", tempVar2>>; - curPos := tempVar2; - foundTokenSpecs := << >> ; - lastToken := FALSE; - notLastToken := FALSE; - call findTokenSpecs(); -\* if (tempVar2 >= 0 + JavaLen(Padding)) -\* { print "done with run" ; -\* assert FALSE -\* }; -\* if (returnVal = tempVar1) -\* { print "same" \* ,"lastToken: ", lastToken, ", notLastToken: ", notLastToken>> -\* } -\* else -\* { print returnVal; - \* print <<"lastToken: ", lastToken, ", notLastToken: ", notLastToken>>; -\* }; - tempVar1 := returnVal; - tempVar2 := tempVar2 + 1; - }; - print "goodby world"; - -} \* end Main Body -} \* End algorithm - -*****************************************************************************************) -\* BEGIN TRANSLATION -\* Procedure variable left of procedure findMaximalIdCharSeq at line 317 col 28 changed to left_ -\* Procedure variable rt of procedure findMaximalIdCharSeq at line 317 col 40 changed to rt_ -\* Procedure variable ipos of procedure findMatchingLeftInner at line 343 col 12 changed to ipos_ -\* Procedure variable jdelim of procedure findMatchingLeftInner at line 343 col 24 changed to jdelim_ -\* Procedure variable delimLen of procedure findMatchingLeftInner at line 343 col 32 changed to delimLen_ -\* Parameter pos of procedure findTokenIn at line 292 col 23 changed to pos_ -\* Parameter pos of procedure findMaximalIdCharSeq at line 316 col 32 changed to pos_f -\* Parameter pos of procedure findMatchingLeftParen at line 336 col 33 changed to pos_fi -\* Parameter pos of procedure findMatchingLeftInner at line 342 col 33 changed to pos_fin -\* Parameter delim of procedure findMatchingLeftInner at line 342 col 38 changed to delim_ -\* Parameter pos of procedure findMatchingRightParen at line 365 col 34 changed to pos_find -CONSTANT defaultInitValue +Known Bugs: +- The operator may not be found if you click on a space inside its name, as in + "Bar (x) ! y" + +Known Features: +- Detects the ">" and "<" in " <3> " as operators. + +- If at a label that is also the name of an operator, then that operator + will be found. + +- An operator appearing inside a comment or a string will be found. + +------------------------------- MODULE FindOpWithoutPrint ------------------------------- +EXTENDS Integers, Sequences, TLC +----------------------------------------------------------------------------- +(***************************************************************************) +(* Operations for doing things in Java coordinates. *) +(***************************************************************************) +JavaSeq(seq) == [i \in 0 .. (Len(seq)-1) |-> seq[i+1]] + \* Turns a TLA sequence into a Java array. + +JavaSubseq(seq, b, e) == [i \in 0 .. (e-1-b) |-> seq[i+b]] + \* Corresponds to Java's Subseq String method. + +JavaLen(seq) == IF DOMAIN seq = {} + THEN 0 + ELSE 1 + + CHOOSE i \in DOMAIN seq : \A j \in DOMAIN seq : i >= j + \* Corresponds to Java's seq.size() for strings or seq.len for + \* arrays. + +JavaIndex(jarray, elt, fromPos) == + \* For jarray a Java array, it equals the smallest i >= fromPos such that + \* jarray[i] = elt, or -1 if there is no such i + LET Found(i) == jarray[i] = elt + lastIdx == JavaLen(jarray) - 1 + IN IF \E i \in fromPos .. lastIdx : Found(i) + THEN CHOOSE i \in fromPos .. lastIdx : /\ Found(i) + /\ \A j \in fromPos .. (i-1): ~Found(j) + ELSE -1 + +JavaSeqSeq(seq) == [i \in 0 .. (Len(seq)-1) |-> JavaSeq(seq[i+1])] + \* Converts a TLA sequence of TLA sequences to the corresponding + \* array of Java arrays + +JavaAppend(seq, elt) == LET len == JavaLen(seq) + IN [i \in 0..len |-> IF i = len THEN elt ELSE seq[i]] + \* Appends an element to the end of a Java sequence. + +JavaConcatenate(seq1, seq2) == LET len1 == JavaLen(seq1) + len2 == JavaLen(seq2) + IN [i \in 0..(len1 + len2-1) |-> + IF i < len1 THEN seq1[i] + ELSE seq2[i-len1]] + \* Concatenates two Java arrays + +IsJavaArray(array, S) == array \in [0..(JavaLen(array)-1) -> S] + \* True iff array is a Java array of elements of "type" S + +\* NullJavaArray == (-1 :> {}) +----------------------------------------------------------------------------- +(***************************************************************************) +(* The input. *) +(***************************************************************************) + +(***************************************************************************) +(* The contents of the current line of the module. *) +(***************************************************************************) +lineInput == JavaSeq(<< "f", "(" , "b", ")", \* " ", "!", " ", "a", "!" , " ", "g" , \* , " ", " ", " ", "(", "[", "x", "]", ")", " ", +\* "(", "e", ")", " ", +\* "<", "3", "4", ">", "a", "." +" " + >>) + + +(***************************************************************************) +(* The current position (in Java coordinates) of the cursor in the module. *) +(***************************************************************************) +currentPosition == 1 \* 7 +----------------------------------------------------------------------------- +Padding == JavaSeq(<<";", ";", ";", ";", ";", ";", ";", ";", ";", ";">>) + \* In an implementation ";" is replaced by an untypable character + + +GoingLeft == FALSE + \* TRUE would mean that we continue searching for the symbol to the left if + \* find that we're inside a subexpression selector. + +XSymbols == JavaSeqSeq(<< <<"(", "\\", "X", ")">> >>) + \* These are the Operators that contain "X". + \* Note that "\\X" is a TeXSymbol, not an Operator. + +Operators == JavaSeqSeq(<< +\* "-+->", "<=>", "...", "::=", "(+)", "(-)", "(.)", "(/)", "(\\X)", +\* "--", "**", "++", "<:", "<=", "<", ">=", "..", "||", "[]", "<>", +\* "/\\", "\\/", "//", "/", "/=", "~>", "=>", "=<", "=|", "^^", "##", +\* "|-", "|=", "&&", "$$", "??", "%%", "@@", "!!", ":>", ":=", "~", "=", +\* "#", "^", "-", "*", ">", "<", "+", "|", "&", "$", "%", "\\" +<< "(", "+", ")" >>, +<< "-", "+", "-", ">" >>, +<< "<", "=", ">" >>, +<< ".", ".", "." >>, +<< ":", ":", "=" >>, +<< "(", "-", ")" >>, +<< "(", ".", ")" >>, +<< "(", "/", ")" >>, +<< "(", "\\", "X", ")" >>, +<< "-", "-" >>, +<< "*", "*" >>, +<< "+", "+" >>, +<< "<", ":" >>, +<< "<", "=" >>, +<< "<", " " >>, +<< ">", "=" >>, +<< ".", "." >>, +<< "|", "|" >>, +<< "[", "]" >>, +<< "<", ">" >>, +<< "/", "\\" >>, +<< "\\", "/" >>, +<< "/", "/" >>, +<< "/", "=" >>, +<< "~", ">" >>, +<< "=", ">" >>, +<< "=", "<" >>, +<< "=", "|" >>, +<< "^", "^" >>, +<< "#", "#" >>, +<< "|", "-" >>, +<< "|", "=" >>, +<< "&", "&" >>, +<< "$", "$" >>, +<< "?", "?" >>, +<< "%", "%" >>, +<< "@", "@" >>, +<< "!", "!" >>, +<< ":", ">" >>, +<< ":", "=" >>, +<<"^", "+">>, +<<"^", "*">>, +<<"^", "#">>, +<<"~">>, +<<"=">>, +<<"#">>, +<<"^">>, +<<"-">>, +<<"*">>, +<<">">>, +<<"<">>, +<<"+">>, +<<"|">>, +<<"&">>, +<<"$">>, +<<"%">>, +<<"\\">>, +<<"'">> +>>) + +NonOperators == JavaSeqSeq( << +<<"=", "=">>, +<<"-", "-", "-">>, +<<"-", ">">>, +<<"<", "-">>, +<<"*", "*", "*">>, +<<"<", "<">>, +<<">", ">">> +>> ) + +\* ParenOperators == JavaSeqSeq( << +\* << "(", "+", ")" >>, +\* << "(", "-", ")" >>, +\* << "(", ".", ")" >>, +\* << "(", "/", ")" >>, +\* << "(", "\\", "X", ")" >> +\* >> ) + +TeXSymbols == JavaSeqSeq(<< +\* "\\neg", "\\lnot", "\\approx", +\* "\\asymp", "\\bigcirc", "\\bullet", "\\cap", "\\cdot", "\\circ", +\* "\\cong", "\\cup", "\\div", "\\doteq", "\\equiv", "\\geq", "\\gg", +\* "\\in", "\\intersect", "\\union", "\\land", "\\leq", "\\ll", "\\lor", +\* "\\mod", "\\o", "\\odot", "\\ominus", "\\oplus", "\\oslash", +\* "\\otimes", "\\prec", "\\preceq", "\\propto", "\\sim", "\\simeq", +\* "\\sqcap", "\\sqcup", "\\sqsubset", "\\sqsupset", "\\sqsubseteq", +\* "\\sqsupseteq", "\\star", "\\subset", "\\subseteq", "\\succ", +\* "\\succeq", "\\supset", "\\supseteq", "\\uplus", "\\wr", "\\notin", +\* "\\times", "\\X" +<<"\\", "n", "e", "g">>, +<<"\\", "l", "n", "o", "t">>, +<<"\\", "a", "p", "p", "r", "o", "x">>, +<<"\\", "a", "s", "y", "m", "p">>, +<<"\\", "b", "i", "g", "c", "i", "r", "c">>, +<<"\\", "b", "u", "l", "l", "e", "t">>, +<<"\\", "c", "a", "p">>, +<<"\\", "c", "d", "o", "t">>, +<<"\\", "c", "i", "r", "c">>, +<<"\\", "c", "o", "n", "g">>, +<<"\\", "c", "u", "p">>, +<<"\\", "d", "i", "v">>, +<<"\\", "d", "o", "t", "e", "q">>, +<<"\\", "e", "q", "u", "i", "v">>, +<<"\\", "g", "e", "q">>, +<<"\\", "g", "g">>, +<<"\\", "i", "n", "t", "e", "r", "s", "e", "c", "t">>, +<<"\\", "u", "n", "i", "o", "n">>, +<<"\\", "l", "a", "n", "d">>, +<<"\\", "l", "e", "q">>, +<<"\\", "l", "l">>, +<<"\\", "l", "o", "r">>, +<<"\\", "m", "o", "d">>, +<<"\\", "o", "d", "o", "t">>, +<<"\\", "o", "m", "i", "n", "u", "s">>, +<<"\\", "o", "p", "l", "u", "s">>, +<<"\\", "o", "s", "l", "a", "s", "h">>, +<<"\\", "o", "t", "i", "m", "e", "s">>, +<<"\\", "p", "r", "e", "c">>, +<<"\\", "p", "r", "e", "c", "e", "q", "">>, +<<"\\", "p", "r", "o", "p", "t", "o">>, +<<"\\", "s", "i", "m">>, +<<"\\", "s", "i", "m", "e", "q">>, +<<"\\", "s", "q", "c", "a", "p">>, +<<"\\", "s", "q", "c", "u", "p">>, +<<"\\", "s", "q", "s", "u", "b", "s", "e", "t">>, +<<"\\", "s", "q", "s", "u", "p", "s", "e", "t">>, +<<"\\", "s", "q", "s", "u", "b", "s", "e", "t", "e", "q">>, +<<"\\", "s", "q", "s", "u", "p", "s", "e", "t", "e", "q">>, +<<"\\", "s", "t", "a", "r">>, +<<"\\", "s", "u", "b", "s", "e", "t", "">>, +<<"\\", "s", "u", "b", "s", "e", "t", "e", "q">>, +<<"\\", "s", "u", "c", "c">>, +<<"\\", "s", "u", "c", "c", "e", "q">>, +<<"\\", "s", "u", "p", "s", "e", "t", "">>, +<<"\\", "s", "u", "p", "s", "e", "t", "e", "q">>, +<<"\\", "u", "p", "l", "u", "s">>, +<<"\\", "w", "r">>, +<<"\\", "n", "o", "t", "i", "n">>, +<<"\\", "t", "i", "m", "e", "s">>, +<<"\\", "o">>, +<<"\\", "i", "n">>, +<<"\\", "X">> +>>) + +IsNumber(seq) == /\ \A i \in DOMAIN seq : + seq[i] \in {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9"} + /\ (DOMAIN seq) # {} +IdChar == {"a", "b", "c", "d", "e", "f", "g", "h", "i", "n", (* ... *) "X", "Y", "Z", + "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "_"} + +RightDelimiters == JavaSeqSeq(<< <<")">>, <<"]">>, <<"}">>, <<">", ">">> >>) +LeftDelimiters == JavaSeqSeq(<< <<"(">>, <<"[">>, <<"{">>, <<"<", "<">> >>) +----------------------------------------------------------------------------- +TokenSpec == [token : STRING, leftPos : Nat, rightPos : Nat] +NullTokenSpec == [null |-> "null"] +(**************************************************************************** + +--algorithm FindOp { +variables + line = JavaConcatenate (Padding, JavaConcatenate(lineInput, Padding)); + foundTokenSpecs = << >> ; + \* Array records of form [token |-> string of a token, + \* leftPos |-> location in line of left of token, + \* rightPos |-> location in line of just to the + \* right of token] + lastToken = FALSE; \* true if there can be no tokens to right of first + \* one found + notLastToken = FALSE; \* true if we found a "!" to the right of the + \* first token found. + curPos = currentPosition + JavaLen(Padding) ; + returnVal; \* Use for returning values from a procedure call. + tempVar1 ; + tempVar2 ; + tempVar3 ; + +define +{ + FixOrigin(tokarray) == + [i \in DOMAIN tokarray |-> + [token |->tokarray[i].token, rightPos |-> tokarray[i].rightPos - JavaLen(Padding), + leftPos |-> tokarray[i].leftPos - JavaLen(Padding)]] + +}; + +macro vreturn(val) +{ returnVal := val; + return; +} + +\* Returns the TokenSpec for the token in the array tokArray of +\* Java strings (Java array of 1-character strings) in the line +\* containing the character at position pos, or NullTokenSpec +\* if there is none. +procedure findTokenIn(pos, tokArray) + variables i ; tokIdx ; + { i := 0; + while (i < JavaLen(tokArray)) + { tokIdx := JavaIndex(tokArray[i], line[pos], 0); + while (tokIdx # -1) + { with (ftlft = pos - tokIdx, + ftrt = pos - tokIdx + JavaLen(tokArray[i])) + { if (tokArray[i] = JavaSubseq(line, ftlft, ftrt)) + { vreturn([token |-> tokArray[i], + leftPos |-> ftlft, rightPos |-> ftrt]) + }; + }; + tokIdx := JavaIndex(tokArray[i], line[pos], tokIdx+1); + }; + i := i+1; + }; + vreturn(NullTokenSpec); + } + + +\* Returns the TokenSpec of the largest token containing digits, +\* letters, and "_" characters containing position pos. Returns +\* NullTokenSpec if that would be an empty token. +procedure findMaximalIdCharSeq(pos) + variables token = << >>; left = pos; rt = pos; + { \* token := << >>; + \* left := pos; + while (line[left-1] \in IdChar) + { left := left-1; + }; + while (line[rt] \in IdChar) + { rt := rt+1 + }; + if (left = rt) + { vreturn(NullTokenSpec) } + else + { vreturn([token |-> JavaSubseq(line, left, rt), + leftPos |-> left, rightPos |-> rt]) + } + } + +\* Returns the position of the left paren matching a ")" at pos-1. +\* If there is none, it returns -1. +procedure findMatchingLeftParen(pos) +{ if (line[pos-1] # ")") {vreturn(pos)}; + call findMatchingLeftInner(pos, 0); + return; + } + +procedure findMatchingLeftInner(pos, delim) + variables ipos = pos; jdelim; delimLen; +{ delimLen := JavaLen(LeftDelimiters[delim]); + ipos := pos - delimLen; + while (JavaSubseq(line, ipos-delimLen, ipos) # LeftDelimiters[delim]) + { if (line[ipos-1] = ";") {vreturn(-1)}; + ipos := ipos-1; + jdelim := 0; + while (jdelim < JavaLen(LeftDelimiters)) + { if (JavaSubseq(line, ipos-delimLen, ipos) = RightDelimiters[jdelim]) + { call findMatchingLeftInner(ipos, jdelim); + ipos := returnVal; + if (ipos < 0) { vreturn(-1)}; + jdelim := 99 ; \* exit while loop + }; + jdelim := jdelim+1; + } + }; + vreturn(ipos-delimLen); + } + +\* Returns the position of the right paren matching a "(" at position pos. +\* If there is none, it returns -1. +procedure findMatchingRightParen(pos) +{ if (line[pos] # "(") {vreturn(pos)}; + call findMatchingRightInner(pos, 0); + return; + } + +procedure findMatchingRightInner(pos, delim) + variables ipos = pos; jdelim; delimLen; +{ delimLen := JavaLen(RightDelimiters[delim]); + ipos := pos + delimLen; + while (JavaSubseq(line, ipos, ipos+delimLen) # RightDelimiters[delim]) + { if (line[ipos] = ";") {vreturn(-1)}; + ipos := ipos+1; + jdelim := 0; + while (jdelim < JavaLen(RightDelimiters)) + { if (JavaSubseq(line, ipos, ipos+delimLen) = LeftDelimiters[jdelim]) + { call findMatchingRightInner(ipos, jdelim); + ipos := returnVal; + if (ipos < 0) { vreturn(-1)}; + jdelim := 99 ; \* exit while loop + }; + jdelim := jdelim+1; + } + }; + vreturn(ipos+delimLen); + } + +\* Assumes that leftTok and rightTok are two TokenSpecs, +\* possibly NullTokenSpecs, separated by a ">". Checks +\* if they form a step name. If so, it returns the +\* TokenSpec for that step name; otherwise it returns +\* NullTokenSpec. +procedure checkIfStepName(leftTok, rightTok) +{ if (leftTok = NullTokenSpec \/ rightTok = NullTokenSpec) + { vreturn(NullTokenSpec) }; + if ( /\ IsNumber(leftTok.token) + /\ line[leftTok.leftPos-1] = "<" + /\ line[leftTok.leftPos-2] # "<" ) + { vreturn( [token |-> JavaSubseq(line, + leftTok.leftPos-1, + rightTok.rightPos), + leftPos |-> leftTok.leftPos-1, + rightPos |-> rightTok.rightPos] ) + }; + else { vreturn(NullTokenSpec) }; +} +procedure skipLeftOverSpaces() +{ while (line[curPos-1] = " ") { curPos := curPos - 1; }; + return; +} + +procedure skipRightOverSpaces() +{ while (line[curPos] = " ") { curPos := curPos+1; }; + return; +} + +\* Returns a TokenSpec array of possible tokens to be selected +\* when the cursor is at position curPos of line. It +\* returns a zero-length array if there are none. +\* +\* Known Bug: This will not work right in some cases when curPos +\* are spaces inside a name, as in +\* +\* Foo (x, y)!bar +\* ^ +procedure findTokenSpecs() +variables currentToken; left; rt; notDone; foundBangToken; +temp1; temp2; temp3; +{ \* Try to find the current TokenSpec and put it in foundTokenSpecs, + \* leaving curPos to the left of that token. If the token is a + \* step name, we can return that single TokenSpec as the spec. + \* If it is an unnamed step, or if it is not a possible identifier + \* or operator symbol, we return << >> (a zero-length array). + + \* First, we must check if we may be inside a string of form (" ")^* "!" (" ")^* + \* and, if so, move to its left, setting notLastToken to TRUE + if (line[curPos] = " ") + { call skipLeftOverSpaces() ; + if (line[curPos-1] = "!" /\ line[curPos-2] # "!") + { notLastToken := TRUE; + curPos := curPos - 1; + call skipLeftOverSpaces(); + } + } + else if (line[curPos] = "!" /\ line[curPos-1] # "!" /\ line[curPos+1] # "!") + { notLastToken := TRUE; + call skipLeftOverSpaces() + }; + + \* If we're to the right of a ")", we could be inside something like + \* + \* Foo(42) ! bar + \* ^ + \* so we set notLastToken true and move to the left of the parentesized string. + if ( /\ \/ notLastToken + \/ line[curPos] = " " + /\ line[curPos-1] = ")") + { notLastToken := TRUE; + call findMatchingLeftParen(curPos); + if (returnVal < 0) {vreturn(<< >>)}; + curPos := returnVal; + call skipLeftOverSpaces(); + }; + \* If notLastToken is true, then we should be just to the right + \* of the token we are expecting to find to the left of the "!" or the + \* parenthesized expression. + \* Otherwise, if we're not at an IdChar or at an operator, then + \* let's try one character to the left. + \* Note: this happens to work if we're right before "<4>7" or "\in" + \* because both "<" and "\\" are operators. + + if (notLastToken) { curPos := curPos - 1; }; + else + { if (line[curPos] \notin IdChar) + { call findTokenIn(curPos, Operators); + if ( returnVal = NullTokenSpec ) + { curPos := curPos - 1 + } + } + + } ; +\* \* Following code replaced by preceding "else" clause +\* +\* \* If we didn't move left and we're at a "(" or "." that's not the beginning +\* \* of an operator, let's move left 1. +\* if (~ notLastToken /\ line[curPos] = "(") +\* { call findTokenIn(curPos, ParenOperators); +\* if (returnVal = NullTokenSpec) { curPos := curPos - 1 } +\* } +\* else if ( /\ ~ notLastToken +\* /\ line[curPos] = "." +\* /\ line[curPos+1] # "." +\* /\ line[curPos-1] # "." ) +\* { curPos := curPos - 1 }; +\* +\* \* If we're at a space (which means we haven't found a "!" or moved left +\* \* over a parenthesized string), then try as if the cursor were +\* \* one character to the left. +\* if (line[curPos] = " ") { curPos := curPos - 1}; + + if (line[curPos] \in IdChar) + { \* Check if we're at an "X" that is inside an "\X" or "\X". + \* Note: this works because there are no TeXSymbols besides + if (line[curPos] = "X") { call findTokenIn(curPos, XSymbols); } + else {returnVal := NullTokenSpec} ; + if (returnVal # NullTokenSpec) + { foundTokenSpecs := JavaSeq(<<returnVal>>); + curPos := returnVal.leftPos; + lastToken := TRUE; + } + else + { call findMaximalIdCharSeq(curPos); + currentToken := returnVal; + \* Check if token is part of step name. If it is, we set + \* First, see if currentToken could be the token after the "<...>". + if ( line[currentToken.leftPos-1] = ">" ) + { call findMaximalIdCharSeq(currentToken.leftPos - 1); + call checkIfStepName(returnVal, currentToken); + if (returnVal # NullTokenSpec) + { foundTokenSpecs := JavaSeq(<<returnVal>>); + vreturn(FixOrigin(foundTokenSpecs)); + } + }; + \* Next see if it's the token to the left of the ">" + if (line[currentToken.rightPos] = ">" ) + { call findMaximalIdCharSeq(currentToken.rightPos+1); + call checkIfStepName(currentToken, returnVal); + if (returnVal # NullTokenSpec) + { foundTokenSpecs := JavaSeq(<<returnVal>>); + vreturn(FixOrigin(foundTokenSpecs)); + } + }; + + \* Next, check if it's a number, and abort if it is. + if (IsNumber(currentToken.token)) + { vreturn(<< >>) + }; + + \* Next, check if the token appears as "\\"token, in + \* which case we adjust currentTok + left := currentToken.leftPos ; + rt := currentToken.rightPos ; + if ( /\ line[left-1] = "\\" + /\ line[left-2] # "\\" + /\ \E ii \in DOMAIN TeXSymbols : + JavaSubseq(line, left-1, rt) = TeXSymbols[ii] ) + { lastToken := TRUE; + currentToken.leftPos := left - 1 || + currentToken.token := JavaSubseq(line, left-1, rt); + }; + foundTokenSpecs := + JavaSeq( << currentToken >>); + curPos := left; + } + } + else + { call findTokenIn(curPos, Operators); + currentToken := returnVal; + if (currentToken = NullTokenSpec) + { vreturn(<< >>); + }; + call findTokenIn(curPos, NonOperators); + if (returnVal # NullTokenSpec) + { vreturn(<< >>); + }; + + \* Need to check if beginning or end of level specifier + \* of step number. + if ( currentToken.token = JavaSeq(<< "<" >>) ) + { call findMaximalIdCharSeq(currentToken.rightPos); + temp1 := returnVal; + if (/\ temp1 # NullTokenSpec + /\ line[temp1.rightPos] = ">") + { call findMaximalIdCharSeq(temp1.rightPos + 1); + temp2 := returnVal; + call checkIfStepName(temp1, temp2); + if (returnVal # NullTokenSpec) + { foundTokenSpecs := JavaSeq(<<returnVal>>); + vreturn(FixOrigin(foundTokenSpecs)); + } + } + } + else if (currentToken.token = JavaSeq(<< ">" >>)) + { call findMaximalIdCharSeq(currentToken.leftPos); + temp1 := returnVal; + call findMaximalIdCharSeq(currentToken.rightPos); + temp2 := returnVal; + call checkIfStepName(temp1, temp2); + if (returnVal # NullTokenSpec) + { foundTokenSpecs := JavaSeq(<<returnVal>>); + vreturn(FixOrigin(foundTokenSpecs)); + } + }; + + \* We check for the case of a "\\" starting a TeX token. + if (currentToken.token = JavaSeq(<<"\\">>)) + { call findMaximalIdCharSeq(currentToken.rightPos); + if ( /\ returnVal # NullTokenSpec + /\ \E ii \in DOMAIN TeXSymbols : + JavaSubseq(line, + currentToken.leftPos, + returnVal.rightPos) = TeXSymbols[ii] ) + { currentToken.rightPos := returnVal.rightPos || + currentToken.token := JavaSubseq(line, + currentToken.leftPos, + returnVal.rightPos) + } + }; + + foundTokenSpecs := JavaSeq(<<currentToken>>); + curPos := currentToken.leftPos; + lastToken := TRUE; + }; + + assert JavaLen(foundTokenSpecs) = 1; + +\*print <<"1", foundTokenSpecs[0]>>; + \** We have now found the base token. We now look to find extenders + \* to the left of the form idtoken "!". These must be part of the + \* name, so we prepend them to foundTokenSpecs[0]. + curPos := foundTokenSpecs[0].leftPos; + notDone := TRUE; + while (notDone) + { call skipLeftOverSpaces(); + if (curPos < 0 \/ line[curPos-1] = ";") + { notDone := FALSE; } + else + { if (line[curPos-1] = "!" /\ (line[curPos-2] # "!")) + { curPos := curPos - 1; + call skipLeftOverSpaces(); + call findMatchingLeftParen(curPos); + curPos := returnVal; + if (curPos < 0) { notDone := FALSE } + else + { call skipLeftOverSpaces(); + call findMaximalIdCharSeq(curPos); + currentToken := returnVal ; + if ( \/ currentToken = NullTokenSpec + \/ IsNumber(currentToken.token) ) + { notDone := FALSE } + else + { curPos := currentToken.leftPos; + foundTokenSpecs[0] := + [token |-> JavaConcatenate( + JavaAppend(currentToken.token, "!"), + foundTokenSpecs[0].token), + leftPos |-> curPos, + rightPos |-> foundTokenSpecs[0].rightPos]; + } + } + } + else {notDone := FALSE;} + }; + + }; + + assert foundTokenSpecs # << >> ; + + if (lastToken = TRUE) {vreturn(FixOrigin(foundTokenSpecs))}; + +\* print <<"2", foundTokenSpecs[0]>>; + + curPos := foundTokenSpecs[0].rightPos; + foundBangToken := FALSE; + notDone := TRUE; + while (notDone) + { call skipRightOverSpaces(); + call findMatchingRightParen(curPos); + curPos := returnVal; + if (curPos < 0) {notDone := FALSE} + else + { call skipRightOverSpaces(); + if (line[curPos] # "!" \/ line[curPos+1] = "!") + { notDone := FALSE } + else + { curPos := curPos + 1; + call skipRightOverSpaces(); + call findMaximalIdCharSeq(curPos); + currentToken := returnVal ; + if ( \/ currentToken = NullTokenSpec + \/ IsNumber(currentToken.token) ) + { notDone := FALSE } + else + { foundBangToken := TRUE; + foundTokenSpecs := + JavaConcatenate( + JavaSeq( + << [token |-> + JavaConcatenate(JavaAppend(foundTokenSpecs[0].token, + "!"), + currentToken.token), + leftPos |-> foundTokenSpecs[0].leftPos, + rightPos |-> currentToken.rightPos] + >> ) , + foundTokenSpecs); + curPos := currentToken.rightPos; + } + } + } + }; + + if (notLastToken /\ ~ foundBangToken) { vreturn (<< >>) }; + + vreturn(FixOrigin(foundTokenSpecs)); +} \* end findTokenSpecs + +\* Main Body: +{ curPos := JavaLen(Padding); + tempVar1 := (-1 :> ""); + tempVar2 := JavaLen(Padding); +\*curPos := 15; +\*call findTokenSpecs(); +\*print returnVal; +\* print line; + while (tempVar2 < JavaLen(lineInput) + JavaLen(Padding)) + { \* print <<"curPos: ", tempVar2>>; + curPos := tempVar2; + foundTokenSpecs := << >> ; + lastToken := FALSE; + notLastToken := FALSE; + call findTokenSpecs(); +\* if (tempVar2 >= 0 + JavaLen(Padding)) +\* { print "done with run" ; +\* assert FALSE +\* }; +\* if (returnVal = tempVar1) +\* { print "same" \* ,"lastToken: ", lastToken, ", notLastToken: ", notLastToken>> +\* } +\* else +\* { print returnVal; + \* print <<"lastToken: ", lastToken, ", notLastToken: ", notLastToken>>; +\* }; + tempVar1 := returnVal; + tempVar2 := tempVar2 + 1; + }; + print "goodby world"; + +} \* end Main Body +} \* End algorithm + +*****************************************************************************************) +\* BEGIN TRANSLATION +\* Procedure variable left of procedure findMaximalIdCharSeq at line 317 col 28 changed to left_ +\* Procedure variable rt of procedure findMaximalIdCharSeq at line 317 col 40 changed to rt_ +\* Procedure variable ipos of procedure findMatchingLeftInner at line 343 col 12 changed to ipos_ +\* Procedure variable jdelim of procedure findMatchingLeftInner at line 343 col 24 changed to jdelim_ +\* Procedure variable delimLen of procedure findMatchingLeftInner at line 343 col 32 changed to delimLen_ +\* Parameter pos of procedure findTokenIn at line 292 col 23 changed to pos_ +\* Parameter pos of procedure findMaximalIdCharSeq at line 316 col 32 changed to pos_f +\* Parameter pos of procedure findMatchingLeftParen at line 336 col 33 changed to pos_fi +\* Parameter pos of procedure findMatchingLeftInner at line 342 col 33 changed to pos_fin +\* Parameter delim of procedure findMatchingLeftInner at line 342 col 38 changed to delim_ +\* Parameter pos of procedure findMatchingRightParen at line 365 col 34 changed to pos_find +CONSTANT defaultInitValue VARIABLES line, foundTokenSpecs, lastToken, notLastToken, curPos, returnVal, - tempVar1, tempVar2, tempVar3, pc, stack - -(* define statement *) -FixOrigin(tokarray) == -[i \in DOMAIN tokarray |-> - [token |->tokarray[i].token, rightPos |-> tokarray[i].rightPos - JavaLen(Padding), - leftPos |-> tokarray[i].leftPos - JavaLen(Padding)]] - + tempVar1, tempVar2, tempVar3, pc, stack + +(* define statement *) +FixOrigin(tokarray) == +[i \in DOMAIN tokarray |-> + [token |->tokarray[i].token, rightPos |-> tokarray[i].rightPos - JavaLen(Padding), + leftPos |-> tokarray[i].leftPos - JavaLen(Padding)]] + VARIABLES pos_, tokArray, i, tokIdx, pos_f, token, left_, rt_, pos_fi, pos_fin, delim_, ipos_, jdelim_, delimLen_, pos_find, pos, delim, ipos, jdelim, delimLen, leftTok, rightTok, currentToken, left, rt, - notDone, foundBangToken, temp1, temp2, temp3 - + notDone, foundBangToken, temp1, temp2, temp3 + vars == << line, foundTokenSpecs, lastToken, notLastToken, curPos, returnVal, tempVar1, tempVar2, tempVar3, pc, stack, pos_, tokArray, i, tokIdx, pos_f, token, left_, rt_, pos_fi, pos_fin, delim_, ipos_, jdelim_, delimLen_, pos_find, pos, delim, ipos, jdelim, delimLen, leftTok, rightTok, currentToken, left, rt, notDone, foundBangToken, temp1, - temp2, temp3 >> - -Init == (* Global variables *) - /\ line = JavaConcatenate (Padding, JavaConcatenate(lineInput, Padding)) - /\ foundTokenSpecs = << >> - /\ lastToken = FALSE - /\ notLastToken = FALSE - /\ curPos = currentPosition + JavaLen(Padding) - /\ returnVal = defaultInitValue - /\ tempVar1 = defaultInitValue - /\ tempVar2 = defaultInitValue - /\ tempVar3 = defaultInitValue - (* Procedure findTokenIn *) - /\ pos_ = defaultInitValue - /\ tokArray = defaultInitValue - /\ i = defaultInitValue - /\ tokIdx = defaultInitValue - (* Procedure findMaximalIdCharSeq *) - /\ pos_f = defaultInitValue - /\ token = << >> - /\ left_ = pos_f - /\ rt_ = pos_f - (* Procedure findMatchingLeftParen *) - /\ pos_fi = defaultInitValue - (* Procedure findMatchingLeftInner *) - /\ pos_fin = defaultInitValue - /\ delim_ = defaultInitValue - /\ ipos_ = pos_fin - /\ jdelim_ = defaultInitValue - /\ delimLen_ = defaultInitValue - (* Procedure findMatchingRightParen *) - /\ pos_find = defaultInitValue - (* Procedure findMatchingRightInner *) - /\ pos = defaultInitValue - /\ delim = defaultInitValue - /\ ipos = pos - /\ jdelim = defaultInitValue - /\ delimLen = defaultInitValue - (* Procedure checkIfStepName *) - /\ leftTok = defaultInitValue - /\ rightTok = defaultInitValue - (* Procedure findTokenSpecs *) - /\ currentToken = defaultInitValue - /\ left = defaultInitValue - /\ rt = defaultInitValue - /\ notDone = defaultInitValue - /\ foundBangToken = defaultInitValue - /\ temp1 = defaultInitValue - /\ temp2 = defaultInitValue - /\ temp3 = defaultInitValue - /\ stack = << >> - /\ pc = "Lbl_77" - -Lbl_1 == /\ pc = "Lbl_1" - /\ i' = 0 - /\ pc' = "Lbl_2" - /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, - curPos, returnVal, tempVar1, tempVar2, tempVar3, - stack, pos_, tokArray, tokIdx, pos_f, token, left_, - rt_, pos_fi, pos_fin, delim_, ipos_, jdelim_, - delimLen_, pos_find, pos, delim, ipos, jdelim, - delimLen, leftTok, rightTok, currentToken, left, rt, - notDone, foundBangToken, temp1, temp2, temp3 >> - -Lbl_2 == /\ pc = "Lbl_2" - /\ IF i < JavaLen(tokArray) - THEN /\ tokIdx' = JavaIndex(tokArray[i], line[pos_], 0) - /\ pc' = "Lbl_3" - /\ UNCHANGED << returnVal, stack, pos_, tokArray, i >> - ELSE /\ returnVal' = NullTokenSpec - /\ pc' = Head(stack).pc - /\ i' = Head(stack).i - /\ tokIdx' = Head(stack).tokIdx - /\ pos_' = Head(stack).pos_ - /\ tokArray' = Head(stack).tokArray - /\ stack' = Tail(stack) - /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, - curPos, tempVar1, tempVar2, tempVar3, pos_f, token, - left_, rt_, pos_fi, pos_fin, delim_, ipos_, jdelim_, - delimLen_, pos_find, pos, delim, ipos, jdelim, - delimLen, leftTok, rightTok, currentToken, left, rt, - notDone, foundBangToken, temp1, temp2, temp3 >> - -Lbl_3 == /\ pc = "Lbl_3" - /\ IF tokIdx # -1 - THEN /\ LET ftlft == pos_ - tokIdx IN - LET ftrt == pos_ - tokIdx + JavaLen(tokArray[i]) IN - IF tokArray[i] = JavaSubseq(line, ftlft, ftrt) - THEN /\ returnVal' = [token |-> tokArray[i], - leftPos |-> ftlft, rightPos |-> ftrt] - /\ pc' = Head(stack).pc - /\ i' = Head(stack).i - /\ tokIdx' = Head(stack).tokIdx - /\ pos_' = Head(stack).pos_ - /\ tokArray' = Head(stack).tokArray - /\ stack' = Tail(stack) - ELSE /\ pc' = "Lbl_4" - /\ UNCHANGED << returnVal, stack, pos_, - tokArray, i, tokIdx >> - ELSE /\ i' = i+1 - /\ pc' = "Lbl_2" - /\ UNCHANGED << returnVal, stack, pos_, tokArray, tokIdx >> - /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, - curPos, tempVar1, tempVar2, tempVar3, pos_f, token, - left_, rt_, pos_fi, pos_fin, delim_, ipos_, jdelim_, - delimLen_, pos_find, pos, delim, ipos, jdelim, - delimLen, leftTok, rightTok, currentToken, left, rt, - notDone, foundBangToken, temp1, temp2, temp3 >> - -Lbl_4 == /\ pc = "Lbl_4" - /\ tokIdx' = JavaIndex(tokArray[i], line[pos_], tokIdx+1) - /\ pc' = "Lbl_3" - /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, - curPos, returnVal, tempVar1, tempVar2, tempVar3, - stack, pos_, tokArray, i, pos_f, token, left_, rt_, - pos_fi, pos_fin, delim_, ipos_, jdelim_, delimLen_, - pos_find, pos, delim, ipos, jdelim, delimLen, leftTok, - rightTok, currentToken, left, rt, notDone, - foundBangToken, temp1, temp2, temp3 >> - -findTokenIn == Lbl_1 \/ Lbl_2 \/ Lbl_3 \/ Lbl_4 - -Lbl_5 == /\ pc = "Lbl_5" - /\ IF line[left_-1] \in IdChar - THEN /\ left_' = left_-1 - /\ pc' = "Lbl_5" - ELSE /\ pc' = "Lbl_6" - /\ left_' = left_ - /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, - curPos, returnVal, tempVar1, tempVar2, tempVar3, - stack, pos_, tokArray, i, tokIdx, pos_f, token, rt_, - pos_fi, pos_fin, delim_, ipos_, jdelim_, delimLen_, - pos_find, pos, delim, ipos, jdelim, delimLen, leftTok, - rightTok, currentToken, left, rt, notDone, - foundBangToken, temp1, temp2, temp3 >> - -Lbl_6 == /\ pc = "Lbl_6" - /\ IF line[rt_] \in IdChar - THEN /\ rt_' = rt_+1 - /\ pc' = "Lbl_6" - /\ UNCHANGED << returnVal, stack, pos_f, token, left_ >> - ELSE /\ IF left_ = rt_ - THEN /\ returnVal' = NullTokenSpec - /\ pc' = Head(stack).pc - /\ token' = Head(stack).token - /\ left_' = Head(stack).left_ - /\ rt_' = Head(stack).rt_ - /\ pos_f' = Head(stack).pos_f - /\ stack' = Tail(stack) - ELSE /\ returnVal' = [token |-> JavaSubseq(line, left_, rt_), - leftPos |-> left_, rightPos |-> rt_] - /\ pc' = Head(stack).pc - /\ token' = Head(stack).token - /\ left_' = Head(stack).left_ - /\ rt_' = Head(stack).rt_ - /\ pos_f' = Head(stack).pos_f - /\ stack' = Tail(stack) - /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, - curPos, tempVar1, tempVar2, tempVar3, pos_, tokArray, - i, tokIdx, pos_fi, pos_fin, delim_, ipos_, jdelim_, - delimLen_, pos_find, pos, delim, ipos, jdelim, - delimLen, leftTok, rightTok, currentToken, left, rt, - notDone, foundBangToken, temp1, temp2, temp3 >> - -findMaximalIdCharSeq == Lbl_5 \/ Lbl_6 - -Lbl_7 == /\ pc = "Lbl_7" - /\ IF line[pos_fi-1] # ")" - THEN /\ returnVal' = pos_fi - /\ pc' = Head(stack).pc - /\ pos_fi' = Head(stack).pos_fi - /\ stack' = Tail(stack) - ELSE /\ pc' = "Lbl_8" - /\ UNCHANGED << returnVal, stack, pos_fi >> - /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, - curPos, tempVar1, tempVar2, tempVar3, pos_, tokArray, - i, tokIdx, pos_f, token, left_, rt_, pos_fin, delim_, - ipos_, jdelim_, delimLen_, pos_find, pos, delim, ipos, - jdelim, delimLen, leftTok, rightTok, currentToken, - left, rt, notDone, foundBangToken, temp1, temp2, - temp3 >> - -Lbl_8 == /\ pc = "Lbl_8" - /\ /\ delim_' = 0 - /\ pos_fin' = pos_fi - /\ stack' = << [ procedure |-> "findMatchingLeftInner", - pc |-> Head(stack).pc, - ipos_ |-> ipos_, - jdelim_ |-> jdelim_, - delimLen_ |-> delimLen_, - pos_fin |-> pos_fin, - delim_ |-> delim_ ] >> - \o Tail(stack) - /\ ipos_' = pos_fin' - /\ jdelim_' = defaultInitValue - /\ delimLen_' = defaultInitValue - /\ pc' = "Lbl_9" - /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, - curPos, returnVal, tempVar1, tempVar2, tempVar3, pos_, - tokArray, i, tokIdx, pos_f, token, left_, rt_, pos_fi, - pos_find, pos, delim, ipos, jdelim, delimLen, leftTok, - rightTok, currentToken, left, rt, notDone, - foundBangToken, temp1, temp2, temp3 >> - -findMatchingLeftParen == Lbl_7 \/ Lbl_8 - -Lbl_9 == /\ pc = "Lbl_9" - /\ delimLen_' = JavaLen(LeftDelimiters[delim_]) - /\ ipos_' = pos_fin - delimLen_' - /\ pc' = "Lbl_10" - /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, - curPos, returnVal, tempVar1, tempVar2, tempVar3, - stack, pos_, tokArray, i, tokIdx, pos_f, token, left_, - rt_, pos_fi, pos_fin, delim_, jdelim_, pos_find, pos, - delim, ipos, jdelim, delimLen, leftTok, rightTok, - currentToken, left, rt, notDone, foundBangToken, - temp1, temp2, temp3 >> - -Lbl_10 == /\ pc = "Lbl_10" - /\ IF JavaSubseq(line, ipos_-delimLen_, ipos_) # LeftDelimiters[delim_] - THEN /\ IF line[ipos_-1] = ";" - THEN /\ returnVal' = -1 - /\ pc' = Head(stack).pc - /\ ipos_' = Head(stack).ipos_ - /\ jdelim_' = Head(stack).jdelim_ - /\ delimLen_' = Head(stack).delimLen_ - /\ pos_fin' = Head(stack).pos_fin - /\ delim_' = Head(stack).delim_ - /\ stack' = Tail(stack) - ELSE /\ pc' = "Lbl_11" - /\ UNCHANGED << returnVal, stack, pos_fin, - delim_, ipos_, jdelim_, - delimLen_ >> - ELSE /\ returnVal' = ipos_-delimLen_ - /\ pc' = Head(stack).pc - /\ ipos_' = Head(stack).ipos_ - /\ jdelim_' = Head(stack).jdelim_ - /\ delimLen_' = Head(stack).delimLen_ - /\ pos_fin' = Head(stack).pos_fin - /\ delim_' = Head(stack).delim_ - /\ stack' = Tail(stack) - /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, - curPos, tempVar1, tempVar2, tempVar3, pos_, tokArray, - i, tokIdx, pos_f, token, left_, rt_, pos_fi, - pos_find, pos, delim, ipos, jdelim, delimLen, - leftTok, rightTok, currentToken, left, rt, notDone, - foundBangToken, temp1, temp2, temp3 >> - -Lbl_11 == /\ pc = "Lbl_11" - /\ ipos_' = ipos_-1 - /\ jdelim_' = 0 - /\ pc' = "Lbl_12" - /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, - curPos, returnVal, tempVar1, tempVar2, tempVar3, - stack, pos_, tokArray, i, tokIdx, pos_f, token, - left_, rt_, pos_fi, pos_fin, delim_, delimLen_, - pos_find, pos, delim, ipos, jdelim, delimLen, - leftTok, rightTok, currentToken, left, rt, notDone, - foundBangToken, temp1, temp2, temp3 >> - -Lbl_12 == /\ pc = "Lbl_12" - /\ IF jdelim_ < JavaLen(LeftDelimiters) - THEN /\ IF JavaSubseq(line, ipos_-delimLen_, ipos_) = RightDelimiters[jdelim_] - THEN /\ /\ delim_' = jdelim_ - /\ pos_fin' = ipos_ - /\ stack' = << [ procedure |-> "findMatchingLeftInner", - pc |-> "Lbl_13", - ipos_ |-> ipos_, - jdelim_ |-> jdelim_, - delimLen_ |-> delimLen_, - pos_fin |-> pos_fin, - delim_ |-> delim_ ] >> - \o stack - /\ ipos_' = pos_fin' - /\ jdelim_' = defaultInitValue - /\ delimLen_' = defaultInitValue - /\ pc' = "Lbl_9" - ELSE /\ pc' = "Lbl_16" - /\ UNCHANGED << stack, pos_fin, delim_, ipos_, - jdelim_, delimLen_ >> - ELSE /\ pc' = "Lbl_10" - /\ UNCHANGED << stack, pos_fin, delim_, ipos_, jdelim_, - delimLen_ >> - /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, - curPos, returnVal, tempVar1, tempVar2, tempVar3, - pos_, tokArray, i, tokIdx, pos_f, token, left_, rt_, - pos_fi, pos_find, pos, delim, ipos, jdelim, delimLen, - leftTok, rightTok, currentToken, left, rt, notDone, - foundBangToken, temp1, temp2, temp3 >> - -Lbl_16 == /\ pc = "Lbl_16" - /\ jdelim_' = jdelim_+1 - /\ pc' = "Lbl_12" - /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, - curPos, returnVal, tempVar1, tempVar2, tempVar3, - stack, pos_, tokArray, i, tokIdx, pos_f, token, - left_, rt_, pos_fi, pos_fin, delim_, ipos_, - delimLen_, pos_find, pos, delim, ipos, jdelim, - delimLen, leftTok, rightTok, currentToken, left, rt, - notDone, foundBangToken, temp1, temp2, temp3 >> - -Lbl_13 == /\ pc = "Lbl_13" - /\ ipos_' = returnVal - /\ IF ipos_' < 0 - THEN /\ returnVal' = -1 - /\ pc' = "Lbl_14" - ELSE /\ pc' = "Lbl_15" - /\ UNCHANGED returnVal - /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, - curPos, tempVar1, tempVar2, tempVar3, stack, pos_, - tokArray, i, tokIdx, pos_f, token, left_, rt_, - pos_fi, pos_fin, delim_, jdelim_, delimLen_, - pos_find, pos, delim, ipos, jdelim, delimLen, - leftTok, rightTok, currentToken, left, rt, notDone, - foundBangToken, temp1, temp2, temp3 >> - -Lbl_14 == /\ pc = "Lbl_14" - /\ pc' = Head(stack).pc - /\ ipos_' = Head(stack).ipos_ - /\ jdelim_' = Head(stack).jdelim_ - /\ delimLen_' = Head(stack).delimLen_ - /\ pos_fin' = Head(stack).pos_fin - /\ delim_' = Head(stack).delim_ - /\ stack' = Tail(stack) - /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, - curPos, returnVal, tempVar1, tempVar2, tempVar3, - pos_, tokArray, i, tokIdx, pos_f, token, left_, rt_, - pos_fi, pos_find, pos, delim, ipos, jdelim, delimLen, - leftTok, rightTok, currentToken, left, rt, notDone, - foundBangToken, temp1, temp2, temp3 >> - -Lbl_15 == /\ pc = "Lbl_15" - /\ jdelim_' = 99 - /\ pc' = "Lbl_16" - /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, - curPos, returnVal, tempVar1, tempVar2, tempVar3, - stack, pos_, tokArray, i, tokIdx, pos_f, token, - left_, rt_, pos_fi, pos_fin, delim_, ipos_, - delimLen_, pos_find, pos, delim, ipos, jdelim, - delimLen, leftTok, rightTok, currentToken, left, rt, - notDone, foundBangToken, temp1, temp2, temp3 >> - -findMatchingLeftInner == Lbl_9 \/ Lbl_10 \/ Lbl_11 \/ Lbl_12 \/ Lbl_16 - \/ Lbl_13 \/ Lbl_14 \/ Lbl_15 - -Lbl_17 == /\ pc = "Lbl_17" - /\ IF line[pos_find] # "(" - THEN /\ returnVal' = pos_find - /\ pc' = Head(stack).pc - /\ pos_find' = Head(stack).pos_find - /\ stack' = Tail(stack) - ELSE /\ pc' = "Lbl_18" - /\ UNCHANGED << returnVal, stack, pos_find >> - /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, - curPos, tempVar1, tempVar2, tempVar3, pos_, tokArray, - i, tokIdx, pos_f, token, left_, rt_, pos_fi, pos_fin, - delim_, ipos_, jdelim_, delimLen_, pos, delim, ipos, - jdelim, delimLen, leftTok, rightTok, currentToken, - left, rt, notDone, foundBangToken, temp1, temp2, - temp3 >> - -Lbl_18 == /\ pc = "Lbl_18" - /\ /\ delim' = 0 - /\ pos' = pos_find - /\ stack' = << [ procedure |-> "findMatchingRightInner", - pc |-> Head(stack).pc, - ipos |-> ipos, - jdelim |-> jdelim, - delimLen |-> delimLen, - pos |-> pos, - delim |-> delim ] >> - \o Tail(stack) - /\ ipos' = pos' - /\ jdelim' = defaultInitValue - /\ delimLen' = defaultInitValue - /\ pc' = "Lbl_19" - /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, - curPos, returnVal, tempVar1, tempVar2, tempVar3, - pos_, tokArray, i, tokIdx, pos_f, token, left_, rt_, - pos_fi, pos_fin, delim_, ipos_, jdelim_, delimLen_, - pos_find, leftTok, rightTok, currentToken, left, rt, - notDone, foundBangToken, temp1, temp2, temp3 >> - -findMatchingRightParen == Lbl_17 \/ Lbl_18 - -Lbl_19 == /\ pc = "Lbl_19" - /\ delimLen' = JavaLen(RightDelimiters[delim]) - /\ ipos' = pos + delimLen' - /\ pc' = "Lbl_20" - /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, - curPos, returnVal, tempVar1, tempVar2, tempVar3, - stack, pos_, tokArray, i, tokIdx, pos_f, token, - left_, rt_, pos_fi, pos_fin, delim_, ipos_, jdelim_, - delimLen_, pos_find, pos, delim, jdelim, leftTok, - rightTok, currentToken, left, rt, notDone, - foundBangToken, temp1, temp2, temp3 >> - -Lbl_20 == /\ pc = "Lbl_20" - /\ IF JavaSubseq(line, ipos, ipos+delimLen) # RightDelimiters[delim] - THEN /\ IF line[ipos] = ";" - THEN /\ returnVal' = -1 - /\ pc' = Head(stack).pc - /\ ipos' = Head(stack).ipos - /\ jdelim' = Head(stack).jdelim - /\ delimLen' = Head(stack).delimLen - /\ pos' = Head(stack).pos - /\ delim' = Head(stack).delim - /\ stack' = Tail(stack) - ELSE /\ pc' = "Lbl_21" - /\ UNCHANGED << returnVal, stack, pos, delim, - ipos, jdelim, delimLen >> - ELSE /\ returnVal' = ipos+delimLen - /\ pc' = Head(stack).pc - /\ ipos' = Head(stack).ipos - /\ jdelim' = Head(stack).jdelim - /\ delimLen' = Head(stack).delimLen - /\ pos' = Head(stack).pos - /\ delim' = Head(stack).delim - /\ stack' = Tail(stack) - /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, - curPos, tempVar1, tempVar2, tempVar3, pos_, tokArray, - i, tokIdx, pos_f, token, left_, rt_, pos_fi, pos_fin, - delim_, ipos_, jdelim_, delimLen_, pos_find, leftTok, - rightTok, currentToken, left, rt, notDone, - foundBangToken, temp1, temp2, temp3 >> - -Lbl_21 == /\ pc = "Lbl_21" - /\ ipos' = ipos+1 - /\ jdelim' = 0 - /\ pc' = "Lbl_22" - /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, - curPos, returnVal, tempVar1, tempVar2, tempVar3, - stack, pos_, tokArray, i, tokIdx, pos_f, token, - left_, rt_, pos_fi, pos_fin, delim_, ipos_, jdelim_, - delimLen_, pos_find, pos, delim, delimLen, leftTok, - rightTok, currentToken, left, rt, notDone, - foundBangToken, temp1, temp2, temp3 >> - -Lbl_22 == /\ pc = "Lbl_22" - /\ IF jdelim < JavaLen(RightDelimiters) - THEN /\ IF JavaSubseq(line, ipos, ipos+delimLen) = LeftDelimiters[jdelim] - THEN /\ /\ delim' = jdelim - /\ pos' = ipos - /\ stack' = << [ procedure |-> "findMatchingRightInner", - pc |-> "Lbl_23", - ipos |-> ipos, - jdelim |-> jdelim, - delimLen |-> delimLen, - pos |-> pos, - delim |-> delim ] >> - \o stack - /\ ipos' = pos' - /\ jdelim' = defaultInitValue - /\ delimLen' = defaultInitValue - /\ pc' = "Lbl_19" - ELSE /\ pc' = "Lbl_26" - /\ UNCHANGED << stack, pos, delim, ipos, - jdelim, delimLen >> - ELSE /\ pc' = "Lbl_20" - /\ UNCHANGED << stack, pos, delim, ipos, jdelim, delimLen >> - /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, - curPos, returnVal, tempVar1, tempVar2, tempVar3, - pos_, tokArray, i, tokIdx, pos_f, token, left_, rt_, - pos_fi, pos_fin, delim_, ipos_, jdelim_, delimLen_, - pos_find, leftTok, rightTok, currentToken, left, rt, - notDone, foundBangToken, temp1, temp2, temp3 >> - -Lbl_26 == /\ pc = "Lbl_26" - /\ jdelim' = jdelim+1 - /\ pc' = "Lbl_22" - /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, - curPos, returnVal, tempVar1, tempVar2, tempVar3, - stack, pos_, tokArray, i, tokIdx, pos_f, token, - left_, rt_, pos_fi, pos_fin, delim_, ipos_, jdelim_, - delimLen_, pos_find, pos, delim, ipos, delimLen, - leftTok, rightTok, currentToken, left, rt, notDone, - foundBangToken, temp1, temp2, temp3 >> - -Lbl_23 == /\ pc = "Lbl_23" - /\ ipos' = returnVal - /\ IF ipos' < 0 - THEN /\ returnVal' = -1 - /\ pc' = "Lbl_24" - ELSE /\ pc' = "Lbl_25" - /\ UNCHANGED returnVal - /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, - curPos, tempVar1, tempVar2, tempVar3, stack, pos_, - tokArray, i, tokIdx, pos_f, token, left_, rt_, - pos_fi, pos_fin, delim_, ipos_, jdelim_, delimLen_, - pos_find, pos, delim, jdelim, delimLen, leftTok, - rightTok, currentToken, left, rt, notDone, - foundBangToken, temp1, temp2, temp3 >> - -Lbl_24 == /\ pc = "Lbl_24" - /\ pc' = Head(stack).pc - /\ ipos' = Head(stack).ipos - /\ jdelim' = Head(stack).jdelim - /\ delimLen' = Head(stack).delimLen - /\ pos' = Head(stack).pos - /\ delim' = Head(stack).delim - /\ stack' = Tail(stack) - /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, - curPos, returnVal, tempVar1, tempVar2, tempVar3, - pos_, tokArray, i, tokIdx, pos_f, token, left_, rt_, - pos_fi, pos_fin, delim_, ipos_, jdelim_, delimLen_, - pos_find, leftTok, rightTok, currentToken, left, rt, - notDone, foundBangToken, temp1, temp2, temp3 >> - -Lbl_25 == /\ pc = "Lbl_25" - /\ jdelim' = 99 - /\ pc' = "Lbl_26" - /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, - curPos, returnVal, tempVar1, tempVar2, tempVar3, - stack, pos_, tokArray, i, tokIdx, pos_f, token, - left_, rt_, pos_fi, pos_fin, delim_, ipos_, jdelim_, - delimLen_, pos_find, pos, delim, ipos, delimLen, - leftTok, rightTok, currentToken, left, rt, notDone, - foundBangToken, temp1, temp2, temp3 >> - -findMatchingRightInner == Lbl_19 \/ Lbl_20 \/ Lbl_21 \/ Lbl_22 \/ Lbl_26 - \/ Lbl_23 \/ Lbl_24 \/ Lbl_25 - -Lbl_27 == /\ pc = "Lbl_27" - /\ IF leftTok = NullTokenSpec \/ rightTok = NullTokenSpec - THEN /\ returnVal' = NullTokenSpec - /\ pc' = Head(stack).pc - /\ leftTok' = Head(stack).leftTok - /\ rightTok' = Head(stack).rightTok - /\ stack' = Tail(stack) - ELSE /\ pc' = "Lbl_28" - /\ UNCHANGED << returnVal, stack, leftTok, rightTok >> - /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, - curPos, tempVar1, tempVar2, tempVar3, pos_, tokArray, - i, tokIdx, pos_f, token, left_, rt_, pos_fi, pos_fin, - delim_, ipos_, jdelim_, delimLen_, pos_find, pos, - delim, ipos, jdelim, delimLen, currentToken, left, - rt, notDone, foundBangToken, temp1, temp2, temp3 >> - -Lbl_28 == /\ pc = "Lbl_28" - /\ IF /\ IsNumber(leftTok.token) - /\ line[leftTok.leftPos-1] = "<" - /\ line[leftTok.leftPos-2] # "<" - THEN /\ returnVal' = [token |-> JavaSubseq(line, - leftTok.leftPos-1, - rightTok.rightPos), - leftPos |-> leftTok.leftPos-1, - rightPos |-> rightTok.rightPos] - /\ pc' = Head(stack).pc - /\ leftTok' = Head(stack).leftTok - /\ rightTok' = Head(stack).rightTok - /\ stack' = Tail(stack) - ELSE /\ returnVal' = NullTokenSpec - /\ pc' = Head(stack).pc - /\ leftTok' = Head(stack).leftTok - /\ rightTok' = Head(stack).rightTok - /\ stack' = Tail(stack) - /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, - curPos, tempVar1, tempVar2, tempVar3, pos_, tokArray, - i, tokIdx, pos_f, token, left_, rt_, pos_fi, pos_fin, - delim_, ipos_, jdelim_, delimLen_, pos_find, pos, - delim, ipos, jdelim, delimLen, currentToken, left, - rt, notDone, foundBangToken, temp1, temp2, temp3 >> - -checkIfStepName == Lbl_27 \/ Lbl_28 - -Lbl_29 == /\ pc = "Lbl_29" - /\ IF line[curPos-1] = " " - THEN /\ curPos' = curPos - 1 - /\ pc' = "Lbl_29" - /\ stack' = stack - ELSE /\ pc' = Head(stack).pc - /\ stack' = Tail(stack) - /\ UNCHANGED curPos - /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, - returnVal, tempVar1, tempVar2, tempVar3, pos_, - tokArray, i, tokIdx, pos_f, token, left_, rt_, - pos_fi, pos_fin, delim_, ipos_, jdelim_, delimLen_, - pos_find, pos, delim, ipos, jdelim, delimLen, - leftTok, rightTok, currentToken, left, rt, notDone, - foundBangToken, temp1, temp2, temp3 >> - -skipLeftOverSpaces == Lbl_29 - -Lbl_30 == /\ pc = "Lbl_30" - /\ IF line[curPos] = " " - THEN /\ curPos' = curPos+1 - /\ pc' = "Lbl_30" - /\ stack' = stack - ELSE /\ pc' = Head(stack).pc - /\ stack' = Tail(stack) - /\ UNCHANGED curPos - /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, - returnVal, tempVar1, tempVar2, tempVar3, pos_, - tokArray, i, tokIdx, pos_f, token, left_, rt_, - pos_fi, pos_fin, delim_, ipos_, jdelim_, delimLen_, - pos_find, pos, delim, ipos, jdelim, delimLen, - leftTok, rightTok, currentToken, left, rt, notDone, - foundBangToken, temp1, temp2, temp3 >> - -skipRightOverSpaces == Lbl_30 - -Lbl_31 == /\ pc = "Lbl_31" - /\ IF line[curPos] = " " - THEN /\ stack' = << [ procedure |-> "skipLeftOverSpaces", - pc |-> "Lbl_32" ] >> - \o stack - /\ pc' = "Lbl_29" - /\ UNCHANGED notLastToken - ELSE /\ IF line[curPos] = "!" /\ line[curPos-1] # "!" /\ line[curPos+1] # "!" - THEN /\ notLastToken' = TRUE - /\ stack' = << [ procedure |-> "skipLeftOverSpaces", - pc |-> "Lbl_33" ] >> - \o stack - /\ pc' = "Lbl_29" - ELSE /\ pc' = "Lbl_33" - /\ UNCHANGED << notLastToken, stack >> - /\ UNCHANGED << line, foundTokenSpecs, lastToken, curPos, returnVal, - tempVar1, tempVar2, tempVar3, pos_, tokArray, i, - tokIdx, pos_f, token, left_, rt_, pos_fi, pos_fin, - delim_, ipos_, jdelim_, delimLen_, pos_find, pos, - delim, ipos, jdelim, delimLen, leftTok, rightTok, - currentToken, left, rt, notDone, foundBangToken, - temp1, temp2, temp3 >> - -Lbl_32 == /\ pc = "Lbl_32" - /\ IF line[curPos-1] = "!" /\ line[curPos-2] # "!" - THEN /\ notLastToken' = TRUE - /\ curPos' = curPos - 1 - /\ stack' = << [ procedure |-> "skipLeftOverSpaces", - pc |-> "Lbl_33" ] >> - \o stack - /\ pc' = "Lbl_29" - ELSE /\ pc' = "Lbl_33" - /\ UNCHANGED << notLastToken, curPos, stack >> - /\ UNCHANGED << line, foundTokenSpecs, lastToken, returnVal, - tempVar1, tempVar2, tempVar3, pos_, tokArray, i, - tokIdx, pos_f, token, left_, rt_, pos_fi, pos_fin, - delim_, ipos_, jdelim_, delimLen_, pos_find, pos, - delim, ipos, jdelim, delimLen, leftTok, rightTok, - currentToken, left, rt, notDone, foundBangToken, - temp1, temp2, temp3 >> - -Lbl_33 == /\ pc = "Lbl_33" - /\ IF /\ \/ notLastToken - \/ line[curPos] = " " - /\ line[curPos-1] = ")" - THEN /\ notLastToken' = TRUE - /\ /\ pos_fi' = curPos - /\ stack' = << [ procedure |-> "findMatchingLeftParen", - pc |-> "Lbl_34", - pos_fi |-> pos_fi ] >> - \o stack - /\ pc' = "Lbl_7" - ELSE /\ pc' = "Lbl_36" - /\ UNCHANGED << notLastToken, stack, pos_fi >> - /\ UNCHANGED << line, foundTokenSpecs, lastToken, curPos, returnVal, - tempVar1, tempVar2, tempVar3, pos_, tokArray, i, - tokIdx, pos_f, token, left_, rt_, pos_fin, delim_, - ipos_, jdelim_, delimLen_, pos_find, pos, delim, - ipos, jdelim, delimLen, leftTok, rightTok, - currentToken, left, rt, notDone, foundBangToken, - temp1, temp2, temp3 >> - -Lbl_34 == /\ pc = "Lbl_34" - /\ IF returnVal < 0 - THEN /\ returnVal' = << >> - /\ pc' = Head(stack).pc - /\ currentToken' = Head(stack).currentToken - /\ left' = Head(stack).left - /\ rt' = Head(stack).rt - /\ notDone' = Head(stack).notDone - /\ foundBangToken' = Head(stack).foundBangToken - /\ temp1' = Head(stack).temp1 - /\ temp2' = Head(stack).temp2 - /\ temp3' = Head(stack).temp3 - /\ stack' = Tail(stack) - ELSE /\ pc' = "Lbl_35" - /\ UNCHANGED << returnVal, stack, currentToken, left, rt, - notDone, foundBangToken, temp1, temp2, - temp3 >> - /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, - curPos, tempVar1, tempVar2, tempVar3, pos_, tokArray, - i, tokIdx, pos_f, token, left_, rt_, pos_fi, pos_fin, - delim_, ipos_, jdelim_, delimLen_, pos_find, pos, - delim, ipos, jdelim, delimLen, leftTok, rightTok >> - -Lbl_35 == /\ pc = "Lbl_35" - /\ curPos' = returnVal - /\ stack' = << [ procedure |-> "skipLeftOverSpaces", - pc |-> "Lbl_36" ] >> - \o stack - /\ pc' = "Lbl_29" - /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, - returnVal, tempVar1, tempVar2, tempVar3, pos_, - tokArray, i, tokIdx, pos_f, token, left_, rt_, - pos_fi, pos_fin, delim_, ipos_, jdelim_, delimLen_, - pos_find, pos, delim, ipos, jdelim, delimLen, - leftTok, rightTok, currentToken, left, rt, notDone, - foundBangToken, temp1, temp2, temp3 >> - -Lbl_36 == /\ pc = "Lbl_36" - /\ IF notLastToken - THEN /\ curPos' = curPos - 1 - /\ pc' = "Lbl_38" - /\ UNCHANGED << stack, pos_, tokArray, i, tokIdx >> - ELSE /\ IF line[curPos] \notin IdChar - THEN /\ /\ pos_' = curPos - /\ stack' = << [ procedure |-> "findTokenIn", - pc |-> "Lbl_37", - i |-> i, - tokIdx |-> tokIdx, - pos_ |-> pos_, - tokArray |-> tokArray ] >> - \o stack - /\ tokArray' = Operators - /\ i' = defaultInitValue - /\ tokIdx' = defaultInitValue - /\ pc' = "Lbl_1" - ELSE /\ pc' = "Lbl_38" - /\ UNCHANGED << stack, pos_, tokArray, i, - tokIdx >> - /\ UNCHANGED curPos - /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, - returnVal, tempVar1, tempVar2, tempVar3, pos_f, - token, left_, rt_, pos_fi, pos_fin, delim_, ipos_, - jdelim_, delimLen_, pos_find, pos, delim, ipos, - jdelim, delimLen, leftTok, rightTok, currentToken, - left, rt, notDone, foundBangToken, temp1, temp2, - temp3 >> - -Lbl_37 == /\ pc = "Lbl_37" - /\ IF returnVal = NullTokenSpec - THEN /\ curPos' = curPos - 1 - ELSE /\ TRUE - /\ UNCHANGED curPos - /\ pc' = "Lbl_38" - /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, - returnVal, tempVar1, tempVar2, tempVar3, stack, pos_, - tokArray, i, tokIdx, pos_f, token, left_, rt_, - pos_fi, pos_fin, delim_, ipos_, jdelim_, delimLen_, - pos_find, pos, delim, ipos, jdelim, delimLen, - leftTok, rightTok, currentToken, left, rt, notDone, - foundBangToken, temp1, temp2, temp3 >> - -Lbl_38 == /\ pc = "Lbl_38" - /\ IF line[curPos] \in IdChar - THEN /\ IF line[curPos] = "X" - THEN /\ /\ pos_' = curPos - /\ stack' = << [ procedure |-> "findTokenIn", - pc |-> "Lbl_39", - i |-> i, - tokIdx |-> tokIdx, - pos_ |-> pos_, - tokArray |-> tokArray ] >> - \o stack - /\ tokArray' = XSymbols - /\ i' = defaultInitValue - /\ tokIdx' = defaultInitValue - /\ pc' = "Lbl_1" - /\ UNCHANGED returnVal - ELSE /\ returnVal' = NullTokenSpec - /\ pc' = "Lbl_39" - /\ UNCHANGED << stack, pos_, tokArray, i, - tokIdx >> - ELSE /\ /\ pos_' = curPos - /\ stack' = << [ procedure |-> "findTokenIn", - pc |-> "Lbl_48", - i |-> i, - tokIdx |-> tokIdx, - pos_ |-> pos_, - tokArray |-> tokArray ] >> - \o stack - /\ tokArray' = Operators - /\ i' = defaultInitValue - /\ tokIdx' = defaultInitValue - /\ pc' = "Lbl_1" - /\ UNCHANGED returnVal - /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, - curPos, tempVar1, tempVar2, tempVar3, pos_f, token, - left_, rt_, pos_fi, pos_fin, delim_, ipos_, jdelim_, - delimLen_, pos_find, pos, delim, ipos, jdelim, - delimLen, leftTok, rightTok, currentToken, left, rt, - notDone, foundBangToken, temp1, temp2, temp3 >> - -Lbl_39 == /\ pc = "Lbl_39" - /\ IF returnVal # NullTokenSpec - THEN /\ foundTokenSpecs' = JavaSeq(<<returnVal>>) - /\ curPos' = returnVal.leftPos - /\ lastToken' = TRUE - /\ pc' = "Lbl_62" - /\ UNCHANGED << stack, pos_f, token, left_, rt_ >> - ELSE /\ /\ pos_f' = curPos - /\ stack' = << [ procedure |-> "findMaximalIdCharSeq", - pc |-> "Lbl_40", - token |-> token, - left_ |-> left_, - rt_ |-> rt_, - pos_f |-> pos_f ] >> - \o stack - /\ token' = << >> - /\ left_' = pos_f' - /\ rt_' = pos_f' - /\ pc' = "Lbl_5" - /\ UNCHANGED << foundTokenSpecs, lastToken, curPos >> - /\ UNCHANGED << line, notLastToken, returnVal, tempVar1, tempVar2, - tempVar3, pos_, tokArray, i, tokIdx, pos_fi, pos_fin, - delim_, ipos_, jdelim_, delimLen_, pos_find, pos, - delim, ipos, jdelim, delimLen, leftTok, rightTok, - currentToken, left, rt, notDone, foundBangToken, - temp1, temp2, temp3 >> - -Lbl_40 == /\ pc = "Lbl_40" - /\ currentToken' = returnVal - /\ IF line[currentToken'.leftPos-1] = ">" - THEN /\ /\ pos_f' = currentToken'.leftPos - 1 - /\ stack' = << [ procedure |-> "findMaximalIdCharSeq", - pc |-> "Lbl_41", - token |-> token, - left_ |-> left_, - rt_ |-> rt_, - pos_f |-> pos_f ] >> - \o stack - /\ token' = << >> - /\ left_' = pos_f' - /\ rt_' = pos_f' - /\ pc' = "Lbl_5" - ELSE /\ pc' = "Lbl_43" - /\ UNCHANGED << stack, pos_f, token, left_, rt_ >> - /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, - curPos, returnVal, tempVar1, tempVar2, tempVar3, - pos_, tokArray, i, tokIdx, pos_fi, pos_fin, delim_, - ipos_, jdelim_, delimLen_, pos_find, pos, delim, - ipos, jdelim, delimLen, leftTok, rightTok, left, rt, - notDone, foundBangToken, temp1, temp2, temp3 >> - -Lbl_41 == /\ pc = "Lbl_41" - /\ /\ leftTok' = returnVal - /\ rightTok' = currentToken - /\ stack' = << [ procedure |-> "checkIfStepName", - pc |-> "Lbl_42", - leftTok |-> leftTok, - rightTok |-> rightTok ] >> - \o stack - /\ pc' = "Lbl_27" - /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, - curPos, returnVal, tempVar1, tempVar2, tempVar3, - pos_, tokArray, i, tokIdx, pos_f, token, left_, rt_, - pos_fi, pos_fin, delim_, ipos_, jdelim_, delimLen_, - pos_find, pos, delim, ipos, jdelim, delimLen, - currentToken, left, rt, notDone, foundBangToken, - temp1, temp2, temp3 >> - -Lbl_42 == /\ pc = "Lbl_42" - /\ IF returnVal # NullTokenSpec - THEN /\ foundTokenSpecs' = JavaSeq(<<returnVal>>) - /\ returnVal' = FixOrigin(foundTokenSpecs') - /\ pc' = Head(stack).pc - /\ currentToken' = Head(stack).currentToken - /\ left' = Head(stack).left - /\ rt' = Head(stack).rt - /\ notDone' = Head(stack).notDone - /\ foundBangToken' = Head(stack).foundBangToken - /\ temp1' = Head(stack).temp1 - /\ temp2' = Head(stack).temp2 - /\ temp3' = Head(stack).temp3 - /\ stack' = Tail(stack) - ELSE /\ pc' = "Lbl_43" - /\ UNCHANGED << foundTokenSpecs, returnVal, stack, - currentToken, left, rt, notDone, - foundBangToken, temp1, temp2, temp3 >> - /\ UNCHANGED << line, lastToken, notLastToken, curPos, tempVar1, - tempVar2, tempVar3, pos_, tokArray, i, tokIdx, pos_f, - token, left_, rt_, pos_fi, pos_fin, delim_, ipos_, - jdelim_, delimLen_, pos_find, pos, delim, ipos, - jdelim, delimLen, leftTok, rightTok >> - -Lbl_43 == /\ pc = "Lbl_43" - /\ IF line[currentToken.rightPos] = ">" - THEN /\ /\ pos_f' = currentToken.rightPos+1 - /\ stack' = << [ procedure |-> "findMaximalIdCharSeq", - pc |-> "Lbl_44", - token |-> token, - left_ |-> left_, - rt_ |-> rt_, - pos_f |-> pos_f ] >> - \o stack - /\ token' = << >> - /\ left_' = pos_f' - /\ rt_' = pos_f' - /\ pc' = "Lbl_5" - ELSE /\ pc' = "Lbl_46" - /\ UNCHANGED << stack, pos_f, token, left_, rt_ >> - /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, - curPos, returnVal, tempVar1, tempVar2, tempVar3, - pos_, tokArray, i, tokIdx, pos_fi, pos_fin, delim_, - ipos_, jdelim_, delimLen_, pos_find, pos, delim, - ipos, jdelim, delimLen, leftTok, rightTok, - currentToken, left, rt, notDone, foundBangToken, - temp1, temp2, temp3 >> - -Lbl_44 == /\ pc = "Lbl_44" - /\ /\ leftTok' = currentToken - /\ rightTok' = returnVal - /\ stack' = << [ procedure |-> "checkIfStepName", - pc |-> "Lbl_45", - leftTok |-> leftTok, - rightTok |-> rightTok ] >> - \o stack - /\ pc' = "Lbl_27" - /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, - curPos, returnVal, tempVar1, tempVar2, tempVar3, - pos_, tokArray, i, tokIdx, pos_f, token, left_, rt_, - pos_fi, pos_fin, delim_, ipos_, jdelim_, delimLen_, - pos_find, pos, delim, ipos, jdelim, delimLen, - currentToken, left, rt, notDone, foundBangToken, - temp1, temp2, temp3 >> - -Lbl_45 == /\ pc = "Lbl_45" - /\ IF returnVal # NullTokenSpec - THEN /\ foundTokenSpecs' = JavaSeq(<<returnVal>>) - /\ returnVal' = FixOrigin(foundTokenSpecs') - /\ pc' = Head(stack).pc - /\ currentToken' = Head(stack).currentToken - /\ left' = Head(stack).left - /\ rt' = Head(stack).rt - /\ notDone' = Head(stack).notDone - /\ foundBangToken' = Head(stack).foundBangToken - /\ temp1' = Head(stack).temp1 - /\ temp2' = Head(stack).temp2 - /\ temp3' = Head(stack).temp3 - /\ stack' = Tail(stack) - ELSE /\ pc' = "Lbl_46" - /\ UNCHANGED << foundTokenSpecs, returnVal, stack, - currentToken, left, rt, notDone, - foundBangToken, temp1, temp2, temp3 >> - /\ UNCHANGED << line, lastToken, notLastToken, curPos, tempVar1, - tempVar2, tempVar3, pos_, tokArray, i, tokIdx, pos_f, - token, left_, rt_, pos_fi, pos_fin, delim_, ipos_, - jdelim_, delimLen_, pos_find, pos, delim, ipos, - jdelim, delimLen, leftTok, rightTok >> - -Lbl_46 == /\ pc = "Lbl_46" - /\ IF IsNumber(currentToken.token) - THEN /\ returnVal' = << >> - /\ pc' = Head(stack).pc - /\ currentToken' = Head(stack).currentToken - /\ left' = Head(stack).left - /\ rt' = Head(stack).rt - /\ notDone' = Head(stack).notDone - /\ foundBangToken' = Head(stack).foundBangToken - /\ temp1' = Head(stack).temp1 - /\ temp2' = Head(stack).temp2 - /\ temp3' = Head(stack).temp3 - /\ stack' = Tail(stack) - ELSE /\ pc' = "Lbl_47" - /\ UNCHANGED << returnVal, stack, currentToken, left, rt, - notDone, foundBangToken, temp1, temp2, - temp3 >> - /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, - curPos, tempVar1, tempVar2, tempVar3, pos_, tokArray, - i, tokIdx, pos_f, token, left_, rt_, pos_fi, pos_fin, - delim_, ipos_, jdelim_, delimLen_, pos_find, pos, - delim, ipos, jdelim, delimLen, leftTok, rightTok >> - -Lbl_47 == /\ pc = "Lbl_47" - /\ left' = currentToken.leftPos - /\ rt' = currentToken.rightPos - /\ IF /\ line[left'-1] = "\\" - /\ line[left'-2] # "\\" - /\ \E ii \in DOMAIN TeXSymbols : - JavaSubseq(line, left'-1, rt') = TeXSymbols[ii] - THEN /\ lastToken' = TRUE - /\ currentToken' = [currentToken EXCEPT !.leftPos = left' - 1, - !.token = JavaSubseq(line, left'-1, rt')] - ELSE /\ TRUE - /\ UNCHANGED << lastToken, currentToken >> - /\ foundTokenSpecs' = JavaSeq( << currentToken' >>) - /\ curPos' = left' - /\ pc' = "Lbl_62" - /\ UNCHANGED << line, notLastToken, returnVal, tempVar1, tempVar2, - tempVar3, stack, pos_, tokArray, i, tokIdx, pos_f, - token, left_, rt_, pos_fi, pos_fin, delim_, ipos_, - jdelim_, delimLen_, pos_find, pos, delim, ipos, - jdelim, delimLen, leftTok, rightTok, notDone, - foundBangToken, temp1, temp2, temp3 >> - -Lbl_48 == /\ pc = "Lbl_48" - /\ currentToken' = returnVal - /\ IF currentToken' = NullTokenSpec - THEN /\ returnVal' = << >> - /\ pc' = "Lbl_49" - ELSE /\ pc' = "Lbl_50" - /\ UNCHANGED returnVal - /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, - curPos, tempVar1, tempVar2, tempVar3, stack, pos_, - tokArray, i, tokIdx, pos_f, token, left_, rt_, - pos_fi, pos_fin, delim_, ipos_, jdelim_, delimLen_, - pos_find, pos, delim, ipos, jdelim, delimLen, - leftTok, rightTok, left, rt, notDone, foundBangToken, - temp1, temp2, temp3 >> - -Lbl_49 == /\ pc = "Lbl_49" - /\ pc' = Head(stack).pc - /\ currentToken' = Head(stack).currentToken - /\ left' = Head(stack).left - /\ rt' = Head(stack).rt - /\ notDone' = Head(stack).notDone - /\ foundBangToken' = Head(stack).foundBangToken - /\ temp1' = Head(stack).temp1 - /\ temp2' = Head(stack).temp2 - /\ temp3' = Head(stack).temp3 - /\ stack' = Tail(stack) - /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, - curPos, returnVal, tempVar1, tempVar2, tempVar3, - pos_, tokArray, i, tokIdx, pos_f, token, left_, rt_, - pos_fi, pos_fin, delim_, ipos_, jdelim_, delimLen_, - pos_find, pos, delim, ipos, jdelim, delimLen, - leftTok, rightTok >> - -Lbl_50 == /\ pc = "Lbl_50" - /\ /\ pos_' = curPos - /\ stack' = << [ procedure |-> "findTokenIn", - pc |-> "Lbl_51", - i |-> i, - tokIdx |-> tokIdx, - pos_ |-> pos_, - tokArray |-> tokArray ] >> - \o stack - /\ tokArray' = NonOperators - /\ i' = defaultInitValue - /\ tokIdx' = defaultInitValue - /\ pc' = "Lbl_1" - /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, - curPos, returnVal, tempVar1, tempVar2, tempVar3, - pos_f, token, left_, rt_, pos_fi, pos_fin, delim_, - ipos_, jdelim_, delimLen_, pos_find, pos, delim, - ipos, jdelim, delimLen, leftTok, rightTok, - currentToken, left, rt, notDone, foundBangToken, - temp1, temp2, temp3 >> - -Lbl_51 == /\ pc = "Lbl_51" - /\ IF returnVal # NullTokenSpec - THEN /\ returnVal' = << >> - /\ pc' = Head(stack).pc - /\ currentToken' = Head(stack).currentToken - /\ left' = Head(stack).left - /\ rt' = Head(stack).rt - /\ notDone' = Head(stack).notDone - /\ foundBangToken' = Head(stack).foundBangToken - /\ temp1' = Head(stack).temp1 - /\ temp2' = Head(stack).temp2 - /\ temp3' = Head(stack).temp3 - /\ stack' = Tail(stack) - ELSE /\ pc' = "Lbl_52" - /\ UNCHANGED << returnVal, stack, currentToken, left, rt, - notDone, foundBangToken, temp1, temp2, - temp3 >> - /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, - curPos, tempVar1, tempVar2, tempVar3, pos_, tokArray, - i, tokIdx, pos_f, token, left_, rt_, pos_fi, pos_fin, - delim_, ipos_, jdelim_, delimLen_, pos_find, pos, - delim, ipos, jdelim, delimLen, leftTok, rightTok >> - -Lbl_52 == /\ pc = "Lbl_52" - /\ IF currentToken.token = JavaSeq(<< "<" >>) - THEN /\ /\ pos_f' = currentToken.rightPos - /\ stack' = << [ procedure |-> "findMaximalIdCharSeq", - pc |-> "Lbl_53", - token |-> token, - left_ |-> left_, - rt_ |-> rt_, - pos_f |-> pos_f ] >> - \o stack - /\ token' = << >> - /\ left_' = pos_f' - /\ rt_' = pos_f' - /\ pc' = "Lbl_5" - ELSE /\ IF currentToken.token = JavaSeq(<< ">" >>) - THEN /\ /\ pos_f' = currentToken.leftPos - /\ stack' = << [ procedure |-> "findMaximalIdCharSeq", - pc |-> "Lbl_56", - token |-> token, - left_ |-> left_, - rt_ |-> rt_, - pos_f |-> pos_f ] >> - \o stack - /\ token' = << >> - /\ left_' = pos_f' - /\ rt_' = pos_f' - /\ pc' = "Lbl_5" - ELSE /\ pc' = "Lbl_59" - /\ UNCHANGED << stack, pos_f, token, left_, - rt_ >> - /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, - curPos, returnVal, tempVar1, tempVar2, tempVar3, - pos_, tokArray, i, tokIdx, pos_fi, pos_fin, delim_, - ipos_, jdelim_, delimLen_, pos_find, pos, delim, - ipos, jdelim, delimLen, leftTok, rightTok, - currentToken, left, rt, notDone, foundBangToken, - temp1, temp2, temp3 >> - -Lbl_53 == /\ pc = "Lbl_53" - /\ temp1' = returnVal - /\ IF /\ temp1' # NullTokenSpec - /\ line[temp1'.rightPos] = ">" - THEN /\ /\ pos_f' = temp1'.rightPos + 1 - /\ stack' = << [ procedure |-> "findMaximalIdCharSeq", - pc |-> "Lbl_54", - token |-> token, - left_ |-> left_, - rt_ |-> rt_, - pos_f |-> pos_f ] >> - \o stack - /\ token' = << >> - /\ left_' = pos_f' - /\ rt_' = pos_f' - /\ pc' = "Lbl_5" - ELSE /\ pc' = "Lbl_59" - /\ UNCHANGED << stack, pos_f, token, left_, rt_ >> - /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, - curPos, returnVal, tempVar1, tempVar2, tempVar3, - pos_, tokArray, i, tokIdx, pos_fi, pos_fin, delim_, - ipos_, jdelim_, delimLen_, pos_find, pos, delim, - ipos, jdelim, delimLen, leftTok, rightTok, - currentToken, left, rt, notDone, foundBangToken, - temp2, temp3 >> - -Lbl_54 == /\ pc = "Lbl_54" - /\ temp2' = returnVal - /\ /\ leftTok' = temp1 - /\ rightTok' = temp2' - /\ stack' = << [ procedure |-> "checkIfStepName", - pc |-> "Lbl_55", - leftTok |-> leftTok, - rightTok |-> rightTok ] >> - \o stack - /\ pc' = "Lbl_27" - /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, - curPos, returnVal, tempVar1, tempVar2, tempVar3, - pos_, tokArray, i, tokIdx, pos_f, token, left_, rt_, - pos_fi, pos_fin, delim_, ipos_, jdelim_, delimLen_, - pos_find, pos, delim, ipos, jdelim, delimLen, - currentToken, left, rt, notDone, foundBangToken, - temp1, temp3 >> - -Lbl_55 == /\ pc = "Lbl_55" - /\ IF returnVal # NullTokenSpec - THEN /\ foundTokenSpecs' = JavaSeq(<<returnVal>>) - /\ returnVal' = FixOrigin(foundTokenSpecs') - /\ pc' = Head(stack).pc - /\ currentToken' = Head(stack).currentToken - /\ left' = Head(stack).left - /\ rt' = Head(stack).rt - /\ notDone' = Head(stack).notDone - /\ foundBangToken' = Head(stack).foundBangToken - /\ temp1' = Head(stack).temp1 - /\ temp2' = Head(stack).temp2 - /\ temp3' = Head(stack).temp3 - /\ stack' = Tail(stack) - ELSE /\ pc' = "Lbl_59" - /\ UNCHANGED << foundTokenSpecs, returnVal, stack, - currentToken, left, rt, notDone, - foundBangToken, temp1, temp2, temp3 >> - /\ UNCHANGED << line, lastToken, notLastToken, curPos, tempVar1, - tempVar2, tempVar3, pos_, tokArray, i, tokIdx, pos_f, - token, left_, rt_, pos_fi, pos_fin, delim_, ipos_, - jdelim_, delimLen_, pos_find, pos, delim, ipos, - jdelim, delimLen, leftTok, rightTok >> - -Lbl_56 == /\ pc = "Lbl_56" - /\ temp1' = returnVal - /\ /\ pos_f' = currentToken.rightPos - /\ stack' = << [ procedure |-> "findMaximalIdCharSeq", - pc |-> "Lbl_57", - token |-> token, - left_ |-> left_, - rt_ |-> rt_, - pos_f |-> pos_f ] >> - \o stack - /\ token' = << >> - /\ left_' = pos_f' - /\ rt_' = pos_f' - /\ pc' = "Lbl_5" - /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, - curPos, returnVal, tempVar1, tempVar2, tempVar3, - pos_, tokArray, i, tokIdx, pos_fi, pos_fin, delim_, - ipos_, jdelim_, delimLen_, pos_find, pos, delim, - ipos, jdelim, delimLen, leftTok, rightTok, - currentToken, left, rt, notDone, foundBangToken, - temp2, temp3 >> - -Lbl_57 == /\ pc = "Lbl_57" - /\ temp2' = returnVal - /\ /\ leftTok' = temp1 - /\ rightTok' = temp2' - /\ stack' = << [ procedure |-> "checkIfStepName", - pc |-> "Lbl_58", - leftTok |-> leftTok, - rightTok |-> rightTok ] >> - \o stack - /\ pc' = "Lbl_27" - /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, - curPos, returnVal, tempVar1, tempVar2, tempVar3, - pos_, tokArray, i, tokIdx, pos_f, token, left_, rt_, - pos_fi, pos_fin, delim_, ipos_, jdelim_, delimLen_, - pos_find, pos, delim, ipos, jdelim, delimLen, - currentToken, left, rt, notDone, foundBangToken, - temp1, temp3 >> - -Lbl_58 == /\ pc = "Lbl_58" - /\ IF returnVal # NullTokenSpec - THEN /\ foundTokenSpecs' = JavaSeq(<<returnVal>>) - /\ returnVal' = FixOrigin(foundTokenSpecs') - /\ pc' = Head(stack).pc - /\ currentToken' = Head(stack).currentToken - /\ left' = Head(stack).left - /\ rt' = Head(stack).rt - /\ notDone' = Head(stack).notDone - /\ foundBangToken' = Head(stack).foundBangToken - /\ temp1' = Head(stack).temp1 - /\ temp2' = Head(stack).temp2 - /\ temp3' = Head(stack).temp3 - /\ stack' = Tail(stack) - ELSE /\ pc' = "Lbl_59" - /\ UNCHANGED << foundTokenSpecs, returnVal, stack, - currentToken, left, rt, notDone, - foundBangToken, temp1, temp2, temp3 >> - /\ UNCHANGED << line, lastToken, notLastToken, curPos, tempVar1, - tempVar2, tempVar3, pos_, tokArray, i, tokIdx, pos_f, - token, left_, rt_, pos_fi, pos_fin, delim_, ipos_, - jdelim_, delimLen_, pos_find, pos, delim, ipos, - jdelim, delimLen, leftTok, rightTok >> - -Lbl_59 == /\ pc = "Lbl_59" - /\ IF currentToken.token = JavaSeq(<<"\\">>) - THEN /\ /\ pos_f' = currentToken.rightPos - /\ stack' = << [ procedure |-> "findMaximalIdCharSeq", - pc |-> "Lbl_60", - token |-> token, - left_ |-> left_, - rt_ |-> rt_, - pos_f |-> pos_f ] >> - \o stack - /\ token' = << >> - /\ left_' = pos_f' - /\ rt_' = pos_f' - /\ pc' = "Lbl_5" - ELSE /\ pc' = "Lbl_61" - /\ UNCHANGED << stack, pos_f, token, left_, rt_ >> - /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, - curPos, returnVal, tempVar1, tempVar2, tempVar3, - pos_, tokArray, i, tokIdx, pos_fi, pos_fin, delim_, - ipos_, jdelim_, delimLen_, pos_find, pos, delim, - ipos, jdelim, delimLen, leftTok, rightTok, - currentToken, left, rt, notDone, foundBangToken, - temp1, temp2, temp3 >> - -Lbl_60 == /\ pc = "Lbl_60" - /\ IF /\ returnVal # NullTokenSpec - /\ \E ii \in DOMAIN TeXSymbols : - JavaSubseq(line, - currentToken.leftPos, - returnVal.rightPos) = TeXSymbols[ii] - THEN /\ currentToken' = [currentToken EXCEPT !.rightPos = returnVal.rightPos, - !.token = JavaSubseq(line, - currentToken.leftPos, - returnVal.rightPos)] - ELSE /\ TRUE - /\ UNCHANGED currentToken - /\ pc' = "Lbl_61" - /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, - curPos, returnVal, tempVar1, tempVar2, tempVar3, - stack, pos_, tokArray, i, tokIdx, pos_f, token, - left_, rt_, pos_fi, pos_fin, delim_, ipos_, jdelim_, - delimLen_, pos_find, pos, delim, ipos, jdelim, - delimLen, leftTok, rightTok, left, rt, notDone, - foundBangToken, temp1, temp2, temp3 >> - -Lbl_61 == /\ pc = "Lbl_61" - /\ foundTokenSpecs' = JavaSeq(<<currentToken>>) - /\ curPos' = currentToken.leftPos - /\ lastToken' = TRUE - /\ pc' = "Lbl_62" - /\ UNCHANGED << line, notLastToken, returnVal, tempVar1, tempVar2, - tempVar3, stack, pos_, tokArray, i, tokIdx, pos_f, - token, left_, rt_, pos_fi, pos_fin, delim_, ipos_, - jdelim_, delimLen_, pos_find, pos, delim, ipos, - jdelim, delimLen, leftTok, rightTok, currentToken, - left, rt, notDone, foundBangToken, temp1, temp2, - temp3 >> - -Lbl_62 == /\ pc = "Lbl_62" - /\ Assert(JavaLen(foundTokenSpecs) = 1, - "Failure of assertion at line 619, column 3.") - /\ curPos' = foundTokenSpecs[0].leftPos - /\ notDone' = TRUE - /\ pc' = "Lbl_63" - /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, - returnVal, tempVar1, tempVar2, tempVar3, stack, pos_, - tokArray, i, tokIdx, pos_f, token, left_, rt_, - pos_fi, pos_fin, delim_, ipos_, jdelim_, delimLen_, - pos_find, pos, delim, ipos, jdelim, delimLen, - leftTok, rightTok, currentToken, left, rt, - foundBangToken, temp1, temp2, temp3 >> - -Lbl_63 == /\ pc = "Lbl_63" - /\ IF notDone - THEN /\ stack' = << [ procedure |-> "skipLeftOverSpaces", - pc |-> "Lbl_64" ] >> - \o stack - /\ pc' = "Lbl_29" - /\ UNCHANGED << returnVal, currentToken, left, rt, - notDone, foundBangToken, temp1, temp2, - temp3 >> - ELSE /\ Assert(foundTokenSpecs # << >>, - "Failure of assertion at line 661, column 3.") - /\ IF lastToken = TRUE - THEN /\ returnVal' = FixOrigin(foundTokenSpecs) - /\ pc' = Head(stack).pc - /\ currentToken' = Head(stack).currentToken - /\ left' = Head(stack).left - /\ rt' = Head(stack).rt - /\ notDone' = Head(stack).notDone - /\ foundBangToken' = Head(stack).foundBangToken - /\ temp1' = Head(stack).temp1 - /\ temp2' = Head(stack).temp2 - /\ temp3' = Head(stack).temp3 - /\ stack' = Tail(stack) - ELSE /\ pc' = "Lbl_69" - /\ UNCHANGED << returnVal, stack, currentToken, - left, rt, notDone, - foundBangToken, temp1, temp2, - temp3 >> - /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, - curPos, tempVar1, tempVar2, tempVar3, pos_, tokArray, - i, tokIdx, pos_f, token, left_, rt_, pos_fi, pos_fin, - delim_, ipos_, jdelim_, delimLen_, pos_find, pos, - delim, ipos, jdelim, delimLen, leftTok, rightTok >> - -Lbl_64 == /\ pc = "Lbl_64" - /\ IF curPos < 0 \/ line[curPos-1] = ";" - THEN /\ notDone' = FALSE - /\ pc' = "Lbl_63" - /\ UNCHANGED << curPos, stack >> - ELSE /\ IF line[curPos-1] = "!" /\ (line[curPos-2] # "!") - THEN /\ curPos' = curPos - 1 - /\ stack' = << [ procedure |-> "skipLeftOverSpaces", - pc |-> "Lbl_65" ] >> - \o stack - /\ pc' = "Lbl_29" - /\ UNCHANGED notDone - ELSE /\ notDone' = FALSE - /\ pc' = "Lbl_63" - /\ UNCHANGED << curPos, stack >> - /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, - returnVal, tempVar1, tempVar2, tempVar3, pos_, - tokArray, i, tokIdx, pos_f, token, left_, rt_, - pos_fi, pos_fin, delim_, ipos_, jdelim_, delimLen_, - pos_find, pos, delim, ipos, jdelim, delimLen, - leftTok, rightTok, currentToken, left, rt, - foundBangToken, temp1, temp2, temp3 >> - -Lbl_65 == /\ pc = "Lbl_65" - /\ /\ pos_fi' = curPos - /\ stack' = << [ procedure |-> "findMatchingLeftParen", - pc |-> "Lbl_66", - pos_fi |-> pos_fi ] >> - \o stack - /\ pc' = "Lbl_7" - /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, - curPos, returnVal, tempVar1, tempVar2, tempVar3, - pos_, tokArray, i, tokIdx, pos_f, token, left_, rt_, - pos_fin, delim_, ipos_, jdelim_, delimLen_, pos_find, - pos, delim, ipos, jdelim, delimLen, leftTok, - rightTok, currentToken, left, rt, notDone, - foundBangToken, temp1, temp2, temp3 >> - -Lbl_66 == /\ pc = "Lbl_66" - /\ curPos' = returnVal - /\ IF curPos' < 0 - THEN /\ notDone' = FALSE - /\ pc' = "Lbl_63" - /\ stack' = stack - ELSE /\ stack' = << [ procedure |-> "skipLeftOverSpaces", - pc |-> "Lbl_67" ] >> - \o stack - /\ pc' = "Lbl_29" - /\ UNCHANGED notDone - /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, - returnVal, tempVar1, tempVar2, tempVar3, pos_, - tokArray, i, tokIdx, pos_f, token, left_, rt_, - pos_fi, pos_fin, delim_, ipos_, jdelim_, delimLen_, - pos_find, pos, delim, ipos, jdelim, delimLen, - leftTok, rightTok, currentToken, left, rt, - foundBangToken, temp1, temp2, temp3 >> - -Lbl_67 == /\ pc = "Lbl_67" - /\ /\ pos_f' = curPos - /\ stack' = << [ procedure |-> "findMaximalIdCharSeq", - pc |-> "Lbl_68", - token |-> token, - left_ |-> left_, - rt_ |-> rt_, - pos_f |-> pos_f ] >> - \o stack - /\ token' = << >> - /\ left_' = pos_f' - /\ rt_' = pos_f' - /\ pc' = "Lbl_5" - /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, - curPos, returnVal, tempVar1, tempVar2, tempVar3, - pos_, tokArray, i, tokIdx, pos_fi, pos_fin, delim_, - ipos_, jdelim_, delimLen_, pos_find, pos, delim, - ipos, jdelim, delimLen, leftTok, rightTok, - currentToken, left, rt, notDone, foundBangToken, - temp1, temp2, temp3 >> - -Lbl_68 == /\ pc = "Lbl_68" - /\ currentToken' = returnVal - /\ IF \/ currentToken' = NullTokenSpec - \/ IsNumber(currentToken'.token) - THEN /\ notDone' = FALSE - /\ UNCHANGED << foundTokenSpecs, curPos >> - ELSE /\ curPos' = currentToken'.leftPos - /\ foundTokenSpecs' = [foundTokenSpecs EXCEPT ![0] = [token |-> JavaConcatenate( - JavaAppend(currentToken'.token, "!"), - foundTokenSpecs[0].token), - leftPos |-> curPos', - rightPos |-> foundTokenSpecs[0].rightPos]] - /\ UNCHANGED notDone - /\ pc' = "Lbl_63" - /\ UNCHANGED << line, lastToken, notLastToken, returnVal, tempVar1, - tempVar2, tempVar3, stack, pos_, tokArray, i, tokIdx, - pos_f, token, left_, rt_, pos_fi, pos_fin, delim_, - ipos_, jdelim_, delimLen_, pos_find, pos, delim, - ipos, jdelim, delimLen, leftTok, rightTok, left, rt, - foundBangToken, temp1, temp2, temp3 >> - -Lbl_69 == /\ pc = "Lbl_69" - /\ curPos' = foundTokenSpecs[0].rightPos - /\ foundBangToken' = FALSE - /\ notDone' = TRUE - /\ pc' = "Lbl_70" - /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, - returnVal, tempVar1, tempVar2, tempVar3, stack, pos_, - tokArray, i, tokIdx, pos_f, token, left_, rt_, - pos_fi, pos_fin, delim_, ipos_, jdelim_, delimLen_, - pos_find, pos, delim, ipos, jdelim, delimLen, - leftTok, rightTok, currentToken, left, rt, temp1, - temp2, temp3 >> - -Lbl_70 == /\ pc = "Lbl_70" - /\ IF notDone - THEN /\ stack' = << [ procedure |-> "skipRightOverSpaces", - pc |-> "Lbl_71" ] >> - \o stack - /\ pc' = "Lbl_30" - /\ UNCHANGED << returnVal, currentToken, left, rt, - notDone, foundBangToken, temp1, temp2, - temp3 >> - ELSE /\ IF notLastToken /\ ~ foundBangToken - THEN /\ returnVal' = << >> - /\ pc' = Head(stack).pc - /\ currentToken' = Head(stack).currentToken - /\ left' = Head(stack).left - /\ rt' = Head(stack).rt - /\ notDone' = Head(stack).notDone - /\ foundBangToken' = Head(stack).foundBangToken - /\ temp1' = Head(stack).temp1 - /\ temp2' = Head(stack).temp2 - /\ temp3' = Head(stack).temp3 - /\ stack' = Tail(stack) - ELSE /\ pc' = "Lbl_76" - /\ UNCHANGED << returnVal, stack, currentToken, - left, rt, notDone, - foundBangToken, temp1, temp2, - temp3 >> - /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, - curPos, tempVar1, tempVar2, tempVar3, pos_, tokArray, - i, tokIdx, pos_f, token, left_, rt_, pos_fi, pos_fin, - delim_, ipos_, jdelim_, delimLen_, pos_find, pos, - delim, ipos, jdelim, delimLen, leftTok, rightTok >> - -Lbl_71 == /\ pc = "Lbl_71" - /\ /\ pos_find' = curPos - /\ stack' = << [ procedure |-> "findMatchingRightParen", - pc |-> "Lbl_72", - pos_find |-> pos_find ] >> - \o stack - /\ pc' = "Lbl_17" - /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, - curPos, returnVal, tempVar1, tempVar2, tempVar3, - pos_, tokArray, i, tokIdx, pos_f, token, left_, rt_, - pos_fi, pos_fin, delim_, ipos_, jdelim_, delimLen_, - pos, delim, ipos, jdelim, delimLen, leftTok, - rightTok, currentToken, left, rt, notDone, - foundBangToken, temp1, temp2, temp3 >> - -Lbl_72 == /\ pc = "Lbl_72" - /\ curPos' = returnVal - /\ IF curPos' < 0 - THEN /\ notDone' = FALSE - /\ pc' = "Lbl_70" - /\ stack' = stack - ELSE /\ stack' = << [ procedure |-> "skipRightOverSpaces", - pc |-> "Lbl_73" ] >> - \o stack - /\ pc' = "Lbl_30" - /\ UNCHANGED notDone - /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, - returnVal, tempVar1, tempVar2, tempVar3, pos_, - tokArray, i, tokIdx, pos_f, token, left_, rt_, - pos_fi, pos_fin, delim_, ipos_, jdelim_, delimLen_, - pos_find, pos, delim, ipos, jdelim, delimLen, - leftTok, rightTok, currentToken, left, rt, - foundBangToken, temp1, temp2, temp3 >> - -Lbl_73 == /\ pc = "Lbl_73" - /\ IF line[curPos] # "!" \/ line[curPos+1] = "!" - THEN /\ notDone' = FALSE - /\ pc' = "Lbl_70" - /\ UNCHANGED << curPos, stack >> - ELSE /\ curPos' = curPos + 1 - /\ stack' = << [ procedure |-> "skipRightOverSpaces", - pc |-> "Lbl_74" ] >> - \o stack - /\ pc' = "Lbl_30" - /\ UNCHANGED notDone - /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, - returnVal, tempVar1, tempVar2, tempVar3, pos_, - tokArray, i, tokIdx, pos_f, token, left_, rt_, - pos_fi, pos_fin, delim_, ipos_, jdelim_, delimLen_, - pos_find, pos, delim, ipos, jdelim, delimLen, - leftTok, rightTok, currentToken, left, rt, - foundBangToken, temp1, temp2, temp3 >> - -Lbl_74 == /\ pc = "Lbl_74" - /\ /\ pos_f' = curPos - /\ stack' = << [ procedure |-> "findMaximalIdCharSeq", - pc |-> "Lbl_75", - token |-> token, - left_ |-> left_, - rt_ |-> rt_, - pos_f |-> pos_f ] >> - \o stack - /\ token' = << >> - /\ left_' = pos_f' - /\ rt_' = pos_f' - /\ pc' = "Lbl_5" - /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, - curPos, returnVal, tempVar1, tempVar2, tempVar3, - pos_, tokArray, i, tokIdx, pos_fi, pos_fin, delim_, - ipos_, jdelim_, delimLen_, pos_find, pos, delim, - ipos, jdelim, delimLen, leftTok, rightTok, - currentToken, left, rt, notDone, foundBangToken, - temp1, temp2, temp3 >> - -Lbl_75 == /\ pc = "Lbl_75" - /\ currentToken' = returnVal - /\ IF \/ currentToken' = NullTokenSpec - \/ IsNumber(currentToken'.token) - THEN /\ notDone' = FALSE - /\ UNCHANGED << foundTokenSpecs, curPos, foundBangToken >> - ELSE /\ foundBangToken' = TRUE - /\ foundTokenSpecs' = JavaConcatenate( - JavaSeq( - << [token |-> - JavaConcatenate(JavaAppend(foundTokenSpecs[0].token, - "!"), - currentToken'.token), - leftPos |-> foundTokenSpecs[0].leftPos, - rightPos |-> currentToken'.rightPos] - >> ) , - foundTokenSpecs) - /\ curPos' = currentToken'.rightPos - /\ UNCHANGED notDone - /\ pc' = "Lbl_70" - /\ UNCHANGED << line, lastToken, notLastToken, returnVal, tempVar1, - tempVar2, tempVar3, stack, pos_, tokArray, i, tokIdx, - pos_f, token, left_, rt_, pos_fi, pos_fin, delim_, - ipos_, jdelim_, delimLen_, pos_find, pos, delim, - ipos, jdelim, delimLen, leftTok, rightTok, left, rt, - temp1, temp2, temp3 >> - -Lbl_76 == /\ pc = "Lbl_76" - /\ returnVal' = FixOrigin(foundTokenSpecs) - /\ pc' = Head(stack).pc - /\ currentToken' = Head(stack).currentToken - /\ left' = Head(stack).left - /\ rt' = Head(stack).rt - /\ notDone' = Head(stack).notDone - /\ foundBangToken' = Head(stack).foundBangToken - /\ temp1' = Head(stack).temp1 - /\ temp2' = Head(stack).temp2 - /\ temp3' = Head(stack).temp3 - /\ stack' = Tail(stack) - /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, - curPos, tempVar1, tempVar2, tempVar3, pos_, tokArray, - i, tokIdx, pos_f, token, left_, rt_, pos_fi, pos_fin, - delim_, ipos_, jdelim_, delimLen_, pos_find, pos, - delim, ipos, jdelim, delimLen, leftTok, rightTok >> - -findTokenSpecs == Lbl_31 \/ Lbl_32 \/ Lbl_33 \/ Lbl_34 \/ Lbl_35 \/ Lbl_36 - \/ Lbl_37 \/ Lbl_38 \/ Lbl_39 \/ Lbl_40 \/ Lbl_41 \/ Lbl_42 - \/ Lbl_43 \/ Lbl_44 \/ Lbl_45 \/ Lbl_46 \/ Lbl_47 \/ Lbl_48 - \/ Lbl_49 \/ Lbl_50 \/ Lbl_51 \/ Lbl_52 \/ Lbl_53 \/ Lbl_54 - \/ Lbl_55 \/ Lbl_56 \/ Lbl_57 \/ Lbl_58 \/ Lbl_59 \/ Lbl_60 - \/ Lbl_61 \/ Lbl_62 \/ Lbl_63 \/ Lbl_64 \/ Lbl_65 \/ Lbl_66 - \/ Lbl_67 \/ Lbl_68 \/ Lbl_69 \/ Lbl_70 \/ Lbl_71 \/ Lbl_72 - \/ Lbl_73 \/ Lbl_74 \/ Lbl_75 \/ Lbl_76 - -Lbl_77 == /\ pc = "Lbl_77" - /\ curPos' = JavaLen(Padding) - /\ tempVar1' = (-1 :> "") - /\ tempVar2' = JavaLen(Padding) - /\ pc' = "Lbl_78" - /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, - returnVal, tempVar3, stack, pos_, tokArray, i, - tokIdx, pos_f, token, left_, rt_, pos_fi, pos_fin, - delim_, ipos_, jdelim_, delimLen_, pos_find, pos, - delim, ipos, jdelim, delimLen, leftTok, rightTok, - currentToken, left, rt, notDone, foundBangToken, - temp1, temp2, temp3 >> - -Lbl_78 == /\ pc = "Lbl_78" - /\ IF tempVar2 < JavaLen(lineInput) + JavaLen(Padding) - THEN /\ curPos' = tempVar2 - /\ foundTokenSpecs' = << >> - /\ lastToken' = FALSE - /\ notLastToken' = FALSE - /\ stack' = << [ procedure |-> "findTokenSpecs", - pc |-> "Lbl_79", - currentToken |-> currentToken, - left |-> left, - rt |-> rt, - notDone |-> notDone, - foundBangToken |-> foundBangToken, - temp1 |-> temp1, - temp2 |-> temp2, - temp3 |-> temp3 ] >> - \o stack - /\ currentToken' = defaultInitValue - /\ left' = defaultInitValue - /\ rt' = defaultInitValue - /\ notDone' = defaultInitValue - /\ foundBangToken' = defaultInitValue - /\ temp1' = defaultInitValue - /\ temp2' = defaultInitValue - /\ temp3' = defaultInitValue - /\ pc' = "Lbl_31" - ELSE /\ PrintT("goodby world") - /\ pc' = "Done" - /\ UNCHANGED << foundTokenSpecs, lastToken, notLastToken, - curPos, stack, currentToken, left, rt, - notDone, foundBangToken, temp1, temp2, - temp3 >> - /\ UNCHANGED << line, returnVal, tempVar1, tempVar2, tempVar3, pos_, - tokArray, i, tokIdx, pos_f, token, left_, rt_, - pos_fi, pos_fin, delim_, ipos_, jdelim_, delimLen_, - pos_find, pos, delim, ipos, jdelim, delimLen, - leftTok, rightTok >> - -Lbl_79 == /\ pc = "Lbl_79" - /\ tempVar1' = returnVal - /\ tempVar2' = tempVar2 + 1 - /\ pc' = "Lbl_78" - /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, - curPos, returnVal, tempVar3, stack, pos_, tokArray, - i, tokIdx, pos_f, token, left_, rt_, pos_fi, pos_fin, - delim_, ipos_, jdelim_, delimLen_, pos_find, pos, - delim, ipos, jdelim, delimLen, leftTok, rightTok, - currentToken, left, rt, notDone, foundBangToken, - temp1, temp2, temp3 >> - -Next == findTokenIn \/ findMaximalIdCharSeq \/ findMatchingLeftParen - \/ findMatchingLeftInner \/ findMatchingRightParen - \/ findMatchingRightInner \/ checkIfStepName \/ skipLeftOverSpaces - \/ skipRightOverSpaces \/ findTokenSpecs \/ Lbl_77 \/ Lbl_78 \/ Lbl_79 - \/ (* Disjunct to prevent deadlock on termination *) - (pc = "Done" /\ UNCHANGED vars) - -Spec == Init /\ [][Next]_vars - -Termination == <>(pc = "Done") - -\* END TRANSLATION -============================================================================= + temp2, temp3 >> + +Init == (* Global variables *) + /\ line = JavaConcatenate (Padding, JavaConcatenate(lineInput, Padding)) + /\ foundTokenSpecs = << >> + /\ lastToken = FALSE + /\ notLastToken = FALSE + /\ curPos = currentPosition + JavaLen(Padding) + /\ returnVal = defaultInitValue + /\ tempVar1 = defaultInitValue + /\ tempVar2 = defaultInitValue + /\ tempVar3 = defaultInitValue + (* Procedure findTokenIn *) + /\ pos_ = defaultInitValue + /\ tokArray = defaultInitValue + /\ i = defaultInitValue + /\ tokIdx = defaultInitValue + (* Procedure findMaximalIdCharSeq *) + /\ pos_f = defaultInitValue + /\ token = << >> + /\ left_ = pos_f + /\ rt_ = pos_f + (* Procedure findMatchingLeftParen *) + /\ pos_fi = defaultInitValue + (* Procedure findMatchingLeftInner *) + /\ pos_fin = defaultInitValue + /\ delim_ = defaultInitValue + /\ ipos_ = pos_fin + /\ jdelim_ = defaultInitValue + /\ delimLen_ = defaultInitValue + (* Procedure findMatchingRightParen *) + /\ pos_find = defaultInitValue + (* Procedure findMatchingRightInner *) + /\ pos = defaultInitValue + /\ delim = defaultInitValue + /\ ipos = pos + /\ jdelim = defaultInitValue + /\ delimLen = defaultInitValue + (* Procedure checkIfStepName *) + /\ leftTok = defaultInitValue + /\ rightTok = defaultInitValue + (* Procedure findTokenSpecs *) + /\ currentToken = defaultInitValue + /\ left = defaultInitValue + /\ rt = defaultInitValue + /\ notDone = defaultInitValue + /\ foundBangToken = defaultInitValue + /\ temp1 = defaultInitValue + /\ temp2 = defaultInitValue + /\ temp3 = defaultInitValue + /\ stack = << >> + /\ pc = "Lbl_77" + +Lbl_1 == /\ pc = "Lbl_1" + /\ i' = 0 + /\ pc' = "Lbl_2" + /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, + curPos, returnVal, tempVar1, tempVar2, tempVar3, + stack, pos_, tokArray, tokIdx, pos_f, token, left_, + rt_, pos_fi, pos_fin, delim_, ipos_, jdelim_, + delimLen_, pos_find, pos, delim, ipos, jdelim, + delimLen, leftTok, rightTok, currentToken, left, rt, + notDone, foundBangToken, temp1, temp2, temp3 >> + +Lbl_2 == /\ pc = "Lbl_2" + /\ IF i < JavaLen(tokArray) + THEN /\ tokIdx' = JavaIndex(tokArray[i], line[pos_], 0) + /\ pc' = "Lbl_3" + /\ UNCHANGED << returnVal, stack, pos_, tokArray, i >> + ELSE /\ returnVal' = NullTokenSpec + /\ pc' = Head(stack).pc + /\ i' = Head(stack).i + /\ tokIdx' = Head(stack).tokIdx + /\ pos_' = Head(stack).pos_ + /\ tokArray' = Head(stack).tokArray + /\ stack' = Tail(stack) + /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, + curPos, tempVar1, tempVar2, tempVar3, pos_f, token, + left_, rt_, pos_fi, pos_fin, delim_, ipos_, jdelim_, + delimLen_, pos_find, pos, delim, ipos, jdelim, + delimLen, leftTok, rightTok, currentToken, left, rt, + notDone, foundBangToken, temp1, temp2, temp3 >> + +Lbl_3 == /\ pc = "Lbl_3" + /\ IF tokIdx # -1 + THEN /\ LET ftlft == pos_ - tokIdx IN + LET ftrt == pos_ - tokIdx + JavaLen(tokArray[i]) IN + IF tokArray[i] = JavaSubseq(line, ftlft, ftrt) + THEN /\ returnVal' = [token |-> tokArray[i], + leftPos |-> ftlft, rightPos |-> ftrt] + /\ pc' = Head(stack).pc + /\ i' = Head(stack).i + /\ tokIdx' = Head(stack).tokIdx + /\ pos_' = Head(stack).pos_ + /\ tokArray' = Head(stack).tokArray + /\ stack' = Tail(stack) + ELSE /\ pc' = "Lbl_4" + /\ UNCHANGED << returnVal, stack, pos_, + tokArray, i, tokIdx >> + ELSE /\ i' = i+1 + /\ pc' = "Lbl_2" + /\ UNCHANGED << returnVal, stack, pos_, tokArray, tokIdx >> + /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, + curPos, tempVar1, tempVar2, tempVar3, pos_f, token, + left_, rt_, pos_fi, pos_fin, delim_, ipos_, jdelim_, + delimLen_, pos_find, pos, delim, ipos, jdelim, + delimLen, leftTok, rightTok, currentToken, left, rt, + notDone, foundBangToken, temp1, temp2, temp3 >> + +Lbl_4 == /\ pc = "Lbl_4" + /\ tokIdx' = JavaIndex(tokArray[i], line[pos_], tokIdx+1) + /\ pc' = "Lbl_3" + /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, + curPos, returnVal, tempVar1, tempVar2, tempVar3, + stack, pos_, tokArray, i, pos_f, token, left_, rt_, + pos_fi, pos_fin, delim_, ipos_, jdelim_, delimLen_, + pos_find, pos, delim, ipos, jdelim, delimLen, leftTok, + rightTok, currentToken, left, rt, notDone, + foundBangToken, temp1, temp2, temp3 >> + +findTokenIn == Lbl_1 \/ Lbl_2 \/ Lbl_3 \/ Lbl_4 + +Lbl_5 == /\ pc = "Lbl_5" + /\ IF line[left_-1] \in IdChar + THEN /\ left_' = left_-1 + /\ pc' = "Lbl_5" + ELSE /\ pc' = "Lbl_6" + /\ left_' = left_ + /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, + curPos, returnVal, tempVar1, tempVar2, tempVar3, + stack, pos_, tokArray, i, tokIdx, pos_f, token, rt_, + pos_fi, pos_fin, delim_, ipos_, jdelim_, delimLen_, + pos_find, pos, delim, ipos, jdelim, delimLen, leftTok, + rightTok, currentToken, left, rt, notDone, + foundBangToken, temp1, temp2, temp3 >> + +Lbl_6 == /\ pc = "Lbl_6" + /\ IF line[rt_] \in IdChar + THEN /\ rt_' = rt_+1 + /\ pc' = "Lbl_6" + /\ UNCHANGED << returnVal, stack, pos_f, token, left_ >> + ELSE /\ IF left_ = rt_ + THEN /\ returnVal' = NullTokenSpec + /\ pc' = Head(stack).pc + /\ token' = Head(stack).token + /\ left_' = Head(stack).left_ + /\ rt_' = Head(stack).rt_ + /\ pos_f' = Head(stack).pos_f + /\ stack' = Tail(stack) + ELSE /\ returnVal' = [token |-> JavaSubseq(line, left_, rt_), + leftPos |-> left_, rightPos |-> rt_] + /\ pc' = Head(stack).pc + /\ token' = Head(stack).token + /\ left_' = Head(stack).left_ + /\ rt_' = Head(stack).rt_ + /\ pos_f' = Head(stack).pos_f + /\ stack' = Tail(stack) + /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, + curPos, tempVar1, tempVar2, tempVar3, pos_, tokArray, + i, tokIdx, pos_fi, pos_fin, delim_, ipos_, jdelim_, + delimLen_, pos_find, pos, delim, ipos, jdelim, + delimLen, leftTok, rightTok, currentToken, left, rt, + notDone, foundBangToken, temp1, temp2, temp3 >> + +findMaximalIdCharSeq == Lbl_5 \/ Lbl_6 + +Lbl_7 == /\ pc = "Lbl_7" + /\ IF line[pos_fi-1] # ")" + THEN /\ returnVal' = pos_fi + /\ pc' = Head(stack).pc + /\ pos_fi' = Head(stack).pos_fi + /\ stack' = Tail(stack) + ELSE /\ pc' = "Lbl_8" + /\ UNCHANGED << returnVal, stack, pos_fi >> + /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, + curPos, tempVar1, tempVar2, tempVar3, pos_, tokArray, + i, tokIdx, pos_f, token, left_, rt_, pos_fin, delim_, + ipos_, jdelim_, delimLen_, pos_find, pos, delim, ipos, + jdelim, delimLen, leftTok, rightTok, currentToken, + left, rt, notDone, foundBangToken, temp1, temp2, + temp3 >> + +Lbl_8 == /\ pc = "Lbl_8" + /\ /\ delim_' = 0 + /\ pos_fin' = pos_fi + /\ stack' = << [ procedure |-> "findMatchingLeftInner", + pc |-> Head(stack).pc, + ipos_ |-> ipos_, + jdelim_ |-> jdelim_, + delimLen_ |-> delimLen_, + pos_fin |-> pos_fin, + delim_ |-> delim_ ] >> + \o Tail(stack) + /\ ipos_' = pos_fin' + /\ jdelim_' = defaultInitValue + /\ delimLen_' = defaultInitValue + /\ pc' = "Lbl_9" + /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, + curPos, returnVal, tempVar1, tempVar2, tempVar3, pos_, + tokArray, i, tokIdx, pos_f, token, left_, rt_, pos_fi, + pos_find, pos, delim, ipos, jdelim, delimLen, leftTok, + rightTok, currentToken, left, rt, notDone, + foundBangToken, temp1, temp2, temp3 >> + +findMatchingLeftParen == Lbl_7 \/ Lbl_8 + +Lbl_9 == /\ pc = "Lbl_9" + /\ delimLen_' = JavaLen(LeftDelimiters[delim_]) + /\ ipos_' = pos_fin - delimLen_' + /\ pc' = "Lbl_10" + /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, + curPos, returnVal, tempVar1, tempVar2, tempVar3, + stack, pos_, tokArray, i, tokIdx, pos_f, token, left_, + rt_, pos_fi, pos_fin, delim_, jdelim_, pos_find, pos, + delim, ipos, jdelim, delimLen, leftTok, rightTok, + currentToken, left, rt, notDone, foundBangToken, + temp1, temp2, temp3 >> + +Lbl_10 == /\ pc = "Lbl_10" + /\ IF JavaSubseq(line, ipos_-delimLen_, ipos_) # LeftDelimiters[delim_] + THEN /\ IF line[ipos_-1] = ";" + THEN /\ returnVal' = -1 + /\ pc' = Head(stack).pc + /\ ipos_' = Head(stack).ipos_ + /\ jdelim_' = Head(stack).jdelim_ + /\ delimLen_' = Head(stack).delimLen_ + /\ pos_fin' = Head(stack).pos_fin + /\ delim_' = Head(stack).delim_ + /\ stack' = Tail(stack) + ELSE /\ pc' = "Lbl_11" + /\ UNCHANGED << returnVal, stack, pos_fin, + delim_, ipos_, jdelim_, + delimLen_ >> + ELSE /\ returnVal' = ipos_-delimLen_ + /\ pc' = Head(stack).pc + /\ ipos_' = Head(stack).ipos_ + /\ jdelim_' = Head(stack).jdelim_ + /\ delimLen_' = Head(stack).delimLen_ + /\ pos_fin' = Head(stack).pos_fin + /\ delim_' = Head(stack).delim_ + /\ stack' = Tail(stack) + /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, + curPos, tempVar1, tempVar2, tempVar3, pos_, tokArray, + i, tokIdx, pos_f, token, left_, rt_, pos_fi, + pos_find, pos, delim, ipos, jdelim, delimLen, + leftTok, rightTok, currentToken, left, rt, notDone, + foundBangToken, temp1, temp2, temp3 >> + +Lbl_11 == /\ pc = "Lbl_11" + /\ ipos_' = ipos_-1 + /\ jdelim_' = 0 + /\ pc' = "Lbl_12" + /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, + curPos, returnVal, tempVar1, tempVar2, tempVar3, + stack, pos_, tokArray, i, tokIdx, pos_f, token, + left_, rt_, pos_fi, pos_fin, delim_, delimLen_, + pos_find, pos, delim, ipos, jdelim, delimLen, + leftTok, rightTok, currentToken, left, rt, notDone, + foundBangToken, temp1, temp2, temp3 >> + +Lbl_12 == /\ pc = "Lbl_12" + /\ IF jdelim_ < JavaLen(LeftDelimiters) + THEN /\ IF JavaSubseq(line, ipos_-delimLen_, ipos_) = RightDelimiters[jdelim_] + THEN /\ /\ delim_' = jdelim_ + /\ pos_fin' = ipos_ + /\ stack' = << [ procedure |-> "findMatchingLeftInner", + pc |-> "Lbl_13", + ipos_ |-> ipos_, + jdelim_ |-> jdelim_, + delimLen_ |-> delimLen_, + pos_fin |-> pos_fin, + delim_ |-> delim_ ] >> + \o stack + /\ ipos_' = pos_fin' + /\ jdelim_' = defaultInitValue + /\ delimLen_' = defaultInitValue + /\ pc' = "Lbl_9" + ELSE /\ pc' = "Lbl_16" + /\ UNCHANGED << stack, pos_fin, delim_, ipos_, + jdelim_, delimLen_ >> + ELSE /\ pc' = "Lbl_10" + /\ UNCHANGED << stack, pos_fin, delim_, ipos_, jdelim_, + delimLen_ >> + /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, + curPos, returnVal, tempVar1, tempVar2, tempVar3, + pos_, tokArray, i, tokIdx, pos_f, token, left_, rt_, + pos_fi, pos_find, pos, delim, ipos, jdelim, delimLen, + leftTok, rightTok, currentToken, left, rt, notDone, + foundBangToken, temp1, temp2, temp3 >> + +Lbl_16 == /\ pc = "Lbl_16" + /\ jdelim_' = jdelim_+1 + /\ pc' = "Lbl_12" + /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, + curPos, returnVal, tempVar1, tempVar2, tempVar3, + stack, pos_, tokArray, i, tokIdx, pos_f, token, + left_, rt_, pos_fi, pos_fin, delim_, ipos_, + delimLen_, pos_find, pos, delim, ipos, jdelim, + delimLen, leftTok, rightTok, currentToken, left, rt, + notDone, foundBangToken, temp1, temp2, temp3 >> + +Lbl_13 == /\ pc = "Lbl_13" + /\ ipos_' = returnVal + /\ IF ipos_' < 0 + THEN /\ returnVal' = -1 + /\ pc' = "Lbl_14" + ELSE /\ pc' = "Lbl_15" + /\ UNCHANGED returnVal + /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, + curPos, tempVar1, tempVar2, tempVar3, stack, pos_, + tokArray, i, tokIdx, pos_f, token, left_, rt_, + pos_fi, pos_fin, delim_, jdelim_, delimLen_, + pos_find, pos, delim, ipos, jdelim, delimLen, + leftTok, rightTok, currentToken, left, rt, notDone, + foundBangToken, temp1, temp2, temp3 >> + +Lbl_14 == /\ pc = "Lbl_14" + /\ pc' = Head(stack).pc + /\ ipos_' = Head(stack).ipos_ + /\ jdelim_' = Head(stack).jdelim_ + /\ delimLen_' = Head(stack).delimLen_ + /\ pos_fin' = Head(stack).pos_fin + /\ delim_' = Head(stack).delim_ + /\ stack' = Tail(stack) + /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, + curPos, returnVal, tempVar1, tempVar2, tempVar3, + pos_, tokArray, i, tokIdx, pos_f, token, left_, rt_, + pos_fi, pos_find, pos, delim, ipos, jdelim, delimLen, + leftTok, rightTok, currentToken, left, rt, notDone, + foundBangToken, temp1, temp2, temp3 >> + +Lbl_15 == /\ pc = "Lbl_15" + /\ jdelim_' = 99 + /\ pc' = "Lbl_16" + /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, + curPos, returnVal, tempVar1, tempVar2, tempVar3, + stack, pos_, tokArray, i, tokIdx, pos_f, token, + left_, rt_, pos_fi, pos_fin, delim_, ipos_, + delimLen_, pos_find, pos, delim, ipos, jdelim, + delimLen, leftTok, rightTok, currentToken, left, rt, + notDone, foundBangToken, temp1, temp2, temp3 >> + +findMatchingLeftInner == Lbl_9 \/ Lbl_10 \/ Lbl_11 \/ Lbl_12 \/ Lbl_16 + \/ Lbl_13 \/ Lbl_14 \/ Lbl_15 + +Lbl_17 == /\ pc = "Lbl_17" + /\ IF line[pos_find] # "(" + THEN /\ returnVal' = pos_find + /\ pc' = Head(stack).pc + /\ pos_find' = Head(stack).pos_find + /\ stack' = Tail(stack) + ELSE /\ pc' = "Lbl_18" + /\ UNCHANGED << returnVal, stack, pos_find >> + /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, + curPos, tempVar1, tempVar2, tempVar3, pos_, tokArray, + i, tokIdx, pos_f, token, left_, rt_, pos_fi, pos_fin, + delim_, ipos_, jdelim_, delimLen_, pos, delim, ipos, + jdelim, delimLen, leftTok, rightTok, currentToken, + left, rt, notDone, foundBangToken, temp1, temp2, + temp3 >> + +Lbl_18 == /\ pc = "Lbl_18" + /\ /\ delim' = 0 + /\ pos' = pos_find + /\ stack' = << [ procedure |-> "findMatchingRightInner", + pc |-> Head(stack).pc, + ipos |-> ipos, + jdelim |-> jdelim, + delimLen |-> delimLen, + pos |-> pos, + delim |-> delim ] >> + \o Tail(stack) + /\ ipos' = pos' + /\ jdelim' = defaultInitValue + /\ delimLen' = defaultInitValue + /\ pc' = "Lbl_19" + /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, + curPos, returnVal, tempVar1, tempVar2, tempVar3, + pos_, tokArray, i, tokIdx, pos_f, token, left_, rt_, + pos_fi, pos_fin, delim_, ipos_, jdelim_, delimLen_, + pos_find, leftTok, rightTok, currentToken, left, rt, + notDone, foundBangToken, temp1, temp2, temp3 >> + +findMatchingRightParen == Lbl_17 \/ Lbl_18 + +Lbl_19 == /\ pc = "Lbl_19" + /\ delimLen' = JavaLen(RightDelimiters[delim]) + /\ ipos' = pos + delimLen' + /\ pc' = "Lbl_20" + /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, + curPos, returnVal, tempVar1, tempVar2, tempVar3, + stack, pos_, tokArray, i, tokIdx, pos_f, token, + left_, rt_, pos_fi, pos_fin, delim_, ipos_, jdelim_, + delimLen_, pos_find, pos, delim, jdelim, leftTok, + rightTok, currentToken, left, rt, notDone, + foundBangToken, temp1, temp2, temp3 >> + +Lbl_20 == /\ pc = "Lbl_20" + /\ IF JavaSubseq(line, ipos, ipos+delimLen) # RightDelimiters[delim] + THEN /\ IF line[ipos] = ";" + THEN /\ returnVal' = -1 + /\ pc' = Head(stack).pc + /\ ipos' = Head(stack).ipos + /\ jdelim' = Head(stack).jdelim + /\ delimLen' = Head(stack).delimLen + /\ pos' = Head(stack).pos + /\ delim' = Head(stack).delim + /\ stack' = Tail(stack) + ELSE /\ pc' = "Lbl_21" + /\ UNCHANGED << returnVal, stack, pos, delim, + ipos, jdelim, delimLen >> + ELSE /\ returnVal' = ipos+delimLen + /\ pc' = Head(stack).pc + /\ ipos' = Head(stack).ipos + /\ jdelim' = Head(stack).jdelim + /\ delimLen' = Head(stack).delimLen + /\ pos' = Head(stack).pos + /\ delim' = Head(stack).delim + /\ stack' = Tail(stack) + /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, + curPos, tempVar1, tempVar2, tempVar3, pos_, tokArray, + i, tokIdx, pos_f, token, left_, rt_, pos_fi, pos_fin, + delim_, ipos_, jdelim_, delimLen_, pos_find, leftTok, + rightTok, currentToken, left, rt, notDone, + foundBangToken, temp1, temp2, temp3 >> + +Lbl_21 == /\ pc = "Lbl_21" + /\ ipos' = ipos+1 + /\ jdelim' = 0 + /\ pc' = "Lbl_22" + /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, + curPos, returnVal, tempVar1, tempVar2, tempVar3, + stack, pos_, tokArray, i, tokIdx, pos_f, token, + left_, rt_, pos_fi, pos_fin, delim_, ipos_, jdelim_, + delimLen_, pos_find, pos, delim, delimLen, leftTok, + rightTok, currentToken, left, rt, notDone, + foundBangToken, temp1, temp2, temp3 >> + +Lbl_22 == /\ pc = "Lbl_22" + /\ IF jdelim < JavaLen(RightDelimiters) + THEN /\ IF JavaSubseq(line, ipos, ipos+delimLen) = LeftDelimiters[jdelim] + THEN /\ /\ delim' = jdelim + /\ pos' = ipos + /\ stack' = << [ procedure |-> "findMatchingRightInner", + pc |-> "Lbl_23", + ipos |-> ipos, + jdelim |-> jdelim, + delimLen |-> delimLen, + pos |-> pos, + delim |-> delim ] >> + \o stack + /\ ipos' = pos' + /\ jdelim' = defaultInitValue + /\ delimLen' = defaultInitValue + /\ pc' = "Lbl_19" + ELSE /\ pc' = "Lbl_26" + /\ UNCHANGED << stack, pos, delim, ipos, + jdelim, delimLen >> + ELSE /\ pc' = "Lbl_20" + /\ UNCHANGED << stack, pos, delim, ipos, jdelim, delimLen >> + /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, + curPos, returnVal, tempVar1, tempVar2, tempVar3, + pos_, tokArray, i, tokIdx, pos_f, token, left_, rt_, + pos_fi, pos_fin, delim_, ipos_, jdelim_, delimLen_, + pos_find, leftTok, rightTok, currentToken, left, rt, + notDone, foundBangToken, temp1, temp2, temp3 >> + +Lbl_26 == /\ pc = "Lbl_26" + /\ jdelim' = jdelim+1 + /\ pc' = "Lbl_22" + /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, + curPos, returnVal, tempVar1, tempVar2, tempVar3, + stack, pos_, tokArray, i, tokIdx, pos_f, token, + left_, rt_, pos_fi, pos_fin, delim_, ipos_, jdelim_, + delimLen_, pos_find, pos, delim, ipos, delimLen, + leftTok, rightTok, currentToken, left, rt, notDone, + foundBangToken, temp1, temp2, temp3 >> + +Lbl_23 == /\ pc = "Lbl_23" + /\ ipos' = returnVal + /\ IF ipos' < 0 + THEN /\ returnVal' = -1 + /\ pc' = "Lbl_24" + ELSE /\ pc' = "Lbl_25" + /\ UNCHANGED returnVal + /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, + curPos, tempVar1, tempVar2, tempVar3, stack, pos_, + tokArray, i, tokIdx, pos_f, token, left_, rt_, + pos_fi, pos_fin, delim_, ipos_, jdelim_, delimLen_, + pos_find, pos, delim, jdelim, delimLen, leftTok, + rightTok, currentToken, left, rt, notDone, + foundBangToken, temp1, temp2, temp3 >> + +Lbl_24 == /\ pc = "Lbl_24" + /\ pc' = Head(stack).pc + /\ ipos' = Head(stack).ipos + /\ jdelim' = Head(stack).jdelim + /\ delimLen' = Head(stack).delimLen + /\ pos' = Head(stack).pos + /\ delim' = Head(stack).delim + /\ stack' = Tail(stack) + /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, + curPos, returnVal, tempVar1, tempVar2, tempVar3, + pos_, tokArray, i, tokIdx, pos_f, token, left_, rt_, + pos_fi, pos_fin, delim_, ipos_, jdelim_, delimLen_, + pos_find, leftTok, rightTok, currentToken, left, rt, + notDone, foundBangToken, temp1, temp2, temp3 >> + +Lbl_25 == /\ pc = "Lbl_25" + /\ jdelim' = 99 + /\ pc' = "Lbl_26" + /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, + curPos, returnVal, tempVar1, tempVar2, tempVar3, + stack, pos_, tokArray, i, tokIdx, pos_f, token, + left_, rt_, pos_fi, pos_fin, delim_, ipos_, jdelim_, + delimLen_, pos_find, pos, delim, ipos, delimLen, + leftTok, rightTok, currentToken, left, rt, notDone, + foundBangToken, temp1, temp2, temp3 >> + +findMatchingRightInner == Lbl_19 \/ Lbl_20 \/ Lbl_21 \/ Lbl_22 \/ Lbl_26 + \/ Lbl_23 \/ Lbl_24 \/ Lbl_25 + +Lbl_27 == /\ pc = "Lbl_27" + /\ IF leftTok = NullTokenSpec \/ rightTok = NullTokenSpec + THEN /\ returnVal' = NullTokenSpec + /\ pc' = Head(stack).pc + /\ leftTok' = Head(stack).leftTok + /\ rightTok' = Head(stack).rightTok + /\ stack' = Tail(stack) + ELSE /\ pc' = "Lbl_28" + /\ UNCHANGED << returnVal, stack, leftTok, rightTok >> + /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, + curPos, tempVar1, tempVar2, tempVar3, pos_, tokArray, + i, tokIdx, pos_f, token, left_, rt_, pos_fi, pos_fin, + delim_, ipos_, jdelim_, delimLen_, pos_find, pos, + delim, ipos, jdelim, delimLen, currentToken, left, + rt, notDone, foundBangToken, temp1, temp2, temp3 >> + +Lbl_28 == /\ pc = "Lbl_28" + /\ IF /\ IsNumber(leftTok.token) + /\ line[leftTok.leftPos-1] = "<" + /\ line[leftTok.leftPos-2] # "<" + THEN /\ returnVal' = [token |-> JavaSubseq(line, + leftTok.leftPos-1, + rightTok.rightPos), + leftPos |-> leftTok.leftPos-1, + rightPos |-> rightTok.rightPos] + /\ pc' = Head(stack).pc + /\ leftTok' = Head(stack).leftTok + /\ rightTok' = Head(stack).rightTok + /\ stack' = Tail(stack) + ELSE /\ returnVal' = NullTokenSpec + /\ pc' = Head(stack).pc + /\ leftTok' = Head(stack).leftTok + /\ rightTok' = Head(stack).rightTok + /\ stack' = Tail(stack) + /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, + curPos, tempVar1, tempVar2, tempVar3, pos_, tokArray, + i, tokIdx, pos_f, token, left_, rt_, pos_fi, pos_fin, + delim_, ipos_, jdelim_, delimLen_, pos_find, pos, + delim, ipos, jdelim, delimLen, currentToken, left, + rt, notDone, foundBangToken, temp1, temp2, temp3 >> + +checkIfStepName == Lbl_27 \/ Lbl_28 + +Lbl_29 == /\ pc = "Lbl_29" + /\ IF line[curPos-1] = " " + THEN /\ curPos' = curPos - 1 + /\ pc' = "Lbl_29" + /\ stack' = stack + ELSE /\ pc' = Head(stack).pc + /\ stack' = Tail(stack) + /\ UNCHANGED curPos + /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, + returnVal, tempVar1, tempVar2, tempVar3, pos_, + tokArray, i, tokIdx, pos_f, token, left_, rt_, + pos_fi, pos_fin, delim_, ipos_, jdelim_, delimLen_, + pos_find, pos, delim, ipos, jdelim, delimLen, + leftTok, rightTok, currentToken, left, rt, notDone, + foundBangToken, temp1, temp2, temp3 >> + +skipLeftOverSpaces == Lbl_29 + +Lbl_30 == /\ pc = "Lbl_30" + /\ IF line[curPos] = " " + THEN /\ curPos' = curPos+1 + /\ pc' = "Lbl_30" + /\ stack' = stack + ELSE /\ pc' = Head(stack).pc + /\ stack' = Tail(stack) + /\ UNCHANGED curPos + /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, + returnVal, tempVar1, tempVar2, tempVar3, pos_, + tokArray, i, tokIdx, pos_f, token, left_, rt_, + pos_fi, pos_fin, delim_, ipos_, jdelim_, delimLen_, + pos_find, pos, delim, ipos, jdelim, delimLen, + leftTok, rightTok, currentToken, left, rt, notDone, + foundBangToken, temp1, temp2, temp3 >> + +skipRightOverSpaces == Lbl_30 + +Lbl_31 == /\ pc = "Lbl_31" + /\ IF line[curPos] = " " + THEN /\ stack' = << [ procedure |-> "skipLeftOverSpaces", + pc |-> "Lbl_32" ] >> + \o stack + /\ pc' = "Lbl_29" + /\ UNCHANGED notLastToken + ELSE /\ IF line[curPos] = "!" /\ line[curPos-1] # "!" /\ line[curPos+1] # "!" + THEN /\ notLastToken' = TRUE + /\ stack' = << [ procedure |-> "skipLeftOverSpaces", + pc |-> "Lbl_33" ] >> + \o stack + /\ pc' = "Lbl_29" + ELSE /\ pc' = "Lbl_33" + /\ UNCHANGED << notLastToken, stack >> + /\ UNCHANGED << line, foundTokenSpecs, lastToken, curPos, returnVal, + tempVar1, tempVar2, tempVar3, pos_, tokArray, i, + tokIdx, pos_f, token, left_, rt_, pos_fi, pos_fin, + delim_, ipos_, jdelim_, delimLen_, pos_find, pos, + delim, ipos, jdelim, delimLen, leftTok, rightTok, + currentToken, left, rt, notDone, foundBangToken, + temp1, temp2, temp3 >> + +Lbl_32 == /\ pc = "Lbl_32" + /\ IF line[curPos-1] = "!" /\ line[curPos-2] # "!" + THEN /\ notLastToken' = TRUE + /\ curPos' = curPos - 1 + /\ stack' = << [ procedure |-> "skipLeftOverSpaces", + pc |-> "Lbl_33" ] >> + \o stack + /\ pc' = "Lbl_29" + ELSE /\ pc' = "Lbl_33" + /\ UNCHANGED << notLastToken, curPos, stack >> + /\ UNCHANGED << line, foundTokenSpecs, lastToken, returnVal, + tempVar1, tempVar2, tempVar3, pos_, tokArray, i, + tokIdx, pos_f, token, left_, rt_, pos_fi, pos_fin, + delim_, ipos_, jdelim_, delimLen_, pos_find, pos, + delim, ipos, jdelim, delimLen, leftTok, rightTok, + currentToken, left, rt, notDone, foundBangToken, + temp1, temp2, temp3 >> + +Lbl_33 == /\ pc = "Lbl_33" + /\ IF /\ \/ notLastToken + \/ line[curPos] = " " + /\ line[curPos-1] = ")" + THEN /\ notLastToken' = TRUE + /\ /\ pos_fi' = curPos + /\ stack' = << [ procedure |-> "findMatchingLeftParen", + pc |-> "Lbl_34", + pos_fi |-> pos_fi ] >> + \o stack + /\ pc' = "Lbl_7" + ELSE /\ pc' = "Lbl_36" + /\ UNCHANGED << notLastToken, stack, pos_fi >> + /\ UNCHANGED << line, foundTokenSpecs, lastToken, curPos, returnVal, + tempVar1, tempVar2, tempVar3, pos_, tokArray, i, + tokIdx, pos_f, token, left_, rt_, pos_fin, delim_, + ipos_, jdelim_, delimLen_, pos_find, pos, delim, + ipos, jdelim, delimLen, leftTok, rightTok, + currentToken, left, rt, notDone, foundBangToken, + temp1, temp2, temp3 >> + +Lbl_34 == /\ pc = "Lbl_34" + /\ IF returnVal < 0 + THEN /\ returnVal' = << >> + /\ pc' = Head(stack).pc + /\ currentToken' = Head(stack).currentToken + /\ left' = Head(stack).left + /\ rt' = Head(stack).rt + /\ notDone' = Head(stack).notDone + /\ foundBangToken' = Head(stack).foundBangToken + /\ temp1' = Head(stack).temp1 + /\ temp2' = Head(stack).temp2 + /\ temp3' = Head(stack).temp3 + /\ stack' = Tail(stack) + ELSE /\ pc' = "Lbl_35" + /\ UNCHANGED << returnVal, stack, currentToken, left, rt, + notDone, foundBangToken, temp1, temp2, + temp3 >> + /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, + curPos, tempVar1, tempVar2, tempVar3, pos_, tokArray, + i, tokIdx, pos_f, token, left_, rt_, pos_fi, pos_fin, + delim_, ipos_, jdelim_, delimLen_, pos_find, pos, + delim, ipos, jdelim, delimLen, leftTok, rightTok >> + +Lbl_35 == /\ pc = "Lbl_35" + /\ curPos' = returnVal + /\ stack' = << [ procedure |-> "skipLeftOverSpaces", + pc |-> "Lbl_36" ] >> + \o stack + /\ pc' = "Lbl_29" + /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, + returnVal, tempVar1, tempVar2, tempVar3, pos_, + tokArray, i, tokIdx, pos_f, token, left_, rt_, + pos_fi, pos_fin, delim_, ipos_, jdelim_, delimLen_, + pos_find, pos, delim, ipos, jdelim, delimLen, + leftTok, rightTok, currentToken, left, rt, notDone, + foundBangToken, temp1, temp2, temp3 >> + +Lbl_36 == /\ pc = "Lbl_36" + /\ IF notLastToken + THEN /\ curPos' = curPos - 1 + /\ pc' = "Lbl_38" + /\ UNCHANGED << stack, pos_, tokArray, i, tokIdx >> + ELSE /\ IF line[curPos] \notin IdChar + THEN /\ /\ pos_' = curPos + /\ stack' = << [ procedure |-> "findTokenIn", + pc |-> "Lbl_37", + i |-> i, + tokIdx |-> tokIdx, + pos_ |-> pos_, + tokArray |-> tokArray ] >> + \o stack + /\ tokArray' = Operators + /\ i' = defaultInitValue + /\ tokIdx' = defaultInitValue + /\ pc' = "Lbl_1" + ELSE /\ pc' = "Lbl_38" + /\ UNCHANGED << stack, pos_, tokArray, i, + tokIdx >> + /\ UNCHANGED curPos + /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, + returnVal, tempVar1, tempVar2, tempVar3, pos_f, + token, left_, rt_, pos_fi, pos_fin, delim_, ipos_, + jdelim_, delimLen_, pos_find, pos, delim, ipos, + jdelim, delimLen, leftTok, rightTok, currentToken, + left, rt, notDone, foundBangToken, temp1, temp2, + temp3 >> + +Lbl_37 == /\ pc = "Lbl_37" + /\ IF returnVal = NullTokenSpec + THEN /\ curPos' = curPos - 1 + ELSE /\ TRUE + /\ UNCHANGED curPos + /\ pc' = "Lbl_38" + /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, + returnVal, tempVar1, tempVar2, tempVar3, stack, pos_, + tokArray, i, tokIdx, pos_f, token, left_, rt_, + pos_fi, pos_fin, delim_, ipos_, jdelim_, delimLen_, + pos_find, pos, delim, ipos, jdelim, delimLen, + leftTok, rightTok, currentToken, left, rt, notDone, + foundBangToken, temp1, temp2, temp3 >> + +Lbl_38 == /\ pc = "Lbl_38" + /\ IF line[curPos] \in IdChar + THEN /\ IF line[curPos] = "X" + THEN /\ /\ pos_' = curPos + /\ stack' = << [ procedure |-> "findTokenIn", + pc |-> "Lbl_39", + i |-> i, + tokIdx |-> tokIdx, + pos_ |-> pos_, + tokArray |-> tokArray ] >> + \o stack + /\ tokArray' = XSymbols + /\ i' = defaultInitValue + /\ tokIdx' = defaultInitValue + /\ pc' = "Lbl_1" + /\ UNCHANGED returnVal + ELSE /\ returnVal' = NullTokenSpec + /\ pc' = "Lbl_39" + /\ UNCHANGED << stack, pos_, tokArray, i, + tokIdx >> + ELSE /\ /\ pos_' = curPos + /\ stack' = << [ procedure |-> "findTokenIn", + pc |-> "Lbl_48", + i |-> i, + tokIdx |-> tokIdx, + pos_ |-> pos_, + tokArray |-> tokArray ] >> + \o stack + /\ tokArray' = Operators + /\ i' = defaultInitValue + /\ tokIdx' = defaultInitValue + /\ pc' = "Lbl_1" + /\ UNCHANGED returnVal + /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, + curPos, tempVar1, tempVar2, tempVar3, pos_f, token, + left_, rt_, pos_fi, pos_fin, delim_, ipos_, jdelim_, + delimLen_, pos_find, pos, delim, ipos, jdelim, + delimLen, leftTok, rightTok, currentToken, left, rt, + notDone, foundBangToken, temp1, temp2, temp3 >> + +Lbl_39 == /\ pc = "Lbl_39" + /\ IF returnVal # NullTokenSpec + THEN /\ foundTokenSpecs' = JavaSeq(<<returnVal>>) + /\ curPos' = returnVal.leftPos + /\ lastToken' = TRUE + /\ pc' = "Lbl_62" + /\ UNCHANGED << stack, pos_f, token, left_, rt_ >> + ELSE /\ /\ pos_f' = curPos + /\ stack' = << [ procedure |-> "findMaximalIdCharSeq", + pc |-> "Lbl_40", + token |-> token, + left_ |-> left_, + rt_ |-> rt_, + pos_f |-> pos_f ] >> + \o stack + /\ token' = << >> + /\ left_' = pos_f' + /\ rt_' = pos_f' + /\ pc' = "Lbl_5" + /\ UNCHANGED << foundTokenSpecs, lastToken, curPos >> + /\ UNCHANGED << line, notLastToken, returnVal, tempVar1, tempVar2, + tempVar3, pos_, tokArray, i, tokIdx, pos_fi, pos_fin, + delim_, ipos_, jdelim_, delimLen_, pos_find, pos, + delim, ipos, jdelim, delimLen, leftTok, rightTok, + currentToken, left, rt, notDone, foundBangToken, + temp1, temp2, temp3 >> + +Lbl_40 == /\ pc = "Lbl_40" + /\ currentToken' = returnVal + /\ IF line[currentToken'.leftPos-1] = ">" + THEN /\ /\ pos_f' = currentToken'.leftPos - 1 + /\ stack' = << [ procedure |-> "findMaximalIdCharSeq", + pc |-> "Lbl_41", + token |-> token, + left_ |-> left_, + rt_ |-> rt_, + pos_f |-> pos_f ] >> + \o stack + /\ token' = << >> + /\ left_' = pos_f' + /\ rt_' = pos_f' + /\ pc' = "Lbl_5" + ELSE /\ pc' = "Lbl_43" + /\ UNCHANGED << stack, pos_f, token, left_, rt_ >> + /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, + curPos, returnVal, tempVar1, tempVar2, tempVar3, + pos_, tokArray, i, tokIdx, pos_fi, pos_fin, delim_, + ipos_, jdelim_, delimLen_, pos_find, pos, delim, + ipos, jdelim, delimLen, leftTok, rightTok, left, rt, + notDone, foundBangToken, temp1, temp2, temp3 >> + +Lbl_41 == /\ pc = "Lbl_41" + /\ /\ leftTok' = returnVal + /\ rightTok' = currentToken + /\ stack' = << [ procedure |-> "checkIfStepName", + pc |-> "Lbl_42", + leftTok |-> leftTok, + rightTok |-> rightTok ] >> + \o stack + /\ pc' = "Lbl_27" + /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, + curPos, returnVal, tempVar1, tempVar2, tempVar3, + pos_, tokArray, i, tokIdx, pos_f, token, left_, rt_, + pos_fi, pos_fin, delim_, ipos_, jdelim_, delimLen_, + pos_find, pos, delim, ipos, jdelim, delimLen, + currentToken, left, rt, notDone, foundBangToken, + temp1, temp2, temp3 >> + +Lbl_42 == /\ pc = "Lbl_42" + /\ IF returnVal # NullTokenSpec + THEN /\ foundTokenSpecs' = JavaSeq(<<returnVal>>) + /\ returnVal' = FixOrigin(foundTokenSpecs') + /\ pc' = Head(stack).pc + /\ currentToken' = Head(stack).currentToken + /\ left' = Head(stack).left + /\ rt' = Head(stack).rt + /\ notDone' = Head(stack).notDone + /\ foundBangToken' = Head(stack).foundBangToken + /\ temp1' = Head(stack).temp1 + /\ temp2' = Head(stack).temp2 + /\ temp3' = Head(stack).temp3 + /\ stack' = Tail(stack) + ELSE /\ pc' = "Lbl_43" + /\ UNCHANGED << foundTokenSpecs, returnVal, stack, + currentToken, left, rt, notDone, + foundBangToken, temp1, temp2, temp3 >> + /\ UNCHANGED << line, lastToken, notLastToken, curPos, tempVar1, + tempVar2, tempVar3, pos_, tokArray, i, tokIdx, pos_f, + token, left_, rt_, pos_fi, pos_fin, delim_, ipos_, + jdelim_, delimLen_, pos_find, pos, delim, ipos, + jdelim, delimLen, leftTok, rightTok >> + +Lbl_43 == /\ pc = "Lbl_43" + /\ IF line[currentToken.rightPos] = ">" + THEN /\ /\ pos_f' = currentToken.rightPos+1 + /\ stack' = << [ procedure |-> "findMaximalIdCharSeq", + pc |-> "Lbl_44", + token |-> token, + left_ |-> left_, + rt_ |-> rt_, + pos_f |-> pos_f ] >> + \o stack + /\ token' = << >> + /\ left_' = pos_f' + /\ rt_' = pos_f' + /\ pc' = "Lbl_5" + ELSE /\ pc' = "Lbl_46" + /\ UNCHANGED << stack, pos_f, token, left_, rt_ >> + /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, + curPos, returnVal, tempVar1, tempVar2, tempVar3, + pos_, tokArray, i, tokIdx, pos_fi, pos_fin, delim_, + ipos_, jdelim_, delimLen_, pos_find, pos, delim, + ipos, jdelim, delimLen, leftTok, rightTok, + currentToken, left, rt, notDone, foundBangToken, + temp1, temp2, temp3 >> + +Lbl_44 == /\ pc = "Lbl_44" + /\ /\ leftTok' = currentToken + /\ rightTok' = returnVal + /\ stack' = << [ procedure |-> "checkIfStepName", + pc |-> "Lbl_45", + leftTok |-> leftTok, + rightTok |-> rightTok ] >> + \o stack + /\ pc' = "Lbl_27" + /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, + curPos, returnVal, tempVar1, tempVar2, tempVar3, + pos_, tokArray, i, tokIdx, pos_f, token, left_, rt_, + pos_fi, pos_fin, delim_, ipos_, jdelim_, delimLen_, + pos_find, pos, delim, ipos, jdelim, delimLen, + currentToken, left, rt, notDone, foundBangToken, + temp1, temp2, temp3 >> + +Lbl_45 == /\ pc = "Lbl_45" + /\ IF returnVal # NullTokenSpec + THEN /\ foundTokenSpecs' = JavaSeq(<<returnVal>>) + /\ returnVal' = FixOrigin(foundTokenSpecs') + /\ pc' = Head(stack).pc + /\ currentToken' = Head(stack).currentToken + /\ left' = Head(stack).left + /\ rt' = Head(stack).rt + /\ notDone' = Head(stack).notDone + /\ foundBangToken' = Head(stack).foundBangToken + /\ temp1' = Head(stack).temp1 + /\ temp2' = Head(stack).temp2 + /\ temp3' = Head(stack).temp3 + /\ stack' = Tail(stack) + ELSE /\ pc' = "Lbl_46" + /\ UNCHANGED << foundTokenSpecs, returnVal, stack, + currentToken, left, rt, notDone, + foundBangToken, temp1, temp2, temp3 >> + /\ UNCHANGED << line, lastToken, notLastToken, curPos, tempVar1, + tempVar2, tempVar3, pos_, tokArray, i, tokIdx, pos_f, + token, left_, rt_, pos_fi, pos_fin, delim_, ipos_, + jdelim_, delimLen_, pos_find, pos, delim, ipos, + jdelim, delimLen, leftTok, rightTok >> + +Lbl_46 == /\ pc = "Lbl_46" + /\ IF IsNumber(currentToken.token) + THEN /\ returnVal' = << >> + /\ pc' = Head(stack).pc + /\ currentToken' = Head(stack).currentToken + /\ left' = Head(stack).left + /\ rt' = Head(stack).rt + /\ notDone' = Head(stack).notDone + /\ foundBangToken' = Head(stack).foundBangToken + /\ temp1' = Head(stack).temp1 + /\ temp2' = Head(stack).temp2 + /\ temp3' = Head(stack).temp3 + /\ stack' = Tail(stack) + ELSE /\ pc' = "Lbl_47" + /\ UNCHANGED << returnVal, stack, currentToken, left, rt, + notDone, foundBangToken, temp1, temp2, + temp3 >> + /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, + curPos, tempVar1, tempVar2, tempVar3, pos_, tokArray, + i, tokIdx, pos_f, token, left_, rt_, pos_fi, pos_fin, + delim_, ipos_, jdelim_, delimLen_, pos_find, pos, + delim, ipos, jdelim, delimLen, leftTok, rightTok >> + +Lbl_47 == /\ pc = "Lbl_47" + /\ left' = currentToken.leftPos + /\ rt' = currentToken.rightPos + /\ IF /\ line[left'-1] = "\\" + /\ line[left'-2] # "\\" + /\ \E ii \in DOMAIN TeXSymbols : + JavaSubseq(line, left'-1, rt') = TeXSymbols[ii] + THEN /\ lastToken' = TRUE + /\ currentToken' = [currentToken EXCEPT !.leftPos = left' - 1, + !.token = JavaSubseq(line, left'-1, rt')] + ELSE /\ TRUE + /\ UNCHANGED << lastToken, currentToken >> + /\ foundTokenSpecs' = JavaSeq( << currentToken' >>) + /\ curPos' = left' + /\ pc' = "Lbl_62" + /\ UNCHANGED << line, notLastToken, returnVal, tempVar1, tempVar2, + tempVar3, stack, pos_, tokArray, i, tokIdx, pos_f, + token, left_, rt_, pos_fi, pos_fin, delim_, ipos_, + jdelim_, delimLen_, pos_find, pos, delim, ipos, + jdelim, delimLen, leftTok, rightTok, notDone, + foundBangToken, temp1, temp2, temp3 >> + +Lbl_48 == /\ pc = "Lbl_48" + /\ currentToken' = returnVal + /\ IF currentToken' = NullTokenSpec + THEN /\ returnVal' = << >> + /\ pc' = "Lbl_49" + ELSE /\ pc' = "Lbl_50" + /\ UNCHANGED returnVal + /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, + curPos, tempVar1, tempVar2, tempVar3, stack, pos_, + tokArray, i, tokIdx, pos_f, token, left_, rt_, + pos_fi, pos_fin, delim_, ipos_, jdelim_, delimLen_, + pos_find, pos, delim, ipos, jdelim, delimLen, + leftTok, rightTok, left, rt, notDone, foundBangToken, + temp1, temp2, temp3 >> + +Lbl_49 == /\ pc = "Lbl_49" + /\ pc' = Head(stack).pc + /\ currentToken' = Head(stack).currentToken + /\ left' = Head(stack).left + /\ rt' = Head(stack).rt + /\ notDone' = Head(stack).notDone + /\ foundBangToken' = Head(stack).foundBangToken + /\ temp1' = Head(stack).temp1 + /\ temp2' = Head(stack).temp2 + /\ temp3' = Head(stack).temp3 + /\ stack' = Tail(stack) + /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, + curPos, returnVal, tempVar1, tempVar2, tempVar3, + pos_, tokArray, i, tokIdx, pos_f, token, left_, rt_, + pos_fi, pos_fin, delim_, ipos_, jdelim_, delimLen_, + pos_find, pos, delim, ipos, jdelim, delimLen, + leftTok, rightTok >> + +Lbl_50 == /\ pc = "Lbl_50" + /\ /\ pos_' = curPos + /\ stack' = << [ procedure |-> "findTokenIn", + pc |-> "Lbl_51", + i |-> i, + tokIdx |-> tokIdx, + pos_ |-> pos_, + tokArray |-> tokArray ] >> + \o stack + /\ tokArray' = NonOperators + /\ i' = defaultInitValue + /\ tokIdx' = defaultInitValue + /\ pc' = "Lbl_1" + /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, + curPos, returnVal, tempVar1, tempVar2, tempVar3, + pos_f, token, left_, rt_, pos_fi, pos_fin, delim_, + ipos_, jdelim_, delimLen_, pos_find, pos, delim, + ipos, jdelim, delimLen, leftTok, rightTok, + currentToken, left, rt, notDone, foundBangToken, + temp1, temp2, temp3 >> + +Lbl_51 == /\ pc = "Lbl_51" + /\ IF returnVal # NullTokenSpec + THEN /\ returnVal' = << >> + /\ pc' = Head(stack).pc + /\ currentToken' = Head(stack).currentToken + /\ left' = Head(stack).left + /\ rt' = Head(stack).rt + /\ notDone' = Head(stack).notDone + /\ foundBangToken' = Head(stack).foundBangToken + /\ temp1' = Head(stack).temp1 + /\ temp2' = Head(stack).temp2 + /\ temp3' = Head(stack).temp3 + /\ stack' = Tail(stack) + ELSE /\ pc' = "Lbl_52" + /\ UNCHANGED << returnVal, stack, currentToken, left, rt, + notDone, foundBangToken, temp1, temp2, + temp3 >> + /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, + curPos, tempVar1, tempVar2, tempVar3, pos_, tokArray, + i, tokIdx, pos_f, token, left_, rt_, pos_fi, pos_fin, + delim_, ipos_, jdelim_, delimLen_, pos_find, pos, + delim, ipos, jdelim, delimLen, leftTok, rightTok >> + +Lbl_52 == /\ pc = "Lbl_52" + /\ IF currentToken.token = JavaSeq(<< "<" >>) + THEN /\ /\ pos_f' = currentToken.rightPos + /\ stack' = << [ procedure |-> "findMaximalIdCharSeq", + pc |-> "Lbl_53", + token |-> token, + left_ |-> left_, + rt_ |-> rt_, + pos_f |-> pos_f ] >> + \o stack + /\ token' = << >> + /\ left_' = pos_f' + /\ rt_' = pos_f' + /\ pc' = "Lbl_5" + ELSE /\ IF currentToken.token = JavaSeq(<< ">" >>) + THEN /\ /\ pos_f' = currentToken.leftPos + /\ stack' = << [ procedure |-> "findMaximalIdCharSeq", + pc |-> "Lbl_56", + token |-> token, + left_ |-> left_, + rt_ |-> rt_, + pos_f |-> pos_f ] >> + \o stack + /\ token' = << >> + /\ left_' = pos_f' + /\ rt_' = pos_f' + /\ pc' = "Lbl_5" + ELSE /\ pc' = "Lbl_59" + /\ UNCHANGED << stack, pos_f, token, left_, + rt_ >> + /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, + curPos, returnVal, tempVar1, tempVar2, tempVar3, + pos_, tokArray, i, tokIdx, pos_fi, pos_fin, delim_, + ipos_, jdelim_, delimLen_, pos_find, pos, delim, + ipos, jdelim, delimLen, leftTok, rightTok, + currentToken, left, rt, notDone, foundBangToken, + temp1, temp2, temp3 >> + +Lbl_53 == /\ pc = "Lbl_53" + /\ temp1' = returnVal + /\ IF /\ temp1' # NullTokenSpec + /\ line[temp1'.rightPos] = ">" + THEN /\ /\ pos_f' = temp1'.rightPos + 1 + /\ stack' = << [ procedure |-> "findMaximalIdCharSeq", + pc |-> "Lbl_54", + token |-> token, + left_ |-> left_, + rt_ |-> rt_, + pos_f |-> pos_f ] >> + \o stack + /\ token' = << >> + /\ left_' = pos_f' + /\ rt_' = pos_f' + /\ pc' = "Lbl_5" + ELSE /\ pc' = "Lbl_59" + /\ UNCHANGED << stack, pos_f, token, left_, rt_ >> + /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, + curPos, returnVal, tempVar1, tempVar2, tempVar3, + pos_, tokArray, i, tokIdx, pos_fi, pos_fin, delim_, + ipos_, jdelim_, delimLen_, pos_find, pos, delim, + ipos, jdelim, delimLen, leftTok, rightTok, + currentToken, left, rt, notDone, foundBangToken, + temp2, temp3 >> + +Lbl_54 == /\ pc = "Lbl_54" + /\ temp2' = returnVal + /\ /\ leftTok' = temp1 + /\ rightTok' = temp2' + /\ stack' = << [ procedure |-> "checkIfStepName", + pc |-> "Lbl_55", + leftTok |-> leftTok, + rightTok |-> rightTok ] >> + \o stack + /\ pc' = "Lbl_27" + /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, + curPos, returnVal, tempVar1, tempVar2, tempVar3, + pos_, tokArray, i, tokIdx, pos_f, token, left_, rt_, + pos_fi, pos_fin, delim_, ipos_, jdelim_, delimLen_, + pos_find, pos, delim, ipos, jdelim, delimLen, + currentToken, left, rt, notDone, foundBangToken, + temp1, temp3 >> + +Lbl_55 == /\ pc = "Lbl_55" + /\ IF returnVal # NullTokenSpec + THEN /\ foundTokenSpecs' = JavaSeq(<<returnVal>>) + /\ returnVal' = FixOrigin(foundTokenSpecs') + /\ pc' = Head(stack).pc + /\ currentToken' = Head(stack).currentToken + /\ left' = Head(stack).left + /\ rt' = Head(stack).rt + /\ notDone' = Head(stack).notDone + /\ foundBangToken' = Head(stack).foundBangToken + /\ temp1' = Head(stack).temp1 + /\ temp2' = Head(stack).temp2 + /\ temp3' = Head(stack).temp3 + /\ stack' = Tail(stack) + ELSE /\ pc' = "Lbl_59" + /\ UNCHANGED << foundTokenSpecs, returnVal, stack, + currentToken, left, rt, notDone, + foundBangToken, temp1, temp2, temp3 >> + /\ UNCHANGED << line, lastToken, notLastToken, curPos, tempVar1, + tempVar2, tempVar3, pos_, tokArray, i, tokIdx, pos_f, + token, left_, rt_, pos_fi, pos_fin, delim_, ipos_, + jdelim_, delimLen_, pos_find, pos, delim, ipos, + jdelim, delimLen, leftTok, rightTok >> + +Lbl_56 == /\ pc = "Lbl_56" + /\ temp1' = returnVal + /\ /\ pos_f' = currentToken.rightPos + /\ stack' = << [ procedure |-> "findMaximalIdCharSeq", + pc |-> "Lbl_57", + token |-> token, + left_ |-> left_, + rt_ |-> rt_, + pos_f |-> pos_f ] >> + \o stack + /\ token' = << >> + /\ left_' = pos_f' + /\ rt_' = pos_f' + /\ pc' = "Lbl_5" + /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, + curPos, returnVal, tempVar1, tempVar2, tempVar3, + pos_, tokArray, i, tokIdx, pos_fi, pos_fin, delim_, + ipos_, jdelim_, delimLen_, pos_find, pos, delim, + ipos, jdelim, delimLen, leftTok, rightTok, + currentToken, left, rt, notDone, foundBangToken, + temp2, temp3 >> + +Lbl_57 == /\ pc = "Lbl_57" + /\ temp2' = returnVal + /\ /\ leftTok' = temp1 + /\ rightTok' = temp2' + /\ stack' = << [ procedure |-> "checkIfStepName", + pc |-> "Lbl_58", + leftTok |-> leftTok, + rightTok |-> rightTok ] >> + \o stack + /\ pc' = "Lbl_27" + /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, + curPos, returnVal, tempVar1, tempVar2, tempVar3, + pos_, tokArray, i, tokIdx, pos_f, token, left_, rt_, + pos_fi, pos_fin, delim_, ipos_, jdelim_, delimLen_, + pos_find, pos, delim, ipos, jdelim, delimLen, + currentToken, left, rt, notDone, foundBangToken, + temp1, temp3 >> + +Lbl_58 == /\ pc = "Lbl_58" + /\ IF returnVal # NullTokenSpec + THEN /\ foundTokenSpecs' = JavaSeq(<<returnVal>>) + /\ returnVal' = FixOrigin(foundTokenSpecs') + /\ pc' = Head(stack).pc + /\ currentToken' = Head(stack).currentToken + /\ left' = Head(stack).left + /\ rt' = Head(stack).rt + /\ notDone' = Head(stack).notDone + /\ foundBangToken' = Head(stack).foundBangToken + /\ temp1' = Head(stack).temp1 + /\ temp2' = Head(stack).temp2 + /\ temp3' = Head(stack).temp3 + /\ stack' = Tail(stack) + ELSE /\ pc' = "Lbl_59" + /\ UNCHANGED << foundTokenSpecs, returnVal, stack, + currentToken, left, rt, notDone, + foundBangToken, temp1, temp2, temp3 >> + /\ UNCHANGED << line, lastToken, notLastToken, curPos, tempVar1, + tempVar2, tempVar3, pos_, tokArray, i, tokIdx, pos_f, + token, left_, rt_, pos_fi, pos_fin, delim_, ipos_, + jdelim_, delimLen_, pos_find, pos, delim, ipos, + jdelim, delimLen, leftTok, rightTok >> + +Lbl_59 == /\ pc = "Lbl_59" + /\ IF currentToken.token = JavaSeq(<<"\\">>) + THEN /\ /\ pos_f' = currentToken.rightPos + /\ stack' = << [ procedure |-> "findMaximalIdCharSeq", + pc |-> "Lbl_60", + token |-> token, + left_ |-> left_, + rt_ |-> rt_, + pos_f |-> pos_f ] >> + \o stack + /\ token' = << >> + /\ left_' = pos_f' + /\ rt_' = pos_f' + /\ pc' = "Lbl_5" + ELSE /\ pc' = "Lbl_61" + /\ UNCHANGED << stack, pos_f, token, left_, rt_ >> + /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, + curPos, returnVal, tempVar1, tempVar2, tempVar3, + pos_, tokArray, i, tokIdx, pos_fi, pos_fin, delim_, + ipos_, jdelim_, delimLen_, pos_find, pos, delim, + ipos, jdelim, delimLen, leftTok, rightTok, + currentToken, left, rt, notDone, foundBangToken, + temp1, temp2, temp3 >> + +Lbl_60 == /\ pc = "Lbl_60" + /\ IF /\ returnVal # NullTokenSpec + /\ \E ii \in DOMAIN TeXSymbols : + JavaSubseq(line, + currentToken.leftPos, + returnVal.rightPos) = TeXSymbols[ii] + THEN /\ currentToken' = [currentToken EXCEPT !.rightPos = returnVal.rightPos, + !.token = JavaSubseq(line, + currentToken.leftPos, + returnVal.rightPos)] + ELSE /\ TRUE + /\ UNCHANGED currentToken + /\ pc' = "Lbl_61" + /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, + curPos, returnVal, tempVar1, tempVar2, tempVar3, + stack, pos_, tokArray, i, tokIdx, pos_f, token, + left_, rt_, pos_fi, pos_fin, delim_, ipos_, jdelim_, + delimLen_, pos_find, pos, delim, ipos, jdelim, + delimLen, leftTok, rightTok, left, rt, notDone, + foundBangToken, temp1, temp2, temp3 >> + +Lbl_61 == /\ pc = "Lbl_61" + /\ foundTokenSpecs' = JavaSeq(<<currentToken>>) + /\ curPos' = currentToken.leftPos + /\ lastToken' = TRUE + /\ pc' = "Lbl_62" + /\ UNCHANGED << line, notLastToken, returnVal, tempVar1, tempVar2, + tempVar3, stack, pos_, tokArray, i, tokIdx, pos_f, + token, left_, rt_, pos_fi, pos_fin, delim_, ipos_, + jdelim_, delimLen_, pos_find, pos, delim, ipos, + jdelim, delimLen, leftTok, rightTok, currentToken, + left, rt, notDone, foundBangToken, temp1, temp2, + temp3 >> + +Lbl_62 == /\ pc = "Lbl_62" + /\ Assert(JavaLen(foundTokenSpecs) = 1, + "Failure of assertion at line 619, column 3.") + /\ curPos' = foundTokenSpecs[0].leftPos + /\ notDone' = TRUE + /\ pc' = "Lbl_63" + /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, + returnVal, tempVar1, tempVar2, tempVar3, stack, pos_, + tokArray, i, tokIdx, pos_f, token, left_, rt_, + pos_fi, pos_fin, delim_, ipos_, jdelim_, delimLen_, + pos_find, pos, delim, ipos, jdelim, delimLen, + leftTok, rightTok, currentToken, left, rt, + foundBangToken, temp1, temp2, temp3 >> + +Lbl_63 == /\ pc = "Lbl_63" + /\ IF notDone + THEN /\ stack' = << [ procedure |-> "skipLeftOverSpaces", + pc |-> "Lbl_64" ] >> + \o stack + /\ pc' = "Lbl_29" + /\ UNCHANGED << returnVal, currentToken, left, rt, + notDone, foundBangToken, temp1, temp2, + temp3 >> + ELSE /\ Assert(foundTokenSpecs # << >>, + "Failure of assertion at line 661, column 3.") + /\ IF lastToken = TRUE + THEN /\ returnVal' = FixOrigin(foundTokenSpecs) + /\ pc' = Head(stack).pc + /\ currentToken' = Head(stack).currentToken + /\ left' = Head(stack).left + /\ rt' = Head(stack).rt + /\ notDone' = Head(stack).notDone + /\ foundBangToken' = Head(stack).foundBangToken + /\ temp1' = Head(stack).temp1 + /\ temp2' = Head(stack).temp2 + /\ temp3' = Head(stack).temp3 + /\ stack' = Tail(stack) + ELSE /\ pc' = "Lbl_69" + /\ UNCHANGED << returnVal, stack, currentToken, + left, rt, notDone, + foundBangToken, temp1, temp2, + temp3 >> + /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, + curPos, tempVar1, tempVar2, tempVar3, pos_, tokArray, + i, tokIdx, pos_f, token, left_, rt_, pos_fi, pos_fin, + delim_, ipos_, jdelim_, delimLen_, pos_find, pos, + delim, ipos, jdelim, delimLen, leftTok, rightTok >> + +Lbl_64 == /\ pc = "Lbl_64" + /\ IF curPos < 0 \/ line[curPos-1] = ";" + THEN /\ notDone' = FALSE + /\ pc' = "Lbl_63" + /\ UNCHANGED << curPos, stack >> + ELSE /\ IF line[curPos-1] = "!" /\ (line[curPos-2] # "!") + THEN /\ curPos' = curPos - 1 + /\ stack' = << [ procedure |-> "skipLeftOverSpaces", + pc |-> "Lbl_65" ] >> + \o stack + /\ pc' = "Lbl_29" + /\ UNCHANGED notDone + ELSE /\ notDone' = FALSE + /\ pc' = "Lbl_63" + /\ UNCHANGED << curPos, stack >> + /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, + returnVal, tempVar1, tempVar2, tempVar3, pos_, + tokArray, i, tokIdx, pos_f, token, left_, rt_, + pos_fi, pos_fin, delim_, ipos_, jdelim_, delimLen_, + pos_find, pos, delim, ipos, jdelim, delimLen, + leftTok, rightTok, currentToken, left, rt, + foundBangToken, temp1, temp2, temp3 >> + +Lbl_65 == /\ pc = "Lbl_65" + /\ /\ pos_fi' = curPos + /\ stack' = << [ procedure |-> "findMatchingLeftParen", + pc |-> "Lbl_66", + pos_fi |-> pos_fi ] >> + \o stack + /\ pc' = "Lbl_7" + /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, + curPos, returnVal, tempVar1, tempVar2, tempVar3, + pos_, tokArray, i, tokIdx, pos_f, token, left_, rt_, + pos_fin, delim_, ipos_, jdelim_, delimLen_, pos_find, + pos, delim, ipos, jdelim, delimLen, leftTok, + rightTok, currentToken, left, rt, notDone, + foundBangToken, temp1, temp2, temp3 >> + +Lbl_66 == /\ pc = "Lbl_66" + /\ curPos' = returnVal + /\ IF curPos' < 0 + THEN /\ notDone' = FALSE + /\ pc' = "Lbl_63" + /\ stack' = stack + ELSE /\ stack' = << [ procedure |-> "skipLeftOverSpaces", + pc |-> "Lbl_67" ] >> + \o stack + /\ pc' = "Lbl_29" + /\ UNCHANGED notDone + /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, + returnVal, tempVar1, tempVar2, tempVar3, pos_, + tokArray, i, tokIdx, pos_f, token, left_, rt_, + pos_fi, pos_fin, delim_, ipos_, jdelim_, delimLen_, + pos_find, pos, delim, ipos, jdelim, delimLen, + leftTok, rightTok, currentToken, left, rt, + foundBangToken, temp1, temp2, temp3 >> + +Lbl_67 == /\ pc = "Lbl_67" + /\ /\ pos_f' = curPos + /\ stack' = << [ procedure |-> "findMaximalIdCharSeq", + pc |-> "Lbl_68", + token |-> token, + left_ |-> left_, + rt_ |-> rt_, + pos_f |-> pos_f ] >> + \o stack + /\ token' = << >> + /\ left_' = pos_f' + /\ rt_' = pos_f' + /\ pc' = "Lbl_5" + /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, + curPos, returnVal, tempVar1, tempVar2, tempVar3, + pos_, tokArray, i, tokIdx, pos_fi, pos_fin, delim_, + ipos_, jdelim_, delimLen_, pos_find, pos, delim, + ipos, jdelim, delimLen, leftTok, rightTok, + currentToken, left, rt, notDone, foundBangToken, + temp1, temp2, temp3 >> + +Lbl_68 == /\ pc = "Lbl_68" + /\ currentToken' = returnVal + /\ IF \/ currentToken' = NullTokenSpec + \/ IsNumber(currentToken'.token) + THEN /\ notDone' = FALSE + /\ UNCHANGED << foundTokenSpecs, curPos >> + ELSE /\ curPos' = currentToken'.leftPos + /\ foundTokenSpecs' = [foundTokenSpecs EXCEPT ![0] = [token |-> JavaConcatenate( + JavaAppend(currentToken'.token, "!"), + foundTokenSpecs[0].token), + leftPos |-> curPos', + rightPos |-> foundTokenSpecs[0].rightPos]] + /\ UNCHANGED notDone + /\ pc' = "Lbl_63" + /\ UNCHANGED << line, lastToken, notLastToken, returnVal, tempVar1, + tempVar2, tempVar3, stack, pos_, tokArray, i, tokIdx, + pos_f, token, left_, rt_, pos_fi, pos_fin, delim_, + ipos_, jdelim_, delimLen_, pos_find, pos, delim, + ipos, jdelim, delimLen, leftTok, rightTok, left, rt, + foundBangToken, temp1, temp2, temp3 >> + +Lbl_69 == /\ pc = "Lbl_69" + /\ curPos' = foundTokenSpecs[0].rightPos + /\ foundBangToken' = FALSE + /\ notDone' = TRUE + /\ pc' = "Lbl_70" + /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, + returnVal, tempVar1, tempVar2, tempVar3, stack, pos_, + tokArray, i, tokIdx, pos_f, token, left_, rt_, + pos_fi, pos_fin, delim_, ipos_, jdelim_, delimLen_, + pos_find, pos, delim, ipos, jdelim, delimLen, + leftTok, rightTok, currentToken, left, rt, temp1, + temp2, temp3 >> + +Lbl_70 == /\ pc = "Lbl_70" + /\ IF notDone + THEN /\ stack' = << [ procedure |-> "skipRightOverSpaces", + pc |-> "Lbl_71" ] >> + \o stack + /\ pc' = "Lbl_30" + /\ UNCHANGED << returnVal, currentToken, left, rt, + notDone, foundBangToken, temp1, temp2, + temp3 >> + ELSE /\ IF notLastToken /\ ~ foundBangToken + THEN /\ returnVal' = << >> + /\ pc' = Head(stack).pc + /\ currentToken' = Head(stack).currentToken + /\ left' = Head(stack).left + /\ rt' = Head(stack).rt + /\ notDone' = Head(stack).notDone + /\ foundBangToken' = Head(stack).foundBangToken + /\ temp1' = Head(stack).temp1 + /\ temp2' = Head(stack).temp2 + /\ temp3' = Head(stack).temp3 + /\ stack' = Tail(stack) + ELSE /\ pc' = "Lbl_76" + /\ UNCHANGED << returnVal, stack, currentToken, + left, rt, notDone, + foundBangToken, temp1, temp2, + temp3 >> + /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, + curPos, tempVar1, tempVar2, tempVar3, pos_, tokArray, + i, tokIdx, pos_f, token, left_, rt_, pos_fi, pos_fin, + delim_, ipos_, jdelim_, delimLen_, pos_find, pos, + delim, ipos, jdelim, delimLen, leftTok, rightTok >> + +Lbl_71 == /\ pc = "Lbl_71" + /\ /\ pos_find' = curPos + /\ stack' = << [ procedure |-> "findMatchingRightParen", + pc |-> "Lbl_72", + pos_find |-> pos_find ] >> + \o stack + /\ pc' = "Lbl_17" + /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, + curPos, returnVal, tempVar1, tempVar2, tempVar3, + pos_, tokArray, i, tokIdx, pos_f, token, left_, rt_, + pos_fi, pos_fin, delim_, ipos_, jdelim_, delimLen_, + pos, delim, ipos, jdelim, delimLen, leftTok, + rightTok, currentToken, left, rt, notDone, + foundBangToken, temp1, temp2, temp3 >> + +Lbl_72 == /\ pc = "Lbl_72" + /\ curPos' = returnVal + /\ IF curPos' < 0 + THEN /\ notDone' = FALSE + /\ pc' = "Lbl_70" + /\ stack' = stack + ELSE /\ stack' = << [ procedure |-> "skipRightOverSpaces", + pc |-> "Lbl_73" ] >> + \o stack + /\ pc' = "Lbl_30" + /\ UNCHANGED notDone + /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, + returnVal, tempVar1, tempVar2, tempVar3, pos_, + tokArray, i, tokIdx, pos_f, token, left_, rt_, + pos_fi, pos_fin, delim_, ipos_, jdelim_, delimLen_, + pos_find, pos, delim, ipos, jdelim, delimLen, + leftTok, rightTok, currentToken, left, rt, + foundBangToken, temp1, temp2, temp3 >> + +Lbl_73 == /\ pc = "Lbl_73" + /\ IF line[curPos] # "!" \/ line[curPos+1] = "!" + THEN /\ notDone' = FALSE + /\ pc' = "Lbl_70" + /\ UNCHANGED << curPos, stack >> + ELSE /\ curPos' = curPos + 1 + /\ stack' = << [ procedure |-> "skipRightOverSpaces", + pc |-> "Lbl_74" ] >> + \o stack + /\ pc' = "Lbl_30" + /\ UNCHANGED notDone + /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, + returnVal, tempVar1, tempVar2, tempVar3, pos_, + tokArray, i, tokIdx, pos_f, token, left_, rt_, + pos_fi, pos_fin, delim_, ipos_, jdelim_, delimLen_, + pos_find, pos, delim, ipos, jdelim, delimLen, + leftTok, rightTok, currentToken, left, rt, + foundBangToken, temp1, temp2, temp3 >> + +Lbl_74 == /\ pc = "Lbl_74" + /\ /\ pos_f' = curPos + /\ stack' = << [ procedure |-> "findMaximalIdCharSeq", + pc |-> "Lbl_75", + token |-> token, + left_ |-> left_, + rt_ |-> rt_, + pos_f |-> pos_f ] >> + \o stack + /\ token' = << >> + /\ left_' = pos_f' + /\ rt_' = pos_f' + /\ pc' = "Lbl_5" + /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, + curPos, returnVal, tempVar1, tempVar2, tempVar3, + pos_, tokArray, i, tokIdx, pos_fi, pos_fin, delim_, + ipos_, jdelim_, delimLen_, pos_find, pos, delim, + ipos, jdelim, delimLen, leftTok, rightTok, + currentToken, left, rt, notDone, foundBangToken, + temp1, temp2, temp3 >> + +Lbl_75 == /\ pc = "Lbl_75" + /\ currentToken' = returnVal + /\ IF \/ currentToken' = NullTokenSpec + \/ IsNumber(currentToken'.token) + THEN /\ notDone' = FALSE + /\ UNCHANGED << foundTokenSpecs, curPos, foundBangToken >> + ELSE /\ foundBangToken' = TRUE + /\ foundTokenSpecs' = JavaConcatenate( + JavaSeq( + << [token |-> + JavaConcatenate(JavaAppend(foundTokenSpecs[0].token, + "!"), + currentToken'.token), + leftPos |-> foundTokenSpecs[0].leftPos, + rightPos |-> currentToken'.rightPos] + >> ) , + foundTokenSpecs) + /\ curPos' = currentToken'.rightPos + /\ UNCHANGED notDone + /\ pc' = "Lbl_70" + /\ UNCHANGED << line, lastToken, notLastToken, returnVal, tempVar1, + tempVar2, tempVar3, stack, pos_, tokArray, i, tokIdx, + pos_f, token, left_, rt_, pos_fi, pos_fin, delim_, + ipos_, jdelim_, delimLen_, pos_find, pos, delim, + ipos, jdelim, delimLen, leftTok, rightTok, left, rt, + temp1, temp2, temp3 >> + +Lbl_76 == /\ pc = "Lbl_76" + /\ returnVal' = FixOrigin(foundTokenSpecs) + /\ pc' = Head(stack).pc + /\ currentToken' = Head(stack).currentToken + /\ left' = Head(stack).left + /\ rt' = Head(stack).rt + /\ notDone' = Head(stack).notDone + /\ foundBangToken' = Head(stack).foundBangToken + /\ temp1' = Head(stack).temp1 + /\ temp2' = Head(stack).temp2 + /\ temp3' = Head(stack).temp3 + /\ stack' = Tail(stack) + /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, + curPos, tempVar1, tempVar2, tempVar3, pos_, tokArray, + i, tokIdx, pos_f, token, left_, rt_, pos_fi, pos_fin, + delim_, ipos_, jdelim_, delimLen_, pos_find, pos, + delim, ipos, jdelim, delimLen, leftTok, rightTok >> + +findTokenSpecs == Lbl_31 \/ Lbl_32 \/ Lbl_33 \/ Lbl_34 \/ Lbl_35 \/ Lbl_36 + \/ Lbl_37 \/ Lbl_38 \/ Lbl_39 \/ Lbl_40 \/ Lbl_41 \/ Lbl_42 + \/ Lbl_43 \/ Lbl_44 \/ Lbl_45 \/ Lbl_46 \/ Lbl_47 \/ Lbl_48 + \/ Lbl_49 \/ Lbl_50 \/ Lbl_51 \/ Lbl_52 \/ Lbl_53 \/ Lbl_54 + \/ Lbl_55 \/ Lbl_56 \/ Lbl_57 \/ Lbl_58 \/ Lbl_59 \/ Lbl_60 + \/ Lbl_61 \/ Lbl_62 \/ Lbl_63 \/ Lbl_64 \/ Lbl_65 \/ Lbl_66 + \/ Lbl_67 \/ Lbl_68 \/ Lbl_69 \/ Lbl_70 \/ Lbl_71 \/ Lbl_72 + \/ Lbl_73 \/ Lbl_74 \/ Lbl_75 \/ Lbl_76 + +Lbl_77 == /\ pc = "Lbl_77" + /\ curPos' = JavaLen(Padding) + /\ tempVar1' = (-1 :> "") + /\ tempVar2' = JavaLen(Padding) + /\ pc' = "Lbl_78" + /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, + returnVal, tempVar3, stack, pos_, tokArray, i, + tokIdx, pos_f, token, left_, rt_, pos_fi, pos_fin, + delim_, ipos_, jdelim_, delimLen_, pos_find, pos, + delim, ipos, jdelim, delimLen, leftTok, rightTok, + currentToken, left, rt, notDone, foundBangToken, + temp1, temp2, temp3 >> + +Lbl_78 == /\ pc = "Lbl_78" + /\ IF tempVar2 < JavaLen(lineInput) + JavaLen(Padding) + THEN /\ curPos' = tempVar2 + /\ foundTokenSpecs' = << >> + /\ lastToken' = FALSE + /\ notLastToken' = FALSE + /\ stack' = << [ procedure |-> "findTokenSpecs", + pc |-> "Lbl_79", + currentToken |-> currentToken, + left |-> left, + rt |-> rt, + notDone |-> notDone, + foundBangToken |-> foundBangToken, + temp1 |-> temp1, + temp2 |-> temp2, + temp3 |-> temp3 ] >> + \o stack + /\ currentToken' = defaultInitValue + /\ left' = defaultInitValue + /\ rt' = defaultInitValue + /\ notDone' = defaultInitValue + /\ foundBangToken' = defaultInitValue + /\ temp1' = defaultInitValue + /\ temp2' = defaultInitValue + /\ temp3' = defaultInitValue + /\ pc' = "Lbl_31" + ELSE /\ PrintT("goodby world") + /\ pc' = "Done" + /\ UNCHANGED << foundTokenSpecs, lastToken, notLastToken, + curPos, stack, currentToken, left, rt, + notDone, foundBangToken, temp1, temp2, + temp3 >> + /\ UNCHANGED << line, returnVal, tempVar1, tempVar2, tempVar3, pos_, + tokArray, i, tokIdx, pos_f, token, left_, rt_, + pos_fi, pos_fin, delim_, ipos_, jdelim_, delimLen_, + pos_find, pos, delim, ipos, jdelim, delimLen, + leftTok, rightTok >> + +Lbl_79 == /\ pc = "Lbl_79" + /\ tempVar1' = returnVal + /\ tempVar2' = tempVar2 + 1 + /\ pc' = "Lbl_78" + /\ UNCHANGED << line, foundTokenSpecs, lastToken, notLastToken, + curPos, returnVal, tempVar3, stack, pos_, tokArray, + i, tokIdx, pos_f, token, left_, rt_, pos_fi, pos_fin, + delim_, ipos_, jdelim_, delimLen_, pos_find, pos, + delim, ipos, jdelim, delimLen, leftTok, rightTok, + currentToken, left, rt, notDone, foundBangToken, + temp1, temp2, temp3 >> + +Next == findTokenIn \/ findMaximalIdCharSeq \/ findMatchingLeftParen + \/ findMatchingLeftInner \/ findMatchingRightParen + \/ findMatchingRightInner \/ checkIfStepName \/ skipLeftOverSpaces + \/ skipRightOverSpaces \/ findTokenSpecs \/ Lbl_77 \/ Lbl_78 \/ Lbl_79 + \/ (* Disjunct to prevent deadlock on termination *) + (pc = "Done" /\ UNCHANGED vars) + +Spec == Init /\ [][Next]_vars + +Termination == <>(pc = "Done") + +\* END TRANSLATION +============================================================================= \* Modification History -\* Last modified Tue Jun 21 11:33:07 CEST 2011 by markus -\* Last modified Tue Jun 21 02:08:39 PDT 2011 by lamport -\* Created Mon Sep 20 06:42:12 PDT 2010 by lamport - -c: if (lastToken = TRUE) {return FixOrigin(foundTokens);} - while -} \* end algorithm -****************************************************************************) - +\* Last modified Tue Jun 21 11:33:07 CEST 2011 by markus +\* Last modified Tue Jun 21 02:08:39 PDT 2011 by lamport +\* Created Mon Sep 20 06:42:12 PDT 2010 by lamport + +c: if (lastToken = TRUE) {return FixOrigin(foundTokens);} + while +} \* end algorithm +****************************************************************************) + diff --git a/tlatools/test-model/Bug279/InitStateBug.cfg b/tlatools/test-model/Bug279/InitStateBug.cfg new file mode 100644 index 0000000000000000000000000000000000000000..5db1b74d8239cc161ffbc357c088cdbc8f41f02c --- /dev/null +++ b/tlatools/test-model/Bug279/InitStateBug.cfg @@ -0,0 +1,4 @@ +\* INIT definition +INIT Init +NEXT Next +\* Generated on Wed Mar 07 12:58:05 PST 2012 \ No newline at end of file diff --git a/tlatools/test-model/Bug279/InitStateBug.tla b/tlatools/test-model/Bug279/InitStateBug.tla new file mode 100644 index 0000000000000000000000000000000000000000..53c2c4226d146f1163a7f8b4ea6948b6215d6ed7 --- /dev/null +++ b/tlatools/test-model/Bug279/InitStateBug.tla @@ -0,0 +1,39 @@ +---------------------------- MODULE InitStateBug ---------------------------- +EXTENDS Naturals + +RECURSIVE AllSubs(_) +AllSubs(S) == + IF S = {} THEN {{}} ELSE + LET + c == CHOOSE c \in S : TRUE + T == S \ {c} + TT == AllSubs(T) + IN + TT \cup { t \cup {c} : t \in TT } + +RECURSIVE Sum(_) +Sum(S) == + IF S = {} THEN 0 ELSE + LET c == CHOOSE c \in S : TRUE + IN c + Sum(S \ {c}) + +Ch(S) == CHOOSE v \in S : Sum(v) > 4 + +S1 == SUBSET (1..20) \* Increased from 1..8 to 1..20 to also test performance with this spec. +S2 == AllSubs(1..8) + +VARIABLE pc, set, fun + +Init == pc = 0 /\ set = {} /\ fun = {} + +Next == + CASE + pc = 0 -> pc' = 1 /\ set' = S1 /\ fun' = Ch(set') + [] + pc = 1 -> pc' = 2 /\ set' = S2 /\ fun' = Ch(set') + [] + OTHER -> FALSE +============================================================================= +\* Modification History +\* Last modified Wed Mar 07 10:27:18 PST 2012 by tomr +\* Created Wed Mar 07 10:24:35 PST 2012 by tomr diff --git a/tlatools/test-model/CallGotoUnlabeledTest.tla b/tlatools/test-model/CallGotoUnlabeledTest.tla new file mode 100644 index 0000000000000000000000000000000000000000..9fe0642ff8ab380047d4ae69554e538e56fd8cd7 --- /dev/null +++ b/tlatools/test-model/CallGotoUnlabeledTest.tla @@ -0,0 +1,71 @@ +---- MODULE CallGotoUnlabeledTest ---- + +(* --algorithm CallGotoUnlabeledTest + +variable x = 0; + +procedure Foo() +begin +\*Q: \* Not needed anymore. + x := x + 1; + return; +end procedure; + +begin +A: + call Foo(); + goto C; +\*B: \* Not needed anymore. + x := 13; +C: + x := x - 1; + goto A; + +end algorithm *) +\* BEGIN TRANSLATION - the hash of the PCal code: PCal-4786c31224fe8555dc7ad50128a92fc6 +VARIABLES x, pc, stack + +vars == << x, pc, stack >> + +Init == (* Global variables *) + /\ x = 0 + /\ stack = << >> + /\ pc = "A" + +Lbl_1 == /\ pc = "Lbl_1" + /\ x' = x + 1 + /\ pc' = Head(stack).pc + /\ stack' = Tail(stack) + +Foo == Lbl_1 + +A == /\ pc = "A" + /\ stack' = << [ procedure |-> "Foo", + pc |-> "C" ] >> + \o stack + /\ pc' = "Lbl_1" + /\ x' = x + +Lbl_2 == /\ pc = "Lbl_2" + /\ x' = 13 + /\ pc' = "C" + /\ stack' = stack + +C == /\ pc = "C" + /\ x' = x - 1 + /\ pc' = "A" + /\ stack' = stack + +(* Allow infinite stuttering to prevent deadlock on termination. *) +Terminating == pc = "Done" /\ UNCHANGED vars + +Next == Foo \/ A \/ Lbl_2 \/ C + \/ Terminating + +Spec == Init /\ [][Next]_vars + +Termination == <>(pc = "Done") + +\* END TRANSLATION - the hash of the generated TLA code (remove to silence divergence warnings): TLA-8ca0b91b1573d7c7ae4c4135afa5f61b + +==== diff --git a/tlatools/test-model/CodePlexBug08/AgentRingMC.tla b/tlatools/test-model/CodePlexBug08/AgentRingMC.tla index acd6fc07679129f8295e00f3e36e0b31f561a952..04ff75f3250fe770d575baa2dd06bba3aacffa4d 100644 --- a/tlatools/test-model/CodePlexBug08/AgentRingMC.tla +++ b/tlatools/test-model/CodePlexBug08/AgentRingMC.tla @@ -23,5 +23,5 @@ prop_142745588773041000 == Test ---- ============================================================================= -\* Modification History -\* Created Fri Mar 27 12:31:27 CET 2015 by makuppe +\* Modification History +\* Created Fri Mar 27 12:31:27 CET 2015 by makuppe diff --git a/tlatools/test-model/CodePlexBug08/InlineMC.cfg b/tlatools/test-model/CodePlexBug08/InlineMC.cfg new file mode 100644 index 0000000000000000000000000000000000000000..a2b7e06f96db60091276304ced5553107b06b3ee --- /dev/null +++ b/tlatools/test-model/CodePlexBug08/InlineMC.cfg @@ -0,0 +1,4 @@ +CONSTANT +N = 6 +SPECIFICATION +Spec diff --git a/tlatools/test-model/CodePlexBug08/InlineMC.tla b/tlatools/test-model/CodePlexBug08/InlineMC.tla new file mode 100644 index 0000000000000000000000000000000000000000..e9ff6750e51ebce262d9b91c52fa7e2c2df41324 --- /dev/null +++ b/tlatools/test-model/CodePlexBug08/InlineMC.tla @@ -0,0 +1,4 @@ +---- MODULE InlineMC ---- +EXTENDS EWD840, TLC + +=================== \ No newline at end of file diff --git a/tlatools/test-model/CodePlexBug08/checkpoint.zip b/tlatools/test-model/CodePlexBug08/checkpoint.zip new file mode 100644 index 0000000000000000000000000000000000000000..b9c9d7d520634a13a9fe457bddf49cb78247b532 Binary files /dev/null and b/tlatools/test-model/CodePlexBug08/checkpoint.zip differ diff --git a/tlatools/test-model/CodePlexBug21.cfg b/tlatools/test-model/CodePlexBug21.cfg new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/tlatools/test-model/CodePlexBug21.tla b/tlatools/test-model/CodePlexBug21.tla new file mode 100644 index 0000000000000000000000000000000000000000..3df19a634c534e57d12049cd9d5429fbb0304df2 --- /dev/null +++ b/tlatools/test-model/CodePlexBug21.tla @@ -0,0 +1,13 @@ +--------------------------- MODULE CodePlexBug21 --------------------------- +EXTENDS Naturals, TLC + +func1 == [x \in 1..3 |-> x] +func2 == <<1,2,3>> + +ASSUME func1 = func2 \* evaluated to TRUE +ASSUME 3 :> 11 @@ func1 = <<1,2,11>> \* evaluated to TRUE + +ASSUME 3 :> 11 @@ func2 = <<1,2,11>> \* erroneously evaluated to FALSE + +ASSUME 4 :> 11 @@ func2 = <<1,2,3,11>> \* throws a TLC error +============================================================================= diff --git a/tlatools/test-model/Continue.cfg b/tlatools/test-model/Continue.cfg new file mode 100644 index 0000000000000000000000000000000000000000..b7702f14d2ece8edb2bf761738836a80877cd133 --- /dev/null +++ b/tlatools/test-model/Continue.cfg @@ -0,0 +1,7 @@ +INIT Init +NEXT Next + +CONSTRAINT Constraint + +INVARIANT Inv1 +INVARIANT Inv2 \ No newline at end of file diff --git a/tlatools/test-model/Continue.tla b/tlatools/test-model/Continue.tla new file mode 100644 index 0000000000000000000000000000000000000000..c5330b9ccbe3159e083403d444ec771c0a179105 --- /dev/null +++ b/tlatools/test-model/Continue.tla @@ -0,0 +1,20 @@ +---- MODULE Continue ---- +EXTENDS Naturals + +VARIABLES x,y + +Init == x = 1 /\ y = 1 + +Next == /\ x' = x + 1 + /\ \/ /\ x = 1 + /\ y' \in {1,2} + \/ /\ UNCHANGED y + +\* For the test to meaningful, it is vital that workers +\* actually continue state space exploration after the +\* first invariant violation (for trace files to be written). +Inv1 == y < 2 +Inv2 == x < 30 + +Constraint == Inv1 /\ Inv2 +==== diff --git a/tlatools/test-model/DepthFirstErrorTrace.cfg b/tlatools/test-model/DepthFirstErrorTrace.cfg new file mode 100644 index 0000000000000000000000000000000000000000..c8cffbe64c3dcc8bce148218f2e7b66a7537f573 --- /dev/null +++ b/tlatools/test-model/DepthFirstErrorTrace.cfg @@ -0,0 +1,4 @@ +SPECIFICATION +Spec +INVARIANT +Inv \ No newline at end of file diff --git a/tlatools/test-model/DepthFirstErrorTrace.tla b/tlatools/test-model/DepthFirstErrorTrace.tla new file mode 100644 index 0000000000000000000000000000000000000000..af057477a9bd4c3362a915f8fe67da96df904752 --- /dev/null +++ b/tlatools/test-model/DepthFirstErrorTrace.tla @@ -0,0 +1,9 @@ +--------------------------- MODULE DepthFirstErrorTrace --------------------------- +EXTENDS Integers + +VARIABLES x + +Spec == (x=0) /\ [][x'=x+1]_<<x>> + +Inv == x < 7 +============================================================================= diff --git a/tlatools/test-model/DepthFirstTerminate.cfg b/tlatools/test-model/DepthFirstTerminate.cfg new file mode 100644 index 0000000000000000000000000000000000000000..bc224b9def594b0819de3f0b45ede63ea8213c7b --- /dev/null +++ b/tlatools/test-model/DepthFirstTerminate.cfg @@ -0,0 +1,2 @@ +SPECIFICATION +Spec diff --git a/tlatools/test-model/DepthFirstTerminate.tla b/tlatools/test-model/DepthFirstTerminate.tla new file mode 100644 index 0000000000000000000000000000000000000000..d7193b8a66cd0df78a0847a70974f22bc664a477 --- /dev/null +++ b/tlatools/test-model/DepthFirstTerminate.tla @@ -0,0 +1,59 @@ +---------------------------- MODULE DepthFirstTerminate ---------------------------- +EXTENDS Integers + +(* + +--algorithm Untitled +{ + variables u = 24 ; v \in 1 .. 50; + { + print <<u, v>>; + while (u # 0) { + if (u < v) { + u := v || v := u + }; + u := u - v ; + }; + print <<"have gcd", v>>; + } +} +*) +\* BEGIN TRANSLATION +VARIABLES u, v, pc + +vars == << u, v, pc >> + +Init == (* Global variables *) + /\ u = 24 + /\ v \in 1 .. 50 + /\ pc = "Lbl_1" + +Lbl_1 == /\ pc = "Lbl_1" + /\ pc' = "Lbl_2" + /\ UNCHANGED << u, v >> + +Lbl_2 == /\ pc = "Lbl_2" + /\ IF u # 0 + THEN /\ IF u < v + THEN /\ /\ u' = v + /\ v' = u + ELSE /\ TRUE + /\ UNCHANGED << u, v >> + /\ pc' = "Lbl_3" + ELSE /\ pc' = "Done" + /\ UNCHANGED << u, v >> + +Lbl_3 == /\ pc = "Lbl_3" + /\ u' = u - v + /\ pc' = "Lbl_2" + /\ v' = v + +Next == Lbl_1 \/ Lbl_2 \/ Lbl_3 + \/ (* Disjunct to prevent deadlock on termination *) + (pc = "Done" /\ UNCHANGED vars) + +Spec == /\ Init /\ [][Next]_vars + +\* END TRANSLATION + +============================================================================= diff --git a/tlatools/test-model/DieHard.cfg b/tlatools/test-model/DieHard.cfg new file mode 100644 index 0000000000000000000000000000000000000000..c8cffbe64c3dcc8bce148218f2e7b66a7537f573 --- /dev/null +++ b/tlatools/test-model/DieHard.cfg @@ -0,0 +1,4 @@ +SPECIFICATION +Spec +INVARIANT +Inv \ No newline at end of file diff --git a/tlatools/test-model/DieHard.tla b/tlatools/test-model/DieHard.tla new file mode 100644 index 0000000000000000000000000000000000000000..e35c24c86f88dcda5eedc753dab7d8915a5dcc7f --- /dev/null +++ b/tlatools/test-model/DieHard.tla @@ -0,0 +1,87 @@ +---------------------------- MODULE DieHard ---------------------------- +EXTENDS TLC, Integers + +Min(m, n) == IF m < n THEN m ELSE n + +NONDET == "nondet" +FILL_BIG == "fill big" +EMPTY_BIG == "empty big" +FILL_SMALL == "fill small" +EMPTY_SMALL == "empty small" +BIG_TO_SMALL == "pour big to small" +SMALL_TO_BIG == "pour small to big" + +(*************************************************************************** +--algorithm DieHardDemo { + variables bigBucket = 0, smallBucket = 0, action = NONDET; + + process (FixWater = 1) + variables water_to_pour = 0; + { + LB1: while (TRUE) + { + either { action := FILL_BIG; bigBucket := 5; } + or { action := EMPTY_BIG; bigBucket := 0; } + or { action := FILL_SMALL; smallBucket := 3; } + or { action := EMPTY_SMALL; smallBucket := 0; } + or { + action := BIG_TO_SMALL; + water_to_pour := Min(3 - smallBucket, bigBucket); + smallBucket := smallBucket + water_to_pour; + bigBucket := bigBucket - water_to_pour; + } + or { + action := SMALL_TO_BIG; + water_to_pour := Min(5 - bigBucket, smallBucket); + smallBucket := smallBucket - water_to_pour; + bigBucket := bigBucket + water_to_pour; + } + } + } +} + +***************************************************************************) +\* BEGIN TRANSLATION +VARIABLES bigBucket, smallBucket, action, water_to_pour + +vars == << bigBucket, smallBucket, action, water_to_pour >> + +ProcSet == {1} + +Init == (* Global variables *) + /\ bigBucket = 0 + /\ smallBucket = 0 + /\ action = NONDET + (* Process FixWater *) + /\ water_to_pour = 0 + +FixWater == \/ /\ action' = FILL_BIG + /\ bigBucket' = 5 + /\ UNCHANGED <<smallBucket, water_to_pour>> + \/ /\ action' = EMPTY_BIG + /\ bigBucket' = 0 + /\ UNCHANGED <<smallBucket, water_to_pour>> + \/ /\ action' = FILL_SMALL + /\ smallBucket' = 3 + /\ UNCHANGED <<bigBucket, water_to_pour>> + \/ /\ action' = EMPTY_SMALL + /\ smallBucket' = 0 + /\ UNCHANGED <<bigBucket, water_to_pour>> + \/ /\ action' = BIG_TO_SMALL + /\ water_to_pour' = Min(3 - smallBucket, bigBucket) + /\ smallBucket' = smallBucket + water_to_pour' + /\ bigBucket' = bigBucket - water_to_pour' + \/ /\ action' = SMALL_TO_BIG + /\ water_to_pour' = Min(5 - bigBucket, smallBucket) + /\ smallBucket' = smallBucket - water_to_pour' + /\ bigBucket' = bigBucket + water_to_pour' + +Next == FixWater + +Spec == Init /\ [][Next]_vars + +Inv == bigBucket # 4 + +\* END TRANSLATION + +============================================================================= diff --git a/tlatools/test-model/DieHardTLA.cfg b/tlatools/test-model/DieHardTLA.cfg new file mode 100644 index 0000000000000000000000000000000000000000..bb7713361d8dc3c0f1759e274a1b41aaacc3411a --- /dev/null +++ b/tlatools/test-model/DieHardTLA.cfg @@ -0,0 +1,4 @@ +SPECIFICATION +Spec +INVARIANT +TypeOK \ No newline at end of file diff --git a/tlatools/test-model/DieHardTLA.tla b/tlatools/test-model/DieHardTLA.tla new file mode 100644 index 0000000000000000000000000000000000000000..78d44d483e7540b4de6476503dd9a8c316c66eba --- /dev/null +++ b/tlatools/test-model/DieHardTLA.tla @@ -0,0 +1,44 @@ +------------------------------ MODULE DieHardTLA ------------------------------ +EXTENDS Integers + +VARIABLES small, big + +TypeOK == /\ small \in 0..3 + /\ big \in 0..5 + +Init == /\ big = 0 + /\ small = 0 + +FillSmall == /\ small' = 3 + /\ big' = big + +FillBig == /\ big' = 5 + /\ small' = small + +EmptySmall == /\ small' = 0 + /\ big' = big + +EmptyBig == /\ big' = 0 + /\ small' = small + +SmallToBig == IF big + small =< 5 + THEN /\ big' = big + small + /\ small' = 0 + ELSE /\ big' = 5 + /\ small' = small - (5 - big) + +BigToSmall == IF big + small =< 3 + THEN /\ big' = 0 + /\ small' = big + small + ELSE /\ big' = small - (3 - big) + /\ small' = 3 + +Next == \/ FillSmall + \/ FillBig + \/ EmptySmall + \/ EmptyBig + \/ SmallToBig + \/ BigToSmall + +Spec == Init /\ [][Next]_<<small, big>> +============================================================================= diff --git a/tlatools/test-model/DistributedTrace.cfg b/tlatools/test-model/DistributedTrace.cfg new file mode 100644 index 0000000000000000000000000000000000000000..c8cffbe64c3dcc8bce148218f2e7b66a7537f573 --- /dev/null +++ b/tlatools/test-model/DistributedTrace.cfg @@ -0,0 +1,4 @@ +SPECIFICATION +Spec +INVARIANT +Inv \ No newline at end of file diff --git a/tlatools/test-model/DistributedTrace.tla b/tlatools/test-model/DistributedTrace.tla new file mode 100644 index 0000000000000000000000000000000000000000..6149b74a924ac27de52a3461e0124a21b13eabf5 --- /dev/null +++ b/tlatools/test-model/DistributedTrace.tla @@ -0,0 +1,14 @@ +-------------------------- MODULE DistributedTrace -------------------------- +EXTENDS Integers, Sequences + +VARIABLES x +vars == <<x>> + +Init == x \in 1..10 + +Next == x' = x + 1 + +Spec == /\ Init /\ [][Next]_vars + +Inv == x < 20 +============================================================================= diff --git a/tlatools/test-model/DoInitFunctor/DoInitFunctorEvalException.cfg b/tlatools/test-model/DoInitFunctor/DoInitFunctorEvalException.cfg new file mode 100644 index 0000000000000000000000000000000000000000..00592a934cbecee867aa47abbdde199473d8fd74 --- /dev/null +++ b/tlatools/test-model/DoInitFunctor/DoInitFunctorEvalException.cfg @@ -0,0 +1,6 @@ +CONSTRAINT +const_4711 +SPECIFICATION +Spec +INVARIANT +NotNine diff --git a/tlatools/test-model/DoInitFunctor/DoInitFunctorEvalException.tla b/tlatools/test-model/DoInitFunctor/DoInitFunctorEvalException.tla new file mode 100644 index 0000000000000000000000000000000000000000..c71fb286d50ae1b6eddeba21be20e72cc613ea33 --- /dev/null +++ b/tlatools/test-model/DoInitFunctor/DoInitFunctorEvalException.tla @@ -0,0 +1,19 @@ +--------------------------- MODULE DoInitFunctorEvalException --------------------------- +EXTENDS Integers + +VARIABLES x +vars == <<x>> + +Init == x \in 1..10 + +Next == x' = 0 + +Spec == Init /\ [][Next]_vars + +(* This function is used as a model constraint to trigger an EvalException (because it doesn't return Bool) *) +F(a) == {} +const_4711 == F(x) + +(* This property is violated by one of the init states *) +NotNine == x # 9 +============================================================================= diff --git a/tlatools/test-model/DoInitFunctor/DoInitFunctorInvariant.cfg b/tlatools/test-model/DoInitFunctor/DoInitFunctorInvariant.cfg new file mode 100644 index 0000000000000000000000000000000000000000..5fd6a98a18bd4dcdd7201895f3989c0a7a472a0c --- /dev/null +++ b/tlatools/test-model/DoInitFunctor/DoInitFunctorInvariant.cfg @@ -0,0 +1,4 @@ +SPECIFICATION +Spec +INVARIANT +NotNine diff --git a/tlatools/test-model/DoInitFunctor/DoInitFunctorInvariant.tla b/tlatools/test-model/DoInitFunctor/DoInitFunctorInvariant.tla new file mode 100644 index 0000000000000000000000000000000000000000..73c16c5702db1d15d92a4e20a20f69e6dddb84e4 --- /dev/null +++ b/tlatools/test-model/DoInitFunctor/DoInitFunctorInvariant.tla @@ -0,0 +1,15 @@ +--------------------------- MODULE DoInitFunctorInvariant --------------------------- +EXTENDS Integers + +VARIABLES x +vars == <<x>> + +Init == x \in 1..10 + +Next == x' = 0 + +Spec == Init /\ [][Next]_vars + +(* This property is violated by one of the init states *) +NotNine == x # 9 +============================================================================= diff --git a/tlatools/test-model/DoInitFunctor/DoInitFunctorInvariantContinue.cfg b/tlatools/test-model/DoInitFunctor/DoInitFunctorInvariantContinue.cfg new file mode 100644 index 0000000000000000000000000000000000000000..6a89e0901c7fa1bcc62bf2ffba508d385d572ca4 --- /dev/null +++ b/tlatools/test-model/DoInitFunctor/DoInitFunctorInvariantContinue.cfg @@ -0,0 +1,4 @@ +SPECIFICATION +Spec +INVARIANT +Inv diff --git a/tlatools/test-model/DoInitFunctor/DoInitFunctorInvariantContinue.tla b/tlatools/test-model/DoInitFunctor/DoInitFunctorInvariantContinue.tla new file mode 100644 index 0000000000000000000000000000000000000000..df785bda8dfb7ca8df064c132e22a5c5826513e5 --- /dev/null +++ b/tlatools/test-model/DoInitFunctor/DoInitFunctorInvariantContinue.tla @@ -0,0 +1,15 @@ +--------------------------- MODULE DoInitFunctorInvariantContinue --------------------------- +EXTENDS Integers + +VARIABLES x +vars == <<x>> + +Init == x \in 1..10 + +Next == x' = 0 + +Spec == Init /\ [][Next]_vars + +(* This property is violated by all of the init states *) +Inv == x \notin Nat +============================================================================= diff --git a/tlatools/test-model/DoInitFunctor/DoInitFunctorMinimalErrorStack.cfg b/tlatools/test-model/DoInitFunctor/DoInitFunctorMinimalErrorStack.cfg new file mode 100644 index 0000000000000000000000000000000000000000..6a89e0901c7fa1bcc62bf2ffba508d385d572ca4 --- /dev/null +++ b/tlatools/test-model/DoInitFunctor/DoInitFunctorMinimalErrorStack.cfg @@ -0,0 +1,4 @@ +SPECIFICATION +Spec +INVARIANT +Inv diff --git a/tlatools/test-model/DoInitFunctor/DoInitFunctorMinimalErrorStack.tla b/tlatools/test-model/DoInitFunctor/DoInitFunctorMinimalErrorStack.tla new file mode 100644 index 0000000000000000000000000000000000000000..f29ac16460c7768c18b783e9386086be4a05250c --- /dev/null +++ b/tlatools/test-model/DoInitFunctor/DoInitFunctorMinimalErrorStack.tla @@ -0,0 +1,15 @@ +------------------ MODULE DoInitFunctorMinimalErrorStack -------------------- +EXTENDS Naturals + +VARIABLES a, b +vars == <<a, b>> + +Init == + /\ a \in [1..2 -> 1..3] + /\ b = TRUE + +Spec == Init /\ [][UNCHANGED vars]_vars + +(* This property is violated by all of the init states *) +Inv == a >= 0 +============================================================================= diff --git a/tlatools/test-model/DoInitFunctor/DoInitFunctorProperty.cfg b/tlatools/test-model/DoInitFunctor/DoInitFunctorProperty.cfg new file mode 100644 index 0000000000000000000000000000000000000000..b92bb43293dbff997539b4da5c9374702f3e0507 --- /dev/null +++ b/tlatools/test-model/DoInitFunctor/DoInitFunctorProperty.cfg @@ -0,0 +1,4 @@ +SPECIFICATION +Spec +PROPERTY +NotNine diff --git a/tlatools/test-model/DoInitFunctor/DoInitFunctorProperty.tla b/tlatools/test-model/DoInitFunctor/DoInitFunctorProperty.tla new file mode 100644 index 0000000000000000000000000000000000000000..d1acd346b9b89ce6607e41148ea71b277946ccbd --- /dev/null +++ b/tlatools/test-model/DoInitFunctor/DoInitFunctorProperty.tla @@ -0,0 +1,15 @@ +--------------------------- MODULE DoInitFunctorProperty --------------------------- +EXTENDS Integers + +VARIABLES x +vars == <<x>> + +Init == x \in 1..10 + +Next == x' = 0 + +Spec == Init /\ [][Next]_vars + +(* This property is violated by one of the init states *) +NotNine == x # 9 +============================================================================= diff --git a/tlatools/test-model/EWD840/MC06.cfg b/tlatools/test-model/EWD840/MC06.cfg new file mode 100644 index 0000000000000000000000000000000000000000..ed1d175d3106a5e3cf5d65bf42ace5b386eb145c --- /dev/null +++ b/tlatools/test-model/EWD840/MC06.cfg @@ -0,0 +1,9 @@ +\* CONSTANT definitions +CONSTANT +N <- const_143073460396411000 +\* SPECIFICATION definition +SPECIFICATION +spec_143073460397412000 +\* PROPERTY definition +PROPERTY +prop_143073460398413000 diff --git a/tlatools/test-model/EWD840/MC06.tla b/tlatools/test-model/EWD840/MC06.tla new file mode 100644 index 0000000000000000000000000000000000000000..71a018b16aeb0327558fb1052a57350ed3dff85d --- /dev/null +++ b/tlatools/test-model/EWD840/MC06.tla @@ -0,0 +1,17 @@ +---- MODULE MC06 ---- +EXTENDS EWD840, TLC + +\* CONSTANT definitions @modelParameterConstants:0N +const_143073460396411000 == +7 +---- + +\* SPECIFICATION definition @modelBehaviorSpec:0 +spec_143073460397412000 == +Spec +---- +\* PROPERTY definition @modelCorrectnessProperties:0 +prop_143073460398413000 == +Liveness +---- +============================================================================= diff --git a/tlatools/test-model/Empty.cfg b/tlatools/test-model/Empty.cfg new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/tlatools/test-model/Empty.tla b/tlatools/test-model/Empty.tla new file mode 100644 index 0000000000000000000000000000000000000000..3e6c3492f671ebc8e947dc4fcecc22505bf3bb71 --- /dev/null +++ b/tlatools/test-model/Empty.tla @@ -0,0 +1,2 @@ +------------------------------- MODULE Empty ------------------------------- +============================================================================ diff --git a/tlatools/test-model/EmptyOrderOfSolutions.cfg b/tlatools/test-model/EmptyOrderOfSolutions.cfg new file mode 100644 index 0000000000000000000000000000000000000000..20524345c8bb1355485ae7282e49cd9a44301eca --- /dev/null +++ b/tlatools/test-model/EmptyOrderOfSolutions.cfg @@ -0,0 +1,4 @@ +SPECIFICATION +Spec +PROPERTY +Prop diff --git a/tlatools/test-model/EmptyOrderOfSolutions.tla b/tlatools/test-model/EmptyOrderOfSolutions.tla new file mode 100644 index 0000000000000000000000000000000000000000..96d3acd32389749608f70073e1fcf0132d6d3291 --- /dev/null +++ b/tlatools/test-model/EmptyOrderOfSolutions.tla @@ -0,0 +1,11 @@ +----- MODULE EmptyOrderOfSolutions ----- +VARIABLES x + +Init == x = 0 + +Next == x' = 1 + +Spec == Init /\ [][Next]_x + +Prop == <>[]TRUE => TRUE +===== diff --git a/tlatools/test-model/EmptySubsetEq.cfg b/tlatools/test-model/EmptySubsetEq.cfg new file mode 100644 index 0000000000000000000000000000000000000000..c8cffbe64c3dcc8bce148218f2e7b66a7537f573 --- /dev/null +++ b/tlatools/test-model/EmptySubsetEq.cfg @@ -0,0 +1,4 @@ +SPECIFICATION +Spec +INVARIANT +Inv \ No newline at end of file diff --git a/tlatools/test-model/EmptySubsetEq.tla b/tlatools/test-model/EmptySubsetEq.tla new file mode 100644 index 0000000000000000000000000000000000000000..d951e633b1d5853aec2aa1a9bb0f9846201fab3f --- /dev/null +++ b/tlatools/test-model/EmptySubsetEq.tla @@ -0,0 +1,13 @@ +--------------------------- MODULE EmptySubsetEq -------------------------- +EXTENDS Integers, FiniteSets + +VARIABLE b + +Init == b = TRUE + +Next == b' = /\ (SUBSET (1..3)) \subseteq (1..4) + +Spec == Init /\ [][Next]_<<b>> + +Inv == b = TRUE +============================================================================= diff --git a/tlatools/test-model/EvalOrder/Base.tla b/tlatools/test-model/EvalOrder/Base.tla new file mode 100644 index 0000000000000000000000000000000000000000..28ae14504aa4588c69bd526ba3bb137a6418bcc6 --- /dev/null +++ b/tlatools/test-model/EvalOrder/Base.tla @@ -0,0 +1,9 @@ +-------------------------------- MODULE Base -------------------------------- +VARIABLES clk +vars == <<clk>> + +Init == clk = 0 + +Spec == Init /\ [][UNCHANGED vars]_vars + +============================================================================= diff --git a/tlatools/test-model/EvalOrder/InitEvalOrder.tla b/tlatools/test-model/EvalOrder/InitEvalOrder.tla new file mode 100644 index 0000000000000000000000000000000000000000..098bc1a98d2ceb693bb9edd3e7cd7f408cdb8bf1 --- /dev/null +++ b/tlatools/test-model/EvalOrder/InitEvalOrder.tla @@ -0,0 +1,23 @@ +--------------------------- MODULE InitEvalOrder --------------------------- +VARIABLE Clk1 +S1 == INSTANCE Base WITH clk <- Clk1 + +VARIABLE Clk2 +S2 == INSTANCE Base WITH clk <- Clk2 + +Spec1 == + /\ S1!Init /\ S2!Init /\ (Clk2 = Clk1) + /\ [][UNCHANGED <<Clk1, Clk2>>]_<<S1!vars, S2!vars>> + +Spec2 == + /\ S2!Init /\ S1!Init /\ (Clk1 = Clk2) + /\ [][UNCHANGED <<Clk1, Clk2>>]_<<S1!vars, S2!vars>> + +Spec3 == + /\ S1!Init /\ S2!Init /\ (Clk1 = Clk2) + /\ [][UNCHANGED <<Clk1, Clk2>>]_<<S1!vars, S2!vars>> + +Spec4 == + /\ S2!Init /\ S1!Init /\ (Clk2 = Clk1) + /\ [][UNCHANGED <<Clk1, Clk2>>]_<<S1!vars, S2!vars>> +============================================================================= diff --git a/tlatools/test-model/EvalOrder/InitEvalOrder1.cfg b/tlatools/test-model/EvalOrder/InitEvalOrder1.cfg new file mode 100644 index 0000000000000000000000000000000000000000..d1f34ad6b4d496bb2024748004055359877a09ef --- /dev/null +++ b/tlatools/test-model/EvalOrder/InitEvalOrder1.cfg @@ -0,0 +1,2 @@ +SPECIFICATION +Spec1 diff --git a/tlatools/test-model/EvalOrder/InitEvalOrder2.cfg b/tlatools/test-model/EvalOrder/InitEvalOrder2.cfg new file mode 100644 index 0000000000000000000000000000000000000000..e041393416d454a2bdd87c66b69feebaf31ddbd9 --- /dev/null +++ b/tlatools/test-model/EvalOrder/InitEvalOrder2.cfg @@ -0,0 +1,2 @@ +SPECIFICATION +Spec2 diff --git a/tlatools/test-model/EvalOrder/InitEvalOrder3.cfg b/tlatools/test-model/EvalOrder/InitEvalOrder3.cfg new file mode 100644 index 0000000000000000000000000000000000000000..da6984d2a15785661ab45dfbce82aefb482c1c8a --- /dev/null +++ b/tlatools/test-model/EvalOrder/InitEvalOrder3.cfg @@ -0,0 +1,2 @@ +SPECIFICATION +Spec3 diff --git a/tlatools/test-model/EvalOrder/InitEvalOrder4.cfg b/tlatools/test-model/EvalOrder/InitEvalOrder4.cfg new file mode 100644 index 0000000000000000000000000000000000000000..789c3de3bc109d466cec77824bda13ad9b4b1da8 --- /dev/null +++ b/tlatools/test-model/EvalOrder/InitEvalOrder4.cfg @@ -0,0 +1,2 @@ +SPECIFICATION +Spec4 diff --git a/tlatools/test-model/EvalOrder/InitEvalOrderBasic.cfg b/tlatools/test-model/EvalOrder/InitEvalOrderBasic.cfg new file mode 100644 index 0000000000000000000000000000000000000000..bc224b9def594b0819de3f0b45ede63ea8213c7b --- /dev/null +++ b/tlatools/test-model/EvalOrder/InitEvalOrderBasic.cfg @@ -0,0 +1,2 @@ +SPECIFICATION +Spec diff --git a/tlatools/test-model/EvalOrder/InitEvalOrderBasic.tla b/tlatools/test-model/EvalOrder/InitEvalOrderBasic.tla new file mode 100644 index 0000000000000000000000000000000000000000..9e70f772e449733b9a29de075dd99c9dd36a9c82 --- /dev/null +++ b/tlatools/test-model/EvalOrder/InitEvalOrderBasic.tla @@ -0,0 +1,6 @@ +--------------------------- MODULE InitEvalOrderBasic --------------------------- +VARIABLES x, y + +Spec == /\ (y=0) /\ (x=0) /\ (y = x) + /\ [][UNCHANGED <<x,y>>]_<<x,y>> +============================================================================= diff --git a/tlatools/test-model/EvaluatingValueTest.cfg b/tlatools/test-model/EvaluatingValueTest.cfg new file mode 100644 index 0000000000000000000000000000000000000000..f78adb5fb4413592b83a20ec7c7738dcc9cfcef4 --- /dev/null +++ b/tlatools/test-model/EvaluatingValueTest.cfg @@ -0,0 +1,2 @@ +SPECIFICATION Spec +INVARIANT Inv \ No newline at end of file diff --git a/tlatools/test-model/EvaluatingValueTest.tla b/tlatools/test-model/EvaluatingValueTest.tla new file mode 100644 index 0000000000000000000000000000000000000000..c4c3bb24a9ce7b16a853ef740459bb1666e33361 --- /dev/null +++ b/tlatools/test-model/EvaluatingValueTest.tla @@ -0,0 +1,18 @@ +------------------------ MODULE EvaluatingValueTest ------------------------ +EXTENDS Naturals + +VARIABLES x + +Init == x = 1 + +A == x' = FALSE \* This action is replaced by the @Evaluation in EvaluatingValueTest.java + +Spec == (x = 1) /\ [][A]_x + +Inv == x \in Nat + +THEOREM Spec => []Inv + +Prop == <>(x = 42) + +============================================================================ diff --git a/tlatools/test-model/FingerprintExceptionInit.cfg b/tlatools/test-model/FingerprintExceptionInit.cfg new file mode 100644 index 0000000000000000000000000000000000000000..3de9a85ec4e2058d21906ad4967bf50084684036 --- /dev/null +++ b/tlatools/test-model/FingerprintExceptionInit.cfg @@ -0,0 +1,5 @@ +INIT +Init + +NEXT +Next \ No newline at end of file diff --git a/tlatools/test-model/FingerprintExceptionInit.tla b/tlatools/test-model/FingerprintExceptionInit.tla new file mode 100644 index 0000000000000000000000000000000000000000..647fc4e6fd198e673d6b3945c9b1a7fe7c2b1a39 --- /dev/null +++ b/tlatools/test-model/FingerprintExceptionInit.tla @@ -0,0 +1,11 @@ +------------------------ MODULE FingerprintExceptionInit -------------------- + +EXTENDS Integers + +VARIABLE x + +Init == x = SUBSET(SUBSET(1..36)) +Next == IF {1} \in SUBSET(1..31) THEN x' = SUBSET(SUBSET(1..32)) ELSE x' = 2 +Spec == Init /\ [][Next]_x + +============================================================================= \ No newline at end of file diff --git a/tlatools/test-model/FingerprintExceptionNext.cfg b/tlatools/test-model/FingerprintExceptionNext.cfg new file mode 100644 index 0000000000000000000000000000000000000000..3de9a85ec4e2058d21906ad4967bf50084684036 --- /dev/null +++ b/tlatools/test-model/FingerprintExceptionNext.cfg @@ -0,0 +1,5 @@ +INIT +Init + +NEXT +Next \ No newline at end of file diff --git a/tlatools/test-model/FingerprintExceptionNext.tla b/tlatools/test-model/FingerprintExceptionNext.tla new file mode 100644 index 0000000000000000000000000000000000000000..009b6d79491a586a770fa3ee754afd832edae9f2 --- /dev/null +++ b/tlatools/test-model/FingerprintExceptionNext.tla @@ -0,0 +1,11 @@ +------------------------ MODULE FingerprintExceptionNext -------------------- + +EXTENDS Integers + +VARIABLE x + +Init == x = 0 +Next == IF {1} \in SUBSET(1..31) THEN x' = SUBSET(SUBSET(1..32)) ELSE x' = 2 +Spec == Init /\ [][Next]_x + +============================================================================= \ No newline at end of file diff --git a/tlatools/test-model/Github179a.cfg b/tlatools/test-model/Github179a.cfg new file mode 100644 index 0000000000000000000000000000000000000000..3662e06f049e7c9069b5137ddfc038d9eb799c36 --- /dev/null +++ b/tlatools/test-model/Github179a.cfg @@ -0,0 +1,4 @@ +INIT +Init +NEXT +Next \ No newline at end of file diff --git a/tlatools/test-model/Github179a.tla b/tlatools/test-model/Github179a.tla new file mode 100644 index 0000000000000000000000000000000000000000..fd046e45a32ead359ea53c1cf490f8db1303aa0f --- /dev/null +++ b/tlatools/test-model/Github179a.tla @@ -0,0 +1,25 @@ +---- MODULE Github179a ---- +EXTENDS TLC, Naturals + +VARIABLE v + +Max(s) == { x \in s : \A y \in s : x >= y} + +(* Attempted to check equality of integer 1 with non-integer: {1} *) +sillyExpr == + {s \in SUBSET {1,2,3} : + \A x \in s : IF x = Max(s) + THEN TRUE + ELSE (x+1) \in s} +---- + +ASSUME PrintT(<<"$!@$!@$!@$!@$!", sillyExpr>>) +---- + +Init == +FALSE/\v = 0 +---- +Next == +FALSE/\ v'= v +---- +============================================================================= diff --git a/tlatools/test-model/Github179b.cfg b/tlatools/test-model/Github179b.cfg new file mode 100644 index 0000000000000000000000000000000000000000..3662e06f049e7c9069b5137ddfc038d9eb799c36 --- /dev/null +++ b/tlatools/test-model/Github179b.cfg @@ -0,0 +1,4 @@ +INIT +Init +NEXT +Next \ No newline at end of file diff --git a/tlatools/test-model/Github179b.tla b/tlatools/test-model/Github179b.tla new file mode 100644 index 0000000000000000000000000000000000000000..adcd7ea71a7ad30334038fe0fe6dab8f5b1b9c3c --- /dev/null +++ b/tlatools/test-model/Github179b.tla @@ -0,0 +1,18 @@ +---- MODULE Github179b ---- +EXTENDS TLC, Naturals + +VARIABLE v + +Max(s) == { x \in s : \A y \in s : x >= y} + +sillyExpr == +{s \in SUBSET {1,2,3,4} : \A x \in s : IF x = Max(s) THEN TRUE ELSE (x+1) \in s} +---- + +Init == +Print(sillyExpr, FALSE)/\v = 0 +---- +Next == +FALSE/\ v'= v +---- +============================================================================= diff --git a/tlatools/test-model/Github179c.cfg b/tlatools/test-model/Github179c.cfg new file mode 100644 index 0000000000000000000000000000000000000000..3662e06f049e7c9069b5137ddfc038d9eb799c36 --- /dev/null +++ b/tlatools/test-model/Github179c.cfg @@ -0,0 +1,4 @@ +INIT +Init +NEXT +Next \ No newline at end of file diff --git a/tlatools/test-model/Github179c.tla b/tlatools/test-model/Github179c.tla new file mode 100644 index 0000000000000000000000000000000000000000..37a89a6e2d0c1a9f51725b77fb7d768c7c8b1d7a --- /dev/null +++ b/tlatools/test-model/Github179c.tla @@ -0,0 +1,17 @@ +---- MODULE Github179c ---- +EXTENDS TLC, Naturals + +VARIABLE v + +Max(s) == { x \in s : \A y \in s : x >= y} + +sillyExpr == +{s \in SUBSET {1,2,3,4} : + \A x \in s : IF x = Max(s) + THEN TRUE + ELSE (x+1) \in s} + +Init == v = 0 + +Next == PrintT(sillyExpr) = FALSE /\ v'= v +===== diff --git a/tlatools/test-model/Github358.tla b/tlatools/test-model/Github358.tla new file mode 100644 index 0000000000000000000000000000000000000000..6ff29f99fc11a89475e8cc951347f1fd56c87c33 --- /dev/null +++ b/tlatools/test-model/Github358.tla @@ -0,0 +1,7 @@ +------------------------------ MODULE Github358 ----------------------------- +(*--algorithm Github358 +begin + A:: + skip; +end algorithm; *) +============================================================================= diff --git a/tlatools/test-model/Github361.cfg b/tlatools/test-model/Github361.cfg new file mode 100644 index 0000000000000000000000000000000000000000..3662e06f049e7c9069b5137ddfc038d9eb799c36 --- /dev/null +++ b/tlatools/test-model/Github361.cfg @@ -0,0 +1,4 @@ +INIT +Init +NEXT +Next \ No newline at end of file diff --git a/tlatools/test-model/Github361.tla b/tlatools/test-model/Github361.tla new file mode 100644 index 0000000000000000000000000000000000000000..ec06c2f5c467632330b2f8c671301d81f8186624 --- /dev/null +++ b/tlatools/test-model/Github361.tla @@ -0,0 +1,24 @@ +---- MODULE Github361 ---- +EXTENDS TLC, Naturals + +Nodes == {1, 2, 3, 4, 5} +null == 6 \* not in Nodes + +Vertices == [nodes : SUBSET Nodes, succ : Nodes \cup {null}, dead : BOOLEAN] + +Partitions == {P \in SUBSET Vertices : + /\ \A v1, v2 \in P : + (v1 # v2) => ((v1.nodes \cap v2.nodes) = {}) + /\ \A v \in P : /\ v.nodes # {} + /\ \E w \in P : \/ v.succ = null + \/ v.succ \in w.nodes} + +VARIABLES partition + +TypeOK == partition \in Partitions + +Init == partition = {} + +Next == UNCHANGED partition + +============================================================================= diff --git a/tlatools/test-model/Github362.cfg b/tlatools/test-model/Github362.cfg new file mode 100644 index 0000000000000000000000000000000000000000..dbbd7f2a293042e29cff5cca9741e11fa1cbe4f3 --- /dev/null +++ b/tlatools/test-model/Github362.cfg @@ -0,0 +1,4 @@ +INIT Init +NEXT Next +PROPERTY Correct +CONSTANT overloadedConst = 4711 diff --git a/tlatools/test-model/Github362.tla b/tlatools/test-model/Github362.tla new file mode 100644 index 0000000000000000000000000000000000000000..058242a52c374c77939eb7488c25482bde189be3 --- /dev/null +++ b/tlatools/test-model/Github362.tla @@ -0,0 +1,34 @@ +---- CONFIG Github362 ---- +INIT Init +NEXT Next +PROPERTY Correct +CONSTANT overloadedConst = 4711 +==== + +---- MODULE Github362 ---- + +EXTENDS TLC + +CONSTANT overloadedConst + +VARIABLES overloadedName + +\* Github362B.tla contains the definition "overloadedName == x". It is +\* unrelated to the variable "overloadedName" defined above. +Abstract == INSTANCE Github362B WITH + x <- "x", + C <- 42 + +Init == + /\ overloadedName = "fizzbuzz" + /\ overloadedConst = 4711 + /\ Print(<<"Evaluated initial state in A; overloadedName is: ", overloadedName>>, TRUE) \* fizzbuzz + /\ Print(<<"From A's perspective, B's overloadedName is: ", Abstract!overloadedName>>, TRUE) \* x + /\ Print(<<"Evaluated initial state in A; overloadedConst is: ", overloadedConst>>, TRUE) \* 4711 + /\ Print(<<"From A's perspective, B's overloadedConst is: ", Abstract!overloadedConst>>, TRUE) \* 42 + +Next == UNCHANGED overloadedName + +Correct == Abstract!Spec + +================== diff --git a/tlatools/test-model/Github362B.tla b/tlatools/test-model/Github362B.tla new file mode 100644 index 0000000000000000000000000000000000000000..9a5e0e20870709f16145a4ce9a2bd4f6a8a83748 --- /dev/null +++ b/tlatools/test-model/Github362B.tla @@ -0,0 +1,29 @@ +---- MODULE Github362B ---- + +\* Auxiliary spec instantiated by Github362.tla. + +EXTENDS TLC + +CONSTANT C + +VARIABLES x + +\* This name also exists in Github362.tla with a different definition (it is a +\* variable rather than a user-defined operator). The definition in Github362 +\* should not conflict with this one. +overloadedName == x + +overloadedConst == C + +Init == + /\ Print(<<"Evaluating initial state in B; overloadedName is ", overloadedName>>, TRUE) \* x + /\ overloadedName = "x" \* overloadedName -> variable x -> value "x" + /\ x = "x" + /\ Print(<<"Evaluating initial state in B; overloadedConst is ", overloadedConst>>, TRUE) \* 42 + /\ overloadedConst = 42 \* overloadedConst -> const C -> 42 + /\ C = 42 + +Next == UNCHANGED x +Spec == Init /\ [][Next]_x + +================== diff --git a/tlatools/test-model/Github391.cfg b/tlatools/test-model/Github391.cfg new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/tlatools/test-model/Github391.tla b/tlatools/test-model/Github391.tla new file mode 100644 index 0000000000000000000000000000000000000000..896130f8d9e5fb75ef86f4318c5eb95f916bbbec --- /dev/null +++ b/tlatools/test-model/Github391.tla @@ -0,0 +1,10 @@ +---- MODULE Github391 ---- +EXTENDS Integers + +\* IntValue +x == {1157660672, -989822976} +y == {-989822976, 1157660672} + +ASSUME x = y + +============================================================================= diff --git a/tlatools/test-model/Github407.cfg b/tlatools/test-model/Github407.cfg new file mode 100644 index 0000000000000000000000000000000000000000..3662e06f049e7c9069b5137ddfc038d9eb799c36 --- /dev/null +++ b/tlatools/test-model/Github407.cfg @@ -0,0 +1,4 @@ +INIT +Init +NEXT +Next \ No newline at end of file diff --git a/tlatools/test-model/Github407.tla b/tlatools/test-model/Github407.tla new file mode 100644 index 0000000000000000000000000000000000000000..9e46fcdabd7f28edff63943883f0a1f1397f3b83 --- /dev/null +++ b/tlatools/test-model/Github407.tla @@ -0,0 +1,9 @@ +---- MODULE Github407 ---- +Data == {"d1", "d2"} + +VARIABLES state + +Init == state = {} + +Next == \E e \in Data: state' = state \union {e} +============================================================================= diff --git a/tlatools/test-model/Github429.tla b/tlatools/test-model/Github429.tla new file mode 100644 index 0000000000000000000000000000000000000000..8ad0cd5b51170b7d00c1998a5979b9f1e1957d3c --- /dev/null +++ b/tlatools/test-model/Github429.tla @@ -0,0 +1,14 @@ +---- MODULE Github429 ---- + +---- MODULE Alpha ---- + +====================== + +---- MODULE Beta ---- +EXTENDS Alpha + + +VARIABLE y +====================== + +================================== diff --git a/tlatools/test-model/Github432.cfg b/tlatools/test-model/Github432.cfg new file mode 100644 index 0000000000000000000000000000000000000000..f7daf023446246482952679ff0fba218fdae092f --- /dev/null +++ b/tlatools/test-model/Github432.cfg @@ -0,0 +1,8 @@ +CONSTANT Humans = {%1%} +CONSTANT Others = {%2%} + +INIT Init +NEXT Next +SYMMETRY symmetry +INVARIANT TypeOK + diff --git a/tlatools/test-model/Github432.tla b/tlatools/test-model/Github432.tla new file mode 100644 index 0000000000000000000000000000000000000000..33e33e33fe7db82d0b0f7f9a6ab3c06605d98202 --- /dev/null +++ b/tlatools/test-model/Github432.tla @@ -0,0 +1,22 @@ +---- MODULE Github432 ---- + +EXTENDS TLC + +CONSTANT Humans, Others + +symmetry == Permutations(Humans) \union Permutations(Others) + +VARIABLES set + +TypeOK == + /\ set \subseteq Humans + +Init == + /\ set = {} + +Next == + \E h \in Humans: + set' = set \union {h} + +================================== + diff --git a/tlatools/test-model/Github461.cfg b/tlatools/test-model/Github461.cfg new file mode 100644 index 0000000000000000000000000000000000000000..e5a3896997d57e27da8ca619e152212920e137d1 --- /dev/null +++ b/tlatools/test-model/Github461.cfg @@ -0,0 +1,4 @@ +INIT +Init +NEXT +Next diff --git a/tlatools/test-model/Github461.tla b/tlatools/test-model/Github461.tla new file mode 100644 index 0000000000000000000000000000000000000000..58beab5b7e764bbfe3bbae7bf902923eec77a7b8 --- /dev/null +++ b/tlatools/test-model/Github461.tla @@ -0,0 +1,12 @@ +----------------------------- MODULE Github461 ----------------------------- +EXTENDS Integers, TLC + +VARIABLES x + +Init == x = 0 + +Next == + /\ Assert(x < 4, "Failure of assertion at line 8, column 4.") + /\ x' = x + 1 + +==== diff --git a/tlatools/test-model/Github798I.tla b/tlatools/test-model/Github798I.tla new file mode 100644 index 0000000000000000000000000000000000000000..91cf9e095a5a0141aea4b8d053f21fc03e608669 --- /dev/null +++ b/tlatools/test-model/Github798I.tla @@ -0,0 +1,31 @@ +---------------------------- MODULE Github798I ---------------------------- +S == {"n1", "n2"} + +Direct(xx, yy) == + /\ xx \in [S -> {"a"}] + /\ yy \in [S -> S] + /\ \A s \in S : + xx[s] = "a" => yy[s] = s + +Indirect(xx, yy) == + /\ Direct(xx, yy) + +InIndirect(xx, yy) == + LET zz == xx IN + Indirect(zz, yy) + +VARIABLE x, y + +vars == <<x, y>> + +Init == + InIndirect(x, y) + +Spec == + Init /\ [][InIndirect(x', y')]_vars + +========================================================================== +---------------------------- CONFIG Github798I ----------------------------- +SPECIFICATION + Spec +=========================================================================== diff --git a/tlatools/test-model/Github798N.tla b/tlatools/test-model/Github798N.tla new file mode 100644 index 0000000000000000000000000000000000000000..08171ccc799143aabeac22a0eb09eca0b6dbb636 --- /dev/null +++ b/tlatools/test-model/Github798N.tla @@ -0,0 +1,35 @@ +---------------------------- MODULE Github798N --------------------------- +S == {"n1", "n2"} + +Direct(xx, yy) == + /\ xx \in [S -> {"a"}] + /\ yy \in [S -> S] + /\ \A s \in S : + xx[s] = "a" => yy[s] = s + +Indirect(xx, yy) == + /\ Direct(xx, yy) + +InIndirect(xx, yy) == + LET zz == xx IN + Indirect(zz, yy) + +VARIABLE x, y + +vars == <<x, y>> + +Init == + /\ x = {} + /\ y = {} + +Next == + InIndirect(x', y') + +Spec == + Init /\ [][Next]_vars + +========================================================================== +---------------------------- CONFIG Github798N ---------------------------- +SPECIFICATION + Spec +=========================================================================== diff --git a/tlatools/test-model/IllegalOperatorTest.tla b/tlatools/test-model/IllegalOperatorTest.tla new file mode 100644 index 0000000000000000000000000000000000000000..d1748f8cbd4c046eb8acf5e4ed6f6b7d94559ce5 --- /dev/null +++ b/tlatools/test-model/IllegalOperatorTest.tla @@ -0,0 +1,4 @@ +------------------------- MODULE IllegalOperatorTest ------------------------ +D(Op(_)) == Op(42) +Foo == D(D) +============================================================================= diff --git a/tlatools/test-model/IncompleteNext.cfg b/tlatools/test-model/IncompleteNext.cfg new file mode 100644 index 0000000000000000000000000000000000000000..bc224b9def594b0819de3f0b45ede63ea8213c7b --- /dev/null +++ b/tlatools/test-model/IncompleteNext.cfg @@ -0,0 +1,2 @@ +SPECIFICATION +Spec diff --git a/tlatools/test-model/IncompleteNext.tla b/tlatools/test-model/IncompleteNext.tla new file mode 100644 index 0000000000000000000000000000000000000000..8ce0581a7d9a03619b0a8c51c4f78386fbc94c84 --- /dev/null +++ b/tlatools/test-model/IncompleteNext.tla @@ -0,0 +1,7 @@ +--------------------------- MODULE IncompleteNext --------------------------- +EXTENDS Integers, TLC + +VARIABLES x, y + +Spec == (x=0) /\ (y=0) /\ [][x'=x+1]_<<x,y>> +============================================================================= diff --git a/tlatools/test-model/IncompleteNextMultipleActions.cfg b/tlatools/test-model/IncompleteNextMultipleActions.cfg new file mode 100644 index 0000000000000000000000000000000000000000..bc224b9def594b0819de3f0b45ede63ea8213c7b --- /dev/null +++ b/tlatools/test-model/IncompleteNextMultipleActions.cfg @@ -0,0 +1,2 @@ +SPECIFICATION +Spec diff --git a/tlatools/test-model/IncompleteNextMultipleActions.tla b/tlatools/test-model/IncompleteNextMultipleActions.tla new file mode 100644 index 0000000000000000000000000000000000000000..d39ea6a603226b3bc571f83b2004712c08a49472 --- /dev/null +++ b/tlatools/test-model/IncompleteNextMultipleActions.tla @@ -0,0 +1,11 @@ +--------------------------- MODULE IncompleteNextMultipleActions --------------------------- +EXTENDS Integers, TLC + +VARIABLES x, y, z + +A1 == x'= x + 1 + +A2 == x'= 1 /\ y' = 1 /\ z' = 1 + +Spec == (x=0) /\ (y=0) /\ (z=0) /\ [][A1 \/ A2]_<<x,y,z>> +============================================================================= diff --git a/tlatools/test-model/LivenessConstraintWarning.cfg b/tlatools/test-model/LivenessConstraintWarning.cfg new file mode 100644 index 0000000000000000000000000000000000000000..b5dd13e7e5bd487b690bde48224c3aa13061a285 --- /dev/null +++ b/tlatools/test-model/LivenessConstraintWarning.cfg @@ -0,0 +1,8 @@ +CONSTRAINT +Constraint + +SPECIFICATION +Spec + +PROPERTY +Prop \ No newline at end of file diff --git a/tlatools/test-model/LivenessConstraintWarning.tla b/tlatools/test-model/LivenessConstraintWarning.tla new file mode 100644 index 0000000000000000000000000000000000000000..69a8e6a3071d8ce2ba0b9b442924d6f54719abc7 --- /dev/null +++ b/tlatools/test-model/LivenessConstraintWarning.tla @@ -0,0 +1,24 @@ +----------------------------- MODULE LivenessConstraintWarning ----------------------------- +EXTENDS Integers, Sequences + +VARIABLES pc, history +vars == <<pc, history>> + +Init == /\ pc = "A" + /\ history = <<>> + +A == /\ pc = "A" + /\ pc' = "B" + /\ history' = history \o <<pc>> + +B == /\ pc = "B" + /\ pc' = "A" + /\ UNCHANGED history + +Spec == Init /\ [][A \/ B]_vars /\ WF_vars(A \/ B) + +Constraint == Len(history) < 3 + +Prop == <>(pc = "Done") + +============================================================================== diff --git a/tlatools/test-model/Loop/SystemLoop.cfg b/tlatools/test-model/Loop/SystemLoop.cfg new file mode 100644 index 0000000000000000000000000000000000000000..bc6955541b374c7b1e9d24f2f1380e98a172e650 --- /dev/null +++ b/tlatools/test-model/Loop/SystemLoop.cfg @@ -0,0 +1,4 @@ +SPECIFICATION +Spec +PROPERTY +Liveness \ No newline at end of file diff --git a/tlatools/test-model/Loop/SystemLoop.tla b/tlatools/test-model/Loop/SystemLoop.tla new file mode 100644 index 0000000000000000000000000000000000000000..cdb0b7a89aa75c55e659be950b49ddfbd29ab819 --- /dev/null +++ b/tlatools/test-model/Loop/SystemLoop.tla @@ -0,0 +1,23 @@ +----------------------------- MODULE SystemLoop ----------------------------- + +(* System LOOP as described by Manna & Pneuli on page 423ff *) + +VARIABLES x +vars == <<x>> + +Init == x = 0 + +One == x = 0 /\ x' = 1 +Two == x = 1 /\ x' = 2 +Three == x = 2 /\ x' = 3 +Back == x = 3 /\ x' = 0 + +Next == One \/ Two \/ Three \/ Back + +Spec == Init /\ [][Next]_vars + +SpecWeakFair == Spec /\ WF_vars(Next) + +Liveness == []<>(x=3) + +============================================================================= diff --git a/tlatools/test-model/Loop/SystemLoopWeakFair.cfg b/tlatools/test-model/Loop/SystemLoopWeakFair.cfg new file mode 100644 index 0000000000000000000000000000000000000000..b8fb187f59c22cd4118b7eac56293c021598a9ca --- /dev/null +++ b/tlatools/test-model/Loop/SystemLoopWeakFair.cfg @@ -0,0 +1,4 @@ +SPECIFICATION +SpecWeakFair +PROPERTY +Liveness \ No newline at end of file diff --git a/tlatools/test-model/MinimalSetOfInitStates.cfg b/tlatools/test-model/MinimalSetOfInitStates.cfg new file mode 100644 index 0000000000000000000000000000000000000000..e3e8c3d2505a0163440775d92531a92d4321811c --- /dev/null +++ b/tlatools/test-model/MinimalSetOfInitStates.cfg @@ -0,0 +1,2 @@ +INIT Init +NEXT Next diff --git a/tlatools/test-model/MinimalSetOfInitStates.tla b/tlatools/test-model/MinimalSetOfInitStates.tla new file mode 100644 index 0000000000000000000000000000000000000000..cd0480fa0da8661b7d71c1267dd4311172ab8110 --- /dev/null +++ b/tlatools/test-model/MinimalSetOfInitStates.tla @@ -0,0 +1,62 @@ +--------- MODULE MinimalSetOfInitStates ------------ +VARIABLE x + +\* x = 0 true because _second_ conjunct is true. +\* The allAssigned branch in Tool#getInitStates(ActionItemList, TLCState, IStateFunctor) +\* prevents TLC from generating three states with x = 0. +I1a == /\ x = 0 + /\ \/ 1 = 1 + \/ 2 = 2 + \/ 3 = 3 + +\* x = 1 true because _first_ conjunct is true. +\* The allAssigned branch in Tool#getInitStates(ActionItemList, TLCState, IStateFunctor) +\* _does not_ prevents TLC from generating three states with x = 1. +I1b == /\ \/ 1 = 1 + \/ 2 = 2 + \/ 3 = 3 + /\ x = 1 + +\* x = 2 true because _first_ conjunct is true. +\* The allAssigned branch in Tool#getInitStates(ActionItemList, TLCState, IStateFunctor) +\* _does not_ prevents TLC from generating three states with x = 2. The "~~" trick prevents +\* TLC from generating three states with x = 2 because the default case in Tool line 729 +\* simply evaluates the first conjunct without generating states. +I1c == /\ ~~(\/ 1 = 1 + \/ 2 = 2 + \/ 3 = 3) + /\ x = 2 + +\* Doesn't assign 1 bc pred is false, thus not counted as init. +I2 == /\ x = 3 + /\ \/ 1 = 2 + \/ 2 = 3 + \/ 3 = 1 + +\* Doesn't assign 1 because pred is false, +\* but counted as init because pred comes first. +I3 == /\ \/ 1 = 2 + \/ 2 = 3 + \/ 3 = 1 + /\ x = 4 + +I4 == \/ x = 5 + \/ x = 6 + /\ \/ \/ 1 = 2 + \/ 1 = 1 + \/ 2 = 3 + \/ 3 = 2 + +I5 == /\ x = 7 + +Init == \/ I1a + \/ I1b + \/ I1c + \/ I2 + \/ I3 + \/ I4 + \/ I5 + + +Next == UNCHANGED x +==================================================== diff --git a/tlatools/test-model/MinimalSetOfNextStates.cfg b/tlatools/test-model/MinimalSetOfNextStates.cfg new file mode 100644 index 0000000000000000000000000000000000000000..fd959cf27ec2f77af7c36fb2497c44c0c88b6d0e --- /dev/null +++ b/tlatools/test-model/MinimalSetOfNextStates.cfg @@ -0,0 +1,3 @@ +INIT Init +NEXT Next +INVARIANT Inv diff --git a/tlatools/test-model/MinimalSetOfNextStates.tla b/tlatools/test-model/MinimalSetOfNextStates.tla new file mode 100644 index 0000000000000000000000000000000000000000..d211a935d3b78beab2c57a059fe3a253eed9a9f2 --- /dev/null +++ b/tlatools/test-model/MinimalSetOfNextStates.tla @@ -0,0 +1,61 @@ +--------- MODULE MinimalSetOfNextStates ------------ +VARIABLE x + +Init == x = 42 + + +\* x = 0 true because _second_ conjunct is true. +\* The allAssigned branch in Tool#getInitStates(ActionItemList, TLCState, IStateFunctor) +\* prevents TLC from generating duplicate states with x = 0. +N1a == /\ x' = 0 + /\ \/ 1 = 1 + \/ 2 = 2 + \/ 3 = 3 + +\* x = 1 true because _first_ conjunct is true. +\* The allAssigned branch in Tool#getInitStates(ActionItemList, TLCState, IStateFunctor) +\* _does not_ prevents TLC from generating duplicate states with x = 1. +N1b == /\ \/ 1 = 1 + \/ 2 = 2 + \/ 3 = 3 + /\ x' = 1 + +\* x = 2 true because _first_ conjunct is true. +\* The allAssigned branch in Tool#getInitStates(ActionItemList, TLCState, IStateFunctor) +\* _does not_ prevents TLC from generating three states with x = 2. The "~~" trick prevents +\* TLC from generating duplicate states with x = 2 because Tool simply evaluates the first +\* conjunct without generating states. +N1c == /\ ~~(\/ 1 = 1 + \/ 2 = 2 + \/ 3 = 3) + /\ x' = 2 + +N2 == /\ x' = 3 \* Pred FALSE + /\ \/ 1 = 2 + \/ 2 = 3 + \/ 3 = 1 + +N3 == /\ \/ 1 = 2 \* Pred FALSE + \/ 2 = 3 + \/ 3 = 1 + /\ x' = 4 + +N4 == \/ x' = 5 + \/ x' = 6 + /\ \/ \/ 1 = 2 + \/ 1 = 1 + \/ 2 = 3 + \/ 3 = 2 + +N5 == /\ x' = 7 + +Next == \/ N1a + \/ N1b + \/ N1c + \/ N2 + \/ N3 + \/ N4 + \/ N5 + +Inv == x \in {42,0,1,2,5,6,7} +==================================================== diff --git a/tlatools/test-model/MinimumDiameter.cfg b/tlatools/test-model/MinimumDiameter.cfg new file mode 100644 index 0000000000000000000000000000000000000000..bc224b9def594b0819de3f0b45ede63ea8213c7b --- /dev/null +++ b/tlatools/test-model/MinimumDiameter.cfg @@ -0,0 +1,2 @@ +SPECIFICATION +Spec diff --git a/tlatools/test-model/MinimumDiameter.tla b/tlatools/test-model/MinimumDiameter.tla new file mode 100644 index 0000000000000000000000000000000000000000..470e11f3c8194fdfb3dd450a9de3f58773009471 --- /dev/null +++ b/tlatools/test-model/MinimumDiameter.tla @@ -0,0 +1,5 @@ +--------------------------- MODULE MinimumDiameter --------------------------- +VARIABLES x + +Spec == (x = 0) /\ [][UNCHANGED x]_x +============================================================================= diff --git a/tlatools/test-model/MissingBodyInWhile.tla b/tlatools/test-model/MissingBodyInWhile.tla new file mode 100644 index 0000000000000000000000000000000000000000..12427132e914c5a82ba8057f220f3eb0af5b9dd8 --- /dev/null +++ b/tlatools/test-model/MissingBodyInWhile.tla @@ -0,0 +1,10 @@ +------------------------------ MODULE MissingBodyInWhile ------------------------------ +(*************************************************************************** +--algorithm MissingBodyInWhile +begin + while TRUE do + end while; +end algorithm + ***************************************************************************) + +============================================================================= diff --git a/tlatools/test-model/MissingBodyInWith.tla b/tlatools/test-model/MissingBodyInWith.tla new file mode 100644 index 0000000000000000000000000000000000000000..e674a3270aac01eb55c69c8975e79d4118509f8a --- /dev/null +++ b/tlatools/test-model/MissingBodyInWith.tla @@ -0,0 +1,10 @@ +------------------------------ MODULE MissingBodyInWith ------------------------------ +(*************************************************************************** +--algorithm MissingBodyInWith +begin + with x = 0 do + end with; +end algorithm + ***************************************************************************) + +============================================================================= diff --git a/tlatools/test-model/ModuleOverwrites/ModuleOverwrites.cfg b/tlatools/test-model/ModuleOverwrites/ModuleOverwrites.cfg new file mode 100644 index 0000000000000000000000000000000000000000..07b4e01c6a7aa827151c2f43bac3e72657abe857 --- /dev/null +++ b/tlatools/test-model/ModuleOverwrites/ModuleOverwrites.cfg @@ -0,0 +1,6 @@ +INIT Init +NEXT Next +INVARIANT +InvNoModuleOverwrite +InvAsModuleOverwrite +InvAsModuleOverwriteLinear \ No newline at end of file diff --git a/tlatools/test-model/ModuleOverwrites/ModuleOverwrites.tla b/tlatools/test-model/ModuleOverwrites/ModuleOverwrites.tla new file mode 100644 index 0000000000000000000000000000000000000000..6995b9fcce0c7d5351787ea27271fd623e1adbd6 --- /dev/null +++ b/tlatools/test-model/ModuleOverwrites/ModuleOverwrites.tla @@ -0,0 +1,32 @@ +-------------------------- MODULE ModuleOverwrites -------------------------- +EXTENDS Integers, Sequences + +VARIABLES t + +Init == t = [ i \in 1..10 |-> i ] + +Next == t' = t + +----------------------------------------------------------------------------- + +noDupesOverwrite(arr, emp) == FALSE + +InvAsModuleOverwrite == noDupesOverwrite(t, -1) + +----------------------------------------------------------------------------- + +noDupesOverwriteLinear(arr, emp) == FALSE + +InvAsModuleOverwriteLinear == noDupesOverwriteLinear(t, -1) + +----------------------------------------------------------------------------- + +noDupes(arr, emp) == LET sub == SelectSeq(t, LAMBDA e: e # -1) + abs(number) == IF number < 0 THEN -1 * number ELSE number + IN IF Len(sub) < 2 THEN TRUE + ELSE \A i \in 1..(Len(sub) - 1): + \A j \in (i+1)..Len(sub): + abs(sub[i]) # abs(sub[j]) + +InvNoModuleOverwrite == noDupes(t, -1) +============================================================================= diff --git a/tlatools/test-model/PlusCalOptions.cfg b/tlatools/test-model/PlusCalOptions.cfg new file mode 100644 index 0000000000000000000000000000000000000000..79662a1bb6a95c9fe61024917806a8412009a671 --- /dev/null +++ b/tlatools/test-model/PlusCalOptions.cfg @@ -0,0 +1,23 @@ +\* CONSTANT definitions +CONSTANT +N <- const_158025887640018000 +\* INVARIANT definition +INVARIANT +TypeInvariant +Invariant +\* PROPERTY definition +PROPERTY +Termination +\* INIT definition +INIT +_SpecTEInit +\* NEXT definition +NEXT +_SpecTENext +\* Action Constraint definition +\* ACTION_CONSTRAINT +\* _SpecTEActionConstraint +CONSTANT +_TETrace <- def_ov_15802590929252000 + +\* Generated on Tue Jan 28 16:51:32 PST 2020 diff --git a/tlatools/test-model/PlusCalOptions.tla b/tlatools/test-model/PlusCalOptions.tla new file mode 100644 index 0000000000000000000000000000000000000000..30c8db874ee13bdd0b2c3488456448a58a8f8540 --- /dev/null +++ b/tlatools/test-model/PlusCalOptions.tla @@ -0,0 +1,597 @@ +@!@!@STARTMSG 1000:0 @!@!@ +Will generate a SpecTE file pair if error states are encountered. +@!@!@ENDMSG 1000 @!@!@ +@!@!@STARTMSG 2262:0 @!@!@ +TLC2 Version 2.15 of Day Month 20?? +@!@!@ENDMSG 2262 @!@!@ +@!@!@STARTMSG 2187:0 @!@!@ +Running breadth-first search Model-Checking with fp 4 and seed 5688400317107100584 with 1 worker on 12 cores with 2428MB heap and 5460MB offheap memory [pid: 81433] (Mac OS X 10.13.6 x86_64, AdoptOpenJDK 13.0.1 x86_64, OffHeapDiskFPSet, DiskStateQueue). +@!@!@ENDMSG 2187 @!@!@ +@!@!@STARTMSG 2220:0 @!@!@ +Starting SANY... +@!@!@ENDMSG 2220 @!@!@ +Parsing file /Users/loki/arbeit/microsoft/dev/tlaplus/runtime-org.lamport.tla.toolbox.product.product.product/QueensPluscal_1579286757638/QueensPluscal.toolbox/FourQueens/MC.tla +Parsing file /Users/loki/arbeit/microsoft/dev/tlaplus/runtime-org.lamport.tla.toolbox.product.product.product/QueensPluscal_1579286757638/QueensPluscal.toolbox/FourQueens/QueensPluscal.tla +Parsing file /Users/loki/arbeit/microsoft/dev/quaeler_repo/tlaplus/tlatools/class/tla2sany/StandardModules/TLC.tla +Parsing file /Users/loki/arbeit/microsoft/dev/quaeler_repo/tlaplus/tlatools/class/tla2sany/StandardModules/Naturals.tla +Parsing file /Users/loki/arbeit/microsoft/dev/quaeler_repo/tlaplus/tlatools/class/tla2sany/StandardModules/Sequences.tla +Parsing file /Users/loki/arbeit/microsoft/dev/quaeler_repo/tlaplus/tlatools/class/tla2sany/StandardModules/FiniteSets.tla +Semantic processing of module Naturals +Semantic processing of module Sequences +Semantic processing of module QueensPluscal +Semantic processing of module FiniteSets +Semantic processing of module TLC +Semantic processing of module MC +@!@!@STARTMSG 2219:0 @!@!@ +SANY finished. +@!@!@ENDMSG 2219 @!@!@ +@!@!@STARTMSG 2185:0 @!@!@ +Starting... (2020-01-28 16:51:31) +@!@!@ENDMSG 2185 @!@!@ +@!@!@STARTMSG 2212:0 @!@!@ +Implied-temporal checking--satisfiability problem has 1 branches. +@!@!@ENDMSG 2212 @!@!@ +@!@!@STARTMSG 2189:0 @!@!@ +Computing initial states... +@!@!@ENDMSG 2189 @!@!@ +@!@!@STARTMSG 2190:0 @!@!@ +Finished computing initial states: 1 distinct state generated at 2020-01-28 16:51:32. +@!@!@ENDMSG 2190 @!@!@ +@!@!@STARTMSG 2111:1 @!@!@ +Evaluating invariant Invariant failed. +Attempted to apply function: +<<1, 1, 1>> +to argument 0, which is not in the domain of the function. +@!@!@ENDMSG 2111 @!@!@ +@!@!@STARTMSG 2121:1 @!@!@ +The behavior up to this point is: +@!@!@ENDMSG 2121 @!@!@ +@!@!@STARTMSG 2217:4 @!@!@ +1: <Initial predicate> +/\ todo = {<<>>} +/\ sols = {} +/\ pc = "nxtQ" + +@!@!@ENDMSG 2217 @!@!@ +@!@!@STARTMSG 2217:4 @!@!@ +2: <nxtQ line 77, col 9 to line 91, col 48 of module QueensPluscal> +/\ todo = {<<1>>, <<2>>, <<3>>} +/\ sols = {} +/\ pc = "nxtQ" + +@!@!@ENDMSG 2217 @!@!@ +@!@!@STARTMSG 2217:4 @!@!@ +3: <nxtQ line 77, col 9 to line 91, col 48 of module QueensPluscal> +/\ todo = {<<2>>, <<3>>, <<1, 3>>} +/\ sols = {} +/\ pc = "nxtQ" + +@!@!@ENDMSG 2217 @!@!@ +@!@!@STARTMSG 2217:4 @!@!@ +4: <nxtQ line 77, col 9 to line 91, col 48 of module QueensPluscal> +/\ todo = {<<3>>, <<1, 3>>} +/\ sols = {} +/\ pc = "nxtQ" + +@!@!@ENDMSG 2217 @!@!@ +@!@!@STARTMSG 2217:4 @!@!@ +5: <nxtQ line 77, col 9 to line 91, col 48 of module QueensPluscal> +/\ todo = {<<1, 3>>, <<3, 1>>} +/\ sols = {} +/\ pc = "nxtQ" + +@!@!@ENDMSG 2217 @!@!@ +@!@!@STARTMSG 2217:4 @!@!@ +6: <nxtQ line 77, col 9 to line 91, col 48 of module QueensPluscal> +/\ todo = {<<3, 1>>} +/\ sols = {} +/\ pc = "nxtQ" + +@!@!@ENDMSG 2217 @!@!@ +@!@!@STARTMSG 2217:4 @!@!@ +7: <nxtQ line 77, col 9 to line 91, col 48 of module QueensPluscal> +/\ todo = {} +/\ sols = {} +/\ pc = "nxtQ" + +@!@!@ENDMSG 2217 @!@!@ +@!@!@STARTMSG 2201:0 @!@!@ +The coverage statistics at 2020-01-28 16:51:32 +@!@!@ENDMSG 2201 @!@!@ +@!@!@STARTMSG 2773:0 @!@!@ +<Init line 72, col 1 to line 72, col 4 of module QueensPluscal>: 2:2 +@!@!@ENDMSG 2773 @!@!@ +@!@!@STARTMSG 2221:0 @!@!@ + line 73, col 9 to line 75, col 22 of module QueensPluscal: 2 +@!@!@ENDMSG 2221 @!@!@ +@!@!@STARTMSG 2772:0 @!@!@ +<nxtQ line 77, col 1 to line 77, col 4 of module QueensPluscal>: 18:45 +@!@!@ENDMSG 2772 @!@!@ +@!@!@STARTMSG 2221:0 @!@!@ + line 77, col 12 to line 77, col 22 of module QueensPluscal: 45 +@!@!@ENDMSG 2221 @!@!@ +@!@!@STARTMSG 2221:0 @!@!@ + |line 77, col 12 to line 77, col 13 of module QueensPluscal: 23 +@!@!@ENDMSG 2221 @!@!@ +@!@!@STARTMSG 2221:0 @!@!@ + line 78, col 15 to line 78, col 23 of module QueensPluscal: 23 +@!@!@ENDMSG 2221 @!@!@ +@!@!@STARTMSG 2221:0 @!@!@ + line 84, col 35 to line 84, col 42 of module QueensPluscal: 45 +@!@!@ENDMSG 2221 @!@!@ +@!@!@STARTMSG 2221:0 @!@!@ + line 85, col 42 to line 85, col 64 of module QueensPluscal: 16 +@!@!@ENDMSG 2221 @!@!@ +@!@!@STARTMSG 2221:0 @!@!@ + |line 85, col 50 to line 85, col 64 of module QueensPluscal: 17 +@!@!@ENDMSG 2221 @!@!@ +@!@!@STARTMSG 2221:0 @!@!@ + line 86, col 42 to line 86, col 67 of module QueensPluscal: 16 +@!@!@ENDMSG 2221 @!@!@ +@!@!@STARTMSG 2221:0 @!@!@ + |line 86, col 51 to line 86, col 66 of module QueensPluscal: 17 +@!@!@ENDMSG 2221 @!@!@ +@!@!@STARTMSG 2221:0 @!@!@ + ||line 86, col 51 to line 86, col 54 of module QueensPluscal: 17 +@!@!@ENDMSG 2221 @!@!@ +@!@!@STARTMSG 2221:0 @!@!@ + ||line 86, col 63 to line 86, col 66 of module QueensPluscal: 17 +@!@!@ENDMSG 2221 @!@!@ +@!@!@STARTMSG 2775:0 @!@!@ + |||line 83, col 41 to line 83, col 73 of module QueensPluscal: 17:30 +@!@!@ENDMSG 2775 @!@!@ +@!@!@STARTMSG 2221:0 @!@!@ + ||||line 83, col 68 to line 83, col 71 of module QueensPluscal: 17 +@!@!@ENDMSG 2221 @!@!@ +@!@!@STARTMSG 2775:0 @!@!@ + |||||line 81, col 39 to line 82, col 94 of module QueensPluscal: 17:17 +@!@!@ENDMSG 2775 @!@!@ +@!@!@STARTMSG 2221:0 @!@!@ + ||||||line 81, col 54 to line 82, col 92 of module QueensPluscal: 51 +@!@!@ENDMSG 2221 @!@!@ +@!@!@STARTMSG 2221:0 @!@!@ + |||||||line 81, col 56 to line 82, col 92 of module QueensPluscal: 51 +@!@!@ENDMSG 2221 @!@!@ +@!@!@STARTMSG 2221:0 @!@!@ + ||||||||line 82, col 56 to line 82, col 92 of module QueensPluscal: 68 +@!@!@ENDMSG 2221 @!@!@ +@!@!@STARTMSG 2221:0 @!@!@ + |||||||||line 20, col 3 to line 22, col 34 of module QueensPluscal: 68 +@!@!@ENDMSG 2221 @!@!@ +@!@!@STARTMSG 2221:0 @!@!@ + ||||||||||line 20, col 6 to line 20, col 26 of module QueensPluscal: 68 +@!@!@ENDMSG 2221 @!@!@ +@!@!@STARTMSG 2221:0 @!@!@ + ||||||||||line 21, col 6 to line 21, col 34 of module QueensPluscal: 51 +@!@!@ENDMSG 2221 @!@!@ +@!@!@STARTMSG 2221:0 @!@!@ + ||||||||||line 22, col 6 to line 22, col 34 of module QueensPluscal: 34 +@!@!@ENDMSG 2221 @!@!@ +@!@!@STARTMSG 2221:0 @!@!@ + |||||||||line 82, col 65 to line 82, col 81 of module QueensPluscal: 68 +@!@!@ENDMSG 2221 @!@!@ +@!@!@STARTMSG 2221:0 @!@!@ + |||||||||line 82, col 84 to line 82, col 84 of module QueensPluscal: 68 +@!@!@ENDMSG 2221 @!@!@ +@!@!@STARTMSG 2221:0 @!@!@ + |||||||||line 82, col 87 to line 82, col 90 of module QueensPluscal: 68 +@!@!@ENDMSG 2221 @!@!@ +@!@!@STARTMSG 2221:0 @!@!@ + ||||||||line 81, col 65 to line 81, col 80 of module QueensPluscal: 51 +@!@!@ENDMSG 2221 @!@!@ +@!@!@STARTMSG 2221:0 @!@!@ + ||||||line 81, col 47 to line 81, col 50 of module QueensPluscal: 17 +@!@!@ENDMSG 2221 @!@!@ +@!@!@STARTMSG 2221:0 @!@!@ + line 87, col 42 to line 87, col 80 of module QueensPluscal: 28 +@!@!@ENDMSG 2221 @!@!@ +@!@!@STARTMSG 2221:0 @!@!@ + |line 87, col 51 to line 87, col 79 of module QueensPluscal: 28 +@!@!@ENDMSG 2221 @!@!@ +@!@!@STARTMSG 2221:0 @!@!@ + ||line 87, col 52 to line 87, col 66 of module QueensPluscal: 28 +@!@!@ENDMSG 2221 @!@!@ +@!@!@STARTMSG 2221:0 @!@!@ + ||line 87, col 76 to line 87, col 79 of module QueensPluscal: 28 +@!@!@ENDMSG 2221 @!@!@ +@!@!@STARTMSG 2775:0 @!@!@ + |||line 83, col 41 to line 83, col 73 of module QueensPluscal: 28:71 +@!@!@ENDMSG 2775 @!@!@ +@!@!@STARTMSG 2221:0 @!@!@ + ||||line 83, col 43 to line 83, col 58 of module QueensPluscal: 22 +@!@!@ENDMSG 2221 @!@!@ +@!@!@STARTMSG 2221:0 @!@!@ + ||||line 83, col 68 to line 83, col 71 of module QueensPluscal: 28 +@!@!@ENDMSG 2221 @!@!@ +@!@!@STARTMSG 2775:0 @!@!@ + |||||line 81, col 39 to line 82, col 94 of module QueensPluscal: 28:50 +@!@!@ENDMSG 2775 @!@!@ +@!@!@STARTMSG 2221:0 @!@!@ + ||||||line 81, col 54 to line 82, col 92 of module QueensPluscal: 84 +@!@!@ENDMSG 2221 @!@!@ +@!@!@STARTMSG 2221:0 @!@!@ + |||||||line 81, col 56 to line 82, col 92 of module QueensPluscal: 84 +@!@!@ENDMSG 2221 @!@!@ +@!@!@STARTMSG 2221:0 @!@!@ + ||||||||line 82, col 56 to line 82, col 92 of module QueensPluscal: 78 +@!@!@ENDMSG 2221 @!@!@ +@!@!@STARTMSG 2221:0 @!@!@ + |||||||||line 20, col 3 to line 22, col 34 of module QueensPluscal: 78 +@!@!@ENDMSG 2221 @!@!@ +@!@!@STARTMSG 2221:0 @!@!@ + ||||||||||line 20, col 6 to line 20, col 26 of module QueensPluscal: 78 +@!@!@ENDMSG 2221 @!@!@ +@!@!@STARTMSG 2221:0 @!@!@ + ||||||||||line 21, col 6 to line 21, col 34 of module QueensPluscal: 52 +@!@!@ENDMSG 2221 @!@!@ +@!@!@STARTMSG 2221:0 @!@!@ + ||||||||||line 22, col 6 to line 22, col 34 of module QueensPluscal: 35 +@!@!@ENDMSG 2221 @!@!@ +@!@!@STARTMSG 2221:0 @!@!@ + |||||||||line 82, col 65 to line 82, col 81 of module QueensPluscal: 78 +@!@!@ENDMSG 2221 @!@!@ +@!@!@STARTMSG 2221:0 @!@!@ + |||||||||line 82, col 84 to line 82, col 84 of module QueensPluscal: 78 +@!@!@ENDMSG 2221 @!@!@ +@!@!@STARTMSG 2221:0 @!@!@ + |||||||||line 82, col 87 to line 82, col 90 of module QueensPluscal: 78 +@!@!@ENDMSG 2221 @!@!@ +@!@!@STARTMSG 2221:0 @!@!@ + ||||||||line 81, col 65 to line 81, col 80 of module QueensPluscal: 84 +@!@!@ENDMSG 2221 @!@!@ +@!@!@STARTMSG 2221:0 @!@!@ + ||||||line 81, col 47 to line 81, col 50 of module QueensPluscal: 28 +@!@!@ENDMSG 2221 @!@!@ +@!@!@STARTMSG 2221:0 @!@!@ + line 88, col 42 to line 88, col 53 of module QueensPluscal: 28 +@!@!@ENDMSG 2221 @!@!@ +@!@!@STARTMSG 2221:0 @!@!@ + line 79, col 37 to line 79, col 40 of module QueensPluscal: 23 +@!@!@ENDMSG 2221 @!@!@ +@!@!@STARTMSG 2221:0 @!@!@ + line 89, col 23 to line 89, col 34 of module QueensPluscal: 44 +@!@!@ENDMSG 2221 @!@!@ +@!@!@STARTMSG 2221:0 @!@!@ + line 90, col 23 to line 90, col 34 of module QueensPluscal: 0 +@!@!@ENDMSG 2221 @!@!@ +@!@!@STARTMSG 2221:0 @!@!@ + line 91, col 23 to line 91, col 48 of module QueensPluscal: 0 +@!@!@ENDMSG 2221 @!@!@ +@!@!@STARTMSG 2772:0 @!@!@ +<Terminating line 94, col 1 to line 94, col 11 of module QueensPluscal>: 0:0 +@!@!@ENDMSG 2772 @!@!@ +@!@!@STARTMSG 2221:0 @!@!@ + line 94, col 16 to line 94, col 26 of module QueensPluscal: 16 +@!@!@ENDMSG 2221 @!@!@ +@!@!@STARTMSG 2221:0 @!@!@ + line 94, col 31 to line 94, col 44 of module QueensPluscal: 0 +@!@!@ENDMSG 2221 @!@!@ +@!@!@STARTMSG 2774:0 @!@!@ +<TypeInvariant line 105, col 1 to line 105, col 13 of module QueensPluscal> +@!@!@ENDMSG 2774 @!@!@ +@!@!@STARTMSG 2221:0 @!@!@ + line 106, col 3 to line 107, col 62 of module QueensPluscal: 19 +@!@!@ENDMSG 2221 @!@!@ +@!@!@STARTMSG 2221:0 @!@!@ + |line 106, col 6 to line 106, col 62 of module QueensPluscal: 19 +@!@!@ENDMSG 2221 @!@!@ +@!@!@STARTMSG 2221:0 @!@!@ + ||line 106, col 6 to line 106, col 32 of module QueensPluscal: 19 +@!@!@ENDMSG 2221 @!@!@ +@!@!@STARTMSG 2221:0 @!@!@ + ||line 106, col 37 to line 106, col 62 of module QueensPluscal: 19 +@!@!@ENDMSG 2221 @!@!@ +@!@!@STARTMSG 2221:0 @!@!@ + |||line 106, col 53 to line 106, col 62 of module QueensPluscal: 34 +@!@!@ENDMSG 2221 @!@!@ +@!@!@STARTMSG 2221:0 @!@!@ + |||line 106, col 46 to line 106, col 49 of module QueensPluscal: 19 +@!@!@ENDMSG 2221 @!@!@ +@!@!@STARTMSG 2221:0 @!@!@ + |line 107, col 6 to line 107, col 62 of module QueensPluscal: 19 +@!@!@ENDMSG 2221 @!@!@ +@!@!@STARTMSG 2774:0 @!@!@ +<Invariant line 111, col 1 to line 111, col 9 of module QueensPluscal> +@!@!@ENDMSG 2774 @!@!@ +@!@!@STARTMSG 2221:0 @!@!@ + line 112, col 3 to line 113, col 42 of module QueensPluscal: 19 +@!@!@ENDMSG 2221 @!@!@ +@!@!@STARTMSG 2221:0 @!@!@ + |line 112, col 6 to line 112, col 29 of module QueensPluscal: 19 +@!@!@ENDMSG 2221 @!@!@ +@!@!@STARTMSG 2221:0 @!@!@ + |line 113, col 6 to line 113, col 42 of module QueensPluscal: 19 +@!@!@ENDMSG 2221 @!@!@ +@!@!@STARTMSG 2221:0 @!@!@ + ||line 113, col 6 to line 113, col 14 of module QueensPluscal: 19 +@!@!@ENDMSG 2221 @!@!@ +@!@!@STARTMSG 2221:0 @!@!@ + ||line 113, col 19 to line 113, col 42 of module QueensPluscal: 1 +@!@!@ENDMSG 2221 @!@!@ +@!@!@STARTMSG 2202:0 @!@!@ +End of statistics. +@!@!@ENDMSG 2202 @!@!@ +@!@!@STARTMSG 2200:0 @!@!@ +Progress(7) at 2020-01-28 16:51:32: 33 states generated (968 s/min), 19 distinct states found (557 ds/min), 2 states left on queue. +@!@!@ENDMSG 2200 @!@!@ +@!@!@STARTMSG 2199:0 @!@!@ +33 states generated, 19 distinct states found, 2 states left on queue. +@!@!@ENDMSG 2199 @!@!@ +@!@!@STARTMSG 2194:0 @!@!@ +The depth of the complete state graph search is 7. +@!@!@ENDMSG 2194 @!@!@ +@!@!@STARTMSG 2268:0 @!@!@ +The average outdegree of the complete state graph is 1 (minimum is 0, the maximum 3 and the 95th percentile is 3). +@!@!@ENDMSG 2268 @!@!@ +@!@!@STARTMSG 1000:0 @!@!@ +The model check run produced error-states - we will generate the SpecTE files now. +@!@!@ENDMSG 1000 @!@!@ +@!@!@STARTMSG 1000:0 @!@!@ +The file /Users/loki/arbeit/microsoft/dev/tlaplus/runtime-org.lamport.tla.toolbox.product.product.product/QueensPluscal_1579286757638/QueensPluscal.toolbox/FourQueens/SpecTE.tla has been created. +@!@!@ENDMSG 1000 @!@!@ +@!@!@STARTMSG 1000:0 @!@!@ +The file /Users/loki/arbeit/microsoft/dev/tlaplus/runtime-org.lamport.tla.toolbox.product.product.product/QueensPluscal_1579286757638/QueensPluscal.toolbox/FourQueens/SpecTE.cfg has been created. +@!@!@ENDMSG 1000 @!@!@ +@!@!@STARTMSG 2186:0 @!@!@ +Finished in 2069ms at (2020-01-28 16:51:32) +@!@!@ENDMSG 2186 @!@!@ +---- MODULE SpecTE ---- +EXTENDS Sequences, Toolbox, TLC, Naturals + +\* +\* QueensPluscal follows +\* + +(***************************************************************************) +(* Formulation of the N-queens problem and an iterative algorithm to solve *) +(* the problem in TLA+. Since there must be exactly one queen in every row *) +(* we represent placements of queens as functions of the form *) +(* queens \in [ 1..N -> 1..N ] *) +(* where queens[i] gives the column of the queen in row i. Note that such *) +(* a function is just a sequence of length N. *) +(* We will also consider partial solutions, also represented as sequences *) +(* of length \leq N. *) +(***************************************************************************) + +CONSTANT N \** number of queens and size of the board +ASSUME N \in Nat \ {0} + +(* The following predicate determines if queens i and j attack each other + in a placement of queens (represented by a sequence as above). *) +Attacks(queens,i,j) == + \/ queens[i] = queens[j] \** same column + \/ queens[i] - queens[j] = i - j \** first diagonal + \/ queens[j] - queens[i] = i - j \** second diagonal + +(* A placement represents a (partial) solution if no two different queens + attack each other in it. *) +IsSolution(queens) == + \A i \in 1 .. Len(queens)-1 : \A j \in (i - 1) .. Len(queens) : + ~ Attacks(queens,i,j) + +(* Compute the set of solutions of the N-queens problem. *) +Solutions == { queens \in [1..N -> 1..N] : IsSolution(queens) } + +(***************************************************************************) +(* We now describe an algorithm that iteratively computes the set of *) +(* solutions of the N-queens problem by successively placing queens. *) +(* The current state of the algorithm is given by two variables: *) +(* - todo contains a set of partial solutions, *) +(* - sols contains the set of full solutions found so far. *) +(* At every step, the algorithm picks some partial solution and computes *) +(* all possible extensions by the next queen. If N queens have been placed *) +(* these extensions are in fact full solutions, otherwise they are added *) +(* to the set todo. *) +(***************************************************************************) + +(* --algorithm Queens + variables + todo = { << >> }; + sols = {}; + + begin +nxtQ: while todo # {} + do + with queens \in todo, + nxtQ = Len(queens) + 1, + cols = { c \in 1..N : ~ \E i \in 1 .. Len(queens) : + Attacks( Append(queens, c), i, nxtQ ) }, + exts = { Append(queens,q) : q \in cols } + do + if (nxtQ = N) + then todo := todo \ {queens}; sols := sols \union exts; + else todo := (todo \ {queens}) \union exts; + end if; + end with; + end while; + end algorithm +*) +\* BEGIN TRANSLATION - the hash of the PCal code: PCal-2ed860b26a15ec8be61ed184eb8c9ae0 +VARIABLES todo, sols, pc + +vars == << todo, sols, pc >> + +Init == (* Global variables *) + /\ todo = { << >> } + /\ sols = {} + /\ pc = "nxtQ" + +nxtQ == /\ pc = "nxtQ" + /\ IF todo # {} + THEN /\ \E queens \in todo: + LET nxtQ == Len(queens) + 1 IN + LET cols == { c \in 1..N : ~ \E i \in 1 .. Len(queens) : + Attacks( Append(queens, c), i, nxtQ ) } IN + LET exts == { Append(queens,q) : q \in cols } IN + IF (nxtQ = N) + THEN /\ todo' = todo \ {queens} + /\ sols' = (sols \union exts) + ELSE /\ todo' = ((todo \ {queens}) \union exts) + /\ sols' = sols + /\ pc' = "nxtQ" + ELSE /\ pc' = "Done" + /\ UNCHANGED << todo, sols >> + +(* Allow infinite stuttering to prevent deadlock on termination. *) +Terminating == pc = "Done" /\ UNCHANGED vars + +Next == nxtQ + \/ Terminating + +Spec == Init /\ [][Next]_vars + +Termination == <>(pc = "Done") + +\* END TRANSLATION - the hash of the generated TLA code (remove to silence divergence warnings): TLA-fcc4c3104f960f6e4881fc76793c3cb5 + +TypeInvariant == + /\ todo \in SUBSET Seq(1 .. N) /\ \A s \in todo : Len(s) < N + /\ sols \in SUBSET Seq(1 .. N) /\ \A s \in sols : Len(s) = N + +(* The set of sols contains only solutions, and contains all solutions + when todo is empty. *) +Invariant == + /\ sols \subseteq Solutions + /\ todo = {} => Solutions \subseteq sols + +(* Assert that no solutions are ever computed so that TLC displays one *) +NoSolutions == sols = {} + +(* Add a fairness condition to ensure progress as long as todo is nonempty *) +Liveness == WF_vars(nxtQ) +LiveSpec == Spec /\ Liveness + + +\* +\* SpecTE follows +\* + + +\* CONSTANT definitions @modelParameterConstants:0N +const_158025887640018000 == +3 +---- + +\* TRACE Sub-Action definition 0 +LiveSpec_sa_0 == + ( + /\ todo = ({<<>>}) + /\ sols = ({}) + /\ pc = ("nxtQ") + /\ todo' = ({<<1>>, <<2>>, <<3>>}) + /\ sols' = ({}) + /\ pc' = ("nxtQ") + ) + +\* TRACE Sub-Action definition 1 +LiveSpec_sa_1 == + ( + /\ todo = ({<<1>>, <<2>>, <<3>>}) + /\ sols = ({}) + /\ pc = ("nxtQ") + /\ todo' = ({<<2>>, <<3>>, <<1, 3>>}) + /\ sols' = ({}) + /\ pc' = ("nxtQ") + ) + +\* TRACE Sub-Action definition 2 +LiveSpec_sa_2 == + ( + /\ todo = ({<<2>>, <<3>>, <<1, 3>>}) + /\ sols = ({}) + /\ pc = ("nxtQ") + /\ todo' = ({<<3>>, <<1, 3>>}) + /\ sols' = ({}) + /\ pc' = ("nxtQ") + ) + +\* TRACE Sub-Action definition 3 +LiveSpec_sa_3 == + ( + /\ todo = ({<<3>>, <<1, 3>>}) + /\ sols = ({}) + /\ pc = ("nxtQ") + /\ todo' = ({<<1, 3>>, <<3, 1>>}) + /\ sols' = ({}) + /\ pc' = ("nxtQ") + ) + +\* TRACE Sub-Action definition 4 +LiveSpec_sa_4 == + ( + /\ todo = ({<<1, 3>>, <<3, 1>>}) + /\ sols = ({}) + /\ pc = ("nxtQ") + /\ todo' = ({<<3, 1>>}) + /\ sols' = ({}) + /\ pc' = ("nxtQ") + ) + +\* TRACE Sub-Action definition 5 +LiveSpec_sa_5 == + ( + /\ todo = ({<<3, 1>>}) + /\ sols = ({}) + /\ pc = ("nxtQ") + /\ todo' = ({}) + /\ sols' = ({}) + /\ pc' = ("nxtQ") + ) + +\* TRACE Action Constraint definition traceExploreActionConstraint +_SpecTEActionConstraint == +<< +LiveSpec_sa_0, +LiveSpec_sa_1, +LiveSpec_sa_2, +LiveSpec_sa_3, +LiveSpec_sa_4, +LiveSpec_sa_5 +>>[TLCGet("level")] +---- + +def_ov_15802590929252000 == +<< +([todo |-> {<<>>},sols |-> {},pc |-> "nxtQ"]), +([todo |-> {<<1>>, <<2>>, <<3>>},sols |-> {},pc |-> "nxtQ"]), +([todo |-> {<<2>>, <<3>>, <<1, 3>>},sols |-> {},pc |-> "nxtQ"]), +([todo |-> {<<3>>, <<1, 3>>},sols |-> {},pc |-> "nxtQ"]), +([todo |-> {<<1, 3>>, <<3, 1>>},sols |-> {},pc |-> "nxtQ"]), +([todo |-> {<<3, 1>>},sols |-> {},pc |-> "nxtQ"]), +([todo |-> {},sols |-> {},pc |-> "nxtQ"]) +>> +---- + + +\* VARIABLE TraceExp + +\* TRACE INIT definition traceExploreInit +_SpecTEInit == + /\ todo = ( + {<<>>} + ) + /\ sols = ( + {} + ) + /\ pc = ( + "nxtQ" + ) +\* /\ TraceExp = TRUE + +---- + +\* TRACE NEXT definition traceExploreNext +_SpecTENext == +/\ \/ LiveSpec_sa_0 + \/ LiveSpec_sa_1 + \/ LiveSpec_sa_2 + \/ LiveSpec_sa_3 + \/ LiveSpec_sa_4 + \/ LiveSpec_sa_5 +\* /\ TraceExp' = TraceExp + + + +============================================================================= +\* Modification History +\* Created Tue Jan 28 16:47:56 PST 2020 by loki diff --git a/tlatools/test-model/PrintTraceRace/MC.cfg b/tlatools/test-model/PrintTraceRace/MC.cfg new file mode 100644 index 0000000000000000000000000000000000000000..99575a812c2dc63dbacd75840a606638206d29f9 --- /dev/null +++ b/tlatools/test-model/PrintTraceRace/MC.cfg @@ -0,0 +1,10 @@ +\* INIT definition +INIT +init_143958263805435000 +\* NEXT definition +NEXT +next_143958263806436000 +\* INVARIANT definition +INVARIANT +inv_143958263807437000 +\* Generated on Fri Aug 14 13:03:58 PDT 2015 \ No newline at end of file diff --git a/tlatools/test-model/PrintTraceRace/MC.tla b/tlatools/test-model/PrintTraceRace/MC.tla new file mode 100644 index 0000000000000000000000000000000000000000..d2d57c2754978f29f3831b7ac0977c7d043294cc --- /dev/null +++ b/tlatools/test-model/PrintTraceRace/MC.tla @@ -0,0 +1,16 @@ +---- MODULE MC ---- +EXTENDS PrintTraceRace, TLC + +\* INIT definition @modelBehaviorInit:0 +init_143958263805435000 == +TestInit +---- +\* NEXT definition @modelBehaviorNext:0 +next_143958263806436000 == +TestNext +---- +\* INVARIANT definition @modelCorrectnessInvariants:0 +inv_143958263807437000 == +TestTypeInv +---- +============================================================================= diff --git a/tlatools/test-model/PrintTraceRace/PrintTraceRace.tla b/tlatools/test-model/PrintTraceRace/PrintTraceRace.tla new file mode 100644 index 0000000000000000000000000000000000000000..671671164094477aa0481a60d452b820f7f13826 --- /dev/null +++ b/tlatools/test-model/PrintTraceRace/PrintTraceRace.tla @@ -0,0 +1,19 @@ +---------------------- MODULE PrintTraceRace ------------------------- +EXTENDS Naturals, Sequences +VARIABLES S + +TestTypeInv == S \in [ q: Seq({1..2}), + i: 0..3] + +TestInit == S = [q |-> << >>, + i |-> 1] + +Send == /\ (S.i \leq 2) + /\ (S' = [S EXCEPT !.i = (S.i + 1),!.q = Append(S.q,S.i)]) + +Done == /\ S.i > 2 + /\ UNCHANGED << S >> + +TestNext == \/ Send + \/ Done +============================================================ \ No newline at end of file diff --git a/tlatools/test-model/RandomElement.cfg b/tlatools/test-model/RandomElement.cfg new file mode 100644 index 0000000000000000000000000000000000000000..c8cffbe64c3dcc8bce148218f2e7b66a7537f573 --- /dev/null +++ b/tlatools/test-model/RandomElement.cfg @@ -0,0 +1,4 @@ +SPECIFICATION +Spec +INVARIANT +Inv \ No newline at end of file diff --git a/tlatools/test-model/RandomElement.tla b/tlatools/test-model/RandomElement.tla new file mode 100644 index 0000000000000000000000000000000000000000..a6f235186e58fc39f7a4fc638b64fcfb33d2a624 --- /dev/null +++ b/tlatools/test-model/RandomElement.tla @@ -0,0 +1,17 @@ +--------------------------- MODULE RandomElement --------------------------- +EXTENDS Integers, TLC + +VARIABLE x, y + +Init == /\ x = RandomElement(1..1000) + /\ y = 0 + +Next == /\ \/ x' = RandomElement(1..1000) + \/ x' = x + /\ y' = y + 1 + + +Spec == Init /\ [][Next]_<<x,y>> + +Inv == y < 10 +============================================================================= diff --git a/tlatools/test-model/RandomElementXandY.cfg b/tlatools/test-model/RandomElementXandY.cfg new file mode 100644 index 0000000000000000000000000000000000000000..c8cffbe64c3dcc8bce148218f2e7b66a7537f573 --- /dev/null +++ b/tlatools/test-model/RandomElementXandY.cfg @@ -0,0 +1,4 @@ +SPECIFICATION +Spec +INVARIANT +Inv \ No newline at end of file diff --git a/tlatools/test-model/RandomElementXandY.tla b/tlatools/test-model/RandomElementXandY.tla new file mode 100644 index 0000000000000000000000000000000000000000..c804ce1cfa05ed5c002bfe8f7ef388aae6bc25c5 --- /dev/null +++ b/tlatools/test-model/RandomElementXandY.tla @@ -0,0 +1,16 @@ +--------------------------- MODULE RandomElementXandY --------------------------- +EXTENDS Integers, TLC + +VARIABLE x, y + +Init == /\ x = 0 + /\ y = 0 + +Next == /\ x' = RandomElement(0..1) + /\ y' = RandomElement(0..1) + + +Spec == Init /\ [][Next]_<<x,y>> + +Inv == x = y +============================================================================= diff --git a/tlatools/test-model/RandomSubset.cfg b/tlatools/test-model/RandomSubset.cfg new file mode 100644 index 0000000000000000000000000000000000000000..c8cffbe64c3dcc8bce148218f2e7b66a7537f573 --- /dev/null +++ b/tlatools/test-model/RandomSubset.cfg @@ -0,0 +1,4 @@ +SPECIFICATION +Spec +INVARIANT +Inv \ No newline at end of file diff --git a/tlatools/test-model/RandomSubset.tla b/tlatools/test-model/RandomSubset.tla new file mode 100644 index 0000000000000000000000000000000000000000..b9def6f097273dfe0999f1ca51e8065a5f8082a8 --- /dev/null +++ b/tlatools/test-model/RandomSubset.tla @@ -0,0 +1,17 @@ +--------------------------- MODULE RandomSubset --------------------------- +EXTENDS Integers, Randomization + +VARIABLE x, y, z + +Init == /\ x \in RandomSubset(1001, 1..100000000) + /\ y \in RandomSubset(2, 100000000..100000010) + /\ z = TRUE + +Next == /\ UNCHANGED <<x, y>> + /\ z' = FALSE + + +Spec == Init /\ [][Next]_<<x,y,z>> + +Inv == z = TRUE +============================================================================= diff --git a/tlatools/test-model/RandomSubsetNext.cfg b/tlatools/test-model/RandomSubsetNext.cfg new file mode 100644 index 0000000000000000000000000000000000000000..c8cffbe64c3dcc8bce148218f2e7b66a7537f573 --- /dev/null +++ b/tlatools/test-model/RandomSubsetNext.cfg @@ -0,0 +1,4 @@ +SPECIFICATION +Spec +INVARIANT +Inv \ No newline at end of file diff --git a/tlatools/test-model/RandomSubsetNext.tla b/tlatools/test-model/RandomSubsetNext.tla new file mode 100644 index 0000000000000000000000000000000000000000..0feeb377e3e9287307aec25b2e898572024cbb0e --- /dev/null +++ b/tlatools/test-model/RandomSubsetNext.tla @@ -0,0 +1,15 @@ +------------------------- MODULE RandomSubsetNext ------------------------- +EXTENDS Integers, Randomization + +VARIABLE x, y + +Init == /\ x \in RandomSubset(10, 1..1000) + /\ y = 0 + +Next == /\ x'\in RandomSubset(10, 1..1000) + /\ y' = y + 1 + +Spec == Init /\ [][Next]_<<x,y>> + +Inv == y < 10 +============================================================================= diff --git a/tlatools/test-model/RandomSubsetSetOfFcns.cfg b/tlatools/test-model/RandomSubsetSetOfFcns.cfg new file mode 100644 index 0000000000000000000000000000000000000000..c8cffbe64c3dcc8bce148218f2e7b66a7537f573 --- /dev/null +++ b/tlatools/test-model/RandomSubsetSetOfFcns.cfg @@ -0,0 +1,4 @@ +SPECIFICATION +Spec +INVARIANT +Inv \ No newline at end of file diff --git a/tlatools/test-model/RandomSubsetSetOfFcns.tla b/tlatools/test-model/RandomSubsetSetOfFcns.tla new file mode 100644 index 0000000000000000000000000000000000000000..968b8467522724e0fa79b7504444686758a79309 --- /dev/null +++ b/tlatools/test-model/RandomSubsetSetOfFcns.tla @@ -0,0 +1,21 @@ +--------------------------- MODULE RandomSubsetSetOfFcns --------------------------- +EXTENDS Integers, Randomization + +VARIABLE x + +S == {1,2,3,4,5,6,7,8,9} +T == 1..10 + +\* [S->T] has 10^8 elements of which we want 1k. +\* Explicitly enumerating all 10^8 elements will +\* definitely timeout the tests. |[S -> T]| of +\* sizes exceeding Integer.MAX_VALUE are not yet +\* supported. +Init == /\ x \in RandomSubset(1000, [ S -> T ]) + +Next == /\ UNCHANGED <<x>> + +Spec == Init /\ [][Next]_<<x>> + +Inv == TRUE +============================================================================= diff --git a/tlatools/test-model/SetPredValue.cfg b/tlatools/test-model/SetPredValue.cfg new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/tlatools/test-model/SetPredValue.tla b/tlatools/test-model/SetPredValue.tla new file mode 100644 index 0000000000000000000000000000000000000000..dbdd30c5d8965f9a09a9ec0777c3e724b0f837a3 --- /dev/null +++ b/tlatools/test-model/SetPredValue.tla @@ -0,0 +1,13 @@ +---- MODULE SetPredValue ---- +EXTENDS TLC, FiniteSets, Integers + +Range(F) == { F[x] : x \in DOMAIN F } +Min(n, m) == IF n < m THEN n ELSE m + +LP(S, T, p) == LET ss == { s \in SUBSET S : Cardinality(s) = p} + IN { e \in [ T -> ss ] : + Cardinality(Range(e)) = Min(Cardinality(T), Cardinality(ss)) } + +ASSUME PrintT(LP(1..4, {"a","b","c"}, 2)) + +============================= diff --git a/tlatools/test-model/StandardModules.cfg b/tlatools/test-model/StandardModules.cfg new file mode 100644 index 0000000000000000000000000000000000000000..647db03515f896fda377c6760f9db2bb57cc380c --- /dev/null +++ b/tlatools/test-model/StandardModules.cfg @@ -0,0 +1,2 @@ +INIT Init +NEXT Next \ No newline at end of file diff --git a/tlatools/test-model/StandardModules.tla b/tlatools/test-model/StandardModules.tla new file mode 100644 index 0000000000000000000000000000000000000000..b1316d0c1e7521d9eb90b9ac3cb6409fd5c3bcfa --- /dev/null +++ b/tlatools/test-model/StandardModules.tla @@ -0,0 +1,6 @@ +------------------------------- MODULE StandardModules ------------------------------- +EXTENDS Bags, Sequences, FiniteSets, TLC, RealTime, Naturals, Integers, Randomization +VARIABLES x +Init == FALSE /\ x = 0 +Next == [][FALSE /\ x' = x]_<<x>> +============================================================================ diff --git a/tlatools/test-model/SubsetEq.cfg b/tlatools/test-model/SubsetEq.cfg new file mode 100644 index 0000000000000000000000000000000000000000..c8cffbe64c3dcc8bce148218f2e7b66a7537f573 --- /dev/null +++ b/tlatools/test-model/SubsetEq.cfg @@ -0,0 +1,4 @@ +SPECIFICATION +Spec +INVARIANT +Inv \ No newline at end of file diff --git a/tlatools/test-model/SubsetEq.tla b/tlatools/test-model/SubsetEq.tla new file mode 100644 index 0000000000000000000000000000000000000000..8f92096de78df8f2ea787cf481ef2c98248b88f4 --- /dev/null +++ b/tlatools/test-model/SubsetEq.tla @@ -0,0 +1,22 @@ +------------------------------ MODULE SubsetEq ------------------------------ +EXTENDS Integers, FiniteSets + +VARIABLE b + +Init == b = TRUE + +Next == b' = /\ (SUBSET (1..23) \subseteq SUBSET (1..42)) + /\ ~(SUBSET (1..42) \subseteq SUBSET (1..23)) + /\ ~(SUBSET (1..42) \subseteq SUBSET (2..42)) + /\ (SUBSET (2..42) \subseteq SUBSET (1..42)) + /\ (SUBSET {1,2,3} \subseteq SUBSET {1,2,3,4}) + /\ (SUBSET {} \subseteq SUBSET {}) + /\ (SUBSET {} \subseteq SUBSET {1}) + /\ ~(SUBSET {1} \subseteq SUBSET {}) + /\ (SUBSET {1} \subseteq SUBSET Int) + /\ (SUBSET {1} \subseteq SUBSET Nat) + +Spec == Init /\ [][Next]_<<b>> + +Inv == b = TRUE /\ b \in BOOLEAN +============================================================================= diff --git a/tlatools/test-model/TLCGetLevel.cfg b/tlatools/test-model/TLCGetLevel.cfg new file mode 100644 index 0000000000000000000000000000000000000000..774452b7125ec39c1e777f0bb0bb59fa54527b34 --- /dev/null +++ b/tlatools/test-model/TLCGetLevel.cfg @@ -0,0 +1,6 @@ +INIT +Init +NEXT +Next +PROPERTY +Prop diff --git a/tlatools/test-model/TLCGetLevel.tla b/tlatools/test-model/TLCGetLevel.tla new file mode 100644 index 0000000000000000000000000000000000000000..e2522d3c6d17fb40bf8d27a81b8c4f276f171292 --- /dev/null +++ b/tlatools/test-model/TLCGetLevel.tla @@ -0,0 +1,16 @@ +--------------------------- MODULE TLCGetLevel ---------------------------- +EXTENDS Integers, TLC + +VARIABLES x, y + +ASSUME(TLCGet("level") = 0) + +Init == /\ x = 0 + /\ y = TLCGet("level") + +Next == /\ x < 3 + /\ x' = x + 1 + /\ y' = TLCGet("level") + +Prop == <>[](x = 2) +============================================================================= diff --git a/tlatools/test-model/TLCGetNamedUndefined.cfg b/tlatools/test-model/TLCGetNamedUndefined.cfg new file mode 100644 index 0000000000000000000000000000000000000000..d4ca8dc341ceb40f8d5582f9bc0e52ff7ef99ce3 --- /dev/null +++ b/tlatools/test-model/TLCGetNamedUndefined.cfg @@ -0,0 +1,5 @@ +CONSTANTS +A <- diameter +B <- distinct +C <- queue +D <- level diff --git a/tlatools/test-model/TLCGetNamedUndefined.tla b/tlatools/test-model/TLCGetNamedUndefined.tla new file mode 100644 index 0000000000000000000000000000000000000000..854da116c482dac2e94ac6d2cf9f7c340963c4be --- /dev/null +++ b/tlatools/test-model/TLCGetNamedUndefined.tla @@ -0,0 +1,13 @@ +------------------------ MODULE TLCGetNamedUndefined ------------------------ +EXTENDS TLC + +diameter == TLCGet("diameter") +distinct == TLCGet("distinct") +queue == TLCGet("queue") +level == TLCGet("level") + +CONSTANT A,B,C,D + +ASSUME(TRUE) + +============================================================================= diff --git a/tlatools/test-model/TLCGetNonDeterminism.cfg b/tlatools/test-model/TLCGetNonDeterminism.cfg new file mode 100644 index 0000000000000000000000000000000000000000..774452b7125ec39c1e777f0bb0bb59fa54527b34 --- /dev/null +++ b/tlatools/test-model/TLCGetNonDeterminism.cfg @@ -0,0 +1,6 @@ +INIT +Init +NEXT +Next +PROPERTY +Prop diff --git a/tlatools/test-model/TLCGetNonDeterminism.tla b/tlatools/test-model/TLCGetNonDeterminism.tla new file mode 100644 index 0000000000000000000000000000000000000000..5dcd2adcd3e17249022b33e045120fb64648e206 --- /dev/null +++ b/tlatools/test-model/TLCGetNonDeterminism.tla @@ -0,0 +1,48 @@ +--------------------------- MODULE TLCGetNonDeterminism ---------------------------- +EXTENDS Integers, TLC + +\* A monotonic counter: + +VARIABLES x,y + +Init == /\ TLCSet(1, 0) + /\ x = 0 + /\ y = TLCGet(1) + +\* Replacing "TLCGet(1) + 1" with "x'" does not trigger the TLC bug. +\* "x'" is clearly defined but TLCGet(1) returns the local value of +\* a non-deterministically chosen worker from the set of TLC workers +\* (the local values are inconsistent and depend on worker threading). +\* +\* In other words, this next-state relation does not correspond to a +\* single behavior <x=1,y=1>, <x=2,y=2>, <x=3,y=3> but to a set of +\* behaviors where the value of y \in 1..3 depends on the actual +\* worker schedule. E.g. with three workers, the behavior might be: +\* <x=1,y=1-a>, <x=2,y=1-b>, <x=3,y=1-c> with 1-a being the local value +\* of worker a, 1-b the local value of worker b, ... +Next == /\ x < 3 + /\ TLCSet(1, TLCGet(1) + 1) + /\ x' = x + 1 + /\ y' = TLCGet(1) + +\* This bug surfaces at a later stage, when TLC re-creates the error +\* trace for the violation of Prop below: +\* Error: TLC threw an unexpected exception. +\* This was probably caused by an error in the spec or model. +\* The error occurred when TLC was checking liveness. +\* The exception was a tlc2.tool.EvalException +\* : Failed to recover the next state from its fingerprint. +\* +\* During error trace re-creation (where TLC re-creates the actual states +\* from a path in the fingerprint graph, TLC always runs with a single worker. +\* Consequently, it always create the expected <x=1,y=1>, <x=2,y=2>, <x=3,y=3> +\* behavior. This behavior however gets rejected because the fingerprints +\* in the path do not match the fingerprint of the re-created states. + +------------------------------------------------------------------------------ + +\* Force an arbitrary liveness violation by allowing +\* x to go up to 3 but expect x to remain -lt 3 forever. +Prop == <>[](x < 3) + +============================================================================= diff --git a/tlatools/test-model/TLCSet.cfg b/tlatools/test-model/TLCSet.cfg new file mode 100644 index 0000000000000000000000000000000000000000..bc224b9def594b0819de3f0b45ede63ea8213c7b --- /dev/null +++ b/tlatools/test-model/TLCSet.cfg @@ -0,0 +1,2 @@ +SPECIFICATION +Spec diff --git a/tlatools/test-model/TLCSet.tla b/tlatools/test-model/TLCSet.tla new file mode 100644 index 0000000000000000000000000000000000000000..f9008c735ddf4dbe5ac5c7fc397f89b82722035f --- /dev/null +++ b/tlatools/test-model/TLCSet.tla @@ -0,0 +1,20 @@ +--------------------------- MODULE TLCSet --------------------------- +EXTENDS Integers, TLC + +VARIABLES x + +Init == /\ x = 0 + /\ TLCGet("diameter") = 1 + /\ TLCGet("queue") = 0 + /\ TLCGet("distinct") = 0 + /\ TLCGet("duration") >= 0 \* Init might take more than a seconds since startup. + +Next == /\ x' = x + 1 + /\ TLCGet("distinct") = x + 1 + /\ TLCGet("diameter") = x + 1 \* As byproduct check that trace is strictly monotonically increasing. + /\ TLCGet("queue") = 0 \* queue is always empty because spec is a single behavior. + /\ TLCGet("duration") >= 0 \* Next might evaluate within the first second of model checking. + /\ TLCSet("exit", x = 4223) + +Spec == Init /\ [][Next]_x +============================================================================= diff --git a/tlatools/test-model/TLCSetInit.cfg b/tlatools/test-model/TLCSetInit.cfg new file mode 100644 index 0000000000000000000000000000000000000000..e5a3896997d57e27da8ca619e152212920e137d1 --- /dev/null +++ b/tlatools/test-model/TLCSetInit.cfg @@ -0,0 +1,4 @@ +INIT +Init +NEXT +Next diff --git a/tlatools/test-model/TLCSetInit.tla b/tlatools/test-model/TLCSetInit.tla new file mode 100644 index 0000000000000000000000000000000000000000..20fd8cd5bb4379ba5960037ea42b0e734d7ebb5f --- /dev/null +++ b/tlatools/test-model/TLCSetInit.tla @@ -0,0 +1,9 @@ +----- MODULE TLCSetInit ----- +EXTENDS Integers, TLC + +VARIABLES x + +Init == x = 1 /\ TLCSet(1,"TLCSet works.") +Next == x < 10 /\ x' = x + 1 /\ PrintT(TLCGet(1)) + +===== \ No newline at end of file diff --git a/tlatools/test-model/TSnapShot/MC.cfg b/tlatools/test-model/TSnapShot/MC.cfg new file mode 100644 index 0000000000000000000000000000000000000000..445d96f9cfbddc7d9ae4d51d67bff194b463e7e8 --- /dev/null +++ b/tlatools/test-model/TSnapShot/MC.cfg @@ -0,0 +1,25 @@ +\* MV CONSTANT declarations +CONSTANTS +w1 = w1 +\* MV CONSTANT declarations +CONSTANTS +r1 = r1 +\* MV CONSTANT definitions +CONSTANT +Writer <- const_1446808092434108000 +\* MV CONSTANT definitions +CONSTANT +Reader <- const_1446808092444109000 +\* CONSTANT definition +CONSTANT +Nat <- def_ov_1446808092454110000 +\* CONSTRAINT definition +CONSTRAINT +constr_1446808092464111000 +\* SPECIFICATION definition +SPECIFICATION +spec_1446808092484113000 +\* INVARIANT definition +INVARIANT +inv_1446808092494114000 +\* Generated on Fri Nov 06 12:08:12 CET 2015 \ No newline at end of file diff --git a/tlatools/test-model/TSnapShot/MC.tla b/tlatools/test-model/TSnapShot/MC.tla new file mode 100644 index 0000000000000000000000000000000000000000..c1ac31b94e748c5dddf4b79cef44230878e12558 --- /dev/null +++ b/tlatools/test-model/TSnapShot/MC.tla @@ -0,0 +1,52 @@ +---- MODULE MC ---- +EXTENDS TSnapShot, TLC + +\* MV CONSTANT declarations@modelParameterConstants +CONSTANTS +w1 +---- + +\* MV CONSTANT declarations@modelParameterConstants +CONSTANTS +r1 +---- + +\* MV CONSTANT definitions Writer +const_1446808092434108000 == +{w1} +---- + +\* MV CONSTANT definitions Reader +const_1446808092444109000 == +{r1} +---- + +\* CONSTANT definition @modelParameterDefinitions:0 +def_ov_1446808092454110000 == +0..2 +---- +\* CONSTRAINT definition @modelParameterContraint:0 +constr_1446808092464111000 == +LET maxNat == CHOOSE n \in Nat : \A m \in Nat : n >= m IN +\A w \in Writer : iwriter[w] < maxNat +---- +\* Constant expression definition @modelExpressionEval +const_expr_1446808092474112000 == +(w1 :> (w1 :> (w1 :> 0)) @@ r1 :> (w1 :> (w1 :> 0))) \in [Process -> [Writer -> Memory]] +---- + +\* Constant expression ASSUME statement @modelExpressionEval +ASSUME PrintT(<<"$!@$!@$!@$!@$!",const_expr_1446808092474112000>>) +---- + +\* SPECIFICATION definition @modelBehaviorSpec:0 +spec_1446808092484113000 == +Spec +---- +\* INVARIANT definition @modelCorrectnessInvariants:0 +inv_1446808092494114000 == +TypeOK2 +---- +============================================================================= +\* Modification History +\* Created Fri Nov 06 12:08:12 CET 2015 by markus diff --git a/tlatools/test-model/TSnapShot/TSnapShot.tla b/tlatools/test-model/TSnapShot/TSnapShot.tla new file mode 100644 index 0000000000000000000000000000000000000000..79e914c22c23ad5daf27907f7812e591214342a8 --- /dev/null +++ b/tlatools/test-model/TSnapShot/TSnapShot.tla @@ -0,0 +1,170 @@ +----------------------------- MODULE TSnapShot ----------------------------- +(**************************************************************************** +This spec describes an algorithm by Afek et al. described in + + https://en.wikipedia.org/wiki/Shared_snapshot_objects + +This is the unbounded memory algorithm described there. + +This implementation satisfies the TSnapSpec spec, with internal +variables hidden. +****************************************************************************) +EXTENDS Integers, TLC + +CONSTANTS Reader, Writer + +ASSUME Reader \cap Writer = { } + +Process == Reader \cup Writer +(***************************************************************************) +(* To make it easier to modify the spec to one in which processes write *) +(* values in some arbitrary set, we write `Value' instead of `Nat'. *) +(***************************************************************************) +Value == Nat +Memory == [Writer -> Value] +InitMemory == [w \in Writer |-> 0] +NotAMemory == [w \in Writer |-> -1] + +VARIABLES + rstate, wstate, \* These variables are the same as in TSnapSpec, since + \* they describe the externally visible state. + rwstate, \* Like rstate, except has a copy for the writer's state + \* of reading as well + mem, iwriter, \* The same as in TSnapSpec + snap, \* snap[w] is the last snapshot value obtained by + \* writer w during its read. + \* The following are arrays indexed by Reader \cup Writer, since + \* both readers and writers perform a read operation. + rd1, rd2, \* Two arrays of arrays holding the values obtained in + \* the two reads. + (*view1,*) view2, \* Two arrays of arrays holding the snap obtained in + \* the second reads. + notRd1, notRd2, \* Two arrays holding the sets of writers whose values + \* haven't yet been read by each of the reads. + moved \* moved[r][w] is the number of times different + \* values of mem[w] have been read by r during its + \* current read operation. + +vars == <<rstate, wstate, rwstate, mem, iwriter, rd1, rd2, + (*view1,*) view2, notRd1, notRd2, moved, snap>> + +TypeOK == (* /\ rstate \in [Reader -> Memory \cup {NotAMemory}] + /\ wstate \in [Writer -> {"idle", "busy"}] + /\ rwstate \in [Process -> Memory \cup {NotAMemory}] + /\ mem \in Memory + /\ iwriter \in [Writer -> Nat] + /\ snap \in [Writer -> Memory] + /\ rd1 \in [Process -> Memory] + /\ rd2 \in [Process -> Memory] *) +\* /\ view1 \in [Process -> [Writer -> Memory]] + /\ view2 \in [Process -> [Writer -> Memory]] +(* /\ notRd1 \in [Process -> SUBSET Writer] + /\ notRd2 \in [Process -> SUBSET Writer] + /\ moved \in [Process -> [Writer -> BOOLEAN]] *) +TypeOK2 == view2 \in [Process -> [Writer -> Memory]] +Init == /\ rstate = [r \in Reader |-> InitMemory] + /\ rwstate = [r \in Process |-> InitMemory] + /\ wstate = [r \in Writer |-> "idle"] + /\ mem = InitMemory + /\ iwriter = [r \in Writer |-> 0] + /\ rd1 = [r \in Process |-> InitMemory] + /\ rd2 = [r \in Process |-> InitMemory] +\* /\ view1 = [r \in Process |-> [w \in Writer |-> Memory]] + /\ view2 = [r \in Process |-> [w \in Writer |-> InitMemory]] + /\ notRd1 = [r \in Process |-> Writer] + /\ notRd2 = [r \in Process |-> Writer] + /\ moved = [r \in Process |-> [w \in Writer |-> FALSE]] + /\ snap = [r \in Writer |-> InitMemory] + +TypeOK3 == PrintT(TypeOK2) +(***************************************************************************) +(* Define Assign(array, idx, val) to essentially mean *) +(* *) +(* array[idx] := val *) +(* *) +(* so the reader of the spec doesn't have to deal with EXCEPT. *) +(***************************************************************************) +Assign(array, idx, val) == array' = [array EXCEPT ![idx] = val] + +(***************************************************************************) +(* Define Assign(array, idx1, idx2, val) to essentially mean *) +(* *) +(* array[idx1][idx2] := val *) +(* *) +(* so the reader of the spec doesn't have to deal with EXCEPT. *) +(***************************************************************************) +AAssign(array, idx1, idx2, val) == array' = [array EXCEPT ![idx1][idx2] = val] + + +BeginRead(r) == /\ rstate[r] # NotAMemory + /\ Assign(rstate, r, NotAMemory) + /\ Assign(rwstate, r, NotAMemory) + /\ UNCHANGED <<wstate, mem, iwriter, rd1, rd2, + view2, notRd1, notRd2, moved, snap>> + +\*vars == <<rstate, wstate, rwstate, mem, iwriter, rd1, rd2, +\* (*view1,*) view2, notRd1, notRd2, moved, snap>> +DoRd1(r) == /\ rwstate[r] = NotAMemory + /\ \E w \in notRd1[r] : /\ Assign(notRd1, r, notRd1[r] \ {w}) + /\ AAssign(rd1, r, w, mem[w]) +\* /\ AAssign(view1, r, w, scan[w]) + /\ UNCHANGED <<rstate, rwstate, wstate, mem, iwriter, rd2, view2, notRd2, + moved, snap>> + +DoRd2(r) == /\ (rwstate[r] = NotAMemory) /\ (notRd1[r] = {}) + /\ \E w \in notRd2[r] : /\ Assign(notRd2, r, notRd2[r] \ {w}) + /\ AAssign(rd2, r, w, mem[w]) + /\ AAssign(view2, r, w, snap[w]) + /\ UNCHANGED <<rstate, rwstate, wstate, mem, iwriter, rd1, notRd1, + moved, snap>> + +TryEnding(r) == /\ (rwstate[r] = NotAMemory) /\ (notRd1[r] = {}) + /\ IF rd1[r] = rd2[r] + THEN Assign(rwstate, r, rd1[r]) + ELSE LET F(w) == /\ rd1[r][w] # rd2[r][w] + /\ moved[r][w] + IN IF \E w \in Writer : F(w) + THEN Assign(rwstate,r, + view2[CHOOSE w \in Writer : F(w)]) + ELSE /\ UNCHANGED rwstate + /\ moved[r] = [w \in Writer |-> + moved[r] \/ (rd1[r][w] # rd2[r][w])] + /\ IF r \in Reader + THEN Assign(rstate, r, rwstate'[r]) + ELSE UNCHANGED rstate + /\ Assign(moved, r, [w \in Writer |-> FALSE]) + /\ Assign(notRd1, r, Writer) + /\ Assign(notRd2, r, Writer) + /\ Assign(rd1, r, InitMemory) \* To reduce state space + /\ Assign(rd2, r, InitMemory) \* To reduce state space + /\ Assign(view2, r, InitMemory) \* To reduce state space + /\ UNCHANGED <<wstate, mem, snap, iwriter>> + + +BeginWrite(w) == /\ wstate[w] = "idle" + /\ Assign(wstate, w, "busy") + /\ Assign(iwriter, w, iwriter[w] + 1) + /\ Assign(rwstate, w, NotAMemory) + /\ UNCHANGED <<rstate, mem, rd1, rd2, view2, notRd1, notRd2, moved, snap>> + +DoWrite(w) == /\ iwriter[w] # mem[w] + /\ rwstate[w] # NotAMemory + /\ Assign(mem, w, mem[w]+1) + /\ Assign(snap, w, rwstate[w]) + /\ UNCHANGED <<rstate, wstate, iwriter, rd1, rd2, view2, notRd1, + notRd2, moved>> + +EndWrite(w) == /\ wstate[w] = "busy" + /\ Assign(wstate, w, "idle") + /\ UNCHANGED <<rstate, rwstate, mem, snap, iwriter, rd1, rd2, + view2, notRd1, notRd2, moved>> + +Next == \/ \E w \in Writer : BeginWrite(w) \/ DoWrite(w) \/ EndWrite(w) + \/ \E r \in Reader : + BeginRead(r) \/ DoRd1(r) \/ DoRd2(r) \/ TryEnding(r) + +Spec == Init /\ [][Next]_vars +============================================================================= +\* Modification History +\* Last modified Mon Nov 02 18:16:48 PST 2015 by lamport +\* Created Mon Nov 02 13:33:53 PST 2015 by lamport diff --git a/tlatools/test-model/Test4.cfg b/tlatools/test-model/Test4.cfg new file mode 100644 index 0000000000000000000000000000000000000000..81bbfad60f51f7a0061d092d12270f050a4ec618 --- /dev/null +++ b/tlatools/test-model/Test4.cfg @@ -0,0 +1 @@ +SPECIFICATION Spec \ No newline at end of file diff --git a/tlatools/test-model/Test4.tla b/tlatools/test-model/Test4.tla new file mode 100644 index 0000000000000000000000000000000000000000..15affe77f9f9ac7f57fda1f6555a7aff3df30cec --- /dev/null +++ b/tlatools/test-model/Test4.tla @@ -0,0 +1,8 @@ +---------- MODULE Test4 ----------- +VARIABLE x + +ASSUME TRUE = FALSE \* No, it's not! + +Spec == x = 0 /\ [][UNCHANGED x]_x + +============================================== diff --git a/tlatools/test-model/TraceWithLargeSetOfInitialStatesTest.cfg b/tlatools/test-model/TraceWithLargeSetOfInitialStatesTest.cfg new file mode 100644 index 0000000000000000000000000000000000000000..c8cffbe64c3dcc8bce148218f2e7b66a7537f573 --- /dev/null +++ b/tlatools/test-model/TraceWithLargeSetOfInitialStatesTest.cfg @@ -0,0 +1,4 @@ +SPECIFICATION +Spec +INVARIANT +Inv \ No newline at end of file diff --git a/tlatools/test-model/TraceWithLargeSetOfInitialStatesTest.tla b/tlatools/test-model/TraceWithLargeSetOfInitialStatesTest.tla new file mode 100644 index 0000000000000000000000000000000000000000..f9a90034431352c6bccbbc1544fbcec7b99f6eb2 --- /dev/null +++ b/tlatools/test-model/TraceWithLargeSetOfInitialStatesTest.tla @@ -0,0 +1,9 @@ +---------------- MODULE TraceWithLargeSetOfInitialStatesTest ---------------- +EXTENDS Integers + +VARIABLES x,y + +Spec == (x \in 1..11 /\ y = FALSE) /\ [][x' = x /\ y' = TRUE]_<<x,y>> + +Inv == y = FALSE +============================================================================= \ No newline at end of file diff --git a/tlatools/test-model/UserModuleOverride.cfg b/tlatools/test-model/UserModuleOverride.cfg new file mode 100644 index 0000000000000000000000000000000000000000..20524345c8bb1355485ae7282e49cd9a44301eca --- /dev/null +++ b/tlatools/test-model/UserModuleOverride.cfg @@ -0,0 +1,4 @@ +SPECIFICATION +Spec +PROPERTY +Prop diff --git a/tlatools/test-model/UserModuleOverride.class b/tlatools/test-model/UserModuleOverride.class new file mode 100644 index 0000000000000000000000000000000000000000..64e8fae620e506c2d8b882ee131ce78afcf729c1 Binary files /dev/null and b/tlatools/test-model/UserModuleOverride.class differ diff --git a/tlatools/test-model/UserModuleOverride.java b/tlatools/test-model/UserModuleOverride.java new file mode 100644 index 0000000000000000000000000000000000000000..6cbac2ff78480e399a26c0b66c2ddb937ef74ea3 --- /dev/null +++ b/tlatools/test-model/UserModuleOverride.java @@ -0,0 +1,18 @@ +import tlc2.value.impl.BoolValue; +import tlc2.value.impl.Value; + +// manually compiled with Java 1.8 +public class UserModuleOverride { + public static Value Get() { + return BoolValue.ValTrue; + } + + public static Value Get2(Value v1) { + return BoolValue.ValFalse; + } + + + public static Value Get3() { + return BoolValue.ValFalse; + } +} diff --git a/tlatools/test-model/UserModuleOverride.tla b/tlatools/test-model/UserModuleOverride.tla new file mode 100644 index 0000000000000000000000000000000000000000..258e5c7efc6a204aa5dc5f861cbfef0baed1d08f --- /dev/null +++ b/tlatools/test-model/UserModuleOverride.tla @@ -0,0 +1,16 @@ +--------------------------- MODULE UserModuleOverride ----------------------- +EXTENDS Naturals +VARIABLES x + +Get == FALSE + +Get2 == TRUE + +Init == x = Get + +Next == x' = Get2 + +Spec == Init /\ [][Next]_<<x>> + +Prop == [](x = TRUE) +============================================================================= diff --git a/tlatools/test-model/UserModuleOverrideAnnotation.cfg b/tlatools/test-model/UserModuleOverrideAnnotation.cfg new file mode 100644 index 0000000000000000000000000000000000000000..20524345c8bb1355485ae7282e49cd9a44301eca --- /dev/null +++ b/tlatools/test-model/UserModuleOverrideAnnotation.cfg @@ -0,0 +1,4 @@ +SPECIFICATION +Spec +PROPERTY +Prop diff --git a/tlatools/test-model/UserModuleOverrideAnnotation.tla b/tlatools/test-model/UserModuleOverrideAnnotation.tla new file mode 100644 index 0000000000000000000000000000000000000000..7e85bed2def057647cb62fe79593959cddb3be7a --- /dev/null +++ b/tlatools/test-model/UserModuleOverrideAnnotation.tla @@ -0,0 +1,15 @@ +--------------------------- MODULE UserModuleOverrideAnnotation ----------------------- +EXTENDS Naturals +VARIABLES x + +Get == FALSE +Get2 == FALSE + +Init == x = Get + +Next == x' = Get2 + +Spec == Init /\ [][Next]_<<x>> + +Prop == [](x = TRUE) +============================================================================= diff --git a/tlatools/test-model/UserModuleOverrideBase.cfg b/tlatools/test-model/UserModuleOverrideBase.cfg new file mode 100644 index 0000000000000000000000000000000000000000..20524345c8bb1355485ae7282e49cd9a44301eca --- /dev/null +++ b/tlatools/test-model/UserModuleOverrideBase.cfg @@ -0,0 +1,4 @@ +SPECIFICATION +Spec +PROPERTY +Prop diff --git a/tlatools/test-model/UserModuleOverrideBase.tla b/tlatools/test-model/UserModuleOverrideBase.tla new file mode 100644 index 0000000000000000000000000000000000000000..77b33caa16d9017b53e11b7bf81204f32d8b0bde --- /dev/null +++ b/tlatools/test-model/UserModuleOverrideBase.tla @@ -0,0 +1,12 @@ +--------------------------- MODULE UserModuleOverrideBase ----------------------- +EXTENDS Naturals, UserModuleOverrideFromJar +VARIABLES x + +Init == x = Get + +Next == x' = Get2 + +Spec == Init /\ [][Next]_<<x>> + +Prop == [](x = TRUE) +============================================================================= diff --git a/tlatools/test-model/ViewMap.cfg b/tlatools/test-model/ViewMap.cfg new file mode 100644 index 0000000000000000000000000000000000000000..96d6a36718587ae6ccaab59c90e98f3b9b069693 --- /dev/null +++ b/tlatools/test-model/ViewMap.cfg @@ -0,0 +1,24 @@ +\* MV CONSTANT declarations +CONSTANTS +c1 = c1 +c2 = c2 +\* MV CONSTANT declarations +CONSTANTS +p1 = p1 +\* MV CONSTANT definitions +CONSTANT +C = {c1, c2} +\* MV CONSTANT definitions +CONSTANT +P = {p1} +\* CONSTANT definitions +CONSTANT +K = 1 +\* VIEW definition +VIEW +view +\* SPECIFICATION definition +SPECIFICATION +Spec +INVARIANT +Inv diff --git a/tlatools/test-model/ViewMap.tla b/tlatools/test-model/ViewMap.tla new file mode 100644 index 0000000000000000000000000000000000000000..ec4eccb93d6c20e274b6699ac30fa8f5fa2e45f5 --- /dev/null +++ b/tlatools/test-model/ViewMap.tla @@ -0,0 +1,102 @@ +--------------------------- MODULE ViewMap --------------------------- +EXTENDS Integers, FiniteSets, Sequences + +CONSTANT P, \* Set of Producers + C, \* Set of Consumers + K \* Capacity of buffer/queue + +RemoveLast(seq) == [ i \in 1..(Len(seq) - 1) |-> seq[i] ] + +(* +--algorithm JBuffer01Pcal { + variables buffer = <<>>, waitset = {}; + + process (p \in P) { + lbp: while (Len(buffer) >= K) { + (* wait *) + waitset := waitset \cup {self}; + }; + buffer := Append(buffer, "d"); + (* notify *) + if (waitset # {}) { + with (x \in waitset) { + waitset := waitset \ {x}; + } + }; + goto lbp; + } + + process (c \in C) { + lbc: while (Len(buffer) = 0) { + (* wait *) + waitset := waitset \cup {self}; + }; + buffer := RemoveLast(buffer); + (* notify *) + if (waitset # {}) { + with (x \in waitset) { + waitset := waitset \ {x}; + } + }; + goto lbc; + } +} +*) +\* BEGIN TRANSLATION +VARIABLES buffer, waitset, pc + +vars == << buffer, waitset, pc >> + +ProcSet == (P) \cup (C) + +Init == (* Global variables *) + /\ buffer = <<>> + /\ waitset = {} + /\ pc = [self \in ProcSet |-> CASE self \in P -> "lbp" + [] self \in C -> "lbc"] + +lbp(self) == /\ pc[self] = "lbp" + /\ IF Len(buffer) >= K + THEN /\ waitset' = (waitset \cup {self}) + /\ pc' = [pc EXCEPT ![self] = "lbp"] + /\ UNCHANGED buffer + ELSE /\ buffer' = Append(buffer, "d") + /\ IF waitset # {} + THEN /\ \E x \in waitset: + waitset' = waitset \ {x} + ELSE /\ TRUE + /\ UNCHANGED waitset + /\ pc' = [pc EXCEPT ![self] = "lbp"] + +p(self) == lbp(self) + +lbc(self) == /\ pc[self] = "lbc" + /\ IF Len(buffer) = 0 + THEN /\ waitset' = (waitset \cup {self}) + /\ pc' = [pc EXCEPT ![self] = "lbc"] + /\ UNCHANGED buffer + ELSE /\ buffer' = RemoveLast(buffer) + /\ IF waitset # {} + THEN /\ \E x \in waitset: + waitset' = waitset \ {x} + ELSE /\ TRUE + /\ UNCHANGED waitset + /\ pc' = [pc EXCEPT ![self] = "lbc"] + +c(self) == lbc(self) + +Next == (\E self \in P: p(self)) + \/ (\E self \in C: c(self)) + \/ (* Disjunct to prevent deadlock on termination *) + ((\A self \in ProcSet: pc[self] = "Done") /\ UNCHANGED vars) + +Spec == Init /\ [][Next]_vars + +Termination == <>(\A self \in ProcSet: pc[self] = "Done") + +\* END TRANSLATION + +Inv == waitset # ProcSet + +view == <<buffer, waitset>> +============================================================================= diff --git a/tlatools/test-model/VoteProof/VoteProof.tla b/tlatools/test-model/VoteProof/VoteProof.tla index 75a9c636b5215607e5c6947c1795b6bc985b4628..518f983d1fcd66ad0caff9a504ab45eaed5c28f7 100644 --- a/tlatools/test-model/VoteProof/VoteProof.tla +++ b/tlatools/test-model/VoteProof/VoteProof.tla @@ -1,785 +1,785 @@ ------------------------------ MODULE VoteProof ------------------------------ - -(*************************************************************************) -(* !!!! REGRESSION TESTS ONLY !!!! *) -(* *) -(* This file is not meant as a reference to TLA+ in general, nor for *) -(* VoteProof in particular. Please search the web for an official *) -(* version of the VoteProof spec. *) -(* *) -(*************************************************************************) - -EXTENDS Integers , FiniteSets, TLC, TLAPS - -CONSTANT Value, \* As in module Consensus, the set of choosable values. - Acceptor, \* The set of all acceptors. - Quorum \* The set of all quorums. - -ASSUME QA == /\ \A Q \in Quorum : Q \subseteq Acceptor - /\ \A Q1, Q2 \in Quorum : Q1 \cap Q2 # {} - -THEOREM QuorumNonEmpty == \A Q \in Quorum : Q # {} -PROOF BY QA - -Ballot == Nat - -(*************************** ---algorithm Voting { - variables votes = [a \in Acceptor |-> {}], - maxBal = [a \in Acceptor |-> -1]; - define { - VotedFor(a, b, v) == <<b, v>> \in votes[a] - DidNotVoteIn(a, b) == \A v \in Value : ~ VotedFor(a, b, v) - - SafeAt(b, v) == - LET SA[bb \in Ballot] == - \/ bb = 0 - \/ \E Q \in Quorum : - /\ \A a \in Q : maxBal[a] \geq bb - /\ \E c \in -1..(bb-1) : - /\ (c # -1) => /\ SA[c] - /\ \A a \in Q : - \A w \in Value : - VotedFor(a, c, w) => (w = v) - /\ \A d \in (c+1)..(bb-1), a \in Q : DidNotVoteIn(a, d) - IN SA[b] - } - macro IncreaseMaxBal(b) { - when b > maxBal[self] ; - maxBal[self] := b - } - - macro VoteFor(b, v) { - when /\ maxBal[self] \leq b - /\ DidNotVoteIn(self, b) - /\ \A p \in Acceptor \ {self} : - \A w \in Value : VotedFor(p, b, w) => (w = v) - /\ SafeAt(b, v) ; - votes[self] := votes[self] \cup {<<b, v>>}; - maxBal[self] := b - } - - process (acceptor \in Acceptor) { - acc : while (TRUE) { - with (b \in Ballot) { - either IncreaseMaxBal(b) - or with (v \in Value) { VoteFor(b, v) } - } - } - } -} - -The following is the TLA+ specification produced by the translation. -Blank lines, produced by the translation because of the comments, have -been deleted. -****************************) -\* BEGIN TRANSLATION -VARIABLES votes, maxBal - -(* define statement *) -VotedFor(a, b, v) == <<b, v>> \in votes[a] - - - -DidNotVoteIn(a, b) == \A v \in Value : ~ VotedFor(a, b, v) - - - - - - - - - - - - - - - - - - - - - -SafeAt(b, v) == - LET SA[bb \in Ballot] == - - - - \/ bb = 0 - \/ \E Q \in Quorum : - /\ \A a \in Q : maxBal[a] \geq bb - /\ \E c \in -1..(bb-1) : - /\ (c # -1) => /\ SA[c] - /\ \A a \in Q : - \A w \in Value : - VotedFor(a, c, w) => (w = v) - /\ \A d \in (c+1)..(bb-1), a \in Q : DidNotVoteIn(a, d) - IN SA[b] - - -vars == << votes, maxBal >> - -ProcSet == (Acceptor) - -Init == (* Global variables *) - /\ votes = [a \in Acceptor |-> {}] - /\ maxBal = [a \in Acceptor |-> -1] - -acceptor(self) == \E b \in Ballot: - \/ /\ b > maxBal[self] - /\ maxBal' = [maxBal EXCEPT ![self] = b] - /\ votes' = votes - \/ /\ \E v \in Value: - /\ /\ maxBal[self] \leq b - /\ DidNotVoteIn(self, b) - /\ \A p \in Acceptor \ {self} : - \A w \in Value : VotedFor(p, b, w) => (w = v) - /\ SafeAt(b, v) - /\ votes' = [votes EXCEPT ![self] = votes[self] \cup {<<b, v>>}] - /\ maxBal' = [maxBal EXCEPT ![self] = b] - -Next == (\E self \in Acceptor: acceptor(self)) - -Spec == Init /\ [][Next]_vars - -\* END TRANSLATION - -THEOREM RecursiveFcnOfNat == - ASSUME NEW Def(_,_), - \A n \in Nat : - \A g, h : (\A i \in 0..(n-1) : g[i] = h[i]) => (Def(g, n) = Def(h, n)) - PROVE LET f[n \in Nat] == Def(f, n) - IN f = [n \in Nat |-> Def(f, n)] -PROOF OMITTED - -THEOREM SafeAtProp == - \A b \in Ballot, v \in Value : - SafeAt(b, v) = - \/ b = 0 - \/ \E Q \in Quorum : - /\ \A a \in Q : maxBal[a] \geq b - /\ \E c \in -1..(b-1) : - /\ (c # -1) => /\ SafeAt(c, v) - /\ \A a \in Q : - \A w \in Value : - VotedFor(a, c, w) => (w = v) - /\ \A d \in (c+1)..(b-1), a \in Q : DidNotVoteIn(a, d) -<1>1. SUFFICES ASSUME NEW v \in Value - PROVE \A b \in Ballot : SafeAtProp!(b, v) - OBVIOUS -<1> USE DEF Ballot -<1> DEFINE Def(SA, bb) == - \/ bb = 0 - \/ \E Q \in Quorum : - /\ \A a \in Q : maxBal[a] \geq bb - /\ \E c \in -1..(bb-1) : - /\ (c # -1) => /\ SA[c] - /\ \A a \in Q : - \A w \in Value : - VotedFor(a, c, w) => (w = v) - /\ \A d \in (c+1)..(bb-1), a \in Q : DidNotVoteIn(a, d) - SA[bb \in Ballot] == Def(SA, bb) -<1>2. \A b : SafeAt(b, v) = SA[b] - BY DEF SafeAt -<1>3. \A n \in Nat : - \A g, h : (\A i \in 0..(n-1) : g[i] = h[i]) => (Def(g, n) = Def(h, n)) - BY SMT -<1> HIDE DEF Def -<1>4. SA = [b \in Ballot |-> Def(SA, b)] - BY ONLY <1>3, RecursiveFcnOfNat -<1>5. \A b \in Ballot : SA[b] = Def(SA, b) - BY <1>4 -<1>6. QED - BY <1>2, <1>5 DEF SafeAt, Def - -TypeOK == /\ votes \in [Acceptor -> SUBSET (Ballot \X Value)] - /\ maxBal \in [Acceptor -> Ballot \cup {-1}] - -ChosenIn(b, v) == \E Q \in Quorum : \A a \in Q : VotedFor(a, b, v) - -chosen == {v \in Value : \E b \in Ballot : ChosenIn(b, v)} - -AXIOM SimpleNatInduction == \A f : /\ f[0] - /\ \A n \in Nat : f[n] => f[n+1] - => \A n \in Nat : f[n] - -THEOREM GeneralNatInduction == - \A f : /\ f[0] - /\ \A n \in Nat : (\A j \in 0..n : f[j]) => f[n+1] - => \A n \in Nat : f[n] -<1>1. SUFFICES ASSUME NEW f, - f[0], - \A m \in Nat : (\A j \in 0..m : f[j]) => f[m+1], - NEW n \in Nat - PROVE f[n] - OBVIOUS -<1> DEFINE g == [m \in Nat |-> \A j \in 0..m : f[j]] -<1>2. g[0] - BY <1>1, SMT -<1>3. ASSUME NEW k \in Nat, g[k] - PROVE g[k+1] - BY <1>1, <1>3, SMT -<1>4. \A k \in Nat : g[k] - BY <1>2, <1>3, SimpleNatInduction -<1>5. QED - BY <1>4, SMT - -LEMMA SafeLemma == - TypeOK => - \A b \in Ballot : - \A v \in Value : - SafeAt(b, v) => - \A c \in 0..(b-1) : - \E Q \in Quorum : - \A a \in Q : /\ maxBal[a] >= c - /\ \/ DidNotVoteIn(a, c) - \/ VotedFor(a, c, v) -<1> SUFFICES ASSUME TypeOK - PROVE SafeLemma!2 - OBVIOUS -<1> DEFINE P[b \in Ballot] == \A c \in 0..b : SafeLemma!2!(c) -<1>1. P[0] - BY SMT DEF Ballot -<1>2. ASSUME NEW b \in Ballot, P[b] - PROVE P[b+1] - <2>1. /\ b+1 \in Ballot - /\ (b+1) - 1 = b - BY SMT DEF Ballot - <2>2. 0..(b+1) = (0..b) \cup {b+1} - BY SMT DEF Ballot - <2>3. SUFFICES ASSUME NEW v \in Value, - SafeAt(b+1, v), - NEW c \in 0..b - PROVE \E Q \in Quorum : - \A a \in Q : /\ maxBal[a] >= c - /\ \/ DidNotVoteIn(a, c) - \/ VotedFor(a, c, v) - BY <1>2, <2>1, <2>2 - <2>4. PICK Q \in Quorum : - /\ \A a \in Q : maxBal[a] \geq (b+1) - /\ \E cc \in -1..b : - /\ (cc # -1) => /\ SafeAt(cc, v) - /\ \A a \in Q : - \A w \in Value : - VotedFor(a, cc, w) => (w = v) - /\ \A d \in (cc+1)..b, a \in Q : DidNotVoteIn(a, d) - <3>1. b+1 # 0 - BY SMT DEF Ballot - <3>2. SafeAt(b+1,v) = SafeAtProp!(b+1,v)!2 - BY SafeAtProp, <2>1 - <3>3. @ = SafeAtProp!(b+1,v)!2!2 - BY <3>1 - <3>4. @ = \E Q \in Quorum : - /\ \A a \in Q : maxBal[a] \geq (b+1) - /\ \E cc \in -1..b : - /\ (cc # -1) => /\ SafeAt(cc, v) - /\ \A a \in Q : - \A w \in Value : - VotedFor(a, cc, w) => (w = v) - /\ \A d \in (cc+1)..b, a \in Q : DidNotVoteIn(a, d) - BY <2>1 - <3>5. QED - BY <3>2, <3>3, <3>4, <2>3 - <2>5. PICK cc \in -1..b : - /\ (cc # -1) => /\ SafeAt(cc, v) - /\ \A a \in Q : - \A w \in Value : - VotedFor(a, cc, w) => (w = v) - /\ \A d \in (cc+1)..b, a \in Q : DidNotVoteIn(a, d) - BY <2>4 - <2>6. CASE c > cc - BY <2>6, <2>5, <2>4, QA, SMT DEF TypeOK, Ballot - <2>7. CASE c =< cc - <3>1. /\ cc \in 0..b - /\ cc # -1 - BY <2>7, SMT DEF Ballot - <3>2. SafeLemma!2!(cc)!(v) - BY <1>2, <3>1 - <3>3. SafeAt(cc, v) - BY <2>5, <3>1 - <3>4. CASE c = cc - BY <3>4, <3>1, <2>5, <2>4, SMT DEF Ballot, TypeOK, DidNotVoteIn - <3>5. CASE c < cc - <4>1. c \in 0..(cc-1) - BY <3>1, <3>5, SMT - <4>2. SafeLemma!2!(cc) - BY <3>1, <1>2 - <4>3. QED - BY <4>1,<4>2, <4>2, <3>3 - <3>6. QED - BY <2>7, <3>4, <3>5, SMT DEF Ballot - <2>8. QED - BY <2>6, <2>7, SMT DEF Ballot -<1>3. \A b \in Ballot : P[b] - BY <1>1, <1>2, SimpleNatInduction DEF Ballot -<1>4. QED - BY <1>3, Z3 DEF Ballot \* SMT fails - -VInv1 == \A a \in Acceptor, b \in Ballot, v, w \in Value : - VotedFor(a, b, v) /\ VotedFor(a, b, w) => (v = w) - -VInv2 == \A a \in Acceptor, b \in Ballot, v \in Value : - VotedFor(a, b, v) => SafeAt(b, v) - -VInv3 == \A a1, a2 \in Acceptor, b \in Ballot, v1, v2 \in Value : - VotedFor(a1, b, v1) /\ VotedFor(a2, b, v2) => (v1 = v2) - -THEOREM VInv3 => VInv1 -BY DEF VInv1, VInv3 - -LEMMA VT0 == /\ TypeOK - /\ VInv1 - /\ VInv2 - => \A v, w \in Value, b, c \in Ballot : - (b > c) /\ SafeAt(b, v) /\ ChosenIn(c, w) => (v = w) -<1> SUFFICES ASSUME TypeOK, VInv1, VInv2, - NEW v \in Value, NEW w \in Value - PROVE \A b, c \in Ballot : - (b > c) /\ SafeAt(b, v) /\ ChosenIn(c, w) => (v = w) - OBVIOUS -<1> P == [b \in Ballot |-> - \A c \in Ballot : - (b > c) /\ SafeAt(b, v) /\ ChosenIn(c, w) => (v = w)] - -<1>1. P[0] - BY SMT DEF Ballot (* This works *) -<1>2. ASSUME NEW b \in Ballot, \A i \in 0..b : P[i] - PROVE P[b+1] - <2>1. b+1 \in Ballot - BY SMT DEF Ballot - <2>2. SUFFICES ASSUME NEW c \in Ballot, b+1 > c, SafeAt(b+1, v), ChosenIn(c, w) - PROVE v=w - BY <2>1 - <2>3. PICK Q \in Quorum : \A a \in Q : VotedFor(a, c, w) - BY <2>2 DEF ChosenIn - <2>4. b+1 # 0 /\ ((b+1)-1 = b) - BY SMT DEF Ballot - <2>5. PICK QQ \in Quorum, - d \in -1..((b+1)-1) : - /\ (d # -1) => /\ SafeAt(d, v) - /\ \A a \in QQ : - \A x \in Value : - VotedFor(a, d, x) => (x = v) - /\ \A e \in (d+1)..((b+1)-1), a \in QQ : DidNotVoteIn(a, e) - BY <2>1, <2>2, <2>4, SafeAtProp - <2> PICK aa \in QQ \cap Q : TRUE - BY QA - <2>6. c \leq d - BY <2>2, <2>3, <2>5, SMT DEF DidNotVoteIn, Ballot - <2>7. d # -1 - BY <2>6, SMT DEF Ballot - <2>8. CASE c = d - BY <2>3, <2>5, <2>7, <2>8 - <2>9. CASE d > c - <3>1. SafeAt(d, v) - BY <2>5, <2>7 - <3>2. d \in Ballot /\ d \in 0..b - BY <2>6, SMT DEF Ballot - <3>3. P[d] - BY <1>2, <3>2 - <3>4. QED - BY <2>2, <2>9, <3>1, <3>2, <3>3 - <2>10. QED - BY <2>3, <2>6, <2>8, <2>9, SMT DEF Ballot -<1>3. \A b \in Ballot : P[b] - BY <1>1, <1>2, GeneralNatInduction DEF Ballot - -<1>4. QED - BY <1>3 - -THEOREM VT1 == /\ TypeOK - /\ VInv1 - /\ VInv2 - => \A v, w : - (v \in chosen) /\ (w \in chosen) => (v = w) -<1>1. SUFFICES ASSUME TypeOK, VInv1, VInv2, - NEW v, NEW w, - v \in chosen, w \in chosen - PROVE v = w - OBVIOUS -<1>2. v \in Value /\ w \in Value - BY <1>1 DEF chosen -<1>3. PICK b \in Ballot, c \in Ballot : ChosenIn(b, v) /\ ChosenIn(c, w) - BY <1>1 DEF chosen -<1>4. PICK Q \in Quorum, R \in Quorum : - /\ \A a \in Q : VotedFor(a, b, v) - /\ \A a \in R : VotedFor(a, c, w) - BY <1>3 DEF ChosenIn -<1>5. PICK av \in Q, aw \in R: /\ VotedFor(av, b, v) - /\ VotedFor(aw, c, w) - BY <1>4, QuorumNonEmpty -<1>6. SafeAt(b, v) /\ SafeAt(c, w) - BY <1>1, <1>2, <1>5, QA DEF VInv2 -<1>7. CASE b = c - <2> PICK a \in Q \cap R : TRUE - BY QA - <2>1. /\ VotedFor(a, b, v) - /\ VotedFor(a, c, w) - BY <1>4 - <2>2. QED - BY <1>1, <1>2, <1>7, <2>1, QA DEF VInv1 -<1>8. CASE b > c - BY <1>1, <1>6, <1>3, <1>8, VT0, <1>2 \* <2>1 -<1>9. CASE c > b - BY <1>1, <1>6, <1>3, <1>9, VT0, <1>2 \* <2>1 -<1>10. QED - BY <1>7, <1>8, <1>9 DEF Ballot - -VInv4 == \A a \in Acceptor, b \in Ballot : - maxBal[a] < b => DidNotVoteIn(a, b) - -VInv == TypeOK /\ VInv2 /\ VInv3 /\ VInv4 - -IncreaseMaxBal(self, b) == - /\ b > maxBal[self] - /\ maxBal' = [maxBal EXCEPT ![self] = b] - /\ UNCHANGED votes - -VoteFor(self, b, v) == - /\ maxBal[self] \leq b - /\ DidNotVoteIn(self, b) - /\ \A p \in Acceptor \ {self} : - \A w \in Value : VotedFor(p, b, w) => (w = v) - /\ SafeAt(b, v) - /\ votes' = [votes EXCEPT ![self] = votes[self] \cup {<<b, v>>}] - /\ maxBal' = [maxBal EXCEPT ![self] = b] - -BallotAction(self, b) == - \/ IncreaseMaxBal(self, b) - \/ \E v \in Value : VoteFor(self, b, v) - -ASSUME AcceptorNonempty == Acceptor # {} - -LEMMA NextDef == - TypeOK => - (Next = \E self \in Acceptor : - \E b \in Ballot : BallotAction(self, b) ) -<1> HAVE TypeOK -<1>2. Next = \E self \in Acceptor: acceptor(self) - BY AcceptorNonempty DEF Next, ProcSet -<1>3. @ = NextDef!2!2 - BY DEF Next, BallotAction, IncreaseMaxBal, VoteFor, ProcSet, acceptor -<1>4. QED - BY <1>2, <1>3 - -THEOREM InductiveInvariance == VInv /\ [Next]_vars => VInv' -<1>1. VInv /\ (vars' = vars) => VInv' - \* SMT can't do this because it requires the definition of SafeAt, - \* which uses a recursive function definition. - <2> SUFFICES ASSUME VInv, vars' = vars - PROVE VInv' - OBVIOUS - <2> USE DEF vars, VInv - <2>1. TypeOK' - BY DEF TypeOK - <2>2. VInv2' - BY DEF VInv2, VotedFor, SafeAt, DidNotVoteIn - <2>3. VInv3' - BY DEF VInv3, VotedFor - <2>4. VInv4' - BY DEF VInv4, DidNotVoteIn, VotedFor - <2>5. QED - BY <2>1, <2>2, <2>3, <2>4 - -<1> SUFFICES ASSUME VInv, - NEW self \in Acceptor, - NEW b \in Ballot, - BallotAction(self, b) - PROVE VInv' - BY <1>1, NextDef DEF VInv - -<1>2. TypeOK' - <2>1. CASE IncreaseMaxBal(self, b) - BY <2>1 DEF IncreaseMaxBal, VInv, TypeOK - <2>2. CASE \E v \in Value : VoteFor(self, b, v) - BY <2>2 DEF VInv, TypeOK, VoteFor \* SMT and Zenon failed - <2>3. QED - BY <2>1, <2>2 DEF BallotAction - -<1>3. ASSUME NEW a \in Acceptor, NEW c \in Ballot, NEW w \in Value, - ~VotedFor(a, c, w), VotedFor(a, c, w)' - PROVE (a = self) /\ (c = b) /\ VoteFor(self, b, w) - BY <1>3, SMT DEF IncreaseMaxBal, VInv, TypeOK, VotedFor, VoteFor, BallotAction - -<1>4. ASSUME NEW a \in Acceptor - PROVE /\ maxBal[a] \in Ballot \cup {-1} - /\ maxBal'[a] \in Ballot \cup {-1} - /\ maxBal'[a] >= maxBal[a] - BY SMT DEF VInv, TypeOK, IncreaseMaxBal, VInv, VoteFor, BallotAction, - DidNotVoteIn, VotedFor, Ballot - -<1>5. ASSUME NEW c \in Ballot, NEW w \in Value, - SafeAt(c, w) - PROVE SafeAt(c, w)' - <2>SafeAtPropPrime. SafeAtProp' - BY SafeAtProp, PTL - <2> DEFINE P[i \in Ballot] == \A j \in 0..i : SafeAt(j, w) => SafeAt(j, w)' - <2>1. P[0] - <3>1. 0 \in Ballot /\ \A i \in 0..0 : i = 0 - BY SMT DEF Ballot - <3>2. QED - BY <2>SafeAtPropPrime, <3>1 - <2>2. ASSUME NEW d \in Ballot, P[d] - PROVE P[d+1] - <3>1. d+1 \in Ballot /\ d+1 # 0 - BY SMT DEF Ballot - <3>2. SUFFICES ASSUME NEW e \in 0..(d+1), SafeAt(e, w) - PROVE SafeAt(e, w)' - BY <3>1 - <3>3. e \in 0..d \/ e = d+1 - BY SMT DEF Ballot - <3>4. CASE e \in 0..d - BY <2>2, <3>2, <3>4 - <3>5. CASE e = d+1 - <4>1. PICK Q \in Quorum : SafeAtProp!(e, w)!2!2!(Q) - BY <3>1, <3>2, <3>5, SafeAtProp - <4>2. \A aa \in Q : maxBal'[aa] \geq e - BY <1>4, <4>1, QA, SMT DEF Ballot - <4>3. \E cc \in -1..(e-1) : - /\ (cc # -1) => /\ SafeAt(cc, w)' - /\ \A ax \in Q : - \A z \in Value : - VotedFor(ax, cc, z)' => (z = w) - /\ \A dd \in (cc+1)..(e-1), ax \in Q : DidNotVoteIn(ax, dd)' - <5>1. PICK cc \in -1..(e-1) : - /\ (cc # -1) => /\ SafeAt(cc, w) - /\ \A ax \in Q : - \A z \in Value : - VotedFor(ax, cc, z) => (z = w) - /\ \A dd \in (cc+1)..(e-1), ax \in Q : DidNotVoteIn(ax, dd) - BY <1>5, <3>3, <2>2, <4>1 - <5>2. (cc # -1) => (SafeAt(cc, w) => SafeAt(cc, w)') - BY <2>2 DEF Ballot - <5>3. CASE IncreaseMaxBal(self, b) - <6>1. /\ \A x, y, z : VotedFor(x, y, z)' = VotedFor(x,y,z) - /\ \A x, y: DidNotVoteIn(x, y)' = DidNotVoteIn(x, y) - BY <5>3 DEF IncreaseMaxBal, VotedFor, DidNotVoteIn - <6>2 QED - BY <5>1, <5>2, <6>1 - <5>4. CASE \E v \in Value : VoteFor(self, b, v) - <6> PICK v \in Value : VoteFor(self, b, v) - BY <5>4 - <6> QED - BY <5>4, <4>2, <5>1, <5>2, <2>2, QA - DEF VoteFor, VotedFor, DidNotVoteIn, VInv, TypeOK, Ballot - <5>5. QED - BY <5>3, <5>4 DEF BallotAction - <4>4. \/ e = 0 - \/ \E Q_1 \in Quorum : - /\ \A aa \in Q_1 : maxBal'[aa] \geq e - /\ \E c_1 \in -1..e - 1 : - /\ c_1 # -1 - => (/\ SafeAt(c_1, w)' - /\ \A aa \in Q_1 : - \A w_1 \in Value : - VotedFor(aa, c_1, w_1)' => w_1 = w) - /\ \A d_1 \in c_1 + 1..e - 1, aa \in Q_1 : - DidNotVoteIn(aa, d_1)' - BY <4>2, <4>3, <3>1, <3>5 - <4>5. e \in Ballot - BY <3>1, <3>5 - <4>6. SafeAt(e, w)' = <4>4 - BY <2>SafeAtPropPrime, <4>5 - <4>7. QED - BY <4>2, <4>3, <4>6 - <3>6. QED - BY <3>3, <3>4, <3>5 - <2>3. \A d \in Ballot : P[d] - BY <2>1, <2>2, SimpleNatInduction DEF Ballot - <2>4. QED - BY <2>3, <1>5, SMT DEF Ballot - -<1>6. VInv2' - BY <1>3, <1>5, SMT DEF VInv, VInv2, VoteFor - -<1>7. VInv3' - BY <1>3, SMT DEF VoteFor, DidNotVoteIn, VInv, TypeOK, VotedFor, VInv, VInv3 - -<1>8. VInv4' - BY <1>3, <1>4, SMT DEF Ballot, VInv, VInv4, VoteFor, DidNotVoteIn, TypeOK - -<1>9. QED - BY <1>2, <1>6, <1>7, <1>8 DEF VInv - -THEOREM InitImpliesInv == Init => VInv -<1> SUFFICES ASSUME Init PROVE VInv - OBVIOUS -<1> USE DEF Init -<1>1. TypeOK - BY DEF TypeOK, ProcSet -<1>2. VInv2 - BY DEF VInv2, VotedFor -<1>3. VInv3 - BY DEF VInv3, VotedFor -<1>4. VInv4 - BY DEF VInv4, DidNotVoteIn, VotedFor -<1>5. QED - BY <1>1, <1>2, <1>3, <1>4 DEF VInv - -THEOREM VT2 == Spec => []VInv - BY InductiveInvariance, InitImpliesInv, PTL DEF Spec - -C == INSTANCE Consensus - -THEOREM VT3 == Spec => C!Spec -<1>1. Init => C!Init - BY QuorumNonEmpty, QA, SMT DEF Init, C!Init, chosen, ChosenIn, VotedFor - -<1>2. ASSUME VInv, VInv', [Next]_vars - PROVE [C!Next]_C!vars - <2> USE VInv - <2>1. CASE vars' = vars - BY <2>1 DEF vars, C!vars, chosen, ChosenIn, VotedFor - <2>2. SUFFICES ASSUME NEW self \in Acceptor, - NEW b \in Ballot, - BallotAction(self, b) - PROVE [C!Next]_C!vars - BY <1>2, <2>1, NextDef DEF VInv - <2>3. ASSUME IncreaseMaxBal(self, b) - PROVE C!vars' = C!vars - BY <2>3 DEF IncreaseMaxBal, C!vars, chosen, ChosenIn, VotedFor - <2>4. ASSUME NEW v \in Value, - VoteFor(self, b, v) - PROVE [C!Next]_C!vars - <3>3. ASSUME NEW w \in chosen - PROVE w \in chosen' - BY <3>3, <2>4, <1>2, QA, SMT - DEF VoteFor, VInv, TypeOK, chosen, ChosenIn, VotedFor - <3>VT1P. VT1' - BY VT1, PTL - <3>1. ASSUME NEW w \in chosen, - v \in chosen' - PROVE w = v - BY <1>2, <3>1, <3>3, <3>VT1P, SMT DEF VInv, VInv1, VInv3 - <3>2. ASSUME NEW w, w \notin chosen, w \in chosen' - PROVE w = v - BY <3>2, <2>4, <1>2, QA, SMT - DEF chosen, ChosenIn, VotedFor, VoteFor, VInv, TypeOK - <3>4. CASE chosen' = chosen - BY <3>4 DEF C!vars - <3>5. CASE chosen' # chosen - <4>1. chosen \subseteq chosen' - BY <3>3 - <4>2. PICK w \in chosen' : w \notin chosen - BY <3>5, <4>1 - <4>3. w = v - BY <4>2, <3>2 - <4>4. chosen' = chosen \cup {v} - BY <3>2, <4>1, <4>3 - <4>5. chosen = {} - BY <4>4, <3>1, <3>5 - <4>6. QED - BY <4>4, <4>5 DEF C!Next - <3>6. QED - BY <3>4, <3>5 - <2>5. QED - BY <2>2, <2>3, <2>4 DEF BallotAction - -<1>3. QED - BY <1>1, <1>2, VT2, PTL DEF C!Spec, Spec - -ASSUME AcceptorFinite == IsFiniteSet(Acceptor) - -ASSUME ValueNonempty == Value # {} - -AXIOM SubsetOfFiniteSetFinite == - \A S, T : IsFiniteSet(T) /\ (S \subseteq T) => IsFiniteSet(S) - -AXIOM FiniteSetHasMax == - \A S \in SUBSET Int : - IsFiniteSet(S) /\ (S # {}) => \E max \in S : \A x \in S : max >= x - -AXIOM IntervalFinite == \A i, j \in Int : IsFiniteSet(i..j) - -THEOREM VT4 == TypeOK /\ VInv2 /\ VInv3 => - \A Q \in Quorum, b \in Ballot : - (\A a \in Q : (maxBal[a] >= b)) => \E v \in Value : SafeAt(b,v) - -<1>1. SUFFICES ASSUME TypeOK, VInv2, VInv3, - NEW Q \in Quorum, NEW b \in Ballot, - (\A a \in Q : (maxBal[a] >= b)) - PROVE \E v \in Value : SafeAt(b, v) - OBVIOUS -<1>2. CASE b = 0 - BY ValueNonempty, <1>1, SafeAtProp, <1>2 -<1>3. SUFFICES ASSUME b # 0 - PROVE \E v \in Value : SafeAt(b, v) - BY <1>2 -<1>4. SUFFICES \E v \in Value : - \E c \in -1..(b-1) : - /\ (c # -1) => /\ SafeAt(c, v) - /\ \A a \in Q : - \A w \in Value : - VotedFor(a, c, w) => (w = v) - /\ \A d \in (c+1)..(b-1), a \in Q : DidNotVoteIn(a, d) - <2>1. SUFFICES ASSUME NEW v \in Value, - <1>4!1!(v) - PROVE SafeAt(b, v) - OBVIOUS - <2>2. SafeAtProp!(b, v) - BY SafeAtProp - <2>3. QED - BY <2>1, <2>2, <1>1, <1>3 -<1>5. CASE \A a \in Q, c \in 0..(b-1) : DidNotVoteIn(a, c) - <2>1. PICK v \in Value : TRUE - BY ValueNonempty - <2> -1 \in -1..(b-1) - BY SMT DEF Ballot - <2>2. WITNESS v \in Value - <2>3. WITNESS -1 \in -1..(b-1) - <2>4. QED - BY <1>5 -<1>6. CASE \E a \in Q, c \in 0..(b-1) : ~DidNotVoteIn(a, c) - <2>1. PICK c \in 0..(b-1) : - /\ \E a \in Q : ~DidNotVoteIn(a, c) - /\ \A d \in (c+1)..(b-1), a \in Q : DidNotVoteIn(a, d) - <3> DEFINE S == {c \in 0..(b-1) : \E a \in Q : ~DidNotVoteIn(a, c)} - <3>1. S # {} - BY <1>6 - <3>2. PICK c \in S : \A d \in S : c >= d - <4>1. (0 \in Int) /\ (b-1 \in Int) /\ (\A x \in 0..(b-1) : x \in Int) - BY <1>3, SMT DEF Ballot - <4>2. (S \in SUBSET Int) - BY <4>1 - <4>3. IsFiniteSet(S) - BY <4>1, IntervalFinite, SubsetOfFiniteSetFinite - <4>4. QED - BY <3>1, <4>2, <4>3, FiniteSetHasMax - <3>3. c \in 0..(b-1) - OBVIOUS - <3>4. \A d \in (c+1)..(b-1) : d \in 0..(b-1) /\ ~(c >= d) - BY <3>3, SMT DEF Ballot (* This works *) - <3>5. \A d \in (c+1)..(b-1), a \in Q : DidNotVoteIn(a, d) - BY <3>2, <3>4 - <3>6. \E a \in Q : ~DidNotVoteIn(a, c) - BY <3>1 - <3>7. QED - BY <3>3, <3>5, <3>6 - <2>2. (c \in -1..(b-1)) /\ (c # -1) /\ (c \in Ballot) - BY SMT DEF Ballot - <2>3. PICK a0 \in Q : ~DidNotVoteIn(a0, c) - BY <2>1 - <2>4. PICK v \in Value : VotedFor(a0, c, v) - BY <2>3 DEF DidNotVoteIn - <2>5. \A a \in Q : \A w \in Value : - VotedFor(a, c, w) => (w = v) - BY <2>2, <2>4, QA, <1>1 DEF VInv3 - <2>6. SafeAt(c, v) - BY <1>1, <2>4, QA, <2>2 DEF VInv2 - <2>7. QED - BY <2>1, <2>2, <2>5, <2>6 - -<1>7. QED - BY <1>5, <1>6 - -LiveAssumption == - \E Q \in Quorum, b \in Ballot : - \A self \in Q : - /\ WF_vars(BallotAction(self, b)) - /\ [] [\A c \in Ballot : (c > b) => ~ BallotAction(self, c)]_vars - -LiveSpec == Spec /\ LiveAssumption -=============================================================================== +----------------------------- MODULE VoteProof ------------------------------ + +(*************************************************************************) +(* !!!! REGRESSION TESTS ONLY !!!! *) +(* *) +(* This file is not meant as a reference to TLA+ in general, nor for *) +(* VoteProof in particular. Please search the web for an official *) +(* version of the VoteProof spec. *) +(* *) +(*************************************************************************) + +EXTENDS Integers , FiniteSets, TLC, TLAPS + +CONSTANT Value, \* As in module Consensus, the set of choosable values. + Acceptor, \* The set of all acceptors. + Quorum \* The set of all quorums. + +ASSUME QA == /\ \A Q \in Quorum : Q \subseteq Acceptor + /\ \A Q1, Q2 \in Quorum : Q1 \cap Q2 # {} + +THEOREM QuorumNonEmpty == \A Q \in Quorum : Q # {} +PROOF BY QA + +Ballot == Nat + +(*************************** +--algorithm Voting { + variables votes = [a \in Acceptor |-> {}], + maxBal = [a \in Acceptor |-> -1]; + define { + VotedFor(a, b, v) == <<b, v>> \in votes[a] + DidNotVoteIn(a, b) == \A v \in Value : ~ VotedFor(a, b, v) + + SafeAt(b, v) == + LET SA[bb \in Ballot] == + \/ bb = 0 + \/ \E Q \in Quorum : + /\ \A a \in Q : maxBal[a] \geq bb + /\ \E c \in -1..(bb-1) : + /\ (c # -1) => /\ SA[c] + /\ \A a \in Q : + \A w \in Value : + VotedFor(a, c, w) => (w = v) + /\ \A d \in (c+1)..(bb-1), a \in Q : DidNotVoteIn(a, d) + IN SA[b] + } + macro IncreaseMaxBal(b) { + when b > maxBal[self] ; + maxBal[self] := b + } + + macro VoteFor(b, v) { + when /\ maxBal[self] \leq b + /\ DidNotVoteIn(self, b) + /\ \A p \in Acceptor \ {self} : + \A w \in Value : VotedFor(p, b, w) => (w = v) + /\ SafeAt(b, v) ; + votes[self] := votes[self] \cup {<<b, v>>}; + maxBal[self] := b + } + + process (acceptor \in Acceptor) { + acc : while (TRUE) { + with (b \in Ballot) { + either IncreaseMaxBal(b) + or with (v \in Value) { VoteFor(b, v) } + } + } + } +} + +The following is the TLA+ specification produced by the translation. +Blank lines, produced by the translation because of the comments, have +been deleted. +****************************) +\* BEGIN TRANSLATION +VARIABLES votes, maxBal + +(* define statement *) +VotedFor(a, b, v) == <<b, v>> \in votes[a] + + + +DidNotVoteIn(a, b) == \A v \in Value : ~ VotedFor(a, b, v) + + + + + + + + + + + + + + + + + + + + + +SafeAt(b, v) == + LET SA[bb \in Ballot] == + + + + \/ bb = 0 + \/ \E Q \in Quorum : + /\ \A a \in Q : maxBal[a] \geq bb + /\ \E c \in -1..(bb-1) : + /\ (c # -1) => /\ SA[c] + /\ \A a \in Q : + \A w \in Value : + VotedFor(a, c, w) => (w = v) + /\ \A d \in (c+1)..(bb-1), a \in Q : DidNotVoteIn(a, d) + IN SA[b] + + +vars == << votes, maxBal >> + +ProcSet == (Acceptor) + +Init == (* Global variables *) + /\ votes = [a \in Acceptor |-> {}] + /\ maxBal = [a \in Acceptor |-> -1] + +acceptor(self) == \E b \in Ballot: + \/ /\ b > maxBal[self] + /\ maxBal' = [maxBal EXCEPT ![self] = b] + /\ votes' = votes + \/ /\ \E v \in Value: + /\ /\ maxBal[self] \leq b + /\ DidNotVoteIn(self, b) + /\ \A p \in Acceptor \ {self} : + \A w \in Value : VotedFor(p, b, w) => (w = v) + /\ SafeAt(b, v) + /\ votes' = [votes EXCEPT ![self] = votes[self] \cup {<<b, v>>}] + /\ maxBal' = [maxBal EXCEPT ![self] = b] + +Next == (\E self \in Acceptor: acceptor(self)) + +Spec == Init /\ [][Next]_vars + +\* END TRANSLATION + +THEOREM RecursiveFcnOfNat == + ASSUME NEW Def(_,_), + \A n \in Nat : + \A g, h : (\A i \in 0..(n-1) : g[i] = h[i]) => (Def(g, n) = Def(h, n)) + PROVE LET f[n \in Nat] == Def(f, n) + IN f = [n \in Nat |-> Def(f, n)] +PROOF OMITTED + +THEOREM SafeAtProp == + \A b \in Ballot, v \in Value : + SafeAt(b, v) = + \/ b = 0 + \/ \E Q \in Quorum : + /\ \A a \in Q : maxBal[a] \geq b + /\ \E c \in -1..(b-1) : + /\ (c # -1) => /\ SafeAt(c, v) + /\ \A a \in Q : + \A w \in Value : + VotedFor(a, c, w) => (w = v) + /\ \A d \in (c+1)..(b-1), a \in Q : DidNotVoteIn(a, d) +<1>1. SUFFICES ASSUME NEW v \in Value + PROVE \A b \in Ballot : SafeAtProp!(b, v) + OBVIOUS +<1> USE DEF Ballot +<1> DEFINE Def(SA, bb) == + \/ bb = 0 + \/ \E Q \in Quorum : + /\ \A a \in Q : maxBal[a] \geq bb + /\ \E c \in -1..(bb-1) : + /\ (c # -1) => /\ SA[c] + /\ \A a \in Q : + \A w \in Value : + VotedFor(a, c, w) => (w = v) + /\ \A d \in (c+1)..(bb-1), a \in Q : DidNotVoteIn(a, d) + SA[bb \in Ballot] == Def(SA, bb) +<1>2. \A b : SafeAt(b, v) = SA[b] + BY DEF SafeAt +<1>3. \A n \in Nat : + \A g, h : (\A i \in 0..(n-1) : g[i] = h[i]) => (Def(g, n) = Def(h, n)) + BY SMT +<1> HIDE DEF Def +<1>4. SA = [b \in Ballot |-> Def(SA, b)] + BY ONLY <1>3, RecursiveFcnOfNat +<1>5. \A b \in Ballot : SA[b] = Def(SA, b) + BY <1>4 +<1>6. QED + BY <1>2, <1>5 DEF SafeAt, Def + +TypeOK == /\ votes \in [Acceptor -> SUBSET (Ballot \X Value)] + /\ maxBal \in [Acceptor -> Ballot \cup {-1}] + +ChosenIn(b, v) == \E Q \in Quorum : \A a \in Q : VotedFor(a, b, v) + +chosen == {v \in Value : \E b \in Ballot : ChosenIn(b, v)} + +AXIOM SimpleNatInduction == \A f : /\ f[0] + /\ \A n \in Nat : f[n] => f[n+1] + => \A n \in Nat : f[n] + +THEOREM GeneralNatInduction == + \A f : /\ f[0] + /\ \A n \in Nat : (\A j \in 0..n : f[j]) => f[n+1] + => \A n \in Nat : f[n] +<1>1. SUFFICES ASSUME NEW f, + f[0], + \A m \in Nat : (\A j \in 0..m : f[j]) => f[m+1], + NEW n \in Nat + PROVE f[n] + OBVIOUS +<1> DEFINE g == [m \in Nat |-> \A j \in 0..m : f[j]] +<1>2. g[0] + BY <1>1, SMT +<1>3. ASSUME NEW k \in Nat, g[k] + PROVE g[k+1] + BY <1>1, <1>3, SMT +<1>4. \A k \in Nat : g[k] + BY <1>2, <1>3, SimpleNatInduction +<1>5. QED + BY <1>4, SMT + +LEMMA SafeLemma == + TypeOK => + \A b \in Ballot : + \A v \in Value : + SafeAt(b, v) => + \A c \in 0..(b-1) : + \E Q \in Quorum : + \A a \in Q : /\ maxBal[a] >= c + /\ \/ DidNotVoteIn(a, c) + \/ VotedFor(a, c, v) +<1> SUFFICES ASSUME TypeOK + PROVE SafeLemma!2 + OBVIOUS +<1> DEFINE P[b \in Ballot] == \A c \in 0..b : SafeLemma!2!(c) +<1>1. P[0] + BY SMT DEF Ballot +<1>2. ASSUME NEW b \in Ballot, P[b] + PROVE P[b+1] + <2>1. /\ b+1 \in Ballot + /\ (b+1) - 1 = b + BY SMT DEF Ballot + <2>2. 0..(b+1) = (0..b) \cup {b+1} + BY SMT DEF Ballot + <2>3. SUFFICES ASSUME NEW v \in Value, + SafeAt(b+1, v), + NEW c \in 0..b + PROVE \E Q \in Quorum : + \A a \in Q : /\ maxBal[a] >= c + /\ \/ DidNotVoteIn(a, c) + \/ VotedFor(a, c, v) + BY <1>2, <2>1, <2>2 + <2>4. PICK Q \in Quorum : + /\ \A a \in Q : maxBal[a] \geq (b+1) + /\ \E cc \in -1..b : + /\ (cc # -1) => /\ SafeAt(cc, v) + /\ \A a \in Q : + \A w \in Value : + VotedFor(a, cc, w) => (w = v) + /\ \A d \in (cc+1)..b, a \in Q : DidNotVoteIn(a, d) + <3>1. b+1 # 0 + BY SMT DEF Ballot + <3>2. SafeAt(b+1,v) = SafeAtProp!(b+1,v)!2 + BY SafeAtProp, <2>1 + <3>3. @ = SafeAtProp!(b+1,v)!2!2 + BY <3>1 + <3>4. @ = \E Q \in Quorum : + /\ \A a \in Q : maxBal[a] \geq (b+1) + /\ \E cc \in -1..b : + /\ (cc # -1) => /\ SafeAt(cc, v) + /\ \A a \in Q : + \A w \in Value : + VotedFor(a, cc, w) => (w = v) + /\ \A d \in (cc+1)..b, a \in Q : DidNotVoteIn(a, d) + BY <2>1 + <3>5. QED + BY <3>2, <3>3, <3>4, <2>3 + <2>5. PICK cc \in -1..b : + /\ (cc # -1) => /\ SafeAt(cc, v) + /\ \A a \in Q : + \A w \in Value : + VotedFor(a, cc, w) => (w = v) + /\ \A d \in (cc+1)..b, a \in Q : DidNotVoteIn(a, d) + BY <2>4 + <2>6. CASE c > cc + BY <2>6, <2>5, <2>4, QA, SMT DEF TypeOK, Ballot + <2>7. CASE c =< cc + <3>1. /\ cc \in 0..b + /\ cc # -1 + BY <2>7, SMT DEF Ballot + <3>2. SafeLemma!2!(cc)!(v) + BY <1>2, <3>1 + <3>3. SafeAt(cc, v) + BY <2>5, <3>1 + <3>4. CASE c = cc + BY <3>4, <3>1, <2>5, <2>4, SMT DEF Ballot, TypeOK, DidNotVoteIn + <3>5. CASE c < cc + <4>1. c \in 0..(cc-1) + BY <3>1, <3>5, SMT + <4>2. SafeLemma!2!(cc) + BY <3>1, <1>2 + <4>3. QED + BY <4>1,<4>2, <4>2, <3>3 + <3>6. QED + BY <2>7, <3>4, <3>5, SMT DEF Ballot + <2>8. QED + BY <2>6, <2>7, SMT DEF Ballot +<1>3. \A b \in Ballot : P[b] + BY <1>1, <1>2, SimpleNatInduction DEF Ballot +<1>4. QED + BY <1>3, Z3 DEF Ballot \* SMT fails + +VInv1 == \A a \in Acceptor, b \in Ballot, v, w \in Value : + VotedFor(a, b, v) /\ VotedFor(a, b, w) => (v = w) + +VInv2 == \A a \in Acceptor, b \in Ballot, v \in Value : + VotedFor(a, b, v) => SafeAt(b, v) + +VInv3 == \A a1, a2 \in Acceptor, b \in Ballot, v1, v2 \in Value : + VotedFor(a1, b, v1) /\ VotedFor(a2, b, v2) => (v1 = v2) + +THEOREM VInv3 => VInv1 +BY DEF VInv1, VInv3 + +LEMMA VT0 == /\ TypeOK + /\ VInv1 + /\ VInv2 + => \A v, w \in Value, b, c \in Ballot : + (b > c) /\ SafeAt(b, v) /\ ChosenIn(c, w) => (v = w) +<1> SUFFICES ASSUME TypeOK, VInv1, VInv2, + NEW v \in Value, NEW w \in Value + PROVE \A b, c \in Ballot : + (b > c) /\ SafeAt(b, v) /\ ChosenIn(c, w) => (v = w) + OBVIOUS +<1> P == [b \in Ballot |-> + \A c \in Ballot : + (b > c) /\ SafeAt(b, v) /\ ChosenIn(c, w) => (v = w)] + +<1>1. P[0] + BY SMT DEF Ballot (* This works *) +<1>2. ASSUME NEW b \in Ballot, \A i \in 0..b : P[i] + PROVE P[b+1] + <2>1. b+1 \in Ballot + BY SMT DEF Ballot + <2>2. SUFFICES ASSUME NEW c \in Ballot, b+1 > c, SafeAt(b+1, v), ChosenIn(c, w) + PROVE v=w + BY <2>1 + <2>3. PICK Q \in Quorum : \A a \in Q : VotedFor(a, c, w) + BY <2>2 DEF ChosenIn + <2>4. b+1 # 0 /\ ((b+1)-1 = b) + BY SMT DEF Ballot + <2>5. PICK QQ \in Quorum, + d \in -1..((b+1)-1) : + /\ (d # -1) => /\ SafeAt(d, v) + /\ \A a \in QQ : + \A x \in Value : + VotedFor(a, d, x) => (x = v) + /\ \A e \in (d+1)..((b+1)-1), a \in QQ : DidNotVoteIn(a, e) + BY <2>1, <2>2, <2>4, SafeAtProp + <2> PICK aa \in QQ \cap Q : TRUE + BY QA + <2>6. c \leq d + BY <2>2, <2>3, <2>5, SMT DEF DidNotVoteIn, Ballot + <2>7. d # -1 + BY <2>6, SMT DEF Ballot + <2>8. CASE c = d + BY <2>3, <2>5, <2>7, <2>8 + <2>9. CASE d > c + <3>1. SafeAt(d, v) + BY <2>5, <2>7 + <3>2. d \in Ballot /\ d \in 0..b + BY <2>6, SMT DEF Ballot + <3>3. P[d] + BY <1>2, <3>2 + <3>4. QED + BY <2>2, <2>9, <3>1, <3>2, <3>3 + <2>10. QED + BY <2>3, <2>6, <2>8, <2>9, SMT DEF Ballot +<1>3. \A b \in Ballot : P[b] + BY <1>1, <1>2, GeneralNatInduction DEF Ballot + +<1>4. QED + BY <1>3 + +THEOREM VT1 == /\ TypeOK + /\ VInv1 + /\ VInv2 + => \A v, w : + (v \in chosen) /\ (w \in chosen) => (v = w) +<1>1. SUFFICES ASSUME TypeOK, VInv1, VInv2, + NEW v, NEW w, + v \in chosen, w \in chosen + PROVE v = w + OBVIOUS +<1>2. v \in Value /\ w \in Value + BY <1>1 DEF chosen +<1>3. PICK b \in Ballot, c \in Ballot : ChosenIn(b, v) /\ ChosenIn(c, w) + BY <1>1 DEF chosen +<1>4. PICK Q \in Quorum, R \in Quorum : + /\ \A a \in Q : VotedFor(a, b, v) + /\ \A a \in R : VotedFor(a, c, w) + BY <1>3 DEF ChosenIn +<1>5. PICK av \in Q, aw \in R: /\ VotedFor(av, b, v) + /\ VotedFor(aw, c, w) + BY <1>4, QuorumNonEmpty +<1>6. SafeAt(b, v) /\ SafeAt(c, w) + BY <1>1, <1>2, <1>5, QA DEF VInv2 +<1>7. CASE b = c + <2> PICK a \in Q \cap R : TRUE + BY QA + <2>1. /\ VotedFor(a, b, v) + /\ VotedFor(a, c, w) + BY <1>4 + <2>2. QED + BY <1>1, <1>2, <1>7, <2>1, QA DEF VInv1 +<1>8. CASE b > c + BY <1>1, <1>6, <1>3, <1>8, VT0, <1>2 \* <2>1 +<1>9. CASE c > b + BY <1>1, <1>6, <1>3, <1>9, VT0, <1>2 \* <2>1 +<1>10. QED + BY <1>7, <1>8, <1>9 DEF Ballot + +VInv4 == \A a \in Acceptor, b \in Ballot : + maxBal[a] < b => DidNotVoteIn(a, b) + +VInv == TypeOK /\ VInv2 /\ VInv3 /\ VInv4 + +IncreaseMaxBal(self, b) == + /\ b > maxBal[self] + /\ maxBal' = [maxBal EXCEPT ![self] = b] + /\ UNCHANGED votes + +VoteFor(self, b, v) == + /\ maxBal[self] \leq b + /\ DidNotVoteIn(self, b) + /\ \A p \in Acceptor \ {self} : + \A w \in Value : VotedFor(p, b, w) => (w = v) + /\ SafeAt(b, v) + /\ votes' = [votes EXCEPT ![self] = votes[self] \cup {<<b, v>>}] + /\ maxBal' = [maxBal EXCEPT ![self] = b] + +BallotAction(self, b) == + \/ IncreaseMaxBal(self, b) + \/ \E v \in Value : VoteFor(self, b, v) + +ASSUME AcceptorNonempty == Acceptor # {} + +LEMMA NextDef == + TypeOK => + (Next = \E self \in Acceptor : + \E b \in Ballot : BallotAction(self, b) ) +<1> HAVE TypeOK +<1>2. Next = \E self \in Acceptor: acceptor(self) + BY AcceptorNonempty DEF Next, ProcSet +<1>3. @ = NextDef!2!2 + BY DEF Next, BallotAction, IncreaseMaxBal, VoteFor, ProcSet, acceptor +<1>4. QED + BY <1>2, <1>3 + +THEOREM InductiveInvariance == VInv /\ [Next]_vars => VInv' +<1>1. VInv /\ (vars' = vars) => VInv' + \* SMT can't do this because it requires the definition of SafeAt, + \* which uses a recursive function definition. + <2> SUFFICES ASSUME VInv, vars' = vars + PROVE VInv' + OBVIOUS + <2> USE DEF vars, VInv + <2>1. TypeOK' + BY DEF TypeOK + <2>2. VInv2' + BY DEF VInv2, VotedFor, SafeAt, DidNotVoteIn + <2>3. VInv3' + BY DEF VInv3, VotedFor + <2>4. VInv4' + BY DEF VInv4, DidNotVoteIn, VotedFor + <2>5. QED + BY <2>1, <2>2, <2>3, <2>4 + +<1> SUFFICES ASSUME VInv, + NEW self \in Acceptor, + NEW b \in Ballot, + BallotAction(self, b) + PROVE VInv' + BY <1>1, NextDef DEF VInv + +<1>2. TypeOK' + <2>1. CASE IncreaseMaxBal(self, b) + BY <2>1 DEF IncreaseMaxBal, VInv, TypeOK + <2>2. CASE \E v \in Value : VoteFor(self, b, v) + BY <2>2 DEF VInv, TypeOK, VoteFor \* SMT and Zenon failed + <2>3. QED + BY <2>1, <2>2 DEF BallotAction + +<1>3. ASSUME NEW a \in Acceptor, NEW c \in Ballot, NEW w \in Value, + ~VotedFor(a, c, w), VotedFor(a, c, w)' + PROVE (a = self) /\ (c = b) /\ VoteFor(self, b, w) + BY <1>3, SMT DEF IncreaseMaxBal, VInv, TypeOK, VotedFor, VoteFor, BallotAction + +<1>4. ASSUME NEW a \in Acceptor + PROVE /\ maxBal[a] \in Ballot \cup {-1} + /\ maxBal'[a] \in Ballot \cup {-1} + /\ maxBal'[a] >= maxBal[a] + BY SMT DEF VInv, TypeOK, IncreaseMaxBal, VInv, VoteFor, BallotAction, + DidNotVoteIn, VotedFor, Ballot + +<1>5. ASSUME NEW c \in Ballot, NEW w \in Value, + SafeAt(c, w) + PROVE SafeAt(c, w)' + <2>SafeAtPropPrime. SafeAtProp' + BY SafeAtProp, PTL + <2> DEFINE P[i \in Ballot] == \A j \in 0..i : SafeAt(j, w) => SafeAt(j, w)' + <2>1. P[0] + <3>1. 0 \in Ballot /\ \A i \in 0..0 : i = 0 + BY SMT DEF Ballot + <3>2. QED + BY <2>SafeAtPropPrime, <3>1 + <2>2. ASSUME NEW d \in Ballot, P[d] + PROVE P[d+1] + <3>1. d+1 \in Ballot /\ d+1 # 0 + BY SMT DEF Ballot + <3>2. SUFFICES ASSUME NEW e \in 0..(d+1), SafeAt(e, w) + PROVE SafeAt(e, w)' + BY <3>1 + <3>3. e \in 0..d \/ e = d+1 + BY SMT DEF Ballot + <3>4. CASE e \in 0..d + BY <2>2, <3>2, <3>4 + <3>5. CASE e = d+1 + <4>1. PICK Q \in Quorum : SafeAtProp!(e, w)!2!2!(Q) + BY <3>1, <3>2, <3>5, SafeAtProp + <4>2. \A aa \in Q : maxBal'[aa] \geq e + BY <1>4, <4>1, QA, SMT DEF Ballot + <4>3. \E cc \in -1..(e-1) : + /\ (cc # -1) => /\ SafeAt(cc, w)' + /\ \A ax \in Q : + \A z \in Value : + VotedFor(ax, cc, z)' => (z = w) + /\ \A dd \in (cc+1)..(e-1), ax \in Q : DidNotVoteIn(ax, dd)' + <5>1. PICK cc \in -1..(e-1) : + /\ (cc # -1) => /\ SafeAt(cc, w) + /\ \A ax \in Q : + \A z \in Value : + VotedFor(ax, cc, z) => (z = w) + /\ \A dd \in (cc+1)..(e-1), ax \in Q : DidNotVoteIn(ax, dd) + BY <1>5, <3>3, <2>2, <4>1 + <5>2. (cc # -1) => (SafeAt(cc, w) => SafeAt(cc, w)') + BY <2>2 DEF Ballot + <5>3. CASE IncreaseMaxBal(self, b) + <6>1. /\ \A x, y, z : VotedFor(x, y, z)' = VotedFor(x,y,z) + /\ \A x, y: DidNotVoteIn(x, y)' = DidNotVoteIn(x, y) + BY <5>3 DEF IncreaseMaxBal, VotedFor, DidNotVoteIn + <6>2 QED + BY <5>1, <5>2, <6>1 + <5>4. CASE \E v \in Value : VoteFor(self, b, v) + <6> PICK v \in Value : VoteFor(self, b, v) + BY <5>4 + <6> QED + BY <5>4, <4>2, <5>1, <5>2, <2>2, QA + DEF VoteFor, VotedFor, DidNotVoteIn, VInv, TypeOK, Ballot + <5>5. QED + BY <5>3, <5>4 DEF BallotAction + <4>4. \/ e = 0 + \/ \E Q_1 \in Quorum : + /\ \A aa \in Q_1 : maxBal'[aa] \geq e + /\ \E c_1 \in -1..e - 1 : + /\ c_1 # -1 + => (/\ SafeAt(c_1, w)' + /\ \A aa \in Q_1 : + \A w_1 \in Value : + VotedFor(aa, c_1, w_1)' => w_1 = w) + /\ \A d_1 \in c_1 + 1..e - 1, aa \in Q_1 : + DidNotVoteIn(aa, d_1)' + BY <4>2, <4>3, <3>1, <3>5 + <4>5. e \in Ballot + BY <3>1, <3>5 + <4>6. SafeAt(e, w)' = <4>4 + BY <2>SafeAtPropPrime, <4>5 + <4>7. QED + BY <4>2, <4>3, <4>6 + <3>6. QED + BY <3>3, <3>4, <3>5 + <2>3. \A d \in Ballot : P[d] + BY <2>1, <2>2, SimpleNatInduction DEF Ballot + <2>4. QED + BY <2>3, <1>5, SMT DEF Ballot + +<1>6. VInv2' + BY <1>3, <1>5, SMT DEF VInv, VInv2, VoteFor + +<1>7. VInv3' + BY <1>3, SMT DEF VoteFor, DidNotVoteIn, VInv, TypeOK, VotedFor, VInv, VInv3 + +<1>8. VInv4' + BY <1>3, <1>4, SMT DEF Ballot, VInv, VInv4, VoteFor, DidNotVoteIn, TypeOK + +<1>9. QED + BY <1>2, <1>6, <1>7, <1>8 DEF VInv + +THEOREM InitImpliesInv == Init => VInv +<1> SUFFICES ASSUME Init PROVE VInv + OBVIOUS +<1> USE DEF Init +<1>1. TypeOK + BY DEF TypeOK, ProcSet +<1>2. VInv2 + BY DEF VInv2, VotedFor +<1>3. VInv3 + BY DEF VInv3, VotedFor +<1>4. VInv4 + BY DEF VInv4, DidNotVoteIn, VotedFor +<1>5. QED + BY <1>1, <1>2, <1>3, <1>4 DEF VInv + +THEOREM VT2 == Spec => []VInv + BY InductiveInvariance, InitImpliesInv, PTL DEF Spec + +C == INSTANCE Consensus + +THEOREM VT3 == Spec => C!Spec +<1>1. Init => C!Init + BY QuorumNonEmpty, QA, SMT DEF Init, C!Init, chosen, ChosenIn, VotedFor + +<1>2. ASSUME VInv, VInv', [Next]_vars + PROVE [C!Next]_C!vars + <2> USE VInv + <2>1. CASE vars' = vars + BY <2>1 DEF vars, C!vars, chosen, ChosenIn, VotedFor + <2>2. SUFFICES ASSUME NEW self \in Acceptor, + NEW b \in Ballot, + BallotAction(self, b) + PROVE [C!Next]_C!vars + BY <1>2, <2>1, NextDef DEF VInv + <2>3. ASSUME IncreaseMaxBal(self, b) + PROVE C!vars' = C!vars + BY <2>3 DEF IncreaseMaxBal, C!vars, chosen, ChosenIn, VotedFor + <2>4. ASSUME NEW v \in Value, + VoteFor(self, b, v) + PROVE [C!Next]_C!vars + <3>3. ASSUME NEW w \in chosen + PROVE w \in chosen' + BY <3>3, <2>4, <1>2, QA, SMT + DEF VoteFor, VInv, TypeOK, chosen, ChosenIn, VotedFor + <3>VT1P. VT1' + BY VT1, PTL + <3>1. ASSUME NEW w \in chosen, + v \in chosen' + PROVE w = v + BY <1>2, <3>1, <3>3, <3>VT1P, SMT DEF VInv, VInv1, VInv3 + <3>2. ASSUME NEW w, w \notin chosen, w \in chosen' + PROVE w = v + BY <3>2, <2>4, <1>2, QA, SMT + DEF chosen, ChosenIn, VotedFor, VoteFor, VInv, TypeOK + <3>4. CASE chosen' = chosen + BY <3>4 DEF C!vars + <3>5. CASE chosen' # chosen + <4>1. chosen \subseteq chosen' + BY <3>3 + <4>2. PICK w \in chosen' : w \notin chosen + BY <3>5, <4>1 + <4>3. w = v + BY <4>2, <3>2 + <4>4. chosen' = chosen \cup {v} + BY <3>2, <4>1, <4>3 + <4>5. chosen = {} + BY <4>4, <3>1, <3>5 + <4>6. QED + BY <4>4, <4>5 DEF C!Next + <3>6. QED + BY <3>4, <3>5 + <2>5. QED + BY <2>2, <2>3, <2>4 DEF BallotAction + +<1>3. QED + BY <1>1, <1>2, VT2, PTL DEF C!Spec, Spec + +ASSUME AcceptorFinite == IsFiniteSet(Acceptor) + +ASSUME ValueNonempty == Value # {} + +AXIOM SubsetOfFiniteSetFinite == + \A S, T : IsFiniteSet(T) /\ (S \subseteq T) => IsFiniteSet(S) + +AXIOM FiniteSetHasMax == + \A S \in SUBSET Int : + IsFiniteSet(S) /\ (S # {}) => \E max \in S : \A x \in S : max >= x + +AXIOM IntervalFinite == \A i, j \in Int : IsFiniteSet(i..j) + +THEOREM VT4 == TypeOK /\ VInv2 /\ VInv3 => + \A Q \in Quorum, b \in Ballot : + (\A a \in Q : (maxBal[a] >= b)) => \E v \in Value : SafeAt(b,v) + +<1>1. SUFFICES ASSUME TypeOK, VInv2, VInv3, + NEW Q \in Quorum, NEW b \in Ballot, + (\A a \in Q : (maxBal[a] >= b)) + PROVE \E v \in Value : SafeAt(b, v) + OBVIOUS +<1>2. CASE b = 0 + BY ValueNonempty, <1>1, SafeAtProp, <1>2 +<1>3. SUFFICES ASSUME b # 0 + PROVE \E v \in Value : SafeAt(b, v) + BY <1>2 +<1>4. SUFFICES \E v \in Value : + \E c \in -1..(b-1) : + /\ (c # -1) => /\ SafeAt(c, v) + /\ \A a \in Q : + \A w \in Value : + VotedFor(a, c, w) => (w = v) + /\ \A d \in (c+1)..(b-1), a \in Q : DidNotVoteIn(a, d) + <2>1. SUFFICES ASSUME NEW v \in Value, + <1>4!1!(v) + PROVE SafeAt(b, v) + OBVIOUS + <2>2. SafeAtProp!(b, v) + BY SafeAtProp + <2>3. QED + BY <2>1, <2>2, <1>1, <1>3 +<1>5. CASE \A a \in Q, c \in 0..(b-1) : DidNotVoteIn(a, c) + <2>1. PICK v \in Value : TRUE + BY ValueNonempty + <2> -1 \in -1..(b-1) + BY SMT DEF Ballot + <2>2. WITNESS v \in Value + <2>3. WITNESS -1 \in -1..(b-1) + <2>4. QED + BY <1>5 +<1>6. CASE \E a \in Q, c \in 0..(b-1) : ~DidNotVoteIn(a, c) + <2>1. PICK c \in 0..(b-1) : + /\ \E a \in Q : ~DidNotVoteIn(a, c) + /\ \A d \in (c+1)..(b-1), a \in Q : DidNotVoteIn(a, d) + <3> DEFINE S == {c \in 0..(b-1) : \E a \in Q : ~DidNotVoteIn(a, c)} + <3>1. S # {} + BY <1>6 + <3>2. PICK c \in S : \A d \in S : c >= d + <4>1. (0 \in Int) /\ (b-1 \in Int) /\ (\A x \in 0..(b-1) : x \in Int) + BY <1>3, SMT DEF Ballot + <4>2. (S \in SUBSET Int) + BY <4>1 + <4>3. IsFiniteSet(S) + BY <4>1, IntervalFinite, SubsetOfFiniteSetFinite + <4>4. QED + BY <3>1, <4>2, <4>3, FiniteSetHasMax + <3>3. c \in 0..(b-1) + OBVIOUS + <3>4. \A d \in (c+1)..(b-1) : d \in 0..(b-1) /\ ~(c >= d) + BY <3>3, SMT DEF Ballot (* This works *) + <3>5. \A d \in (c+1)..(b-1), a \in Q : DidNotVoteIn(a, d) + BY <3>2, <3>4 + <3>6. \E a \in Q : ~DidNotVoteIn(a, c) + BY <3>1 + <3>7. QED + BY <3>3, <3>5, <3>6 + <2>2. (c \in -1..(b-1)) /\ (c # -1) /\ (c \in Ballot) + BY SMT DEF Ballot + <2>3. PICK a0 \in Q : ~DidNotVoteIn(a0, c) + BY <2>1 + <2>4. PICK v \in Value : VotedFor(a0, c, v) + BY <2>3 DEF DidNotVoteIn + <2>5. \A a \in Q : \A w \in Value : + VotedFor(a, c, w) => (w = v) + BY <2>2, <2>4, QA, <1>1 DEF VInv3 + <2>6. SafeAt(c, v) + BY <1>1, <2>4, QA, <2>2 DEF VInv2 + <2>7. QED + BY <2>1, <2>2, <2>5, <2>6 + +<1>7. QED + BY <1>5, <1>6 + +LiveAssumption == + \E Q \in Quorum, b \in Ballot : + \A self \in Q : + /\ WF_vars(BallotAction(self, b)) + /\ [] [\A c \in Ballot : (c > b) => ~ BallotAction(self, c)]_vars + +LiveSpec == Spec /\ LiveAssumption +=============================================================================== diff --git a/tlatools/test-model/checkpoint/InfiniteStateSpace.cfg b/tlatools/test-model/checkpoint/InfiniteStateSpace.cfg new file mode 100644 index 0000000000000000000000000000000000000000..bc224b9def594b0819de3f0b45ede63ea8213c7b --- /dev/null +++ b/tlatools/test-model/checkpoint/InfiniteStateSpace.cfg @@ -0,0 +1,2 @@ +SPECIFICATION +Spec diff --git a/tlatools/test-model/checkpoint/InfiniteStateSpace.tla b/tlatools/test-model/checkpoint/InfiniteStateSpace.tla new file mode 100644 index 0000000000000000000000000000000000000000..89ae5a5ca869a16f69470d257748f81ae17643dc --- /dev/null +++ b/tlatools/test-model/checkpoint/InfiniteStateSpace.tla @@ -0,0 +1,7 @@ +------------------------------ MODULE InfiniteStateSpace ------------------------------ +EXTENDS Naturals + +VARIABLES x,y + +Spec == x = 0 /\ y \in 1..1000 /\ [][x'= x + 1 /\ UNCHANGED y]_<<x,y>> +============================================================================= diff --git a/tlatools/test-model/coverage/A.cfg b/tlatools/test-model/coverage/A.cfg new file mode 100644 index 0000000000000000000000000000000000000000..e3e8c3d2505a0163440775d92531a92d4321811c --- /dev/null +++ b/tlatools/test-model/coverage/A.cfg @@ -0,0 +1,2 @@ +INIT Init +NEXT Next diff --git a/tlatools/test-model/coverage/A.tla b/tlatools/test-model/coverage/A.tla new file mode 100644 index 0000000000000000000000000000000000000000..4032c98fe581a8eafd84e2188acff006e044da8d --- /dev/null +++ b/tlatools/test-model/coverage/A.tla @@ -0,0 +1,18 @@ +---------------------------- MODULE A ---------------------------- +EXTENDS Naturals, FiniteSets +VARIABLE x + +Op1(S) == \A e \in SUBSET S : Cardinality(e) >= 0 + +Init == /\ x = 0 + /\ Op1(1..17) + +\* Max(s) +Op2(n) == CHOOSE i \in 1..n : \A j \in ((1..n) \ {i}) : i > j + +A(n) == x' = Op2(n) + +B(n) == x' = Op2(n) + +Next == A(10) \/ B(3) +============================================================================= diff --git a/tlatools/test-model/coverage/B.cfg b/tlatools/test-model/coverage/B.cfg new file mode 100644 index 0000000000000000000000000000000000000000..e3e8c3d2505a0163440775d92531a92d4321811c --- /dev/null +++ b/tlatools/test-model/coverage/B.cfg @@ -0,0 +1,2 @@ +INIT Init +NEXT Next diff --git a/tlatools/test-model/coverage/B.tla b/tlatools/test-model/coverage/B.tla new file mode 100644 index 0000000000000000000000000000000000000000..53b166455fbc62956435386512ff9894862cc0cf --- /dev/null +++ b/tlatools/test-model/coverage/B.tla @@ -0,0 +1,13 @@ +---------------------------- MODULE B ---------------------------- +VARIABLE x + +Init == x = FALSE + +Switch(var) == ~var + +A == x' = Switch(x) + +B == x' = Switch(x) + +Next == A \/ B +============================================================================= diff --git a/tlatools/test-model/coverage/C.cfg b/tlatools/test-model/coverage/C.cfg new file mode 100644 index 0000000000000000000000000000000000000000..123888c29add0dae20544eefc45e779311ecaa30 --- /dev/null +++ b/tlatools/test-model/coverage/C.cfg @@ -0,0 +1,4 @@ +SPECIFICATION Spec +CONSTRAINT Constraint +INVARIANT Inv +INVARIANT Inv2 diff --git a/tlatools/test-model/coverage/C.tla b/tlatools/test-model/coverage/C.tla new file mode 100644 index 0000000000000000000000000000000000000000..0ee2d5da27eb2a5d279283e9e20a21a6ddc796a5 --- /dev/null +++ b/tlatools/test-model/coverage/C.tla @@ -0,0 +1,63 @@ +---------------------------- MODULE C ---------------------------- +EXTENDS Naturals, FiniteSets +VARIABLES x,y + +c == x +d == y + +vars == <<x,y>> + +a == x' + +Op(S) == \A e \in SUBSET S : Cardinality(e) >= 0 + +Init == /\ x \in 1..3 + /\ y = 0 + +A == /\ c \in Nat + /\ y \in Nat + /\ a = x + 1 + /\ UNCHANGED y + +B == /\ x \in Nat + /\ Op(1..5) + /\ UNCHANGED <<x,d>> + +C == /\ x = 42 + /\ c' = TRUE + /\ y' = FALSE + +D == x' \in 0..x /\ UNCHANGED y + +U1 == x < 0 /\ UNCHANGED vars + +U2 == x < 0 /\ UNCHANGED <<x,y>> + +U3 == x < 0 /\ UNCHANGED x /\ UNCHANGED y + +Next == A \/ B \/ C \/ D \/ U1 \/ U2 \/ U3 + +Spec == Init /\ [][Next]_vars + +Constraint == x < 20 + +(* Coverage/Cost statistics for Invariants too. *) + +expensive(N) == \A e \in SUBSET (1..N) : \A f \in e : f \in 1..N + +Inv == /\ x \in Nat + /\ y \in Nat + /\ expensive(8) + /\ LET frob == TRUE + IN /\ frob = TRUE + /\ \A i \in 1..5: i \in Nat + /\ 42 \in Nat + +Inv2 == /\ x \in Nat + /\ y \in Nat + /\ expensive(9) + /\ LET frob == TRUE + IN /\ frob = TRUE + /\ \A i \in 1..5: i \in Nat + /\ 42 \in Nat +============================================================================= diff --git a/tlatools/test-model/coverage/CoverageStatistics.cfg b/tlatools/test-model/coverage/CoverageStatistics.cfg new file mode 100644 index 0000000000000000000000000000000000000000..46c09d3371bbe6fa743e70c8adcdb79c89ea0723 --- /dev/null +++ b/tlatools/test-model/coverage/CoverageStatistics.cfg @@ -0,0 +1,4 @@ +CONSTRAINT +Constraint +SPECIFICATION +Spec diff --git a/tlatools/test-model/coverage/CoverageStatistics.tla b/tlatools/test-model/coverage/CoverageStatistics.tla new file mode 100644 index 0000000000000000000000000000000000000000..0eb6b975f2838a9e21a6ed2f5fe2e5dbd1f560f0 --- /dev/null +++ b/tlatools/test-model/coverage/CoverageStatistics.tla @@ -0,0 +1,50 @@ +---------------------------- MODULE CoverageStatistics ---------------------------- +EXTENDS Naturals, FiniteSets +VARIABLES x,y + +c == x +d == y + +vars == <<x,y>> + +a == x' + +Init == /\ x \in 1..3 + /\ y = 0 + +A == /\ c \in Nat + /\ y \in Nat + /\ a = x + 1 + /\ UNCHANGED d \* 18 should be covered (6 should be omitted) + +B == /\ x \in Nat + /\ UNCHANGED <<x,d>> \* 21 and 6 should be covered (6 should actually be omitted) + +B2 == /\ x \in Nat + /\ UNCHANGED vars \* 21 and 6 should be covered (6 should actually be omitted) + +C == /\ x = 42 + /\ c' = TRUE + /\ y' = FALSE + +U1 == x < 0 /\ UNCHANGED vars \* vars should be reported as uncovered instead of x and y on line 8 + +U2 == x < 0 /\ UNCHANGED <<x,y>> \* col 28 & 30 + +U3 == x < 0 /\ UNCHANGED x /\ UNCHANGED y \* col 26 & 41 + +U4 == x < 0 /\ UNCHANGED x /\ UNCHANGED d (* d itself instead of y is marked uncovered *) + +UC1 == x \in Nat /\ UNCHANGED <<x,y>> + +UC2 == x \in Nat /\ UNCHANGED x /\ UNCHANGED y + +UC3 == x \in Nat /\ UNCHANGED vars \* Do not mark vars on line 8 as covered + +Next == A \/ B \/ C \/ U1 \/ U2 \/ U3 \/ U4 \/ UC1 \/ UC2 \/ UC3 + +Spec == Init /\ [][Next]_vars + +Constraint == x < 20 + +============================================================================= diff --git a/tlatools/test-model/coverage/D.cfg b/tlatools/test-model/coverage/D.cfg new file mode 100644 index 0000000000000000000000000000000000000000..4d07be4bdd383dc53e46a5cbb701afa9dba3c30d --- /dev/null +++ b/tlatools/test-model/coverage/D.cfg @@ -0,0 +1 @@ +SPECIFICATION Spec diff --git a/tlatools/test-model/coverage/D.tla b/tlatools/test-model/coverage/D.tla new file mode 100644 index 0000000000000000000000000000000000000000..41865d9289b8294117b0c79115a775d686b2354c --- /dev/null +++ b/tlatools/test-model/coverage/D.tla @@ -0,0 +1,18 @@ +---------------------------- MODULE D ---------------------------- +EXTENDS Naturals +VARIABLE x + +Init == x = 0 + +(* No endless recursion in Coverage/Cost with RECURSIVE. *) +RECURSIVE fact(_) +fact(n) == IF n = 1 THEN 1 ELSE n * fact(n - 1) + +A == x' = fact(3) + +B == x' = fact(9) + +Next == A \/ B + +Spec == Init /\ [][Next]_<<x>> +============================================================================= diff --git a/tlatools/test-model/coverage/E.cfg b/tlatools/test-model/coverage/E.cfg new file mode 100644 index 0000000000000000000000000000000000000000..4ebe91bd18e2e5bbb5152db4a9abbaa2ea951bba --- /dev/null +++ b/tlatools/test-model/coverage/E.cfg @@ -0,0 +1,3 @@ +INIT Init +NEXT Next +CONSTANT Op <- Forty2 diff --git a/tlatools/test-model/coverage/E.tla b/tlatools/test-model/coverage/E.tla new file mode 100644 index 0000000000000000000000000000000000000000..fc774b97d8293f62bfd18ed6fad393fe826402fd --- /dev/null +++ b/tlatools/test-model/coverage/E.tla @@ -0,0 +1,13 @@ +---------------------------- MODULE E ---------------------------- +CONSTANT Op(_) + +VARIABLE x + +Id(n) == SUBSET {1,2,3} + +Forty2(n) == Id(n) + +Init == x = 0 + +Next == x' \in Op(x) +============================================================================= diff --git a/tlatools/test-model/coverage/F.cfg b/tlatools/test-model/coverage/F.cfg new file mode 100644 index 0000000000000000000000000000000000000000..e3e8c3d2505a0163440775d92531a92d4321811c --- /dev/null +++ b/tlatools/test-model/coverage/F.cfg @@ -0,0 +1,2 @@ +INIT Init +NEXT Next diff --git a/tlatools/test-model/coverage/F.tla b/tlatools/test-model/coverage/F.tla new file mode 100644 index 0000000000000000000000000000000000000000..3d868458608285b71c5bb346e6b96abd43946d03 --- /dev/null +++ b/tlatools/test-model/coverage/F.tla @@ -0,0 +1,11 @@ +---------------------------- MODULE F ---------------------------- +EXTENDS Naturals + +VARIABLE x + +Op(S, P(_), Q(_, _)) == { s \in S : P(s) /\ Q(s, TRUE) } + +Init == x \in Op({1,2,3,4,5}, LAMBDA s: s > 1, LAMBDA odd, b: odd % 2 # 0 /\ b) + +Next == UNCHANGED x +============================================================================= diff --git a/tlatools/test-model/coverage/G.cfg b/tlatools/test-model/coverage/G.cfg new file mode 100644 index 0000000000000000000000000000000000000000..9b60b87092e262903968bd979325348721290efb --- /dev/null +++ b/tlatools/test-model/coverage/G.cfg @@ -0,0 +1,3 @@ +INIT Init +NEXT Next +INVARIANT Prop \ No newline at end of file diff --git a/tlatools/test-model/coverage/G.tla b/tlatools/test-model/coverage/G.tla new file mode 100644 index 0000000000000000000000000000000000000000..d4592b9989c9b5602bc6f5a5fb4885d40880d006 --- /dev/null +++ b/tlatools/test-model/coverage/G.tla @@ -0,0 +1,13 @@ +---------------------------- MODULE G ---------------------------- +VARIABLES u1 + +vars == << u1 >> + +Init == /\ u1 = TRUE + +Next == /\ UNCHANGED vars + /\ UNCHANGED <<u1>> + /\ UNCHANGED u1 + +Prop == ENABLED Next +============================================================================= diff --git a/tlatools/test-model/coverage/Github314.cfg b/tlatools/test-model/coverage/Github314.cfg new file mode 100644 index 0000000000000000000000000000000000000000..dfd087d6b1871aee9e04413d19ed06afc6318409 --- /dev/null +++ b/tlatools/test-model/coverage/Github314.cfg @@ -0,0 +1,5 @@ +CONSTANT +Nat <- [Naturals]MyNat +\* No bug with Nat <- MyNat +SPECIFICATION +Spec diff --git a/tlatools/test-model/coverage/Github314.tla b/tlatools/test-model/coverage/Github314.tla new file mode 100644 index 0000000000000000000000000000000000000000..0df9fae4db25d8d9034a8c11560e4753dff2fb5c --- /dev/null +++ b/tlatools/test-model/coverage/Github314.tla @@ -0,0 +1,21 @@ +---- MODULE Github314 ---- +EXTENDS Naturals + +\* Begin nested (base) module + ---- MODULE Base ---- + EXTENDS Naturals + + VARIABLES x + + Spec == x = 0 /\ [][(x \in Nat /\ x' = x + 1)]_x + ===== +\* End nested (base) module + +VARIABLES x + +Spec == x = 0 /\ [][(x \in Nat /\ x' = x + 1)]_x + +F == INSTANCE Base + +MyNat == 0..1 +==== diff --git a/tlatools/test-model/coverage/Github377.cfg b/tlatools/test-model/coverage/Github377.cfg new file mode 100644 index 0000000000000000000000000000000000000000..5ace4490d0a5010629d4458e00586ad1a7fc3011 --- /dev/null +++ b/tlatools/test-model/coverage/Github377.cfg @@ -0,0 +1,11 @@ +SPECIFICATION +Spec +INVARIANT +1Inv +1InvNonRec +2Inv +2aInv +2bInv +3aInv +3bInv +4Inv diff --git a/tlatools/test-model/coverage/Github377.tla b/tlatools/test-model/coverage/Github377.tla new file mode 100644 index 0000000000000000000000000000000000000000..0f7b12a0fc32ec4ce7fbcfa1d30019dc485bdf5a --- /dev/null +++ b/tlatools/test-model/coverage/Github377.tla @@ -0,0 +1,75 @@ +---- MODULE Github377 ---- +EXTENDS Integers +VARIABLES x + +Spec == x = TRUE /\ [][UNCHANGED x]_x + +-------------- + +1Inv == + LET recfcn[ i \in {1,2,3} ] == IF i = 1 THEN x = TRUE ELSE recfcn[1] + IN recfcn[1] + +-------------- + +1InvNonRec == + LET recfcn[ i \in {1,2,3} ] == x = TRUE + IN recfcn[1] + +-------------- + +2Inv == + LET outer == + LET innerA[ i \in {1} ] == IF i = 1 THEN x = TRUE ELSE innerA[i] + innerB[ i \in {1} ] == innerA[i] + IN innerB[1] /\ 42 = 31 + 11 /\ innerA[1] + IN outer + +2aInv == + LET outer == + LET innerA[ i \in {1} ] == IF i = 1 THEN x = TRUE ELSE innerA[i] + innerB[ i \in {1} ] == innerA[i] + IN /\ innerA[1] + /\ innerB[1] + IN outer + +2bInv == + LET outer == + LET innerA[ i \in {1} ] == IF i = 1 THEN x = TRUE ELSE innerA[i] + innerB[ i \in {1} ] == innerA[i] + IN innerA[1] /\ innerB[1] + IN outer +-------------- + +3aInv == + LET outer == + LET innerA[ i \in {1} ] == IF i = 1 THEN x = TRUE ELSE innerA[i] + innerB[ i \in {1} ] == innerA[1] + IN /\ LET foo(i) == innerA[i] + IN foo(1) + /\ innerB[1] + IN outer + +-------------- + +3bInv == + LET outer == + LET innerA[ i \in {1} ] == IF i = 1 THEN x = TRUE ELSE innerA[i] + innerB[ i \in {1} ] == innerA[1] + IN LET foo(i) == innerA[i] + IN foo(1) /\ innerB[1] + IN outer + + +-------------- + +4Inv == + LET outer == + LET inner[ i \in {1} ] == + LET innerst[ j \in {1} ] == x = TRUE + IN innerst[1] + IN inner[1] + IN outer + +==== + \ No newline at end of file diff --git a/tlatools/test-model/coverage/H.cfg b/tlatools/test-model/coverage/H.cfg new file mode 100644 index 0000000000000000000000000000000000000000..bc224b9def594b0819de3f0b45ede63ea8213c7b --- /dev/null +++ b/tlatools/test-model/coverage/H.cfg @@ -0,0 +1,2 @@ +SPECIFICATION +Spec diff --git a/tlatools/test-model/coverage/H.tla b/tlatools/test-model/coverage/H.tla new file mode 100644 index 0000000000000000000000000000000000000000..cac9a4d2f35392b104ad29504b7b05a6d39382e3 --- /dev/null +++ b/tlatools/test-model/coverage/H.tla @@ -0,0 +1,27 @@ +------------------------------ MODULE H ------------------------------ +EXTENDS Naturals +VARIABLES x + +MyNat == 0..30 + +TypeOK == x \in MyNat + +Init == x = 0 + +A == x' = 23 + +BandC == \/ /\ x \in MyNat + /\ x' \in 23..25 + \/ /\ x < 25 + /\ x' = 26 + +DandE == (x = 23 /\ x' = 42) \/ (x = 123 /\ x' = 4711) + +Next == A \/ BandC \/ DandE + +Inv == /\ TypeOK + /\ x = 0 + +Spec == Inv /\ [][Next]_x + +============ diff --git a/tlatools/test-model/coverage/I.cfg b/tlatools/test-model/coverage/I.cfg new file mode 100644 index 0000000000000000000000000000000000000000..c8cffbe64c3dcc8bce148218f2e7b66a7537f573 --- /dev/null +++ b/tlatools/test-model/coverage/I.cfg @@ -0,0 +1,4 @@ +SPECIFICATION +Spec +INVARIANT +Inv \ No newline at end of file diff --git a/tlatools/test-model/coverage/I.tla b/tlatools/test-model/coverage/I.tla new file mode 100644 index 0000000000000000000000000000000000000000..b72e8a26860c80abda1862adfb64e61b1d5bc746 --- /dev/null +++ b/tlatools/test-model/coverage/I.tla @@ -0,0 +1,15 @@ +------------------------------ MODULE I ------------------------------ +EXTENDS Naturals +VARIABLES x + +F[ i \in {1,2,3,4,5} ] == + IF i = 1 THEN 1 ELSE F[i-1] + 1 \* Recursive Function Definition + +N[n \in {1,2,3}] == + /\ UNCHANGED <<x>> + +Spec == x \in {1,2,3,4,5} /\ [][\E i \in {1,2,3} : N[i] ]_x + +Inv == \E i \in DOMAIN F: F[i] = x + +============ diff --git a/tlatools/test-model/coverage/J.cfg b/tlatools/test-model/coverage/J.cfg new file mode 100644 index 0000000000000000000000000000000000000000..bc224b9def594b0819de3f0b45ede63ea8213c7b --- /dev/null +++ b/tlatools/test-model/coverage/J.cfg @@ -0,0 +1,2 @@ +SPECIFICATION +Spec diff --git a/tlatools/test-model/coverage/J.tla b/tlatools/test-model/coverage/J.tla new file mode 100644 index 0000000000000000000000000000000000000000..34bb20b7f7c23456c35811cf5d08af79564299cb --- /dev/null +++ b/tlatools/test-model/coverage/J.tla @@ -0,0 +1,16 @@ +------------------------------ MODULE J ------------------------------ + +VARIABLES x, y + +Init == /\ x \in {1,2,3,4,5} + /\ y = [i \in {1,2,3,4,5} |-> 0] + +Foo(rcds) == + CASE x = 1 -> [ rcds EXCEPT ![x] = 42 ] + [] OTHER -> rcds + +Next == /\ y' = Foo(y) + /\ UNCHANGED x + +Spec == Init /\ [][Next]_<<x,y>> +============ diff --git a/tlatools/test-model/coverage/K.cfg b/tlatools/test-model/coverage/K.cfg new file mode 100644 index 0000000000000000000000000000000000000000..bc224b9def594b0819de3f0b45ede63ea8213c7b --- /dev/null +++ b/tlatools/test-model/coverage/K.cfg @@ -0,0 +1,2 @@ +SPECIFICATION +Spec diff --git a/tlatools/test-model/coverage/K.tla b/tlatools/test-model/coverage/K.tla new file mode 100644 index 0000000000000000000000000000000000000000..dfbaa8c4ca1fdb2a3de733475931cd63cd5c6d80 --- /dev/null +++ b/tlatools/test-model/coverage/K.tla @@ -0,0 +1,12 @@ +------------------------------ MODULE K ------------------------------ + +EXTENDS Naturals + +Foo(Op(_), S) == \A s \in S: Op(s) + +Bar(S) == Foo(LAMBDA e: e \in Nat, S) + +VARIABLE x + +Spec == x = Bar({1,2,3}) /\ [][UNCHANGED x]_x +============ diff --git a/tlatools/test-model/coverage/L.cfg b/tlatools/test-model/coverage/L.cfg new file mode 100644 index 0000000000000000000000000000000000000000..bc224b9def594b0819de3f0b45ede63ea8213c7b --- /dev/null +++ b/tlatools/test-model/coverage/L.cfg @@ -0,0 +1,2 @@ +SPECIFICATION +Spec diff --git a/tlatools/test-model/coverage/L.tla b/tlatools/test-model/coverage/L.tla new file mode 100644 index 0000000000000000000000000000000000000000..b0fd28896fcb930b0c72d80c2406e7278528fa5a --- /dev/null +++ b/tlatools/test-model/coverage/L.tla @@ -0,0 +1,10 @@ +------------------------------ MODULE L ------------------------------ +EXTENDS Naturals + +VARIABLE v + +ChooseOne(S, P(_)) == CHOOSE x \in S : P(x) /\ \A y \in S : P(y) + +Spec == v = ChooseOne({1}, LAMBDA x : TRUE) /\ [][UNCHANGED v]_v + +============ diff --git a/tlatools/test-model/pcal/.gitignore b/tlatools/test-model/pcal/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..9c80bdb55a33e4c24f2350408a3b543eb16da4e9 --- /dev/null +++ b/tlatools/test-model/pcal/.gitignore @@ -0,0 +1,52 @@ +*.old +/MPNoParams.cfg +/CallReturn1.cfg +/Euclid2.cfg +/EvenOdd.cfg +/EvenOddBad.cfg +/Factorial.cfg +/Factorial2.cfg +/InnerLabeledIf.cfg +/MultiAssignment.cfg +/MultiProc2.cfg +/MultiprocDefine.cfg +/NoLoop.cfg +/NoLoop2.cfg +/NoParams.cfg +/NotSoSimpleLoop.cfg +/Quicksort.cfg +/SimpleLoop.cfg +/SimpleLoopWithProcedure.cfg +/SimpleMultiProc.cfg +/TestTabs.cfg +/UniprocDefine.cfg +/bug_05_10_03.cfg +/bug_05_12_10a.cfg +/bug_05_12_16b.cfg +/bug_05_12_31.cfg +/bug_06_01_25.cfg +/bug_07_03_30.cfg +/bug_07_06_05.cfg +/Either1.cfg +/Either2.cfg +/Either3.cfg +/Either4.cfg +/Either5.cfg +/Euclid3.cfg +/MPFactorial.cfg +/MPFactorial2.cfg +/TestReplace.cfg +/SubSub.cfg +/TestPCandStack.cfg +/Test.cfg +/ULCallReturn1.cfg +/ULFactorial2.cfg +/ReallySimpleMultiProc.cfg +/NestedMacros.cfg +/FairSeq2.cfg +/DiningPhilosophers2.cfg +/CallReturn2.cfg +/CCallReturn1.cfg +/CMultiprocDefine.cfg +/CEither1.cfg +/StackTest.cfg diff --git a/tlatools/test-model/pcal/Bakery.cfg b/tlatools/test-model/pcal/Bakery.cfg new file mode 100644 index 0000000000000000000000000000000000000000..9209a00c6288aab93977bdf9e85b204efd16702f --- /dev/null +++ b/tlatools/test-model/pcal/Bakery.cfg @@ -0,0 +1,7 @@ +SPECIFICATION Spec +\* Add statements after this line. +CONSTANTS NumProcs = 2 + MaxNum = 3 +INVARIANT Invariant +CONSTRAINT Constraint + diff --git a/tlatools/test-model/pcal/Bakery.tla b/tlatools/test-model/pcal/Bakery.tla new file mode 100644 index 0000000000000000000000000000000000000000..6eafbb39d21cc9c449463147344c19bac3d1c23b --- /dev/null +++ b/tlatools/test-model/pcal/Bakery.tla @@ -0,0 +1,139 @@ +------------------------------- MODULE Bakery -------------------------------- +EXTENDS Naturals, TLC + +CONSTANTS NumProcs, \* The number of processes + MaxNum \* For TLC, the maximum value of num[i]. + +Proc == 1..NumProcs \* The set of processes + +(* + + --algorithm Bakery + variable num = [i \in Proc |-> 0] ; + choosing = [i \in Proc |-> FALSE]; + process proc \in Proc + variables read = { }; max = 0 ; nxt = self ; + begin loop : while TRUE + do choosing[self] := TRUE; + read := { self }; + max := 0 ; + d1 : while read # Proc + do with p \in Proc \ read + do if num[p] > max + then max := num[p]; + end if ; + read := read \cup {p}; + end with ; + end while ; + d2 : num[self] := max + 1 ; + d3 : choosing[self] := FALSE ; + read := { self } ; + w1 : while read # Proc + do with p \in Proc \ read + do when ~ choosing[p] ; + nxt := p ; + end with; + w2: when \/ num[nxt] = 0 + \/ num[nxt] > num[self] + \/ /\ num[nxt] = num[self] + /\ nxt > self ; + read := read \cup { nxt } ; + end while; + cs : when TRUE ; + exit : num[self] := 0; + end while; + end process + end algorithm + + +*) + +\* BEGIN TRANSLATION - the hash of the PCal code: PCal-3787a86d71fe978e1e68a7e275a515ae +VARIABLES num, choosing, pc, read, max, nxt + +vars == << num, choosing, pc, read, max, nxt >> + +ProcSet == (Proc) + +Init == (* Global variables *) + /\ num = [i \in Proc |-> 0] + /\ choosing = [i \in Proc |-> FALSE] + (* Process proc *) + /\ read = [self \in Proc |-> { }] + /\ max = [self \in Proc |-> 0] + /\ nxt = [self \in Proc |-> self] + /\ pc = [self \in ProcSet |-> "loop"] + +loop(self) == /\ pc[self] = "loop" + /\ choosing' = [choosing EXCEPT ![self] = TRUE] + /\ read' = [read EXCEPT ![self] = { self }] + /\ max' = [max EXCEPT ![self] = 0] + /\ pc' = [pc EXCEPT ![self] = "d1"] + /\ UNCHANGED << num, nxt >> + +d1(self) == /\ pc[self] = "d1" + /\ IF read[self] # Proc + THEN /\ \E p \in Proc \ read[self]: + /\ IF num[p] > max[self] + THEN /\ max' = [max EXCEPT ![self] = num[p]] + ELSE /\ TRUE + /\ max' = max + /\ read' = [read EXCEPT ![self] = read[self] \cup {p}] + /\ pc' = [pc EXCEPT ![self] = "d1"] + ELSE /\ pc' = [pc EXCEPT ![self] = "d2"] + /\ UNCHANGED << read, max >> + /\ UNCHANGED << num, choosing, nxt >> + +d2(self) == /\ pc[self] = "d2" + /\ num' = [num EXCEPT ![self] = max[self] + 1] + /\ pc' = [pc EXCEPT ![self] = "d3"] + /\ UNCHANGED << choosing, read, max, nxt >> + +d3(self) == /\ pc[self] = "d3" + /\ choosing' = [choosing EXCEPT ![self] = FALSE] + /\ read' = [read EXCEPT ![self] = { self }] + /\ pc' = [pc EXCEPT ![self] = "w1"] + /\ UNCHANGED << num, max, nxt >> + +w1(self) == /\ pc[self] = "w1" + /\ IF read[self] # Proc + THEN /\ \E p \in Proc \ read[self]: + /\ ~ choosing[p] + /\ nxt' = [nxt EXCEPT ![self] = p] + /\ pc' = [pc EXCEPT ![self] = "w2"] + ELSE /\ pc' = [pc EXCEPT ![self] = "cs"] + /\ nxt' = nxt + /\ UNCHANGED << num, choosing, read, max >> + +w2(self) == /\ pc[self] = "w2" + /\ \/ num[nxt[self]] = 0 + \/ num[nxt[self]] > num[self] + \/ /\ num[nxt[self]] = num[self] + /\ nxt[self] > self + /\ read' = [read EXCEPT ![self] = read[self] \cup { nxt[self] }] + /\ pc' = [pc EXCEPT ![self] = "w1"] + /\ UNCHANGED << num, choosing, max, nxt >> + +cs(self) == /\ pc[self] = "cs" + /\ TRUE + /\ pc' = [pc EXCEPT ![self] = "exit"] + /\ UNCHANGED << num, choosing, read, max, nxt >> + +exit(self) == /\ pc[self] = "exit" + /\ num' = [num EXCEPT ![self] = 0] + /\ pc' = [pc EXCEPT ![self] = "loop"] + /\ UNCHANGED << choosing, read, max, nxt >> + +proc(self) == loop(self) \/ d1(self) \/ d2(self) \/ d3(self) \/ w1(self) + \/ w2(self) \/ cs(self) \/ exit(self) + +Next == (\E self \in Proc: proc(self)) + +Spec == Init /\ [][Next]_vars + +\* END TRANSLATION - the hash of the generated TLA code (remove to silence divergence warnings): TLA-044cd15e6eddcf8edc1cfa67089d17cf + +Constraint == \A i \in Proc : num[i] \leq MaxNum + +Invariant == \A i, j \in Proc : (pc[i] = "cs") /\ (pc[j] = "cs") => (i = j) +============================================================================= diff --git a/tlatools/test-model/pcal/CBakery.cfg b/tlatools/test-model/pcal/CBakery.cfg new file mode 100644 index 0000000000000000000000000000000000000000..160ae67b63d374a55ca52cb9be22f91b17a00535 --- /dev/null +++ b/tlatools/test-model/pcal/CBakery.cfg @@ -0,0 +1,5 @@ +SPECIFICATION Spec +\* Add statements after this line. +CONSTANT NumProcs = 2 MaxNum = 2 +INVARIANT Invariant +CONSTRAINT Constraint diff --git a/tlatools/test-model/pcal/CBakery.tla b/tlatools/test-model/pcal/CBakery.tla new file mode 100644 index 0000000000000000000000000000000000000000..02cbf446cfc7880e483e2a18993961a5700422f3 --- /dev/null +++ b/tlatools/test-model/pcal/CBakery.tla @@ -0,0 +1,128 @@ +------------------------------- MODULE CBakery ------------------------------- +EXTENDS Naturals, TLC + +CONSTANTS NumProcs, \* The number of processes + MaxNum \* For TLC, the maximum value of num[i]. + +Proc == 1..NumProcs \* The set of processes + +(******** + --algorithm Bakery { + variable num = [i \in Proc |-> 0] ; + choosing = [i \in Proc |-> FALSE]; + process (proc \in Proc) + variables read = { }; max = 0 ; nxt = self ; + { loop : while (TRUE) { + choosing[self] := TRUE; + read := { self }; + max := 0 ; + d1 : while (read # Proc) + with (p \in Proc \ read) { + if (num[p] > max) max := num[p]; + read := read \cup {p} } ; + d2 : num[self] := max + 1 ; + d3 : choosing[self] := FALSE ; + read := { self } ; + w1 : while (read # Proc) { + with (p \in Proc \ read) { + when ~ choosing[p] ; + nxt := p } ; + w2: when \/ num[nxt] = 0 + \/ num[nxt] > num[self] + \/ /\ num[nxt] = num[self] + /\ nxt > self ; + read := read \cup { nxt } }; + cs : when TRUE ; + exit : num[self] := 0; } } } +********) + + +\* BEGIN TRANSLATION - the hash of the PCal code: PCal-3787a86d71fe978e1e68a7e275a515ae +VARIABLES num, choosing, pc, read, max, nxt + +vars == << num, choosing, pc, read, max, nxt >> + +ProcSet == (Proc) + +Init == (* Global variables *) + /\ num = [i \in Proc |-> 0] + /\ choosing = [i \in Proc |-> FALSE] + (* Process proc *) + /\ read = [self \in Proc |-> { }] + /\ max = [self \in Proc |-> 0] + /\ nxt = [self \in Proc |-> self] + /\ pc = [self \in ProcSet |-> "loop"] + +loop(self) == /\ pc[self] = "loop" + /\ choosing' = [choosing EXCEPT ![self] = TRUE] + /\ read' = [read EXCEPT ![self] = { self }] + /\ max' = [max EXCEPT ![self] = 0] + /\ pc' = [pc EXCEPT ![self] = "d1"] + /\ UNCHANGED << num, nxt >> + +d1(self) == /\ pc[self] = "d1" + /\ IF read[self] # Proc + THEN /\ \E p \in Proc \ read[self]: + /\ IF num[p] > max[self] + THEN /\ max' = [max EXCEPT ![self] = num[p]] + ELSE /\ TRUE + /\ max' = max + /\ read' = [read EXCEPT ![self] = read[self] \cup {p}] + /\ pc' = [pc EXCEPT ![self] = "d1"] + ELSE /\ pc' = [pc EXCEPT ![self] = "d2"] + /\ UNCHANGED << read, max >> + /\ UNCHANGED << num, choosing, nxt >> + +d2(self) == /\ pc[self] = "d2" + /\ num' = [num EXCEPT ![self] = max[self] + 1] + /\ pc' = [pc EXCEPT ![self] = "d3"] + /\ UNCHANGED << choosing, read, max, nxt >> + +d3(self) == /\ pc[self] = "d3" + /\ choosing' = [choosing EXCEPT ![self] = FALSE] + /\ read' = [read EXCEPT ![self] = { self }] + /\ pc' = [pc EXCEPT ![self] = "w1"] + /\ UNCHANGED << num, max, nxt >> + +w1(self) == /\ pc[self] = "w1" + /\ IF read[self] # Proc + THEN /\ \E p \in Proc \ read[self]: + /\ ~ choosing[p] + /\ nxt' = [nxt EXCEPT ![self] = p] + /\ pc' = [pc EXCEPT ![self] = "w2"] + ELSE /\ pc' = [pc EXCEPT ![self] = "cs"] + /\ nxt' = nxt + /\ UNCHANGED << num, choosing, read, max >> + +w2(self) == /\ pc[self] = "w2" + /\ \/ num[nxt[self]] = 0 + \/ num[nxt[self]] > num[self] + \/ /\ num[nxt[self]] = num[self] + /\ nxt[self] > self + /\ read' = [read EXCEPT ![self] = read[self] \cup { nxt[self] }] + /\ pc' = [pc EXCEPT ![self] = "w1"] + /\ UNCHANGED << num, choosing, max, nxt >> + +cs(self) == /\ pc[self] = "cs" + /\ TRUE + /\ pc' = [pc EXCEPT ![self] = "exit"] + /\ UNCHANGED << num, choosing, read, max, nxt >> + +exit(self) == /\ pc[self] = "exit" + /\ num' = [num EXCEPT ![self] = 0] + /\ pc' = [pc EXCEPT ![self] = "loop"] + /\ UNCHANGED << choosing, read, max, nxt >> + +proc(self) == loop(self) \/ d1(self) \/ d2(self) \/ d3(self) \/ w1(self) + \/ w2(self) \/ cs(self) \/ exit(self) + +Next == (\E self \in Proc: proc(self)) + +Spec == Init /\ [][Next]_vars + +\* END TRANSLATION - the hash of the generated TLA code (remove to silence divergence warnings): TLA-044cd15e6eddcf8edc1cfa67089d17cf +Constraint +== \A i \in Proc : num[i] \leq MaxNum + +Invariant == \A i, j \in Proc : (pc[i] = "cs") /\ (pc[j] = "cs") => (i = j) +============================================================================= diff --git a/tlatools/test-model/pcal/CCallReturn1.tla b/tlatools/test-model/pcal/CCallReturn1.tla new file mode 100644 index 0000000000000000000000000000000000000000..ea517740ba64277e804b33a30f45477f3f01c74f --- /dev/null +++ b/tlatools/test-model/pcal/CCallReturn1.tla @@ -0,0 +1,124 @@ +------------------------------ MODULE CCallReturn1 -------------------------- +EXTENDS Sequences, Naturals, TLC + +(* + --algorithm CallReturn1 { + procedure Proc1(arg1 = 0) + variable u = 1 ; + { p1 : u := 2 ; + call Proc2 ( 2 * u ) ; + p2 : assert u = 2; + assert arg1 = 4 ; + call Proc2 ( 2 * u + 1 ) ; + return ; } + + procedure Proc2(arg2 = 0) + variable v = 42 ; + { q1 : assert v = 42; + assert arg2 \in {4, 5} ; + call Proc3 ( v + arg2 ) ; + return } + + procedure Proc3(arg3 = 0) + { r1 : assert arg3 \in {46, 47} ; + return ; } ; + + { + a1 : call Proc1( 4 ) ; + } } + +*) + +\* BEGIN TRANSLATION - the hash of the PCal code: PCal-eb6f25a53750950964fc51a79ea41da3 +VARIABLES pc, stack, arg1, u, arg2, v, arg3 + +vars == << pc, stack, arg1, u, arg2, v, arg3 >> + +Init == (* Procedure Proc1 *) + /\ arg1 = 0 + /\ u = 1 + (* Procedure Proc2 *) + /\ arg2 = 0 + /\ v = 42 + (* Procedure Proc3 *) + /\ arg3 = 0 + /\ stack = << >> + /\ pc = "a1" + +p1 == /\ pc = "p1" + /\ u' = 2 + /\ /\ arg2' = 2 * u' + /\ stack' = << [ procedure |-> "Proc2", + pc |-> "p2", + v |-> v, + arg2 |-> arg2 ] >> + \o stack + /\ v' = 42 + /\ pc' = "q1" + /\ UNCHANGED << arg1, arg3 >> + +p2 == /\ pc = "p2" + /\ Assert(u = 2, "Failure of assertion at line 10, column 18.") + /\ Assert(arg1 = 4, "Failure of assertion at line 11, column 18.") + /\ /\ arg2' = 2 * u + 1 + /\ stack' = << [ procedure |-> "Proc2", + pc |-> Head(stack).pc, + v |-> v, + arg2 |-> arg2 ] >> + \o Tail(stack) + /\ u' = Head(stack).u + /\ v' = 42 + /\ pc' = "q1" + /\ UNCHANGED << arg1, arg3 >> + +Proc1 == p1 \/ p2 + +q1 == /\ pc = "q1" + /\ Assert(v = 42, "Failure of assertion at line 17, column 18.") + /\ Assert(arg2 \in {4, 5}, + "Failure of assertion at line 18, column 18.") + /\ /\ arg3' = v + arg2 + /\ stack' = << [ procedure |-> "Proc3", + pc |-> Head(stack).pc, + arg3 |-> arg3 ] >> + \o Tail(stack) + /\ v' = Head(stack).v + /\ pc' = "r1" + /\ UNCHANGED << arg1, u, arg2 >> + +Proc2 == q1 + +r1 == /\ pc = "r1" + /\ Assert(arg3 \in {46, 47}, + "Failure of assertion at line 23, column 18.") + /\ pc' = Head(stack).pc + /\ arg3' = Head(stack).arg3 + /\ stack' = Tail(stack) + /\ UNCHANGED << arg1, u, arg2, v >> + +Proc3 == r1 + +a1 == /\ pc = "a1" + /\ /\ arg1' = 4 + /\ stack' = << [ procedure |-> "Proc1", + pc |-> "Done", + u |-> u, + arg1 |-> arg1 ] >> + \o stack + /\ u' = 1 + /\ pc' = "p1" + /\ UNCHANGED << arg2, v, arg3 >> + +(* Allow infinite stuttering to prevent deadlock on termination. *) +Terminating == pc = "Done" /\ UNCHANGED vars + +Next == Proc1 \/ Proc2 \/ Proc3 \/ a1 + \/ Terminating + +Spec == /\ Init /\ [][Next]_vars + /\ WF_vars(Next) + +Termination == <>(pc = "Done") + +\* END TRANSLATION - the hash of the generated TLA code (remove to silence divergence warnings): TLA-54b1efca8092525f1f0eb4eb06b502c9 +============================================================================= diff --git a/tlatools/test-model/pcal/CDiningPhilosophers.cfg b/tlatools/test-model/pcal/CDiningPhilosophers.cfg new file mode 100644 index 0000000000000000000000000000000000000000..3085671a0184b6469a675a0dc98a806ed20d917e --- /dev/null +++ b/tlatools/test-model/pcal/CDiningPhilosophers.cfg @@ -0,0 +1,4 @@ +SPECIFICATION Spec +\* Add statements after this line. +INVARIANT Invariant +CONSTANT N = 4 diff --git a/tlatools/test-model/pcal/CDiningPhilosophers.tla b/tlatools/test-model/pcal/CDiningPhilosophers.tla new file mode 100644 index 0000000000000000000000000000000000000000..18522fc18cc980a20847f76880fa8d4c13162415 --- /dev/null +++ b/tlatools/test-model/pcal/CDiningPhilosophers.tla @@ -0,0 +1,120 @@ +-------------------------- MODULE CDiningPhilosophers ------------------------ + +(***************************************************************************) +(* A simple solution to the Dining Philosophers problem in which processes *) +(* 1 through N-1 pick up their lef then their right forks, and process 0 *) +(* picks them up in the opposite order. *) +(***************************************************************************) + +EXTENDS Naturals + +CONSTANT N + +ASSUME N \in Nat + +(******** +--algorithm DiningPhilosophers { + variable sem = [i \in 0..(N-1) |-> 1] ; + +process (Proc \in 1..(N-1)) { +l1 : while (TRUE) { + { when sem[self] = 1 ; \* Get right fork. + sem[self] := 0 } ; + l2 : { when sem[(self-1) % N] = 1 ; \* Get left fork. + sem[(self-1) % N] := 0 } ; + e : { skip } ; \* Eat + l3 : { sem[self] := 1 } ; \* Release right fork. + l4 : { sem[(self-1) % N] := 1 } ; \* Release left fork. + }} + +process (Proc0 = 0) +{ +l01 : while (TRUE) + { when sem[N-1] = 1 ; \* get left fork + sem[N-1] := 0 ; + l02 : when sem[0] = 1 ; \* get right fork + sem[0] := 0 ; + e0 : skip ; \* eat + l03 : sem[0] := 1 ; \* release left fork + l04 : sem[N-1] := 1 ; \* release right fork + } +} } +********) +\* BEGIN TRANSLATION - the hash of the PCal code: PCal-d34a24305f241c923d0f8daa00682e02 +VARIABLES sem, pc + +vars == << sem, pc >> + +ProcSet == (1..(N-1)) \cup {0} + +Init == (* Global variables *) + /\ sem = [i \in 0..(N-1) |-> 1] + /\ pc = [self \in ProcSet |-> CASE self \in 1..(N-1) -> "l1" + [] self = 0 -> "l01"] + +l1(self) == /\ pc[self] = "l1" + /\ sem[self] = 1 + /\ sem' = [sem EXCEPT ![self] = 0] + /\ pc' = [pc EXCEPT ![self] = "l2"] + +l2(self) == /\ pc[self] = "l2" + /\ sem[(self-1) % N] = 1 + /\ sem' = [sem EXCEPT ![(self-1) % N] = 0] + /\ pc' = [pc EXCEPT ![self] = "e"] + +e(self) == /\ pc[self] = "e" + /\ TRUE + /\ pc' = [pc EXCEPT ![self] = "l3"] + /\ sem' = sem + +l3(self) == /\ pc[self] = "l3" + /\ sem' = [sem EXCEPT ![self] = 1] + /\ pc' = [pc EXCEPT ![self] = "l4"] + +l4(self) == /\ pc[self] = "l4" + /\ sem' = [sem EXCEPT ![(self-1) % N] = 1] + /\ pc' = [pc EXCEPT ![self] = "l1"] + +Proc(self) == l1(self) \/ l2(self) \/ e(self) \/ l3(self) \/ l4(self) + +l01 == /\ pc[0] = "l01" + /\ sem[N-1] = 1 + /\ sem' = [sem EXCEPT ![N-1] = 0] + /\ pc' = [pc EXCEPT ![0] = "l02"] + +l02 == /\ pc[0] = "l02" + /\ sem[0] = 1 + /\ sem' = [sem EXCEPT ![0] = 0] + /\ pc' = [pc EXCEPT ![0] = "e0"] + +e0 == /\ pc[0] = "e0" + /\ TRUE + /\ pc' = [pc EXCEPT ![0] = "l03"] + /\ sem' = sem + +l03 == /\ pc[0] = "l03" + /\ sem' = [sem EXCEPT ![0] = 1] + /\ pc' = [pc EXCEPT ![0] = "l04"] + +l04 == /\ pc[0] = "l04" + /\ sem' = [sem EXCEPT ![N-1] = 1] + /\ pc' = [pc EXCEPT ![0] = "l01"] + +Proc0 == l01 \/ l02 \/ e0 \/ l03 \/ l04 + +Next == Proc0 + \/ (\E self \in 1..(N-1): Proc(self)) + +Spec == /\ Init /\ [][Next]_vars + /\ \A self \in 1..(N-1) : SF_vars(Proc(self)) + /\ SF_vars(Proc0) + +\* END TRANSLATION - the hash of the generated TLA code (remove to silence divergence warnings): TLA-ff7acdd47d76382441ae75c392052170 + +IsEating(i) == IF i = 0 THEN pc[i] = "e0" + ELSE pc[i] = "e" + +Invariant == \A i \in 0..(N-1) : ~ (IsEating(i) /\ IsEating((i+1)%N)) + +StarvationFree == \A i \in 0..(N-1) : []<> IsEating(i) +============================================================================= diff --git a/tlatools/test-model/pcal/CEither1.tla b/tlatools/test-model/pcal/CEither1.tla new file mode 100644 index 0000000000000000000000000000000000000000..4d93a0c8cbf49200ef8b12816dd9acc0271cf1aa --- /dev/null +++ b/tlatools/test-model/pcal/CEither1.tla @@ -0,0 +1,60 @@ +------------------------------- MODULE CEither1 ----------------------------- +EXTENDS Naturals, Sequences, TLC + +(* --algorithm Either { + variables x = 0 ; y = 0 ; + { a: either {x := 1 ; b: x := x + 1;} ; + or { y := 1 ; c: y := y + 1;} ; +\* end either ; + d: assert x+y = 2 ; + } } + +*) + +\* BEGIN TRANSLATION - the hash of the PCal code: PCal-af9f3b0389ad56ee237e79295b3205ff +VARIABLES x, y, pc + +vars == << x, y, pc >> + +Init == (* Global variables *) + /\ x = 0 + /\ y = 0 + /\ pc = "a" + +a == /\ pc = "a" + /\ \/ /\ x' = 1 + /\ pc' = "b" + /\ y' = y + \/ /\ y' = 1 + /\ pc' = "c" + /\ x' = x + +b == /\ pc = "b" + /\ x' = x + 1 + /\ pc' = "d" + /\ y' = y + +c == /\ pc = "c" + /\ y' = y + 1 + /\ pc' = "d" + /\ x' = x + +d == /\ pc = "d" + /\ Assert(x+y = 2, "Failure of assertion at line 9, column 16.") + /\ pc' = "Done" + /\ UNCHANGED << x, y >> + +(* Allow infinite stuttering to prevent deadlock on termination. *) +Terminating == pc = "Done" /\ UNCHANGED vars + +Next == a \/ b \/ c \/ d + \/ Terminating + +Spec == /\ Init /\ [][Next]_vars + /\ WF_vars(Next) + +Termination == <>(pc = "Done") + +\* END TRANSLATION - the hash of the generated TLA code (remove to silence divergence warnings): TLA-91542970fa29f8acc63f0f9ca8f27be3 + +============================================================================= diff --git a/tlatools/test-model/pcal/CMultiprocDefine.tla b/tlatools/test-model/pcal/CMultiprocDefine.tla new file mode 100644 index 0000000000000000000000000000000000000000..9fa61fbdd4ea10881a818eb1184569114e50a685 --- /dev/null +++ b/tlatools/test-model/pcal/CMultiprocDefine.tla @@ -0,0 +1,59 @@ +---------------------------- MODULE CMultiprocDefine ------------------------- +EXTENDS Naturals, Sequences, TLC + +(* +--algorithm MultiprocDefine { + variables n = 0 ; + define { nplus1 == n + 1 + nplus2 == nplus1 + 1 } ; + process (Proc \in {1, 2, 3}) + variables i ; + { main : i := nplus2 ; + assert i = 2 ; + } } + +*) + +\* BEGIN TRANSLATION - the hash of the PCal code: PCal-9c797e1d4e2fc4c259c478fdea1bd5fa +CONSTANT defaultInitValue +VARIABLES n, pc + +(* define statement *) +nplus1 == n + 1 +nplus2 == nplus1 + 1 + +VARIABLE i + +vars == << n, pc, i >> + +ProcSet == ({1, 2, 3}) + +Init == (* Global variables *) + /\ n = 0 + (* Process Proc *) + /\ i = [self \in {1, 2, 3} |-> defaultInitValue] + /\ pc = [self \in ProcSet |-> "main"] + +main(self) == /\ pc[self] = "main" + /\ i' = [i EXCEPT ![self] = nplus2] + /\ Assert(i'[self] = 2, + "Failure of assertion at line 12, column 14.") + /\ pc' = [pc EXCEPT ![self] = "Done"] + /\ n' = n + +Proc(self) == main(self) + +(* Allow infinite stuttering to prevent deadlock on termination. *) +Terminating == /\ \A self \in ProcSet: pc[self] = "Done" + /\ UNCHANGED vars + +Next == (\E self \in {1, 2, 3}: Proc(self)) + \/ Terminating + +Spec == /\ Init /\ [][Next]_vars + /\ \A self \in {1, 2, 3} : WF_vars(Proc(self)) + +Termination == <>(\A self \in ProcSet: pc[self] = "Done") + +\* END TRANSLATION - the hash of the generated TLA code (remove to silence divergence warnings): TLA-70c2856f6a2b2707884c9cedc014d866 +============================================================================= diff --git a/tlatools/test-model/pcal/CallReturn1.tla b/tlatools/test-model/pcal/CallReturn1.tla new file mode 100644 index 0000000000000000000000000000000000000000..ac380f4eb70385c3d809de24998b1b2146c22780 --- /dev/null +++ b/tlatools/test-model/pcal/CallReturn1.tla @@ -0,0 +1,126 @@ +------------------------------ MODULE CallReturn1 --------------------------- +EXTENDS Sequences, Naturals, TLC + +(******** +--algorithm CallReturn1 + procedure Proc1(arg1) + variable u ; + begin p1 : u := 2 ; + call Proc2 ( 2 * u ) ; + p2 : assert u = 2; + assert arg1 = 4 ; + call Proc2 ( 2 * u + 1 ) ; + return ; + end procedure + procedure Proc2(arg2 = 0) + variable v = 42 ; + begin q1 : assert v = 42; + assert arg2 \in {4, 5} ; + call Proc3 ( v + arg2 ) ; + return ; + end procedure + procedure Proc3(arg3 = 0) + begin r1 : assert arg3 \in {46, 47} ; + return ; + end procedure + begin + a1 : call Proc1( 4 ) ; + end algorithm +*****) + +\* BEGIN TRANSLATION - the hash of the PCal code: PCal-3e53299518ce546a0089212b525c3663 +CONSTANT defaultInitValue +VARIABLES pc, stack, arg1, u, arg2, v, arg3 + +vars == << pc, stack, arg1, u, arg2, v, arg3 >> + +Init == (* Procedure Proc1 *) + /\ arg1 = defaultInitValue + /\ u = defaultInitValue + (* Procedure Proc2 *) + /\ arg2 = 0 + /\ v = 42 + (* Procedure Proc3 *) + /\ arg3 = 0 + /\ stack = << >> + /\ pc = "a1" + +p1 == /\ pc = "p1" + /\ u' = 2 + /\ /\ arg2' = 2 * u' + /\ stack' = << [ procedure |-> "Proc2", + pc |-> "p2", + v |-> v, + arg2 |-> arg2 ] >> + \o stack + /\ v' = 42 + /\ pc' = "q1" + /\ UNCHANGED << arg1, arg3 >> + +p2 == /\ pc = "p2" + /\ Assert(u = 2, "Failure of assertion at line 10, column 18.") + /\ Assert(arg1 = 4, "Failure of assertion at line 11, column 18.") + /\ /\ arg2' = 2 * u + 1 + /\ stack' = << [ procedure |-> "Proc2", + pc |-> Head(stack).pc, + v |-> v, + arg2 |-> arg2 ] >> + \o Tail(stack) + /\ u' = Head(stack).u + /\ v' = 42 + /\ pc' = "q1" + /\ UNCHANGED << arg1, arg3 >> + +Proc1 == p1 \/ p2 + +q1 == /\ pc = "q1" + /\ Assert(v = 42, "Failure of assertion at line 17, column 18.") + /\ Assert(arg2 \in {4, 5}, + "Failure of assertion at line 18, column 18.") + /\ /\ arg3' = v + arg2 + /\ stack' = << [ procedure |-> "Proc3", + pc |-> Head(stack).pc, + arg3 |-> arg3 ] >> + \o Tail(stack) + /\ v' = Head(stack).v + /\ pc' = "r1" + /\ UNCHANGED << arg1, u, arg2 >> + +Proc2 == q1 + +r1 == /\ pc = "r1" + /\ Assert(arg3 \in {46, 47}, + "Failure of assertion at line 23, column 18.") + /\ pc' = Head(stack).pc + /\ arg3' = Head(stack).arg3 + /\ stack' = Tail(stack) + /\ UNCHANGED << arg1, u, arg2, v >> + +Proc3 == r1 + +a1 == /\ pc = "a1" + /\ /\ arg1' = 4 + /\ stack' = << [ procedure |-> "Proc1", + pc |-> "Done", + u |-> u, + arg1 |-> arg1 ] >> + \o stack + /\ u' = defaultInitValue + /\ pc' = "p1" + /\ UNCHANGED << arg2, v, arg3 >> + +(* Allow infinite stuttering to prevent deadlock on termination. *) +Terminating == pc = "Done" /\ UNCHANGED vars + +Next == Proc1 \/ Proc2 \/ Proc3 \/ a1 + \/ Terminating + +Spec == /\ Init /\ [][Next]_vars + /\ WF_vars(Next) + +Termination == <>(pc = "Done") + +\* END TRANSLATION - the hash of the generated TLA code (remove to silence divergence warnings): TLA-21e32d8e366d103d73444c2307bf4aba + + +============================================================================= diff --git a/tlatools/test-model/pcal/CallReturn2.tla b/tlatools/test-model/pcal/CallReturn2.tla new file mode 100644 index 0000000000000000000000000000000000000000..c0a5ec3e11faf1885928ebdea5a6219c0e27973e --- /dev/null +++ b/tlatools/test-model/pcal/CallReturn2.tla @@ -0,0 +1,199 @@ +The translator is not properly initializing the local procedure variables. +The assertions indicate how they should be initialized. (The OldPlusCal +and Pcal translations have this problem too.) + +----------------------------- MODULE CallReturn2 ----------------------------- +EXTENDS Naturals, Sequences, TLC + + +(* +--algorithm Test + variable depth = 3 + procedure P(a = 7) + variable x = a ; y = x+1 ; + begin P1: assert a = 1; + assert x = a; + assert y = a+1; + return; + end procedure + procedure Q() + begin Q1: call P(1) ; + return ; + end procedure + procedure PP(aa = 7) + variable xx = aa ; yy = xx+1 ; + begin PP1: if depth > 0 + then assert aa = 1; + assert xx = aa; + assert yy = aa+1; + depth := depth - 1 ; + call PP(1) ; + return; + else return + end if + end procedure + procedure R(r = 0) + variable x + begin R1: x := 2 ; + R2: call S(x) ; + return + end procedure + procedure S(s) + begin S1: assert s = 2 ; + return ; + end procedure + begin A: call P(1) ; + B: call Q() ; + C: call PP(1) ; + end algorithm +*) + +\* BEGIN TRANSLATION - the hash of the PCal code: PCal-84a5cdf59d68214ad905732a585890ae +\* Procedure variable x of procedure P at line 13 col 16 changed to x_ +CONSTANT defaultInitValue +VARIABLES depth, pc, stack, a, x_, y, aa, xx, yy, r, x, s + +vars == << depth, pc, stack, a, x_, y, aa, xx, yy, r, x, s >> + +Init == (* Global variables *) + /\ depth = 3 + (* Procedure P *) + /\ a = 7 + /\ x_ = a + /\ y = x_+1 + (* Procedure PP *) + /\ aa = 7 + /\ xx = aa + /\ yy = xx+1 + (* Procedure R *) + /\ r = 0 + /\ x = defaultInitValue + (* Procedure S *) + /\ s = defaultInitValue + /\ stack = << >> + /\ pc = "A" + +P1 == /\ pc = "P1" + /\ Assert(a = 1, "Failure of assertion at line 14, column 17.") + /\ Assert(x_ = a, "Failure of assertion at line 15, column 17.") + /\ Assert(y = a+1, "Failure of assertion at line 16, column 17.") + /\ pc' = Head(stack).pc + /\ x_' = Head(stack).x_ + /\ y' = Head(stack).y + /\ a' = Head(stack).a + /\ stack' = Tail(stack) + /\ UNCHANGED << depth, aa, xx, yy, r, x, s >> + +P == P1 + +Q1 == /\ pc = "Q1" + /\ /\ a' = 1 + /\ stack' = << [ procedure |-> "P", + pc |-> Head(stack).pc, + x_ |-> x_, + y |-> y, + a |-> a ] >> + \o Tail(stack) + /\ x_' = a' + /\ y' = x_'+1 + /\ pc' = "P1" + /\ UNCHANGED << depth, aa, xx, yy, r, x, s >> + +Q == Q1 + +PP1 == /\ pc = "PP1" + /\ IF depth > 0 + THEN /\ Assert(aa = 1, + "Failure of assertion at line 26, column 24.") + /\ Assert(xx = aa, + "Failure of assertion at line 27, column 22.") + /\ Assert(yy = aa+1, + "Failure of assertion at line 28, column 22.") + /\ depth' = depth - 1 + /\ aa' = 1 + /\ xx' = aa' + /\ yy' = xx'+1 + /\ pc' = "PP1" + /\ stack' = stack + ELSE /\ pc' = Head(stack).pc + /\ xx' = Head(stack).xx + /\ yy' = Head(stack).yy + /\ aa' = Head(stack).aa + /\ stack' = Tail(stack) + /\ depth' = depth + /\ UNCHANGED << a, x_, y, r, x, s >> + +PP == PP1 + +R1 == /\ pc = "R1" + /\ x' = 2 + /\ pc' = "R2" + /\ UNCHANGED << depth, stack, a, x_, y, aa, xx, yy, r, s >> + +R2 == /\ pc = "R2" + /\ /\ s' = x + /\ stack' = << [ procedure |-> "S", + pc |-> Head(stack).pc, + s |-> s ] >> + \o Tail(stack) + /\ x' = Head(stack).x + /\ pc' = "S1" + /\ UNCHANGED << depth, a, x_, y, aa, xx, yy, r >> + +R == R1 \/ R2 + +S1 == /\ pc = "S1" + /\ Assert(s = 2, "Failure of assertion at line 42, column 15.") + /\ pc' = Head(stack).pc + /\ s' = Head(stack).s + /\ stack' = Tail(stack) + /\ UNCHANGED << depth, a, x_, y, aa, xx, yy, r, x >> + +S == S1 + +A == /\ pc = "A" + /\ /\ a' = 1 + /\ stack' = << [ procedure |-> "P", + pc |-> "B", + x_ |-> x_, + y |-> y, + a |-> a ] >> + \o stack + /\ x_' = a' + /\ y' = x_'+1 + /\ pc' = "P1" + /\ UNCHANGED << depth, aa, xx, yy, r, x, s >> + +B == /\ pc = "B" + /\ stack' = << [ procedure |-> "Q", + pc |-> "C" ] >> + \o stack + /\ pc' = "Q1" + /\ UNCHANGED << depth, a, x_, y, aa, xx, yy, r, x, s >> + +C == /\ pc = "C" + /\ /\ aa' = 1 + /\ stack' = << [ procedure |-> "PP", + pc |-> "Done", + xx |-> xx, + yy |-> yy, + aa |-> aa ] >> + \o stack + /\ xx' = aa' + /\ yy' = xx'+1 + /\ pc' = "PP1" + /\ UNCHANGED << depth, a, x_, y, r, x, s >> + +(* Allow infinite stuttering to prevent deadlock on termination. *) +Terminating == pc = "Done" /\ UNCHANGED vars + +Next == P \/ Q \/ PP \/ R \/ S \/ A \/ B \/ C + \/ Terminating + +Spec == /\ Init /\ [][Next]_vars + /\ WF_vars(Next) + +Termination == <>(pc = "Done") + +\* END TRANSLATION - the hash of the generated TLA code (remove to silence divergence warnings): TLA-c8e76178d4d24aee35fa88a19dd439d8 +============================================================================= diff --git a/tlatools/test-model/pcal/DetlefSpec.cfg b/tlatools/test-model/pcal/DetlefSpec.cfg new file mode 100644 index 0000000000000000000000000000000000000000..69620fba44afdea8180a8923da52a55736e4ddf0 --- /dev/null +++ b/tlatools/test-model/pcal/DetlefSpec.cfg @@ -0,0 +1,7 @@ +SPECIFICATION Spec +\* Add statements after this line. +CONSTANTS Val = {v1} + Procs = {p1} + N = 3 + null = null +CONSTRAINT Constraint diff --git a/tlatools/test-model/pcal/DetlefSpec.tla b/tlatools/test-model/pcal/DetlefSpec.tla new file mode 100644 index 0000000000000000000000000000000000000000..923bd524312ce432ff4268489dee3515a1d8d111 --- /dev/null +++ b/tlatools/test-model/pcal/DetlefSpec.tla @@ -0,0 +1,79 @@ +----------------------------- MODULE DetlefSpec ----------------------------- +EXTENDS Sequences, Naturals + +CONSTANTS Val, Procs, null + + +(* --algorithm Spec { + variable queue = << >> ; + process (P \in Procs) + variable rV = null ; { +L1: while (TRUE) { + either + with (v \in Val) { + either { queue := queue \o <<v>> ; + rV := "okay" } + or { queue := <<v>> \o queue ; + rV := "okay"} + or rV := "full" ; + } + or { if (queue # << >>) + { either { rV := Head(queue) ; + queue := Tail(queue) } + or { rV := queue[Len(queue)] ; + queue := [ i \in 1 .. (Len(queue) - 1) |-> queue[i]]} + } + else { rV := "empty" } + } ; +L2: rV := null + } } } +*) + +\* BEGIN TRANSLATION - the hash of the PCal code: PCal-1ffa57620b1dc5120c521caddbcfa0df +VARIABLES queue, pc, rV + +vars == << queue, pc, rV >> + +ProcSet == (Procs) + +Init == (* Global variables *) + /\ queue = << >> + (* Process P *) + /\ rV = [self \in Procs |-> null] + /\ pc = [self \in ProcSet |-> "L1"] + +L1(self) == /\ pc[self] = "L1" + /\ \/ /\ \E v \in Val: + \/ /\ queue' = queue \o <<v>> + /\ rV' = [rV EXCEPT ![self] = "okay"] + \/ /\ queue' = <<v>> \o queue + /\ rV' = [rV EXCEPT ![self] = "okay"] + \/ /\ rV' = [rV EXCEPT ![self] = "full"] + /\ queue' = queue + \/ /\ IF queue # << >> + THEN /\ \/ /\ rV' = [rV EXCEPT ![self] = Head(queue)] + /\ queue' = Tail(queue) + \/ /\ rV' = [rV EXCEPT ![self] = queue[Len(queue)]] + /\ queue' = [ i \in 1 .. (Len(queue) - 1) |-> queue[i]] + ELSE /\ rV' = [rV EXCEPT ![self] = "empty"] + /\ queue' = queue + /\ pc' = [pc EXCEPT ![self] = "L2"] + +L2(self) == /\ pc[self] = "L2" + /\ rV' = [rV EXCEPT ![self] = null] + /\ pc' = [pc EXCEPT ![self] = "L1"] + /\ queue' = queue + +P(self) == L1(self) \/ L2(self) + +Next == (\E self \in Procs: P(self)) + +Spec == /\ Init /\ [][Next]_vars + /\ \A self \in Procs : WF_vars(P(self)) + +\* END TRANSLATION - the hash of the generated TLA code (remove to silence divergence warnings): TLA-a5e9e9156ce3a9bf6c7fb3f657f54020 + +CONSTANT N + +Constraint == Len(queue) \leq N +============================================================================= diff --git a/tlatools/test-model/pcal/DetlefTest.cfg b/tlatools/test-model/pcal/DetlefTest.cfg new file mode 100644 index 0000000000000000000000000000000000000000..71587f4bf5be09b735bbc537455aef25bb113bbd --- /dev/null +++ b/tlatools/test-model/pcal/DetlefTest.cfg @@ -0,0 +1,10 @@ +SPECIFICATION XSpec +CONSTANTS a1=a1 a2=a2 a3=a3 a4=a4 v1=v1 v2=v2 p1=p1 p2=p2 p3=p3 + Val = {v1} + Address = {a1, a2, a3, a4} + Dummy = a1 + Procs = {p1, p2} \* , p3} + null = null + +PROPERTY XQSpec + diff --git a/tlatools/test-model/pcal/DetlefTest.tla b/tlatools/test-model/pcal/DetlefTest.tla new file mode 100644 index 0000000000000000000000000000000000000000..9b73cf79009e626ca463dd1818d09f16ad0321ea --- /dev/null +++ b/tlatools/test-model/pcal/DetlefTest.tla @@ -0,0 +1,49 @@ +----------------------------- MODULE DetlefTest ----------------------------- +EXTENDS Detlefs2 + +qRFrom[adr \in Address] == IF adr = LeftHat + THEN <<Mem[adr].V>> + ELSE qRFrom[Mem[adr].R] \o <<Mem[adr].V>> + +qBar == IF Mem[RightHat].R = RightHat + THEN << >> + ELSE qRFrom[RightHat] + +rVBar == [p \in Procs |-> + CASE + pc[p] \in {"M4", "M7"} -> Mem[rh[p]].V + [] pc[p] = "M8" -> result[p] + [] pc[p] \in {"O5", "O8"} -> Mem[rh[p]].V + [] pc[p] = "O9" -> result[p] + [] OTHER -> rVal[p]] + +pcBar == [p \in Procs |-> IF rVBar[p] = null THEN "L1" ELSE "L2"] + +SpecBar == INSTANCE DetlefSpec WITH rV <- rVBar, pc <- pcBar, + N <- 100 +QSpec == SpecBar!Spec + +CONSTANTS a1, a2, a3, a4, v1, v2, p1, p2, p3 +XInit == +/\ lh = (p1 :> a1 @@ p2 :> a1) +/\ LeftHat = a2 +/\ nd = (p1 :> a2 @@ p2 :> null) +/\ RightHat = a2 +/\ rVal = (p1 :> "okay" @@ p2 :> null) +/\ valBag = (v1 :> 0) +/\ pc = (p1 :> "Return" @@ p2 :> "M2") +/\ temp = (p1 :> TRUE @@ p2 :> a1) +/\ rh = (p1 :> a1 @@ p2 :> a1) +/\ Mem = ( a1 :> [V |-> null, R |-> a1, L |-> a1] @@ + a2 :> [V |-> v1, R |-> a1, L |-> a1] @@ + a3 :> [V |-> null, R |-> null, L |-> null] @@ + a4 :> [V |-> null, R |-> null, L |-> null] ) +/\ queue = << v1 >> +/\ ptr = (p1 :> a1 @@ p2 :> a1) +/\ result = (p1 :> v1 @@ p2 :> v1) +/\ freeList = {a3, a4} + +XSpec == XInit /\ [][Next]_vars + +XQSpec == [][SpecBar!Next]_(SpecBar!vars) +============================================================================= \ No newline at end of file diff --git a/tlatools/test-model/pcal/Detlefs.cfg b/tlatools/test-model/pcal/Detlefs.cfg new file mode 100644 index 0000000000000000000000000000000000000000..ca8ad7597a33b1c48943fa444f567bf0f34b7f6a --- /dev/null +++ b/tlatools/test-model/pcal/Detlefs.cfg @@ -0,0 +1,11 @@ +SPECIFICATION Spec +CONSTANT defaultInitValue = defaultInitValue +\* Add statements after this line. +CONSTANTS Val = {v1} + Address = {a1, a2, a3, a4} + Dummy = a1 + Procs = {p1, p2} \* , p3} + null = null + GC = GC +\* PROPERTY Liveness +SYMMETRY Symmetry diff --git a/tlatools/test-model/pcal/Detlefs.tla b/tlatools/test-model/pcal/Detlefs.tla new file mode 100644 index 0000000000000000000000000000000000000000..4bde20a127acb10d05a574ffb56ca5d6fd4c7e69 --- /dev/null +++ b/tlatools/test-model/pcal/Detlefs.tla @@ -0,0 +1,1006 @@ +------------------------------- MODULE Detlefs ------------------------------ +EXTENDS Naturals, Sequences, TLC, FiniteSets + +CONSTANTS Val, Address, Dummy, Procs, GC +ASSUME Dummy \in Address +ASSUME GC \notin Procs + + +null == CHOOSE n: n \notin Address +Node == [R : Address \cup {null}, L : Address \cup {null}, V : Val] +DummyNode == [R |-> Dummy, L |-> Dummy, V |-> null] +InitNode == [R |-> null, L |-> null, V |-> null] + +(* +\* boolean DCAS(val *addr1, val *addr2, +\* val old1, val old2, +\* val new1, val new2) { +\* atomically { +\* if (( *addr1 == old1) && +\* ( *addr2 == old2)) { +\* *addr1 = new1; +\* *addr2 = new2; +\* return true; +\* } else return false; } } + +--algorithm Snark { +variables Mem = [i \in Address |-> IF i = Dummy THEN DummyNode ELSE InitNode], + freeList = Address \ {Dummy}, + LeftHat = Dummy, + RightHat = Dummy, + rVal = [i \in Procs |-> "okay"] , \* Used for returning values + valBag = [i \in Val |-> 0] ; + \* For testing: valBag[i] is the number of copies of i + \* that can be in the queue. + +macro New(result) { + if (freeList # {}) { + result := CHOOSE a \in freeList : TRUE ; + freeList := freeList \ {result} ; + } else result := null +} + +macro DCAS(result, addr1, addr2, old1, old2, new1, new2) { + if ( /\ addr1 = old1 + /\ addr2 = old2) { + addr1 := new1 || + addr2 := new2 ; + result := TRUE; + } else result := FALSE; } + + +\* val pushRight(val v) { +\* nd = new Node(); /* Allocate new Node structure */ +\* if (nd == null) return "full"; +\* nd�>R = Dummy; +\* nd�>V = v; +\* while (true) { +\* rh = RightHat; /* Labels A, B, */ +\* rhR = rh�>R; /* etc., are used */ +\* if (rhR == rh) { /* in the proof */ +\* nd�>L = Dummy; /* of correctness */ +\* lh = LeftHat; +\* if (DCAS(&RightHat, &LeftHat, rh, lh, nd, nd)) /* A */ +\* return "okay"; +\* } else { +\* nd�>L = rh; +\* if (DCAS(&RightHat, &rh�>R, rh, rhR, nd, nd)) /* B */ +\* return "okay"; +\* } } } // Please forgive this brace style + +procedure pushRight(v) + variables nd = null , rh = Dummy, rhR = Dummy, lh = Dummy, + temp = Dummy ; { +L1: New(nd) ; + if (nd = null) { rVal[self] := "full"; L1a: return } ; +L1b: Mem[nd].R := Dummy || \* Since no other thread can access nd here, + Mem[nd].V := v ; +L4: while (TRUE) { + rh := RightHat; +L5: rhR := Mem[rh].R; +L6: if (rhR = rh) { + Mem[nd].L := Dummy; + lh := LeftHat; +L7: DCAS(temp, RightHat, LeftHat, rh, lh, nd, nd) ; + if (temp) { + rVal[self] := "okay"; L7a: return} + } else { +L8: Mem[nd].L := rh; +L9: DCAS(temp, RightHat, Mem[rh].R, rh, rhR, nd, nd) ; + if (temp) { + rVal[self] := "okay"; L8a: return } +} } } + + +\* val popRight() { +\* while (true) { +\* rh = RightHat; // Delicate order of operations +\* lh = LeftHat; // here (see proof of Theorem 4 +\* if (rh�>R == rh) return "empty"; // and the Conclusions section) +\* if (rh == lh) { +\* if (DCAS(&RightHat, &LeftHat, rh, lh, Dummy, Dummy)) /* C */ +\* return rh�>V; +\* } else { +\* rhL = rh�>L; +\* if (DCAS(&RightHat, &rh�>L, rh, rhL, rhL, rh)) { /* D */ +\* result = rh�>V; +\* rh�>R = Dummy; /* E */ +\* rh�>V = null; /* optional (see text) */ +\* return result; +\* } } } } // Stacking braces this way saves space +\* + +procedure popRight() + variables rh = Dummy, lh = Dummy, rhL = Dummy, + temp = Dummy , result = null ; { +M1: while (TRUE) { + rh := RightHat; +M2: lh := LeftHat; + if (Mem[rh].R = rh) {rVal[self] := "empty"; M2a: return ;} ; +M3: if (rh = lh) { + DCAS(temp, RightHat, LeftHat, rh, lh, Dummy, Dummy) ; + if (temp) { +M4: rVal[self] := Mem[rh].V ; M4a: return;} + } else { +M5: rhL := Mem[rh].L ; +M6: DCAS(temp, RightHat, Mem[rh].L, rh, rhL, rhL, rh) ; + if (temp) { +M7: result := Mem[rh].V; +M8: Mem[rh].R := Dummy || + Mem[rh].V := null; + rVal[self] := result ; M9a: return ; +} } } } + +\* val pushLeft(val v) { +\* nd = new Node(); /* Allocate new Node structure */ +\* if (nd == null) return "full"; +\* nd�>L = Dummy; +\* nd�>V = v; +\* while (true) { +\* lh = LeftHat; +\* lhL = lh�>L; +\* if (lhL == lh) { +\* nd�>R = Dummy; +\* rh = RightHat; +\* if (DCAS(&LeftHat, &RightHat, lh, rh, nd, nd)) /* A' */ +\* return "okay"; +\* } else { +\* nd�>R = lh; +\* if (DCAS(&LeftHat, &lh�>L, lh, lhL, nd, nd)) /* B' */ +\* return "okay"; +\* } } } // We were given a firm limit of 15 pages + +procedure pushLeft(v) + variables nd = null , rh = Dummy, lhL = Dummy, lh = Dummy, + temp = Dummy ; { +N1: New(nd) ; + if (nd = null) {rVal[self] := "full"; N1a: return ;} ; +N1b: Mem[nd].L := Dummy || \* Since no other thread can access nd here, + Mem[nd].V := v; \* we can represent this as a single action. +N2: while (TRUE) { + lh := LeftHat; +N3: lhL := Mem[lh].L; + if (lhL = lh) { +N4: Mem[nd].R := Dummy; +N5: rh := RightHat; +N6: DCAS(temp, LeftHat, RightHat, lh, rh, nd, nd) ; + if (temp) { + rVal[self] := "okay"; nd := null ; N6a: return;} + } else { +N7: Mem[nd].R := lh; +N8: DCAS(temp, LeftHat, Mem[lh].L, lh, lhL, nd, nd) ; + if (temp) { + rVal[self] := "okay"; nd := null ; N8a: return } +} } } + +\* val popLeft() { +\* while (true) { +\* lh = LeftHat; // Delicate order of operations +\* rh = RightHat; // here (see proof of Theorem 4 +\* if (lh�>L == lh) return "empty"; // and the Conclusions section) +\* if (lh == rh) { +\* if (DCAS(&LeftHat, &RightHat, lh, rh, Dummy, Dummy)) /* C' */ +\* return lh�>V; +\* } else { +\* lhR = lh�>R; +\* if (DCAS(&LeftHat, &lh�>R, lh, lhR, lhR, lh)) { /* D' */ +\* result = lh�>V; +\* lh�>L = Dummy; /* E' */ +\* lh�>V = null; /* optional (see text) */ +\* return result; +\* } } } } // Better to stack braces than to omit a lemma + +procedure popLeft() + variables rh = Dummy, lh = Dummy, lhR = Dummy, + temp = Dummy , result = null ; { +O1: while (TRUE) { + lh := LeftHat; +O2: rh := RightHat; +O3: if (Mem[lh].L = lh) {rVal[self] := "empty"; O3a: return ;} ; +O4: if (lh = rh) { + DCAS(temp, LeftHat, RightHat, lh, rh, Dummy, Dummy) ; + if (temp) { +O5: rVal[self] := Mem[lh].V; O5a: return; } + } else { +O6: lhR := Mem[lh].R; +O7: DCAS(temp, LeftHat, Mem[lh].R, lh, lhR, lhR, lh) ; + if (temp) { +O8: result := Mem[lh].V; +O9: Mem[lh].L := Dummy || + Mem[lh].V := null; + rVal[self] := result; O10a: return ; +} } } } + + +\* process (GarbageCollet = GC) { +\* GC1: while (TRUE) { +\* with (adr \in Address \ (freeList \cup {Dummy})) { +\* when Mem[adr].canGC ; +\* when \A b \in Address \ (freeList \cup {Dummy, adr}) : +\* /\ adr # Mem[b].L +\* /\ adr # Mem[b].R ; +\* freeList := freeList \cup {adr} ; +\* Mem[adr] := InitNode; +\* } } } + +process (test \in Procs) +variables pushedVal = null ; { +T1: while(TRUE) { + either { \* push + with (x \in Val) {pushedVal := x} ; + valBag[pushedVal] := valBag[pushedVal] + 1 ; + either call pushLeft(pushedVal) or call pushRight(pushedVal) ; +T2: if (rVal[self] = "full") valBag[pushedVal] := valBag[pushedVal] - 1 + } + or { \* pop + either call popLeft() or call popRight() ; +T3: if (rVal[self] # "empty") { + assert valBag[rVal[self]] > 0 ; + valBag[rVal[self]] := valBag[rVal[self]] - 1; + } +} } }} +*) + +\* BEGIN TRANSLATION +\* Procedure variable nd of procedure pushRight at line 72 col 12 changed to nd_ +\* Procedure variable rh of procedure pushRight at line 72 col 24 changed to rh_ +\* Procedure variable lh of procedure pushRight at line 72 col 49 changed to lh_ +\* Procedure variable temp of procedure pushRight at line 73 col 12 changed to temp_ +\* Procedure variable rh of procedure popRight at line 114 col 12 changed to rh_p +\* Procedure variable lh of procedure popRight at line 114 col 24 changed to lh_p +\* Procedure variable temp of procedure popRight at line 115 col 12 changed to temp_p +\* Procedure variable result of procedure popRight at line 115 col 27 changed to result_ +\* Procedure variable rh of procedure pushLeft at line 154 col 24 changed to rh_pu +\* Procedure variable lh of procedure pushLeft at line 154 col 49 changed to lh_pu +\* Procedure variable temp of procedure pushLeft at line 155 col 12 changed to temp_pu +\* Parameter v of procedure pushRight at line 71 col 21 changed to v_ +CONSTANT defaultInitValue +VARIABLES Mem, freeList, LeftHat, RightHat, rVal, valBag, pc, stack, v_, nd_, + rh_, rhR, lh_, temp_, rh_p, lh_p, rhL, temp_p, result_, v, nd, + rh_pu, lhL, lh_pu, temp_pu, rh, lh, lhR, temp, result, pushedVal + +vars == << Mem, freeList, LeftHat, RightHat, rVal, valBag, pc, stack, v_, nd_, + rh_, rhR, lh_, temp_, rh_p, lh_p, rhL, temp_p, result_, v, nd, + rh_pu, lhL, lh_pu, temp_pu, rh, lh, lhR, temp, result, pushedVal + >> + +ProcSet == (Procs) + +Init == (* Global variables *) + /\ Mem = [i \in Address |-> IF i = Dummy THEN DummyNode ELSE InitNode] + /\ freeList = Address \ {Dummy} + /\ LeftHat = Dummy + /\ RightHat = Dummy + /\ rVal = [i \in Procs |-> "okay"] + /\ valBag = [i \in Val |-> 0] + (* Procedure pushRight *) + /\ v_ = [ self \in ProcSet |-> defaultInitValue] + /\ nd_ = [ self \in ProcSet |-> null] + /\ rh_ = [ self \in ProcSet |-> Dummy] + /\ rhR = [ self \in ProcSet |-> Dummy] + /\ lh_ = [ self \in ProcSet |-> Dummy] + /\ temp_ = [ self \in ProcSet |-> Dummy] + (* Procedure popRight *) + /\ rh_p = [ self \in ProcSet |-> Dummy] + /\ lh_p = [ self \in ProcSet |-> Dummy] + /\ rhL = [ self \in ProcSet |-> Dummy] + /\ temp_p = [ self \in ProcSet |-> Dummy] + /\ result_ = [ self \in ProcSet |-> null] + (* Procedure pushLeft *) + /\ v = [ self \in ProcSet |-> defaultInitValue] + /\ nd = [ self \in ProcSet |-> null] + /\ rh_pu = [ self \in ProcSet |-> Dummy] + /\ lhL = [ self \in ProcSet |-> Dummy] + /\ lh_pu = [ self \in ProcSet |-> Dummy] + /\ temp_pu = [ self \in ProcSet |-> Dummy] + (* Procedure popLeft *) + /\ rh = [ self \in ProcSet |-> Dummy] + /\ lh = [ self \in ProcSet |-> Dummy] + /\ lhR = [ self \in ProcSet |-> Dummy] + /\ temp = [ self \in ProcSet |-> Dummy] + /\ result = [ self \in ProcSet |-> null] + (* Process test *) + /\ pushedVal = [self \in Procs |-> null] + /\ stack = [self \in ProcSet |-> << >>] + /\ pc = [self \in ProcSet |-> "T1"] + +L1(self) == /\ pc[self] = "L1" + /\ IF freeList # {} + THEN /\ nd_' = [nd_ EXCEPT ![self] = CHOOSE a \in freeList : TRUE] + /\ freeList' = freeList \ {nd_'[self]} + ELSE /\ nd_' = [nd_ EXCEPT ![self] = null] + /\ UNCHANGED freeList + /\ IF nd_'[self] = null + THEN /\ rVal' = [rVal EXCEPT ![self] = "full"] + /\ pc' = [pc EXCEPT ![self] = "L1a"] + ELSE /\ pc' = [pc EXCEPT ![self] = "L1b"] + /\ rVal' = rVal + /\ UNCHANGED << Mem, LeftHat, RightHat, valBag, stack, v_, rh_, + rhR, lh_, temp_, rh_p, lh_p, rhL, temp_p, result_, + v, nd, rh_pu, lhL, lh_pu, temp_pu, rh, lh, lhR, + temp, result, pushedVal >> + +L1a(self) == /\ pc[self] = "L1a" + /\ pc' = [pc EXCEPT ![self] = Head(stack[self]).pc] + /\ nd_' = [nd_ EXCEPT ![self] = Head(stack[self]).nd_] + /\ rh_' = [rh_ EXCEPT ![self] = Head(stack[self]).rh_] + /\ rhR' = [rhR EXCEPT ![self] = Head(stack[self]).rhR] + /\ lh_' = [lh_ EXCEPT ![self] = Head(stack[self]).lh_] + /\ temp_' = [temp_ EXCEPT ![self] = Head(stack[self]).temp_] + /\ v_' = [v_ EXCEPT ![self] = Head(stack[self]).v_] + /\ stack' = [stack EXCEPT ![self] = Tail(stack[self])] + /\ UNCHANGED << Mem, freeList, LeftHat, RightHat, rVal, valBag, + rh_p, lh_p, rhL, temp_p, result_, v, nd, rh_pu, + lhL, lh_pu, temp_pu, rh, lh, lhR, temp, result, + pushedVal >> + +L1b(self) == /\ pc[self] = "L1b" + /\ Mem' = [Mem EXCEPT ![nd_[self]].R = Dummy, + ![nd_[self]].V = v_[self]] + /\ pc' = [pc EXCEPT ![self] = "L4"] + /\ UNCHANGED << freeList, LeftHat, RightHat, rVal, valBag, stack, + v_, nd_, rh_, rhR, lh_, temp_, rh_p, lh_p, rhL, + temp_p, result_, v, nd, rh_pu, lhL, lh_pu, + temp_pu, rh, lh, lhR, temp, result, pushedVal >> + +L4(self) == /\ pc[self] = "L4" + /\ rh_' = [rh_ EXCEPT ![self] = RightHat] + /\ pc' = [pc EXCEPT ![self] = "L5"] + /\ UNCHANGED << Mem, freeList, LeftHat, RightHat, rVal, valBag, + stack, v_, nd_, rhR, lh_, temp_, rh_p, lh_p, rhL, + temp_p, result_, v, nd, rh_pu, lhL, lh_pu, temp_pu, + rh, lh, lhR, temp, result, pushedVal >> + +L5(self) == /\ pc[self] = "L5" + /\ rhR' = [rhR EXCEPT ![self] = Mem[rh_[self]].R] + /\ pc' = [pc EXCEPT ![self] = "L6"] + /\ UNCHANGED << Mem, freeList, LeftHat, RightHat, rVal, valBag, + stack, v_, nd_, rh_, lh_, temp_, rh_p, lh_p, rhL, + temp_p, result_, v, nd, rh_pu, lhL, lh_pu, temp_pu, + rh, lh, lhR, temp, result, pushedVal >> + +L6(self) == /\ pc[self] = "L6" + /\ IF rhR[self] = rh_[self] + THEN /\ Mem' = [Mem EXCEPT ![nd_[self]].L = Dummy] + /\ lh_' = [lh_ EXCEPT ![self] = LeftHat] + /\ pc' = [pc EXCEPT ![self] = "L7"] + ELSE /\ pc' = [pc EXCEPT ![self] = "L8"] + /\ UNCHANGED << Mem, lh_ >> + /\ UNCHANGED << freeList, LeftHat, RightHat, rVal, valBag, stack, + v_, nd_, rh_, rhR, temp_, rh_p, lh_p, rhL, temp_p, + result_, v, nd, rh_pu, lhL, lh_pu, temp_pu, rh, lh, + lhR, temp, result, pushedVal >> + +L7(self) == /\ pc[self] = "L7" + /\ IF /\ RightHat = rh_[self] + /\ LeftHat = lh_[self] + THEN /\ /\ LeftHat' = nd_[self] + /\ RightHat' = nd_[self] + /\ temp_' = [temp_ EXCEPT ![self] = TRUE] + ELSE /\ temp_' = [temp_ EXCEPT ![self] = FALSE] + /\ UNCHANGED << LeftHat, RightHat >> + /\ IF temp_'[self] + THEN /\ rVal' = [rVal EXCEPT ![self] = "okay"] + /\ pc' = [pc EXCEPT ![self] = "L7a"] + ELSE /\ pc' = [pc EXCEPT ![self] = "L4"] + /\ rVal' = rVal + /\ UNCHANGED << Mem, freeList, valBag, stack, v_, nd_, rh_, rhR, + lh_, rh_p, lh_p, rhL, temp_p, result_, v, nd, + rh_pu, lhL, lh_pu, temp_pu, rh, lh, lhR, temp, + result, pushedVal >> + +L7a(self) == /\ pc[self] = "L7a" + /\ pc' = [pc EXCEPT ![self] = Head(stack[self]).pc] + /\ nd_' = [nd_ EXCEPT ![self] = Head(stack[self]).nd_] + /\ rh_' = [rh_ EXCEPT ![self] = Head(stack[self]).rh_] + /\ rhR' = [rhR EXCEPT ![self] = Head(stack[self]).rhR] + /\ lh_' = [lh_ EXCEPT ![self] = Head(stack[self]).lh_] + /\ temp_' = [temp_ EXCEPT ![self] = Head(stack[self]).temp_] + /\ v_' = [v_ EXCEPT ![self] = Head(stack[self]).v_] + /\ stack' = [stack EXCEPT ![self] = Tail(stack[self])] + /\ UNCHANGED << Mem, freeList, LeftHat, RightHat, rVal, valBag, + rh_p, lh_p, rhL, temp_p, result_, v, nd, rh_pu, + lhL, lh_pu, temp_pu, rh, lh, lhR, temp, result, + pushedVal >> + +L8(self) == /\ pc[self] = "L8" + /\ Mem' = [Mem EXCEPT ![nd_[self]].L = rh_[self]] + /\ pc' = [pc EXCEPT ![self] = "L9"] + /\ UNCHANGED << freeList, LeftHat, RightHat, rVal, valBag, stack, + v_, nd_, rh_, rhR, lh_, temp_, rh_p, lh_p, rhL, + temp_p, result_, v, nd, rh_pu, lhL, lh_pu, temp_pu, + rh, lh, lhR, temp, result, pushedVal >> + +L9(self) == /\ pc[self] = "L9" + /\ IF /\ RightHat = rh_[self] + /\ (Mem[rh_[self]].R) = rhR[self] + THEN /\ /\ Mem' = [Mem EXCEPT ![rh_[self]].R = nd_[self]] + /\ RightHat' = nd_[self] + /\ temp_' = [temp_ EXCEPT ![self] = TRUE] + ELSE /\ temp_' = [temp_ EXCEPT ![self] = FALSE] + /\ UNCHANGED << Mem, RightHat >> + /\ IF temp_'[self] + THEN /\ rVal' = [rVal EXCEPT ![self] = "okay"] + /\ pc' = [pc EXCEPT ![self] = "L8a"] + ELSE /\ pc' = [pc EXCEPT ![self] = "L4"] + /\ rVal' = rVal + /\ UNCHANGED << freeList, LeftHat, valBag, stack, v_, nd_, rh_, + rhR, lh_, rh_p, lh_p, rhL, temp_p, result_, v, nd, + rh_pu, lhL, lh_pu, temp_pu, rh, lh, lhR, temp, + result, pushedVal >> + +L8a(self) == /\ pc[self] = "L8a" + /\ pc' = [pc EXCEPT ![self] = Head(stack[self]).pc] + /\ nd_' = [nd_ EXCEPT ![self] = Head(stack[self]).nd_] + /\ rh_' = [rh_ EXCEPT ![self] = Head(stack[self]).rh_] + /\ rhR' = [rhR EXCEPT ![self] = Head(stack[self]).rhR] + /\ lh_' = [lh_ EXCEPT ![self] = Head(stack[self]).lh_] + /\ temp_' = [temp_ EXCEPT ![self] = Head(stack[self]).temp_] + /\ v_' = [v_ EXCEPT ![self] = Head(stack[self]).v_] + /\ stack' = [stack EXCEPT ![self] = Tail(stack[self])] + /\ UNCHANGED << Mem, freeList, LeftHat, RightHat, rVal, valBag, + rh_p, lh_p, rhL, temp_p, result_, v, nd, rh_pu, + lhL, lh_pu, temp_pu, rh, lh, lhR, temp, result, + pushedVal >> + +pushRight(self) == L1(self) \/ L1a(self) \/ L1b(self) \/ L4(self) + \/ L5(self) \/ L6(self) \/ L7(self) \/ L7a(self) + \/ L8(self) \/ L9(self) \/ L8a(self) + +M1(self) == /\ pc[self] = "M1" + /\ rh_p' = [rh_p EXCEPT ![self] = RightHat] + /\ pc' = [pc EXCEPT ![self] = "M2"] + /\ UNCHANGED << Mem, freeList, LeftHat, RightHat, rVal, valBag, + stack, v_, nd_, rh_, rhR, lh_, temp_, lh_p, rhL, + temp_p, result_, v, nd, rh_pu, lhL, lh_pu, temp_pu, + rh, lh, lhR, temp, result, pushedVal >> + +M2(self) == /\ pc[self] = "M2" + /\ lh_p' = [lh_p EXCEPT ![self] = LeftHat] + /\ IF Mem[rh_p[self]].R = rh_p[self] + THEN /\ rVal' = [rVal EXCEPT ![self] = "empty"] + /\ pc' = [pc EXCEPT ![self] = "M2a"] + ELSE /\ pc' = [pc EXCEPT ![self] = "M3"] + /\ rVal' = rVal + /\ UNCHANGED << Mem, freeList, LeftHat, RightHat, valBag, stack, + v_, nd_, rh_, rhR, lh_, temp_, rh_p, rhL, temp_p, + result_, v, nd, rh_pu, lhL, lh_pu, temp_pu, rh, lh, + lhR, temp, result, pushedVal >> + +M2a(self) == /\ pc[self] = "M2a" + /\ pc' = [pc EXCEPT ![self] = Head(stack[self]).pc] + /\ rh_p' = [rh_p EXCEPT ![self] = Head(stack[self]).rh_p] + /\ lh_p' = [lh_p EXCEPT ![self] = Head(stack[self]).lh_p] + /\ rhL' = [rhL EXCEPT ![self] = Head(stack[self]).rhL] + /\ temp_p' = [temp_p EXCEPT ![self] = Head(stack[self]).temp_p] + /\ result_' = [result_ EXCEPT ![self] = Head(stack[self]).result_] + /\ stack' = [stack EXCEPT ![self] = Tail(stack[self])] + /\ UNCHANGED << Mem, freeList, LeftHat, RightHat, rVal, valBag, + v_, nd_, rh_, rhR, lh_, temp_, v, nd, rh_pu, lhL, + lh_pu, temp_pu, rh, lh, lhR, temp, result, + pushedVal >> + +M3(self) == /\ pc[self] = "M3" + /\ IF rh_p[self] = lh_p[self] + THEN /\ IF /\ RightHat = rh_p[self] + /\ LeftHat = lh_p[self] + THEN /\ /\ LeftHat' = Dummy + /\ RightHat' = Dummy + /\ temp_p' = [temp_p EXCEPT ![self] = TRUE] + ELSE /\ temp_p' = [temp_p EXCEPT ![self] = FALSE] + /\ UNCHANGED << LeftHat, RightHat >> + /\ IF temp_p'[self] + THEN /\ pc' = [pc EXCEPT ![self] = "M4"] + ELSE /\ pc' = [pc EXCEPT ![self] = "M1"] + ELSE /\ pc' = [pc EXCEPT ![self] = "M5"] + /\ UNCHANGED << LeftHat, RightHat, temp_p >> + /\ UNCHANGED << Mem, freeList, rVal, valBag, stack, v_, nd_, rh_, + rhR, lh_, temp_, rh_p, lh_p, rhL, result_, v, nd, + rh_pu, lhL, lh_pu, temp_pu, rh, lh, lhR, temp, + result, pushedVal >> + +M5(self) == /\ pc[self] = "M5" + /\ rhL' = [rhL EXCEPT ![self] = Mem[rh_p[self]].L] + /\ pc' = [pc EXCEPT ![self] = "M6"] + /\ UNCHANGED << Mem, freeList, LeftHat, RightHat, rVal, valBag, + stack, v_, nd_, rh_, rhR, lh_, temp_, rh_p, lh_p, + temp_p, result_, v, nd, rh_pu, lhL, lh_pu, temp_pu, + rh, lh, lhR, temp, result, pushedVal >> + +M6(self) == /\ pc[self] = "M6" + /\ IF /\ RightHat = rh_p[self] + /\ (Mem[rh_p[self]].L) = rhL[self] + THEN /\ /\ Mem' = [Mem EXCEPT ![rh_p[self]].L = rh_p[self]] + /\ RightHat' = rhL[self] + /\ temp_p' = [temp_p EXCEPT ![self] = TRUE] + ELSE /\ temp_p' = [temp_p EXCEPT ![self] = FALSE] + /\ UNCHANGED << Mem, RightHat >> + /\ IF temp_p'[self] + THEN /\ pc' = [pc EXCEPT ![self] = "M7"] + ELSE /\ pc' = [pc EXCEPT ![self] = "M1"] + /\ UNCHANGED << freeList, LeftHat, rVal, valBag, stack, v_, nd_, + rh_, rhR, lh_, temp_, rh_p, lh_p, rhL, result_, v, + nd, rh_pu, lhL, lh_pu, temp_pu, rh, lh, lhR, temp, + result, pushedVal >> + +M7(self) == /\ pc[self] = "M7" + /\ result_' = [result_ EXCEPT ![self] = Mem[rh_p[self]].V] + /\ pc' = [pc EXCEPT ![self] = "M8"] + /\ UNCHANGED << Mem, freeList, LeftHat, RightHat, rVal, valBag, + stack, v_, nd_, rh_, rhR, lh_, temp_, rh_p, lh_p, + rhL, temp_p, v, nd, rh_pu, lhL, lh_pu, temp_pu, rh, + lh, lhR, temp, result, pushedVal >> + +M8(self) == /\ pc[self] = "M8" + /\ Mem' = [Mem EXCEPT ![rh_p[self]].R = Dummy, + ![rh_p[self]].V = null] + /\ rVal' = [rVal EXCEPT ![self] = result_[self]] + /\ pc' = [pc EXCEPT ![self] = "M9a"] + /\ UNCHANGED << freeList, LeftHat, RightHat, valBag, stack, v_, + nd_, rh_, rhR, lh_, temp_, rh_p, lh_p, rhL, temp_p, + result_, v, nd, rh_pu, lhL, lh_pu, temp_pu, rh, lh, + lhR, temp, result, pushedVal >> + +M9a(self) == /\ pc[self] = "M9a" + /\ pc' = [pc EXCEPT ![self] = Head(stack[self]).pc] + /\ rh_p' = [rh_p EXCEPT ![self] = Head(stack[self]).rh_p] + /\ lh_p' = [lh_p EXCEPT ![self] = Head(stack[self]).lh_p] + /\ rhL' = [rhL EXCEPT ![self] = Head(stack[self]).rhL] + /\ temp_p' = [temp_p EXCEPT ![self] = Head(stack[self]).temp_p] + /\ result_' = [result_ EXCEPT ![self] = Head(stack[self]).result_] + /\ stack' = [stack EXCEPT ![self] = Tail(stack[self])] + /\ UNCHANGED << Mem, freeList, LeftHat, RightHat, rVal, valBag, + v_, nd_, rh_, rhR, lh_, temp_, v, nd, rh_pu, lhL, + lh_pu, temp_pu, rh, lh, lhR, temp, result, + pushedVal >> + +M4(self) == /\ pc[self] = "M4" + /\ rVal' = [rVal EXCEPT ![self] = Mem[rh_p[self]].V] + /\ pc' = [pc EXCEPT ![self] = "M4a"] + /\ UNCHANGED << Mem, freeList, LeftHat, RightHat, valBag, stack, + v_, nd_, rh_, rhR, lh_, temp_, rh_p, lh_p, rhL, + temp_p, result_, v, nd, rh_pu, lhL, lh_pu, temp_pu, + rh, lh, lhR, temp, result, pushedVal >> + +M4a(self) == /\ pc[self] = "M4a" + /\ pc' = [pc EXCEPT ![self] = Head(stack[self]).pc] + /\ rh_p' = [rh_p EXCEPT ![self] = Head(stack[self]).rh_p] + /\ lh_p' = [lh_p EXCEPT ![self] = Head(stack[self]).lh_p] + /\ rhL' = [rhL EXCEPT ![self] = Head(stack[self]).rhL] + /\ temp_p' = [temp_p EXCEPT ![self] = Head(stack[self]).temp_p] + /\ result_' = [result_ EXCEPT ![self] = Head(stack[self]).result_] + /\ stack' = [stack EXCEPT ![self] = Tail(stack[self])] + /\ UNCHANGED << Mem, freeList, LeftHat, RightHat, rVal, valBag, + v_, nd_, rh_, rhR, lh_, temp_, v, nd, rh_pu, lhL, + lh_pu, temp_pu, rh, lh, lhR, temp, result, + pushedVal >> + +popRight(self) == M1(self) \/ M2(self) \/ M2a(self) \/ M3(self) \/ M5(self) + \/ M6(self) \/ M7(self) \/ M8(self) \/ M9a(self) + \/ M4(self) \/ M4a(self) + +N1(self) == /\ pc[self] = "N1" + /\ IF freeList # {} + THEN /\ nd' = [nd EXCEPT ![self] = CHOOSE a \in freeList : TRUE] + /\ freeList' = freeList \ {nd'[self]} + ELSE /\ nd' = [nd EXCEPT ![self] = null] + /\ UNCHANGED freeList + /\ IF nd'[self] = null + THEN /\ rVal' = [rVal EXCEPT ![self] = "full"] + /\ pc' = [pc EXCEPT ![self] = "N1a"] + ELSE /\ pc' = [pc EXCEPT ![self] = "N1b"] + /\ rVal' = rVal + /\ UNCHANGED << Mem, LeftHat, RightHat, valBag, stack, v_, nd_, + rh_, rhR, lh_, temp_, rh_p, lh_p, rhL, temp_p, + result_, v, rh_pu, lhL, lh_pu, temp_pu, rh, lh, + lhR, temp, result, pushedVal >> + +N1a(self) == /\ pc[self] = "N1a" + /\ pc' = [pc EXCEPT ![self] = Head(stack[self]).pc] + /\ nd' = [nd EXCEPT ![self] = Head(stack[self]).nd] + /\ rh_pu' = [rh_pu EXCEPT ![self] = Head(stack[self]).rh_pu] + /\ lhL' = [lhL EXCEPT ![self] = Head(stack[self]).lhL] + /\ lh_pu' = [lh_pu EXCEPT ![self] = Head(stack[self]).lh_pu] + /\ temp_pu' = [temp_pu EXCEPT ![self] = Head(stack[self]).temp_pu] + /\ v' = [v EXCEPT ![self] = Head(stack[self]).v] + /\ stack' = [stack EXCEPT ![self] = Tail(stack[self])] + /\ UNCHANGED << Mem, freeList, LeftHat, RightHat, rVal, valBag, + v_, nd_, rh_, rhR, lh_, temp_, rh_p, lh_p, rhL, + temp_p, result_, rh, lh, lhR, temp, result, + pushedVal >> + +N1b(self) == /\ pc[self] = "N1b" + /\ Mem' = [Mem EXCEPT ![nd[self]].L = Dummy, + ![nd[self]].V = v[self]] + /\ pc' = [pc EXCEPT ![self] = "N2"] + /\ UNCHANGED << freeList, LeftHat, RightHat, rVal, valBag, stack, + v_, nd_, rh_, rhR, lh_, temp_, rh_p, lh_p, rhL, + temp_p, result_, v, nd, rh_pu, lhL, lh_pu, + temp_pu, rh, lh, lhR, temp, result, pushedVal >> + +N2(self) == /\ pc[self] = "N2" + /\ lh_pu' = [lh_pu EXCEPT ![self] = LeftHat] + /\ pc' = [pc EXCEPT ![self] = "N3"] + /\ UNCHANGED << Mem, freeList, LeftHat, RightHat, rVal, valBag, + stack, v_, nd_, rh_, rhR, lh_, temp_, rh_p, lh_p, + rhL, temp_p, result_, v, nd, rh_pu, lhL, temp_pu, + rh, lh, lhR, temp, result, pushedVal >> + +N3(self) == /\ pc[self] = "N3" + /\ lhL' = [lhL EXCEPT ![self] = Mem[lh_pu[self]].L] + /\ IF lhL'[self] = lh_pu[self] + THEN /\ pc' = [pc EXCEPT ![self] = "N4"] + ELSE /\ pc' = [pc EXCEPT ![self] = "N7"] + /\ UNCHANGED << Mem, freeList, LeftHat, RightHat, rVal, valBag, + stack, v_, nd_, rh_, rhR, lh_, temp_, rh_p, lh_p, + rhL, temp_p, result_, v, nd, rh_pu, lh_pu, temp_pu, + rh, lh, lhR, temp, result, pushedVal >> + +N4(self) == /\ pc[self] = "N4" + /\ Mem' = [Mem EXCEPT ![nd[self]].R = Dummy] + /\ pc' = [pc EXCEPT ![self] = "N5"] + /\ UNCHANGED << freeList, LeftHat, RightHat, rVal, valBag, stack, + v_, nd_, rh_, rhR, lh_, temp_, rh_p, lh_p, rhL, + temp_p, result_, v, nd, rh_pu, lhL, lh_pu, temp_pu, + rh, lh, lhR, temp, result, pushedVal >> + +N5(self) == /\ pc[self] = "N5" + /\ rh_pu' = [rh_pu EXCEPT ![self] = RightHat] + /\ pc' = [pc EXCEPT ![self] = "N6"] + /\ UNCHANGED << Mem, freeList, LeftHat, RightHat, rVal, valBag, + stack, v_, nd_, rh_, rhR, lh_, temp_, rh_p, lh_p, + rhL, temp_p, result_, v, nd, lhL, lh_pu, temp_pu, + rh, lh, lhR, temp, result, pushedVal >> + +N6(self) == /\ pc[self] = "N6" + /\ IF /\ LeftHat = lh_pu[self] + /\ RightHat = rh_pu[self] + THEN /\ /\ LeftHat' = nd[self] + /\ RightHat' = nd[self] + /\ temp_pu' = [temp_pu EXCEPT ![self] = TRUE] + ELSE /\ temp_pu' = [temp_pu EXCEPT ![self] = FALSE] + /\ UNCHANGED << LeftHat, RightHat >> + /\ IF temp_pu'[self] + THEN /\ rVal' = [rVal EXCEPT ![self] = "okay"] + /\ nd' = [nd EXCEPT ![self] = null] + /\ pc' = [pc EXCEPT ![self] = "N6a"] + ELSE /\ pc' = [pc EXCEPT ![self] = "N2"] + /\ UNCHANGED << rVal, nd >> + /\ UNCHANGED << Mem, freeList, valBag, stack, v_, nd_, rh_, rhR, + lh_, temp_, rh_p, lh_p, rhL, temp_p, result_, v, + rh_pu, lhL, lh_pu, rh, lh, lhR, temp, result, + pushedVal >> + +N6a(self) == /\ pc[self] = "N6a" + /\ pc' = [pc EXCEPT ![self] = Head(stack[self]).pc] + /\ nd' = [nd EXCEPT ![self] = Head(stack[self]).nd] + /\ rh_pu' = [rh_pu EXCEPT ![self] = Head(stack[self]).rh_pu] + /\ lhL' = [lhL EXCEPT ![self] = Head(stack[self]).lhL] + /\ lh_pu' = [lh_pu EXCEPT ![self] = Head(stack[self]).lh_pu] + /\ temp_pu' = [temp_pu EXCEPT ![self] = Head(stack[self]).temp_pu] + /\ v' = [v EXCEPT ![self] = Head(stack[self]).v] + /\ stack' = [stack EXCEPT ![self] = Tail(stack[self])] + /\ UNCHANGED << Mem, freeList, LeftHat, RightHat, rVal, valBag, + v_, nd_, rh_, rhR, lh_, temp_, rh_p, lh_p, rhL, + temp_p, result_, rh, lh, lhR, temp, result, + pushedVal >> + +N7(self) == /\ pc[self] = "N7" + /\ Mem' = [Mem EXCEPT ![nd[self]].R = lh_pu[self]] + /\ pc' = [pc EXCEPT ![self] = "N8"] + /\ UNCHANGED << freeList, LeftHat, RightHat, rVal, valBag, stack, + v_, nd_, rh_, rhR, lh_, temp_, rh_p, lh_p, rhL, + temp_p, result_, v, nd, rh_pu, lhL, lh_pu, temp_pu, + rh, lh, lhR, temp, result, pushedVal >> + +N8(self) == /\ pc[self] = "N8" + /\ IF /\ LeftHat = lh_pu[self] + /\ (Mem[lh_pu[self]].L) = lhL[self] + THEN /\ /\ LeftHat' = nd[self] + /\ Mem' = [Mem EXCEPT ![lh_pu[self]].L = nd[self]] + /\ temp_pu' = [temp_pu EXCEPT ![self] = TRUE] + ELSE /\ temp_pu' = [temp_pu EXCEPT ![self] = FALSE] + /\ UNCHANGED << Mem, LeftHat >> + /\ IF temp_pu'[self] + THEN /\ rVal' = [rVal EXCEPT ![self] = "okay"] + /\ nd' = [nd EXCEPT ![self] = null] + /\ pc' = [pc EXCEPT ![self] = "N8a"] + ELSE /\ pc' = [pc EXCEPT ![self] = "N2"] + /\ UNCHANGED << rVal, nd >> + /\ UNCHANGED << freeList, RightHat, valBag, stack, v_, nd_, rh_, + rhR, lh_, temp_, rh_p, lh_p, rhL, temp_p, result_, + v, rh_pu, lhL, lh_pu, rh, lh, lhR, temp, result, + pushedVal >> + +N8a(self) == /\ pc[self] = "N8a" + /\ pc' = [pc EXCEPT ![self] = Head(stack[self]).pc] + /\ nd' = [nd EXCEPT ![self] = Head(stack[self]).nd] + /\ rh_pu' = [rh_pu EXCEPT ![self] = Head(stack[self]).rh_pu] + /\ lhL' = [lhL EXCEPT ![self] = Head(stack[self]).lhL] + /\ lh_pu' = [lh_pu EXCEPT ![self] = Head(stack[self]).lh_pu] + /\ temp_pu' = [temp_pu EXCEPT ![self] = Head(stack[self]).temp_pu] + /\ v' = [v EXCEPT ![self] = Head(stack[self]).v] + /\ stack' = [stack EXCEPT ![self] = Tail(stack[self])] + /\ UNCHANGED << Mem, freeList, LeftHat, RightHat, rVal, valBag, + v_, nd_, rh_, rhR, lh_, temp_, rh_p, lh_p, rhL, + temp_p, result_, rh, lh, lhR, temp, result, + pushedVal >> + +pushLeft(self) == N1(self) \/ N1a(self) \/ N1b(self) \/ N2(self) + \/ N3(self) \/ N4(self) \/ N5(self) \/ N6(self) + \/ N6a(self) \/ N7(self) \/ N8(self) \/ N8a(self) + +O1(self) == /\ pc[self] = "O1" + /\ lh' = [lh EXCEPT ![self] = LeftHat] + /\ pc' = [pc EXCEPT ![self] = "O2"] + /\ UNCHANGED << Mem, freeList, LeftHat, RightHat, rVal, valBag, + stack, v_, nd_, rh_, rhR, lh_, temp_, rh_p, lh_p, + rhL, temp_p, result_, v, nd, rh_pu, lhL, lh_pu, + temp_pu, rh, lhR, temp, result, pushedVal >> + +O2(self) == /\ pc[self] = "O2" + /\ rh' = [rh EXCEPT ![self] = RightHat] + /\ pc' = [pc EXCEPT ![self] = "O3"] + /\ UNCHANGED << Mem, freeList, LeftHat, RightHat, rVal, valBag, + stack, v_, nd_, rh_, rhR, lh_, temp_, rh_p, lh_p, + rhL, temp_p, result_, v, nd, rh_pu, lhL, lh_pu, + temp_pu, lh, lhR, temp, result, pushedVal >> + +O3(self) == /\ pc[self] = "O3" + /\ IF Mem[lh[self]].L = lh[self] + THEN /\ rVal' = [rVal EXCEPT ![self] = "empty"] + /\ pc' = [pc EXCEPT ![self] = "O3a"] + ELSE /\ pc' = [pc EXCEPT ![self] = "O4"] + /\ rVal' = rVal + /\ UNCHANGED << Mem, freeList, LeftHat, RightHat, valBag, stack, + v_, nd_, rh_, rhR, lh_, temp_, rh_p, lh_p, rhL, + temp_p, result_, v, nd, rh_pu, lhL, lh_pu, temp_pu, + rh, lh, lhR, temp, result, pushedVal >> + +O3a(self) == /\ pc[self] = "O3a" + /\ pc' = [pc EXCEPT ![self] = Head(stack[self]).pc] + /\ rh' = [rh EXCEPT ![self] = Head(stack[self]).rh] + /\ lh' = [lh EXCEPT ![self] = Head(stack[self]).lh] + /\ lhR' = [lhR EXCEPT ![self] = Head(stack[self]).lhR] + /\ temp' = [temp EXCEPT ![self] = Head(stack[self]).temp] + /\ result' = [result EXCEPT ![self] = Head(stack[self]).result] + /\ stack' = [stack EXCEPT ![self] = Tail(stack[self])] + /\ UNCHANGED << Mem, freeList, LeftHat, RightHat, rVal, valBag, + v_, nd_, rh_, rhR, lh_, temp_, rh_p, lh_p, rhL, + temp_p, result_, v, nd, rh_pu, lhL, lh_pu, + temp_pu, pushedVal >> + +O4(self) == /\ pc[self] = "O4" + /\ IF lh[self] = rh[self] + THEN /\ IF /\ LeftHat = lh[self] + /\ RightHat = rh[self] + THEN /\ /\ LeftHat' = Dummy + /\ RightHat' = Dummy + /\ temp' = [temp EXCEPT ![self] = TRUE] + ELSE /\ temp' = [temp EXCEPT ![self] = FALSE] + /\ UNCHANGED << LeftHat, RightHat >> + /\ IF temp'[self] + THEN /\ pc' = [pc EXCEPT ![self] = "O5"] + ELSE /\ pc' = [pc EXCEPT ![self] = "O1"] + ELSE /\ pc' = [pc EXCEPT ![self] = "O6"] + /\ UNCHANGED << LeftHat, RightHat, temp >> + /\ UNCHANGED << Mem, freeList, rVal, valBag, stack, v_, nd_, rh_, + rhR, lh_, temp_, rh_p, lh_p, rhL, temp_p, result_, + v, nd, rh_pu, lhL, lh_pu, temp_pu, rh, lh, lhR, + result, pushedVal >> + +O6(self) == /\ pc[self] = "O6" + /\ lhR' = [lhR EXCEPT ![self] = Mem[lh[self]].R] + /\ pc' = [pc EXCEPT ![self] = "O7"] + /\ UNCHANGED << Mem, freeList, LeftHat, RightHat, rVal, valBag, + stack, v_, nd_, rh_, rhR, lh_, temp_, rh_p, lh_p, + rhL, temp_p, result_, v, nd, rh_pu, lhL, lh_pu, + temp_pu, rh, lh, temp, result, pushedVal >> + +O7(self) == /\ pc[self] = "O7" + /\ IF /\ LeftHat = lh[self] + /\ (Mem[lh[self]].R) = lhR[self] + THEN /\ /\ LeftHat' = lhR[self] + /\ Mem' = [Mem EXCEPT ![lh[self]].R = lh[self]] + /\ temp' = [temp EXCEPT ![self] = TRUE] + ELSE /\ temp' = [temp EXCEPT ![self] = FALSE] + /\ UNCHANGED << Mem, LeftHat >> + /\ IF temp'[self] + THEN /\ pc' = [pc EXCEPT ![self] = "O8"] + ELSE /\ pc' = [pc EXCEPT ![self] = "O1"] + /\ UNCHANGED << freeList, RightHat, rVal, valBag, stack, v_, nd_, + rh_, rhR, lh_, temp_, rh_p, lh_p, rhL, temp_p, + result_, v, nd, rh_pu, lhL, lh_pu, temp_pu, rh, lh, + lhR, result, pushedVal >> + +O8(self) == /\ pc[self] = "O8" + /\ result' = [result EXCEPT ![self] = Mem[lh[self]].V] + /\ pc' = [pc EXCEPT ![self] = "O9"] + /\ UNCHANGED << Mem, freeList, LeftHat, RightHat, rVal, valBag, + stack, v_, nd_, rh_, rhR, lh_, temp_, rh_p, lh_p, + rhL, temp_p, result_, v, nd, rh_pu, lhL, lh_pu, + temp_pu, rh, lh, lhR, temp, pushedVal >> + +O9(self) == /\ pc[self] = "O9" + /\ Mem' = [Mem EXCEPT ![lh[self]].L = Dummy, + ![lh[self]].V = null] + /\ rVal' = [rVal EXCEPT ![self] = result[self]] + /\ pc' = [pc EXCEPT ![self] = "O10a"] + /\ UNCHANGED << freeList, LeftHat, RightHat, valBag, stack, v_, + nd_, rh_, rhR, lh_, temp_, rh_p, lh_p, rhL, temp_p, + result_, v, nd, rh_pu, lhL, lh_pu, temp_pu, rh, lh, + lhR, temp, result, pushedVal >> + +O10a(self) == /\ pc[self] = "O10a" + /\ pc' = [pc EXCEPT ![self] = Head(stack[self]).pc] + /\ rh' = [rh EXCEPT ![self] = Head(stack[self]).rh] + /\ lh' = [lh EXCEPT ![self] = Head(stack[self]).lh] + /\ lhR' = [lhR EXCEPT ![self] = Head(stack[self]).lhR] + /\ temp' = [temp EXCEPT ![self] = Head(stack[self]).temp] + /\ result' = [result EXCEPT ![self] = Head(stack[self]).result] + /\ stack' = [stack EXCEPT ![self] = Tail(stack[self])] + /\ UNCHANGED << Mem, freeList, LeftHat, RightHat, rVal, valBag, + v_, nd_, rh_, rhR, lh_, temp_, rh_p, lh_p, rhL, + temp_p, result_, v, nd, rh_pu, lhL, lh_pu, + temp_pu, pushedVal >> + +O5(self) == /\ pc[self] = "O5" + /\ rVal' = [rVal EXCEPT ![self] = Mem[lh[self]].V] + /\ pc' = [pc EXCEPT ![self] = "O5a"] + /\ UNCHANGED << Mem, freeList, LeftHat, RightHat, valBag, stack, + v_, nd_, rh_, rhR, lh_, temp_, rh_p, lh_p, rhL, + temp_p, result_, v, nd, rh_pu, lhL, lh_pu, temp_pu, + rh, lh, lhR, temp, result, pushedVal >> + +O5a(self) == /\ pc[self] = "O5a" + /\ pc' = [pc EXCEPT ![self] = Head(stack[self]).pc] + /\ rh' = [rh EXCEPT ![self] = Head(stack[self]).rh] + /\ lh' = [lh EXCEPT ![self] = Head(stack[self]).lh] + /\ lhR' = [lhR EXCEPT ![self] = Head(stack[self]).lhR] + /\ temp' = [temp EXCEPT ![self] = Head(stack[self]).temp] + /\ result' = [result EXCEPT ![self] = Head(stack[self]).result] + /\ stack' = [stack EXCEPT ![self] = Tail(stack[self])] + /\ UNCHANGED << Mem, freeList, LeftHat, RightHat, rVal, valBag, + v_, nd_, rh_, rhR, lh_, temp_, rh_p, lh_p, rhL, + temp_p, result_, v, nd, rh_pu, lhL, lh_pu, + temp_pu, pushedVal >> + +popLeft(self) == O1(self) \/ O2(self) \/ O3(self) \/ O3a(self) \/ O4(self) + \/ O6(self) \/ O7(self) \/ O8(self) \/ O9(self) + \/ O10a(self) \/ O5(self) \/ O5a(self) + +T1(self) == /\ pc[self] = "T1" + /\ \/ /\ \E x \in Val: + pushedVal' = [pushedVal EXCEPT ![self] = x] + /\ valBag' = [valBag EXCEPT ![pushedVal'[self]] = valBag[pushedVal'[self]] + 1] + /\ \/ /\ /\ stack' = [stack EXCEPT ![self] = << [ procedure |-> "pushLeft", + pc |-> "T2", + nd |-> nd[self], + rh_pu |-> rh_pu[self], + lhL |-> lhL[self], + lh_pu |-> lh_pu[self], + temp_pu |-> temp_pu[self], + v |-> v[self] ] >> + \o stack[self]] + /\ v' = [v EXCEPT ![self] = pushedVal'[self]] + /\ nd' = [nd EXCEPT ![self] = null] + /\ rh_pu' = [rh_pu EXCEPT ![self] = Dummy] + /\ lhL' = [lhL EXCEPT ![self] = Dummy] + /\ lh_pu' = [lh_pu EXCEPT ![self] = Dummy] + /\ temp_pu' = [temp_pu EXCEPT ![self] = Dummy] + /\ pc' = [pc EXCEPT ![self] = "N1"] + /\ UNCHANGED <<v_, nd_, rh_, rhR, lh_, temp_>> + \/ /\ /\ stack' = [stack EXCEPT ![self] = << [ procedure |-> "pushRight", + pc |-> "T2", + nd_ |-> nd_[self], + rh_ |-> rh_[self], + rhR |-> rhR[self], + lh_ |-> lh_[self], + temp_ |-> temp_[self], + v_ |-> v_[self] ] >> + \o stack[self]] + /\ v_' = [v_ EXCEPT ![self] = pushedVal'[self]] + /\ nd_' = [nd_ EXCEPT ![self] = null] + /\ rh_' = [rh_ EXCEPT ![self] = Dummy] + /\ rhR' = [rhR EXCEPT ![self] = Dummy] + /\ lh_' = [lh_ EXCEPT ![self] = Dummy] + /\ temp_' = [temp_ EXCEPT ![self] = Dummy] + /\ pc' = [pc EXCEPT ![self] = "L1"] + /\ UNCHANGED <<v, nd, rh_pu, lhL, lh_pu, temp_pu>> + /\ UNCHANGED <<rh_p, lh_p, rhL, temp_p, result_, rh, lh, lhR, temp, result>> + \/ /\ \/ /\ stack' = [stack EXCEPT ![self] = << [ procedure |-> "popLeft", + pc |-> "T3", + rh |-> rh[self], + lh |-> lh[self], + lhR |-> lhR[self], + temp |-> temp[self], + result |-> result[self] ] >> + \o stack[self]] + /\ rh' = [rh EXCEPT ![self] = Dummy] + /\ lh' = [lh EXCEPT ![self] = Dummy] + /\ lhR' = [lhR EXCEPT ![self] = Dummy] + /\ temp' = [temp EXCEPT ![self] = Dummy] + /\ result' = [result EXCEPT ![self] = null] + /\ pc' = [pc EXCEPT ![self] = "O1"] + /\ UNCHANGED <<rh_p, lh_p, rhL, temp_p, result_>> + \/ /\ stack' = [stack EXCEPT ![self] = << [ procedure |-> "popRight", + pc |-> "T3", + rh_p |-> rh_p[self], + lh_p |-> lh_p[self], + rhL |-> rhL[self], + temp_p |-> temp_p[self], + result_ |-> result_[self] ] >> + \o stack[self]] + /\ rh_p' = [rh_p EXCEPT ![self] = Dummy] + /\ lh_p' = [lh_p EXCEPT ![self] = Dummy] + /\ rhL' = [rhL EXCEPT ![self] = Dummy] + /\ temp_p' = [temp_p EXCEPT ![self] = Dummy] + /\ result_' = [result_ EXCEPT ![self] = null] + /\ pc' = [pc EXCEPT ![self] = "M1"] + /\ UNCHANGED <<rh, lh, lhR, temp, result>> + /\ UNCHANGED <<valBag, v_, nd_, rh_, rhR, lh_, temp_, v, nd, rh_pu, lhL, lh_pu, temp_pu, pushedVal>> + /\ UNCHANGED << Mem, freeList, LeftHat, RightHat, rVal >> + +T2(self) == /\ pc[self] = "T2" + /\ IF rVal[self] = "full" + THEN /\ valBag' = [valBag EXCEPT ![pushedVal[self]] = valBag[pushedVal[self]] - 1] + ELSE /\ TRUE + /\ UNCHANGED valBag + /\ pc' = [pc EXCEPT ![self] = "T1"] + /\ UNCHANGED << Mem, freeList, LeftHat, RightHat, rVal, stack, v_, + nd_, rh_, rhR, lh_, temp_, rh_p, lh_p, rhL, temp_p, + result_, v, nd, rh_pu, lhL, lh_pu, temp_pu, rh, lh, + lhR, temp, result, pushedVal >> + +T3(self) == /\ pc[self] = "T3" + /\ IF rVal[self] # "empty" + THEN /\ Assert(valBag[rVal[self]] > 0, + "Failure of assertion at line 238, column 11.") + /\ valBag' = [valBag EXCEPT ![rVal[self]] = valBag[rVal[self]] - 1] + ELSE /\ TRUE + /\ UNCHANGED valBag + /\ pc' = [pc EXCEPT ![self] = "T1"] + /\ UNCHANGED << Mem, freeList, LeftHat, RightHat, rVal, stack, v_, + nd_, rh_, rhR, lh_, temp_, rh_p, lh_p, rhL, temp_p, + result_, v, nd, rh_pu, lhL, lh_pu, temp_pu, rh, lh, + lhR, temp, result, pushedVal >> + +test(self) == T1(self) \/ T2(self) \/ T3(self) + +Next == (\E self \in ProcSet: \/ pushRight(self) \/ popRight(self) + \/ pushLeft(self) \/ popLeft(self)) + \/ (\E self \in Procs: test(self)) + +Spec == Init /\ [][Next]_vars + +\* END TRANSLATION + +Symmetry == Permutations(Address \ {Dummy}) \cup Permutations(Procs) + +Liveness == \A p \in Procs : []<> (pc[p] = "T1") +============================================================================= + +Checked on SVC-LAMPORT-3 with + CONSTANTS Val = {v1, v2} + Address = {a1, a2, a3} + Dummy = a1 + Procs = {p1, p2} + null = null + GC = GC + +found 2886585 states, depth 67, in 17 min 38.3 sec + +============================== + +Run on SVC-LAMPORT-2 with + + CONSTANTS Val = {v1} + Address = {a1, a2, a3, a4} + Dummy = a1 + Procs = {p1, p2, p3} + null = null + GC = GC + +stopped after 1482 min 40 sec, with +109078490 states generated, 37886049 distinct states, 5107502 states +left on queue. diff --git a/tlatools/test-model/pcal/Dijkstra1.cfg b/tlatools/test-model/pcal/Dijkstra1.cfg new file mode 100644 index 0000000000000000000000000000000000000000..2b93631d3e6bbf904652490bceaf2e39627dec59 --- /dev/null +++ b/tlatools/test-model/pcal/Dijkstra1.cfg @@ -0,0 +1,6 @@ +SPECIFICATION Spec +\* Add statements after this line. +CONSTANTS N = 4 + K = 5 +INVARIANT SomeoneHoldsToken +PROPERTY EventuallyJustOneHoldsToken diff --git a/tlatools/test-model/pcal/Dijkstra1.tla b/tlatools/test-model/pcal/Dijkstra1.tla new file mode 100644 index 0000000000000000000000000000000000000000..c10721c98ca9a6e9177245f0188c09d41f654fd9 --- /dev/null +++ b/tlatools/test-model/pcal/Dijkstra1.tla @@ -0,0 +1,84 @@ +--algorithm Dijkstra1 +variable M \in [ProcSet -> 0..(K-1)]; + + process P0 = 0 + begin +p0: while TRUE do + when M[0] = M[N-1]; +p1: M[0] := (M[0] + 1) % K; + end while + end process; + + process Pi \in 1..(N-1) + begin +pi: while (TRUE) do + when M[self] # M[self - 1]; +pj: M[self] := M[self - 1]; + end while + end process; + + end algorithm + +----------- MODULE Dijkstra1 ----------- +EXTENDS FiniteSets, Naturals + +CONSTANT K, N + +ASSUME (K > N) /\ (N > 0) + +\* BEGIN TRANSLATION - the hash of the PCal code: PCal-64143a14f748e286ec27eafe96303703 +VARIABLES M, pc + +vars == << M, pc >> + +ProcSet == {0} \cup (1..(N-1)) + +Init == (* Global variables *) + /\ M \in [ProcSet -> 0..(K-1)] + /\ pc = [self \in ProcSet |-> CASE self = 0 -> "p0" + [] self \in 1..(N-1) -> "pi"] + +p0 == /\ pc[0] = "p0" + /\ M[0] = M[N-1] + /\ pc' = [pc EXCEPT ![0] = "p1"] + /\ M' = M + +p1 == /\ pc[0] = "p1" + /\ M' = [M EXCEPT ![0] = (M[0] + 1) % K] + /\ pc' = [pc EXCEPT ![0] = "p0"] + +P0 == p0 \/ p1 + +pi(self) == /\ pc[self] = "pi" + /\ M[self] # M[self - 1] + /\ pc' = [pc EXCEPT ![self] = "pj"] + /\ M' = M + +pj(self) == /\ pc[self] = "pj" + /\ M' = [M EXCEPT ![self] = M[self - 1]] + /\ pc' = [pc EXCEPT ![self] = "pi"] + +Pi(self) == pi(self) \/ pj(self) + +Next == P0 + \/ (\E self \in 1..(N-1): Pi(self)) + +Spec == /\ Init /\ [][Next]_vars + /\ WF_vars(P0) + /\ \A self \in 1..(N-1) : WF_vars(Pi(self)) + +\* END TRANSLATION - the hash of the generated TLA code (remove to silence divergence warnings): TLA-80735c28b54285a89ec67fce31b85c9d + +HasToken(self) == \/ (self = 0) /\ (M[0] = M[N - 1]) + \/ (self > 0) /\ (M[self] # M[self - 1]) + +TokenHolders == {self \in ProcSet: HasToken(self)} + +SomeoneHoldsToken == Cardinality(TokenHolders) > 0 + +EventuallyJustOneHoldsToken == <>[] (Cardinality(TokenHolders) = 1) + +THEOREM Spec => [] SomeoneHoldsToken + +THEOREM Spec => EventuallyJustOneHoldsToken +======================================== diff --git a/tlatools/test-model/pcal/DiningPhilosophers.cfg b/tlatools/test-model/pcal/DiningPhilosophers.cfg new file mode 100644 index 0000000000000000000000000000000000000000..a174cf298c441c5cb0d1f0028577442a7726703b --- /dev/null +++ b/tlatools/test-model/pcal/DiningPhilosophers.cfg @@ -0,0 +1,5 @@ +SPECIFICATION Spec +\* Add statements after this line. +CONSTANT N = 4 +INVARIANT Invariant +PROPERTY StarvationFree diff --git a/tlatools/test-model/pcal/DiningPhilosophers.tla b/tlatools/test-model/pcal/DiningPhilosophers.tla new file mode 100644 index 0000000000000000000000000000000000000000..17c4c87ed23087175780541c2f824bedb2d8b10d --- /dev/null +++ b/tlatools/test-model/pcal/DiningPhilosophers.tla @@ -0,0 +1,127 @@ +-------------------------- MODULE DiningPhilosophers ------------------------ + +(***************************************************************************) +(* A simple solution to the Dining Philosophers problem in which processes *) +(* 1 through N-1 pick up their lef then their right forks, and process 0 *) +(* picks them up in the opposite order. *) +(***************************************************************************) + +EXTENDS Naturals + +CONSTANT N + +ASSUME N \in Nat + +(********************** + +--algorithm DiningPhilosophers + variable sem = [i \in 0..(N-1) |-> 1] ; + +process Proc \in 1..(N-1) +begin +l1 : while TRUE + do when sem[self] = 1 ; \* Get right fork. + sem[self] := 0 ; + l2 : when sem[(self-1) % N] = 1 ; \* Get left fork. + sem[(self-1) % N] := 0 ; + e : skip ; \* Eat + l3 : sem[self] := 1 ; \* Release right fork. + l4 : sem[(self-1) % N] := 1 ; \* Release left fork. + end while ; +end process + +process Proc0 = 0 +begin +l01 : while TRUE + do when sem[N-1] = 1 ; \* get left fork + sem[N-1] := 0 ; + l02 : when sem[0] = 1 ; \* get right fork + sem[0] := 0 ; + e0 : skip ; \* eat + l03 : sem[0] := 1 ; \* release left fork + l04 : sem[N-1] := 1 ; \* release right fork + end while ; +end process + +end algorithm + +***********************) + +\* BEGIN TRANSLATION - the hash of the PCal code: PCal-d34a24305f241c923d0f8daa00682e02 +VARIABLES sem, pc + +vars == << sem, pc >> + +ProcSet == (1..(N-1)) \cup {0} + +Init == (* Global variables *) + /\ sem = [i \in 0..(N-1) |-> 1] + /\ pc = [self \in ProcSet |-> CASE self \in 1..(N-1) -> "l1" + [] self = 0 -> "l01"] + +l1(self) == /\ pc[self] = "l1" + /\ sem[self] = 1 + /\ sem' = [sem EXCEPT ![self] = 0] + /\ pc' = [pc EXCEPT ![self] = "l2"] + +l2(self) == /\ pc[self] = "l2" + /\ sem[(self-1) % N] = 1 + /\ sem' = [sem EXCEPT ![(self-1) % N] = 0] + /\ pc' = [pc EXCEPT ![self] = "e"] + +e(self) == /\ pc[self] = "e" + /\ TRUE + /\ pc' = [pc EXCEPT ![self] = "l3"] + /\ sem' = sem + +l3(self) == /\ pc[self] = "l3" + /\ sem' = [sem EXCEPT ![self] = 1] + /\ pc' = [pc EXCEPT ![self] = "l4"] + +l4(self) == /\ pc[self] = "l4" + /\ sem' = [sem EXCEPT ![(self-1) % N] = 1] + /\ pc' = [pc EXCEPT ![self] = "l1"] + +Proc(self) == l1(self) \/ l2(self) \/ e(self) \/ l3(self) \/ l4(self) + +l01 == /\ pc[0] = "l01" + /\ sem[N-1] = 1 + /\ sem' = [sem EXCEPT ![N-1] = 0] + /\ pc' = [pc EXCEPT ![0] = "l02"] + +l02 == /\ pc[0] = "l02" + /\ sem[0] = 1 + /\ sem' = [sem EXCEPT ![0] = 0] + /\ pc' = [pc EXCEPT ![0] = "e0"] + +e0 == /\ pc[0] = "e0" + /\ TRUE + /\ pc' = [pc EXCEPT ![0] = "l03"] + /\ sem' = sem + +l03 == /\ pc[0] = "l03" + /\ sem' = [sem EXCEPT ![0] = 1] + /\ pc' = [pc EXCEPT ![0] = "l04"] + +l04 == /\ pc[0] = "l04" + /\ sem' = [sem EXCEPT ![N-1] = 1] + /\ pc' = [pc EXCEPT ![0] = "l01"] + +Proc0 == l01 \/ l02 \/ e0 \/ l03 \/ l04 + +Next == Proc0 + \/ (\E self \in 1..(N-1): Proc(self)) + +Spec == /\ Init /\ [][Next]_vars + /\ \A self \in 1..(N-1) : SF_vars(Proc(self)) + /\ SF_vars(Proc0) + +\* END TRANSLATION - the hash of the generated TLA code (remove to silence divergence warnings): TLA-ff7acdd47d76382441ae75c392052170 + +IsEating(i) == IF i = 0 THEN pc[i] = "e0" + ELSE pc[i] = "e" + +Invariant == \A i \in 0..(N-1) : ~ (IsEating(i) /\ IsEating((i+1)%N)) + +StarvationFree == \A i \in 0..(N-1) : []<> IsEating(i) +============================================================================= diff --git a/tlatools/test-model/pcal/DiningPhilosophers2.tla b/tlatools/test-model/pcal/DiningPhilosophers2.tla new file mode 100644 index 0000000000000000000000000000000000000000..d6dac6d75951822b2036e79b90d08d1cb7c5a91c --- /dev/null +++ b/tlatools/test-model/pcal/DiningPhilosophers2.tla @@ -0,0 +1,220 @@ + +-------------------------- MODULE DiningPhilosophers2 ------------------------ + +(***************************************************************************) +(* A simple solution to the Dining Philosophers problem in which processes *) +(* 1 through N-1 pick up their lef then their right forks, and process 0 *) +(* picks them up in the opposite order. *) +(***************************************************************************) + +EXTENDS Naturals + +CONSTANT N + +ASSUME N \in Nat + +(********************** + +--algorithm DiningPhilosophers + +variable sem = [i \in 0..(N-1) |-> 1] ; + procedure foo () + begin l2 :+ skip; + e :+ skip; + l3 :+ skip; + p4 : skip ; + return ; + end procedure + + procedure foo2 () + begin foo2begin : skip; + return; + end procedure ; + + fair process DummyProcessSet \in -3..0 + begin dp1: skip ; + end process + + fair process DummySingleProc = -42 + begin dp1: skip ; + end process; + + fair process Proc \in 1..(N-1) +begin +l1 : while TRUE + do when sem[self] = 1 ; \* Get right fork. + sem[self] := 0 ; + fox :+ when sem[(self-1) % N] = 1 ; \* Get left fork. + sem[(self-1) % N] := 0 ; + e :+ skip ; \* Eat + l3 :+ sem[self] := 1 ; \* Release right fork. + l4 :+ sem[(self-1) % N] := 1 ; \* Release left fork. + end while ; +end process + +process Leader = 0 \* Proc0 = 0 +begin +l01 : while TRUE + do when sem[N-1] = 1 ; \* get left fork + sem[N-1] := 0 ; + l2xx : when sem[0] = 1 ; \* get right fork + sem[0] := 0 ; + exx : call foo() ; \* eat + l03 :+ sem[0] := 1 ; \* release left fork + call foo() ; + l04 :+ sem[N-1] := 1 ; \* release right fork + end while ; +end process + +end algorithm + +***********************) + +\* BEGIN TRANSLATION +\* Label e of procedure foo at line 23 col 16 changed to e_ +\* Label l3 of procedure foo at line 24 col 17 changed to l3_ +\* Label dp1 of process DummyProcessSet at line 35 col 15 changed to dp1_ +VARIABLES sem, pc, stack + +vars == << sem, pc, stack >> + +ProcSet == (-3..0) \cup {-42} \cup (1..(N-1)) \cup {0} + +Init == (* Global variables *) + /\ sem = [i \in 0..(N-1) |-> 1] + /\ stack = [self \in ProcSet |-> << >>] + /\ pc = [self \in ProcSet |-> CASE self \in -3..0 -> "dp1_" + [] self = -42 -> "dp1" + [] self \in 1..(N-1) -> "l1" + [] self = 0 -> "l01"] + +l2(self) == /\ pc[self] = "l2" + /\ TRUE + /\ pc' = [pc EXCEPT ![self] = "e_"] + /\ UNCHANGED << sem, stack >> + +e_(self) == /\ pc[self] = "e_" + /\ TRUE + /\ pc' = [pc EXCEPT ![self] = "l3_"] + /\ UNCHANGED << sem, stack >> + +l3_(self) == /\ pc[self] = "l3_" + /\ TRUE + /\ pc' = [pc EXCEPT ![self] = "p4"] + /\ UNCHANGED << sem, stack >> + +p4(self) == /\ pc[self] = "p4" + /\ TRUE + /\ pc' = [pc EXCEPT ![self] = Head(stack[self]).pc] + /\ stack' = [stack EXCEPT ![self] = Tail(stack[self])] + /\ sem' = sem + +foo(self) == l2(self) \/ e_(self) \/ l3_(self) \/ p4(self) + +foo2begin(self) == /\ pc[self] = "foo2begin" + /\ TRUE + /\ pc' = [pc EXCEPT ![self] = Head(stack[self]).pc] + /\ stack' = [stack EXCEPT ![self] = Tail(stack[self])] + /\ sem' = sem + +foo2(self) == foo2begin(self) + +dp1_(self) == /\ pc[self] = "dp1_" + /\ TRUE + /\ pc' = [pc EXCEPT ![self] = "Done"] + /\ UNCHANGED << sem, stack >> + +DummyProcessSet(self) == dp1_(self) + +dp1 == /\ pc[-42] = "dp1" + /\ TRUE + /\ pc' = [pc EXCEPT ![-42] = "Done"] + /\ UNCHANGED << sem, stack >> + +DummySingleProc == dp1 + +l1(self) == /\ pc[self] = "l1" + /\ sem[self] = 1 + /\ sem' = [sem EXCEPT ![self] = 0] + /\ pc' = [pc EXCEPT ![self] = "fox"] + /\ stack' = stack + +fox(self) == /\ pc[self] = "fox" + /\ sem[(self-1) % N] = 1 + /\ sem' = [sem EXCEPT ![(self-1) % N] = 0] + /\ pc' = [pc EXCEPT ![self] = "e"] + /\ stack' = stack + +e(self) == /\ pc[self] = "e" + /\ TRUE + /\ pc' = [pc EXCEPT ![self] = "l3"] + /\ UNCHANGED << sem, stack >> + +l3(self) == /\ pc[self] = "l3" + /\ sem' = [sem EXCEPT ![self] = 1] + /\ pc' = [pc EXCEPT ![self] = "l4"] + /\ stack' = stack + +l4(self) == /\ pc[self] = "l4" + /\ sem' = [sem EXCEPT ![(self-1) % N] = 1] + /\ pc' = [pc EXCEPT ![self] = "l1"] + /\ stack' = stack + +Proc(self) == l1(self) \/ fox(self) \/ e(self) \/ l3(self) \/ l4(self) + +l01 == /\ pc[0] = "l01" + /\ sem[N-1] = 1 + /\ sem' = [sem EXCEPT ![N-1] = 0] + /\ pc' = [pc EXCEPT ![0] = "l2xx"] + /\ stack' = stack + +l2xx == /\ pc[0] = "l2xx" + /\ sem[0] = 1 + /\ sem' = [sem EXCEPT ![0] = 0] + /\ pc' = [pc EXCEPT ![0] = "exx"] + /\ stack' = stack + +exx == /\ pc[0] = "exx" + /\ stack' = [stack EXCEPT ![0] = << [ procedure |-> "foo", + pc |-> "l03" ] >> + \o stack[0]] + /\ pc' = [pc EXCEPT ![0] = "l2"] + /\ sem' = sem + +l03 == /\ pc[0] = "l03" + /\ sem' = [sem EXCEPT ![0] = 1] + /\ stack' = [stack EXCEPT ![0] = << [ procedure |-> "foo", + pc |-> "l04" ] >> + \o stack[0]] + /\ pc' = [pc EXCEPT ![0] = "l2"] + +l04 == /\ pc[0] = "l04" + /\ sem' = [sem EXCEPT ![N-1] = 1] + /\ pc' = [pc EXCEPT ![0] = "l01"] + /\ stack' = stack + +Leader == l01 \/ l2xx \/ exx \/ l03 \/ l04 + +Next == DummySingleProc \/ Leader + \/ (\E self \in ProcSet: foo(self) \/ foo2(self)) + \/ (\E self \in -3..0: DummyProcessSet(self)) + \/ (\E self \in 1..(N-1): Proc(self)) + +Spec == /\ Init /\ [][Next]_vars + /\ \A self \in -3..0 : WF_vars(DummyProcessSet(self)) + /\ WF_vars(DummySingleProc) + /\ \A self \in 1..(N-1) : /\ WF_vars(Proc(self)) + /\ SF_vars(fox(self)) /\ SF_vars(e(self)) /\ SF_vars(l3(self)) /\ SF_vars(l4(self)) + /\ /\ WF_vars(Leader) /\ SF_vars(l03) /\ SF_vars(l04) + /\ WF_vars(foo(0)) + /\ SF_vars(l2(0)) /\ SF_vars(e_(0)) /\ SF_vars(l3_(0)) + +\* END TRANSLATION + +IsEating(i) == IF i = 0 THEN pc[i] = "e0" + ELSE pc[i] = "e" + +Invariant == \A i \in 0..(N-1) : ~ (IsEating(i) /\ IsEating((i+1)%N)) + +StarvationFree == \A i \in 0..(N-1) : []<> IsEating(i) +============================================================================= diff --git a/tlatools/test-model/pcal/Either1.tla b/tlatools/test-model/pcal/Either1.tla new file mode 100644 index 0000000000000000000000000000000000000000..93aa90dd84337c5142b56a980d79e41760eaa25c --- /dev/null +++ b/tlatools/test-model/pcal/Either1.tla @@ -0,0 +1,59 @@ +------------------------------- MODULE Either1 ------------------------------ +EXTENDS Naturals, Sequences, TLC + +(* --algorithm Either + variables x = 0 ; y = 0 ; + begin a: either x := 1 ; b: x := x + 1; + or y := 1 ; c: y := y + 1; + end either ; + d: assert x+y = 2 ; + end algorithm +*) + +\* BEGIN TRANSLATION - the hash of the PCal code: PCal-af9f3b0389ad56ee237e79295b3205ff +VARIABLES x, y, pc + +vars == << x, y, pc >> + +Init == (* Global variables *) + /\ x = 0 + /\ y = 0 + /\ pc = "a" + +a == /\ pc = "a" + /\ \/ /\ x' = 1 + /\ pc' = "b" + /\ y' = y + \/ /\ y' = 1 + /\ pc' = "c" + /\ x' = x + +b == /\ pc = "b" + /\ x' = x + 1 + /\ pc' = "d" + /\ y' = y + +c == /\ pc = "c" + /\ y' = y + 1 + /\ pc' = "d" + /\ x' = x + +d == /\ pc = "d" + /\ Assert(x+y = 2, "Failure of assertion at line 9, column 16.") + /\ pc' = "Done" + /\ UNCHANGED << x, y >> + +(* Allow infinite stuttering to prevent deadlock on termination. *) +Terminating == pc = "Done" /\ UNCHANGED vars + +Next == a \/ b \/ c \/ d + \/ Terminating + +Spec == /\ Init /\ [][Next]_vars + /\ WF_vars(Next) + +Termination == <>(pc = "Done") + +\* END TRANSLATION - the hash of the generated TLA code (remove to silence divergence warnings): TLA-91542970fa29f8acc63f0f9ca8f27be3 + +============================================================================= diff --git a/tlatools/test-model/pcal/Either2.tla b/tlatools/test-model/pcal/Either2.tla new file mode 100644 index 0000000000000000000000000000000000000000..81d26103ae3dba9a3d4aebefa52c5d4960163212 --- /dev/null +++ b/tlatools/test-model/pcal/Either2.tla @@ -0,0 +1,71 @@ +------------------------------- MODULE Either2 ------------------------------ +EXTENDS Naturals, Sequences, TLC + +(* --algorithm Either + variables x = 0 ; y = 0 ; z = 0 ; + begin a: either x := 1 ; + b: x := x + 1; + or y := 1 ; c: y := y + 1; + end either ; + d: either when x = 0 ; z := z + 1 ; + or when x = 2 ; z := z + 3 ; + end either ; + assert x+y = 2 ; + assert z = x + 1 ; + end algorithm +*) + +\* BEGIN TRANSLATION - the hash of the PCal code: PCal-92c2a3ead1499974b8b837c3d25d0ccd +VARIABLES x, y, z, pc + +vars == << x, y, z, pc >> + +Init == (* Global variables *) + /\ x = 0 + /\ y = 0 + /\ z = 0 + /\ pc = "a" + +a == /\ pc = "a" + /\ \/ /\ x' = 1 + /\ pc' = "b" + /\ y' = y + \/ /\ y' = 1 + /\ pc' = "c" + /\ x' = x + /\ z' = z + +b == /\ pc = "b" + /\ x' = x + 1 + /\ pc' = "d" + /\ UNCHANGED << y, z >> + +c == /\ pc = "c" + /\ y' = y + 1 + /\ pc' = "d" + /\ UNCHANGED << x, z >> + +d == /\ pc = "d" + /\ \/ /\ x = 0 + /\ z' = z + 1 + \/ /\ x = 2 + /\ z' = z + 3 + /\ Assert(x+y = 2, "Failure of assertion at line 13, column 16.") + /\ Assert(z' = x + 1, "Failure of assertion at line 14, column 16.") + /\ pc' = "Done" + /\ UNCHANGED << x, y >> + +(* Allow infinite stuttering to prevent deadlock on termination. *) +Terminating == pc = "Done" /\ UNCHANGED vars + +Next == a \/ b \/ c \/ d + \/ Terminating + +Spec == /\ Init /\ [][Next]_vars + /\ WF_vars(Next) + +Termination == <>(pc = "Done") + +\* END TRANSLATION - the hash of the generated TLA code (remove to silence divergence warnings): TLA-dc9b13cd9fc5d96b6144b722cc2ffb3d + +============================================================================= diff --git a/tlatools/test-model/pcal/Either3.tla b/tlatools/test-model/pcal/Either3.tla new file mode 100644 index 0000000000000000000000000000000000000000..a1af934e141e141bacc0cce75e091a1ccc94927a --- /dev/null +++ b/tlatools/test-model/pcal/Either3.tla @@ -0,0 +1,76 @@ +------------------------------- MODULE Either3 ------------------------------ +EXTENDS Naturals, Sequences, TLC + +(* --algorithm Either + variables x = 0 ; y = 0 ; z = 0 ; + begin a: either x := 1 ; + b: x := x + 1; + or y := 1 ; c: y := y + 1; + or z := 100 + end either ; + d: either when x = 0 ; z := z + 1 ; + or when x = 2 ; z := z + 3 ; + end either ; + assert (x+y = 2) \/ ((z = 101) /\ (x+y=0)); + assert (z < 100) => (z = x + 1) ; + end algorithm +*) + +\* BEGIN TRANSLATION - the hash of the PCal code: PCal-d275a21163e8b7088902b968d404026a +VARIABLES x, y, z, pc + +vars == << x, y, z, pc >> + +Init == (* Global variables *) + /\ x = 0 + /\ y = 0 + /\ z = 0 + /\ pc = "a" + +a == /\ pc = "a" + /\ \/ /\ x' = 1 + /\ pc' = "b" + /\ UNCHANGED <<y, z>> + \/ /\ y' = 1 + /\ pc' = "c" + /\ UNCHANGED <<x, z>> + \/ /\ z' = 100 + /\ pc' = "d" + /\ UNCHANGED <<x, y>> + +b == /\ pc = "b" + /\ x' = x + 1 + /\ pc' = "d" + /\ UNCHANGED << y, z >> + +c == /\ pc = "c" + /\ y' = y + 1 + /\ pc' = "d" + /\ UNCHANGED << x, z >> + +d == /\ pc = "d" + /\ \/ /\ x = 0 + /\ z' = z + 1 + \/ /\ x = 2 + /\ z' = z + 3 + /\ Assert((x+y = 2) \/ ((z' = 101) /\ (x+y=0)), + "Failure of assertion at line 14, column 16.") + /\ Assert((z' < 100) => (z' = x + 1), + "Failure of assertion at line 15, column 16.") + /\ pc' = "Done" + /\ UNCHANGED << x, y >> + +(* Allow infinite stuttering to prevent deadlock on termination. *) +Terminating == pc = "Done" /\ UNCHANGED vars + +Next == a \/ b \/ c \/ d + \/ Terminating + +Spec == /\ Init /\ [][Next]_vars + /\ WF_vars(Next) + +Termination == <>(pc = "Done") + +\* END TRANSLATION - the hash of the generated TLA code (remove to silence divergence warnings): TLA-db7d9133d865d6309280098cc66b6a85 + +============================================================================= diff --git a/tlatools/test-model/pcal/Either4.tla b/tlatools/test-model/pcal/Either4.tla new file mode 100644 index 0000000000000000000000000000000000000000..926340197d1129ecf0f3250c1e5693cc0fce6103 --- /dev/null +++ b/tlatools/test-model/pcal/Either4.tla @@ -0,0 +1,83 @@ +------------------------------- MODULE Either4 ------------------------------ +EXTENDS Naturals, Sequences, TLC + +(* --algorithm Either + process Foo \in {1, 2} + variables x = 0 ; y = 0 ; z = 0 ; + begin a: either x := 1 ; + b: x := x + 1; + or y := 1 ; c: y := y + 1; + or z := 100 + end either ; + d: either when x = 0 ; z := z + 1 ; + or when x = 2 ; z := z + 3 ; + end either ; + assert (x+y = 2) \/ ((z = 101) /\ (x+y=0)); + assert (z < 100) => (z = x + 1) ; + end process + end algorithm +*) + +\* BEGIN TRANSLATION - the hash of the PCal code: PCal-9a6afbc4f2a88a20f8db8f24ec88b676 +VARIABLES pc, x, y, z + +vars == << pc, x, y, z >> + +ProcSet == ({1, 2}) + +Init == (* Process Foo *) + /\ x = [self \in {1, 2} |-> 0] + /\ y = [self \in {1, 2} |-> 0] + /\ z = [self \in {1, 2} |-> 0] + /\ pc = [self \in ProcSet |-> "a"] + +a(self) == /\ pc[self] = "a" + /\ \/ /\ x' = [x EXCEPT ![self] = 1] + /\ pc' = [pc EXCEPT ![self] = "b"] + /\ UNCHANGED <<y, z>> + \/ /\ y' = [y EXCEPT ![self] = 1] + /\ pc' = [pc EXCEPT ![self] = "c"] + /\ UNCHANGED <<x, z>> + \/ /\ z' = [z EXCEPT ![self] = 100] + /\ pc' = [pc EXCEPT ![self] = "d"] + /\ UNCHANGED <<x, y>> + +b(self) == /\ pc[self] = "b" + /\ x' = [x EXCEPT ![self] = x[self] + 1] + /\ pc' = [pc EXCEPT ![self] = "d"] + /\ UNCHANGED << y, z >> + +c(self) == /\ pc[self] = "c" + /\ y' = [y EXCEPT ![self] = y[self] + 1] + /\ pc' = [pc EXCEPT ![self] = "d"] + /\ UNCHANGED << x, z >> + +d(self) == /\ pc[self] = "d" + /\ \/ /\ x[self] = 0 + /\ z' = [z EXCEPT ![self] = z[self] + 1] + \/ /\ x[self] = 2 + /\ z' = [z EXCEPT ![self] = z[self] + 3] + /\ Assert((x[self]+y[self] = 2) \/ ((z'[self] = 101) /\ (x[self]+y[self]=0)), + "Failure of assertion at line 15, column 16.") + /\ Assert((z'[self] < 100) => (z'[self] = x[self] + 1), + "Failure of assertion at line 16, column 16.") + /\ pc' = [pc EXCEPT ![self] = "Done"] + /\ UNCHANGED << x, y >> + +Foo(self) == a(self) \/ b(self) \/ c(self) \/ d(self) + +(* Allow infinite stuttering to prevent deadlock on termination. *) +Terminating == /\ \A self \in ProcSet: pc[self] = "Done" + /\ UNCHANGED vars + +Next == (\E self \in {1, 2}: Foo(self)) + \/ Terminating + +Spec == /\ Init /\ [][Next]_vars + /\ \A self \in {1, 2} : WF_vars(Foo(self)) + +Termination == <>(\A self \in ProcSet: pc[self] = "Done") + +\* END TRANSLATION - the hash of the generated TLA code (remove to silence divergence warnings): TLA-8050fab129ad8ea32f6a8eefb5f96b57 + +============================================================================= diff --git a/tlatools/test-model/pcal/Either5.tla b/tlatools/test-model/pcal/Either5.tla new file mode 100644 index 0000000000000000000000000000000000000000..ef5123c6ef384edc676cebd2853aa8ff12b2f023 --- /dev/null +++ b/tlatools/test-model/pcal/Either5.tla @@ -0,0 +1,84 @@ +------------------------------- MODULE Either5 ------------------------------ +EXTENDS Naturals, Sequences, TLC + +(* --algorithm Either + variables x = 0 ; y = 0 ; z = 0 ; + procedure Foo(a) + begin c: x := x + a ; + return + end procedure; + begin a: either x := 1 ; y := 0 ; + or y := 1 ; + or call Foo(1) ; + b: assert x = 1 ; + end either ; + d: assert x+y = 1 ; + end algorithm +*) + +\* BEGIN TRANSLATION - the hash of the PCal code: PCal-be6b6d1cc0a0bacc6a419e686126c4c9 +\* Label a at line 10 col 16 changed to a_ +CONSTANT defaultInitValue +VARIABLES x, y, z, pc, stack, a + +vars == << x, y, z, pc, stack, a >> + +Init == (* Global variables *) + /\ x = 0 + /\ y = 0 + /\ z = 0 + (* Procedure Foo *) + /\ a = defaultInitValue + /\ stack = << >> + /\ pc = "a_" + +c == /\ pc = "c" + /\ x' = x + a + /\ pc' = Head(stack).pc + /\ a' = Head(stack).a + /\ stack' = Tail(stack) + /\ UNCHANGED << y, z >> + +Foo == c + +a_ == /\ pc = "a_" + /\ \/ /\ x' = 1 + /\ y' = 0 + /\ pc' = "d" + /\ UNCHANGED <<stack, a>> + \/ /\ y' = 1 + /\ pc' = "d" + /\ UNCHANGED <<x, stack, a>> + \/ /\ /\ a' = 1 + /\ stack' = << [ procedure |-> "Foo", + pc |-> "b", + a |-> a ] >> + \o stack + /\ pc' = "c" + /\ UNCHANGED <<x, y>> + /\ z' = z + +b == /\ pc = "b" + /\ Assert(x = 1, "Failure of assertion at line 13, column 25.") + /\ pc' = "d" + /\ UNCHANGED << x, y, z, stack, a >> + +d == /\ pc = "d" + /\ Assert(x+y = 1, "Failure of assertion at line 15, column 18.") + /\ pc' = "Done" + /\ UNCHANGED << x, y, z, stack, a >> + +(* Allow infinite stuttering to prevent deadlock on termination. *) +Terminating == pc = "Done" /\ UNCHANGED vars + +Next == Foo \/ a_ \/ b \/ d + \/ Terminating + +Spec == /\ Init /\ [][Next]_vars + /\ WF_vars(Next) + +Termination == <>(pc = "Done") + +\* END TRANSLATION - the hash of the generated TLA code (remove to silence divergence warnings): TLA-5c5a7c7b427cee2468d220b4a9fada35 + +============================================================================= diff --git a/tlatools/test-model/pcal/Euclid.cfg b/tlatools/test-model/pcal/Euclid.cfg new file mode 100644 index 0000000000000000000000000000000000000000..886249f10a49afefccd3318c4856c04098d202a5 --- /dev/null +++ b/tlatools/test-model/pcal/Euclid.cfg @@ -0,0 +1,4 @@ +SPECIFICATION Spec +PROPERTY Termination +\* Add statements after this line. +INVARIANT Invariant diff --git a/tlatools/test-model/pcal/Euclid.tla b/tlatools/test-model/pcal/Euclid.tla new file mode 100644 index 0000000000000000000000000000000000000000..f20dd1adbdbf04ec9421faf9b94da0ede4dff356 --- /dev/null +++ b/tlatools/test-model/pcal/Euclid.tla @@ -0,0 +1,93 @@ +-------------------------------- MODULE Euclid ------------------------------- +(***************************************************************************) +(* Euclid's algorithm. *) +(***************************************************************************) + +EXTENDS Naturals, TLC + +\*CONSTANT MaxNum +MaxNum == 20 + +ASSUME MaxNum > 0 + +ASSUME /\ Print(<<"Testing Euclid's algorithm on all numbers between 1 and ", + MaxNum>>, TRUE) + /\ Print("Most time spent evaluating naive definition of GCD for test", + TRUE) + +(****** +Adapted from page 8 of the 2nd edition of Robert Sedgewick's "Algorithms". + +--algorithm Euclid + variables u_ini \in 1 .. MaxNum ; + v_ini \in 1 .. MaxNum ; + u = u_ini ; v = v_ini ; + begin a : while u # 0 + do if u < v then u := v || v := u ; end if ; + b: u := u - v; + end while ; + assert v = GCD(u_ini, v_ini) ; + \* print <<"gcd of ", u_ini, v_ini, " equals ", v >> ; + end algorithm + +*) + +GCD(x, y) == CHOOSE i \in (1..x) \cap (1..y) : + /\ x % i = 0 + /\ y % i = 0 + /\ \A j \in (1..x) \cap (1..y) : + /\ x % j = 0 + /\ y % j = 0 + => i \geq j + + + +\* BEGIN TRANSLATION - the hash of the PCal code: PCal-a069024aba51f10ce87a41824584b8ee +VARIABLES u_ini, v_ini, u, v, pc + +vars == << u_ini, v_ini, u, v, pc >> + +Init == (* Global variables *) + /\ u_ini \in 1 .. MaxNum + /\ v_ini \in 1 .. MaxNum + /\ u = u_ini + /\ v = v_ini + /\ pc = "a" + +a == /\ pc = "a" + /\ IF u # 0 + THEN /\ IF u < v + THEN /\ /\ u' = v + /\ v' = u + ELSE /\ TRUE + /\ UNCHANGED << u, v >> + /\ pc' = "b" + ELSE /\ Assert(v = GCD(u_ini, v_ini), + "Failure of assertion at line 29, column 13.") + /\ pc' = "Done" + /\ UNCHANGED << u, v >> + /\ UNCHANGED << u_ini, v_ini >> + +b == /\ pc = "b" + /\ u' = u - v + /\ pc' = "a" + /\ UNCHANGED << u_ini, v_ini, v >> + +(* Allow infinite stuttering to prevent deadlock on termination. *) +Terminating == pc = "Done" /\ UNCHANGED vars + +Next == a \/ b + \/ Terminating + +Spec == /\ Init /\ [][Next]_vars + /\ WF_vars(Next) + +Termination == <>(pc = "Done") + +\* END TRANSLATION - the hash of the generated TLA code (remove to silence divergence warnings): TLA-969126b68f5e9c79e9cbf33f45804301 + + +Invariant == + (pc = "Done") => (v = GCD(u_ini, v_ini)) + +============================================================================= diff --git a/tlatools/test-model/pcal/Euclid2.tla b/tlatools/test-model/pcal/Euclid2.tla new file mode 100644 index 0000000000000000000000000000000000000000..33b6272a3a0cf518bdd56abe65e2f5e14bae78f5 --- /dev/null +++ b/tlatools/test-model/pcal/Euclid2.tla @@ -0,0 +1,68 @@ +--algorithm EuclidAlg +variables u = 24 ; v \in 1 .. N ; v_ini = v ; +begin +lp: while u # 0 do + if u < v then u := v || v := u ; + end if ; + a: u := u - v; + end while ; + assert v = GCD(24, v_ini) ; +end algorithm + +-------------------------------- MODULE Euclid2 ------------------------------ +EXTENDS Naturals, TLC +\*CONSTANT N +N == 500 + +GCD(x, y) == CHOOSE i \in (1..x) \cap (1..y) : + /\ x % i = 0 + /\ y % i = 0 + /\ \A j \in (1..x) \cap (1..y) : + /\ x % j = 0 + /\ y % j = 0 + => i \geq j + +\* BEGIN TRANSLATION - the hash of the PCal code: PCal-e89e843c1bd319cda8806c448153d025 +VARIABLES u, v, v_ini, pc + +vars == << u, v, v_ini, pc >> + +Init == (* Global variables *) + /\ u = 24 + /\ v \in 1 .. N + /\ v_ini = v + /\ pc = "lp" + +lp == /\ pc = "lp" + /\ IF u # 0 + THEN /\ IF u < v + THEN /\ /\ u' = v + /\ v' = u + ELSE /\ TRUE + /\ UNCHANGED << u, v >> + /\ pc' = "a" + ELSE /\ Assert(v = GCD(24, v_ini), + "Failure of assertion at line 9, column 5.") + /\ pc' = "Done" + /\ UNCHANGED << u, v >> + /\ v_ini' = v_ini + +a == /\ pc = "a" + /\ u' = u - v + /\ pc' = "lp" + /\ UNCHANGED << v, v_ini >> + +(* Allow infinite stuttering to prevent deadlock on termination. *) +Terminating == pc = "Done" /\ UNCHANGED vars + +Next == lp \/ a + \/ Terminating + +Spec == /\ Init /\ [][Next]_vars + /\ WF_vars(Next) + +Termination == <>(pc = "Done") + +\* END TRANSLATION - the hash of the generated TLA code (remove to silence divergence warnings): TLA-13d9c528c7c380488278c40608c11ba4 + +============================================================================= diff --git a/tlatools/test-model/pcal/Euclid3.tla b/tlatools/test-model/pcal/Euclid3.tla new file mode 100644 index 0000000000000000000000000000000000000000..b6484fc4a23a5d42e8b46ce7b2391a35d1ad91ba --- /dev/null +++ b/tlatools/test-model/pcal/Euclid3.tla @@ -0,0 +1,66 @@ +--algorithm EuclidAlg +variables u = 24 ; v \in 1 .. N ; v_ini = v ; +begin +lp: while u # 0 do + if u < v then u := v || v := u ; + end if ; + a: u := u - v; + end while ; + print <<v, "= GCD of 24 and ", v_ini>> ; +end algorithm + +-------------------------------- MODULE Euclid3 ------------------------------ +EXTENDS Naturals, TLC +CONSTANT N + +GCD(x, y) == CHOOSE i \in (1..x) \cap (1..y) : + /\ x % i = 0 + /\ y % i = 0 + /\ \A j \in (1..x) \cap (1..y) : + /\ x % j = 0 + /\ y % j = 0 + => i \geq j + +\* BEGIN TRANSLATION - the hash of the PCal code: PCal-309bc67718dab989f45f39e55b144a3e +VARIABLES u, v, v_ini, pc + +vars == << u, v, v_ini, pc >> + +Init == (* Global variables *) + /\ u = 24 + /\ v \in 1 .. N + /\ v_ini = v + /\ pc = "lp" + +lp == /\ pc = "lp" + /\ IF u # 0 + THEN /\ IF u < v + THEN /\ /\ u' = v + /\ v' = u + ELSE /\ TRUE + /\ UNCHANGED << u, v >> + /\ pc' = "a" + ELSE /\ PrintT(<<v, "= GCD of 24 and ", v_ini>>) + /\ pc' = "Done" + /\ UNCHANGED << u, v >> + /\ v_ini' = v_ini + +a == /\ pc = "a" + /\ u' = u - v + /\ pc' = "lp" + /\ UNCHANGED << v, v_ini >> + +(* Allow infinite stuttering to prevent deadlock on termination. *) +Terminating == pc = "Done" /\ UNCHANGED vars + +Next == lp \/ a + \/ Terminating + +Spec == /\ Init /\ [][Next]_vars + /\ WF_vars(Next) + +Termination == <>(pc = "Done") + +\* END TRANSLATION - the hash of the generated TLA code (remove to silence divergence warnings): TLA-696f44302fe4977c6881615aedc0e885 + +============================================================================= diff --git a/tlatools/test-model/pcal/EvenOdd.tla b/tlatools/test-model/pcal/EvenOdd.tla new file mode 100644 index 0000000000000000000000000000000000000000..a3bc90d7e25d25838d93245d3bae94b21dbff66a --- /dev/null +++ b/tlatools/test-model/pcal/EvenOdd.tla @@ -0,0 +1,116 @@ +--algorithm EvenOdd +variable result = FALSE; +procedure Even (xEven = 0) +begin + Even1: if xEven = 0 then + result := TRUE; + return; + else + call Odd(xEven - 1); + return; + end if; + end procedure +procedure Odd (xOdd = 0) +begin + Odd1: if xOdd = 0 then result := FALSE; + else call Even(xOdd - 1); + end if; + Odd2: return; + end procedure +begin + a1: call Even(N); + a2: print result; +end algorithm + +--------------- MODULE EvenOdd --------------- + +EXTENDS Naturals, Sequences, TLC + +CONSTANT N + +---------------------------------------------- + + +\* BEGIN TRANSLATION - the hash of the PCal code: PCal-a4673a20b45b60ed7b169b671068cf77 +VARIABLES result, pc, stack, xEven, xOdd + +vars == << result, pc, stack, xEven, xOdd >> + +Init == (* Global variables *) + /\ result = FALSE + (* Procedure Even *) + /\ xEven = 0 + (* Procedure Odd *) + /\ xOdd = 0 + /\ stack = << >> + /\ pc = "a1" + +Even1 == /\ pc = "Even1" + /\ IF xEven = 0 + THEN /\ result' = TRUE + /\ pc' = Head(stack).pc + /\ xEven' = Head(stack).xEven + /\ stack' = Tail(stack) + /\ xOdd' = xOdd + ELSE /\ /\ stack' = << [ procedure |-> "Odd", + pc |-> Head(stack).pc, + xOdd |-> xOdd ] >> + \o Tail(stack) + /\ xOdd' = xEven - 1 + /\ pc' = "Odd1" + /\ UNCHANGED << result, xEven >> + +Even == Even1 + +Odd1 == /\ pc = "Odd1" + /\ IF xOdd = 0 + THEN /\ result' = FALSE + /\ pc' = "Odd2" + /\ UNCHANGED << stack, xEven >> + ELSE /\ /\ stack' = << [ procedure |-> "Even", + pc |-> "Odd2", + xEven |-> xEven ] >> + \o stack + /\ xEven' = xOdd - 1 + /\ pc' = "Even1" + /\ UNCHANGED result + /\ xOdd' = xOdd + +Odd2 == /\ pc = "Odd2" + /\ pc' = Head(stack).pc + /\ xOdd' = Head(stack).xOdd + /\ stack' = Tail(stack) + /\ UNCHANGED << result, xEven >> + +Odd == Odd1 \/ Odd2 + +a1 == /\ pc = "a1" + /\ /\ stack' = << [ procedure |-> "Even", + pc |-> "a2", + xEven |-> xEven ] >> + \o stack + /\ xEven' = N + /\ pc' = "Even1" + /\ UNCHANGED << result, xOdd >> + +a2 == /\ pc = "a2" + /\ PrintT(result) + /\ pc' = "Done" + /\ UNCHANGED << result, stack, xEven, xOdd >> + +(* Allow infinite stuttering to prevent deadlock on termination. *) +Terminating == pc = "Done" /\ UNCHANGED vars + +Next == Even \/ Odd \/ a1 \/ a2 + \/ Terminating + +Spec == /\ Init /\ [][Next]_vars + /\ WF_vars(Next) + +Termination == <>(pc = "Done") + +\* END TRANSLATION - the hash of the generated TLA code (remove to silence divergence warnings): TLA-c666315b11da5eecc32bda536975b934 + +============================================== + + diff --git a/tlatools/test-model/pcal/EvenOddBad.tla b/tlatools/test-model/pcal/EvenOddBad.tla new file mode 100644 index 0000000000000000000000000000000000000000..8adb3ecbf36e0264cfda3857947db2060a1088fb --- /dev/null +++ b/tlatools/test-model/pcal/EvenOddBad.tla @@ -0,0 +1,113 @@ +---------------------------- MODULE EvenOddBad ----------------------------- + +EXTENDS Naturals, Sequences, TLC + +(* +--algorithm EvenOddBad +variable result \in { TRUE, FALSE }; +procedure Even (xEven = 0) +begin + Even1: if xEven = 0 then result := TRUE; + else call Odd(xEven - 1); + end if; + e1 : return; + end procedure; +procedure Odd (xOdd = 0) +begin + Odd1: if xOdd = 0 then result := FALSE; + else call Even(xOdd - 1); + end if; + o1 : return; + end procedure +begin + a1: call Even(2); + a2: print result; +end algorithm +*) + +\* BEGIN TRANSLATION - the hash of the PCal code: PCal-4f4604590279cc4b2920a972bf28fdd0 +VARIABLES result, pc, stack, xEven, xOdd + +vars == << result, pc, stack, xEven, xOdd >> + +Init == (* Global variables *) + /\ result \in { TRUE, FALSE } + (* Procedure Even *) + /\ xEven = 0 + (* Procedure Odd *) + /\ xOdd = 0 + /\ stack = << >> + /\ pc = "a1" + +Even1 == /\ pc = "Even1" + /\ IF xEven = 0 + THEN /\ result' = TRUE + /\ pc' = "e1" + /\ UNCHANGED << stack, xOdd >> + ELSE /\ /\ stack' = << [ procedure |-> "Odd", + pc |-> "e1", + xOdd |-> xOdd ] >> + \o stack + /\ xOdd' = xEven - 1 + /\ pc' = "Odd1" + /\ UNCHANGED result + /\ xEven' = xEven + +e1 == /\ pc = "e1" + /\ pc' = Head(stack).pc + /\ xEven' = Head(stack).xEven + /\ stack' = Tail(stack) + /\ UNCHANGED << result, xOdd >> + +Even == Even1 \/ e1 + +Odd1 == /\ pc = "Odd1" + /\ IF xOdd = 0 + THEN /\ result' = FALSE + /\ pc' = "o1" + /\ UNCHANGED << stack, xEven >> + ELSE /\ /\ stack' = << [ procedure |-> "Even", + pc |-> "o1", + xEven |-> xEven ] >> + \o stack + /\ xEven' = xOdd - 1 + /\ pc' = "Even1" + /\ UNCHANGED result + /\ xOdd' = xOdd + +o1 == /\ pc = "o1" + /\ pc' = Head(stack).pc + /\ xOdd' = Head(stack).xOdd + /\ stack' = Tail(stack) + /\ UNCHANGED << result, xEven >> + +Odd == Odd1 \/ o1 + +a1 == /\ pc = "a1" + /\ /\ stack' = << [ procedure |-> "Even", + pc |-> "a2", + xEven |-> xEven ] >> + \o stack + /\ xEven' = 2 + /\ pc' = "Even1" + /\ UNCHANGED << result, xOdd >> + +a2 == /\ pc = "a2" + /\ PrintT(result) + /\ pc' = "Done" + /\ UNCHANGED << result, stack, xEven, xOdd >> + +(* Allow infinite stuttering to prevent deadlock on termination. *) +Terminating == pc = "Done" /\ UNCHANGED vars + +Next == Even \/ Odd \/ a1 \/ a2 + \/ Terminating + +Spec == /\ Init /\ [][Next]_vars + /\ WF_vars(Next) + +Termination == <>(pc = "Done") + +\* END TRANSLATION - the hash of the generated TLA code (remove to silence divergence warnings): TLA-7dc8ff54d82fc57126829898458b6c6f + +============================================================================= diff --git a/tlatools/test-model/pcal/Factorial.tla b/tlatools/test-model/pcal/Factorial.tla new file mode 100644 index 0000000000000000000000000000000000000000..8d70425b20036104248a7f0a268084cf79488449 --- /dev/null +++ b/tlatools/test-model/pcal/Factorial.tla @@ -0,0 +1,83 @@ +------------------------------ MODULE Factorial ----------------------------- +EXTENDS Naturals, Sequences, TLC + +(*************************************************************************** +--algorithm Factorial + variable result = 1; \* are comments ok? + procedure FactProc(arg1 = 0) (* are comments ok? *) + (* what about (* nested multi-line *) + comments? *) + variable u = 1 ; + begin p1 : if arg1 = 0 + then return; \* HERE IS A + else result := result * arg1; + call FactProc ( arg1 - 1 ) ; + return; + end if; + end procedure + begin + a1 : call FactProc( 5 ) ; + a2 : assert result = 120 ; + end algorithm +***************************************************************************) + +\* BEGIN TRANSLATION - the hash of the PCal code: PCal-8354903ee5c9ddae0b8bb4f55d782009 +VARIABLES result, pc, stack, arg1, u + +vars == << result, pc, stack, arg1, u >> + +Init == (* Global variables *) + /\ result = 1 + (* Procedure FactProc *) + /\ arg1 = 0 + /\ u = 1 + /\ stack = << >> + /\ pc = "a1" + +p1 == /\ pc = "p1" + /\ IF arg1 = 0 + THEN /\ pc' = Head(stack).pc + /\ u' = Head(stack).u + /\ arg1' = Head(stack).arg1 + /\ stack' = Tail(stack) + /\ UNCHANGED result + ELSE /\ result' = result * arg1 + /\ arg1' = arg1 - 1 + /\ u' = 1 + /\ pc' = "p1" + /\ stack' = stack + +FactProc == p1 + +a1 == /\ pc = "a1" + /\ /\ arg1' = 5 + /\ stack' = << [ procedure |-> "FactProc", + pc |-> "a2", + u |-> u, + arg1 |-> arg1 ] >> + \o stack + /\ u' = 1 + /\ pc' = "p1" + /\ UNCHANGED result + +a2 == /\ pc = "a2" + /\ Assert(result = 120, "Failure of assertion at line 20, column 10.") + /\ pc' = "Done" + /\ UNCHANGED << result, stack, arg1, u >> + +(* Allow infinite stuttering to prevent deadlock on termination. *) +Terminating == pc = "Done" /\ UNCHANGED vars + +Next == FactProc \/ a1 \/ a2 + \/ Terminating + +Spec == /\ Init /\ [][Next]_vars + /\ WF_vars(Next) + +Termination == <>(pc = "Done") + +\* END TRANSLATION - the hash of the generated TLA code (remove to silence divergence warnings): TLA-53ca468546a563a45762642894b2b2a3 + + +Invariant == result \in Nat +============================================================================= diff --git a/tlatools/test-model/pcal/Factorial2.tla b/tlatools/test-model/pcal/Factorial2.tla new file mode 100644 index 0000000000000000000000000000000000000000..ea34ede1d2d1f2c688e49baf8cbcffb850e1d447 --- /dev/null +++ b/tlatools/test-model/pcal/Factorial2.tla @@ -0,0 +1,132 @@ +---------------------------- MODULE Factorial2 --------------------------- +EXTENDS Naturals, Sequences, TLC + +(*************************************************************************** +Factorial Algorithm with 2 procedures + +--algorithm Factorial + variable result = 1; + procedure FactProc(arg1 = 0 ) + variable u = 1 ; + begin p1 : if arg1 = 0 + then return; + else result := result * arg1; + call FactProc2 ( arg1 - 1 ) ; + b: return; + end if; + end procedure + procedure FactProc2(arg2 = 0) + variable u2 = 1 ; + begin p12 : if arg2 = 0 + then return; + else result := result * arg2; + call FactProc ( arg2 - 1 ) ; + return; + end if; + end procedure + begin + a1 : call FactProc( 5 ) ; + a2 : if result = 120 then print <<"Correct =", 120>>; + else print <<"Error = ", result>> ; + end if; + end algorithm +***************************************************************************) + +\* BEGIN TRANSLATION - the hash of the PCal code: PCal-7a6949afa2a72d41ea8a9ba97e423c98 +VARIABLES result, pc, stack, arg1, u, arg2, u2 + +vars == << result, pc, stack, arg1, u, arg2, u2 >> + +Init == (* Global variables *) + /\ result = 1 + (* Procedure FactProc *) + /\ arg1 = 0 + /\ u = 1 + (* Procedure FactProc2 *) + /\ arg2 = 0 + /\ u2 = 1 + /\ stack = << >> + /\ pc = "a1" + +p1 == /\ pc = "p1" + /\ IF arg1 = 0 + THEN /\ pc' = Head(stack).pc + /\ u' = Head(stack).u + /\ arg1' = Head(stack).arg1 + /\ stack' = Tail(stack) + /\ UNCHANGED << result, arg2, u2 >> + ELSE /\ result' = result * arg1 + /\ /\ arg2' = arg1 - 1 + /\ stack' = << [ procedure |-> "FactProc2", + pc |-> "b", + u2 |-> u2, + arg2 |-> arg2 ] >> + \o stack + /\ u2' = 1 + /\ pc' = "p12" + /\ UNCHANGED << arg1, u >> + +b == /\ pc = "b" + /\ pc' = Head(stack).pc + /\ u' = Head(stack).u + /\ arg1' = Head(stack).arg1 + /\ stack' = Tail(stack) + /\ UNCHANGED << result, arg2, u2 >> + +FactProc == p1 \/ b + +p12 == /\ pc = "p12" + /\ IF arg2 = 0 + THEN /\ pc' = Head(stack).pc + /\ u2' = Head(stack).u2 + /\ arg2' = Head(stack).arg2 + /\ stack' = Tail(stack) + /\ UNCHANGED << result, arg1, u >> + ELSE /\ result' = result * arg2 + /\ /\ arg1' = arg2 - 1 + /\ stack' = << [ procedure |-> "FactProc", + pc |-> Head(stack).pc, + u |-> u, + arg1 |-> arg1 ] >> + \o Tail(stack) + /\ u2' = Head(stack).u2 + /\ u' = 1 + /\ pc' = "p1" + /\ arg2' = arg2 + +FactProc2 == p12 + +a1 == /\ pc = "a1" + /\ /\ arg1' = 5 + /\ stack' = << [ procedure |-> "FactProc", + pc |-> "a2", + u |-> u, + arg1 |-> arg1 ] >> + \o stack + /\ u' = 1 + /\ pc' = "p1" + /\ UNCHANGED << result, arg2, u2 >> + +a2 == /\ pc = "a2" + /\ IF result = 120 + THEN /\ PrintT(<<"Correct =", 120>>) + ELSE /\ PrintT(<<"Error = ", result>>) + /\ pc' = "Done" + /\ UNCHANGED << result, stack, arg1, u, arg2, u2 >> + +(* Allow infinite stuttering to prevent deadlock on termination. *) +Terminating == pc = "Done" /\ UNCHANGED vars + +Next == FactProc \/ FactProc2 \/ a1 \/ a2 + \/ Terminating + +Spec == /\ Init /\ [][Next]_vars + /\ WF_vars(Next) + +Termination == <>(pc = "Done") + +\* END TRANSLATION - the hash of the generated TLA code (remove to silence divergence warnings): TLA-6c949f27864a3ffc1d874b0a64419ab7 + + +Invariant == result \in Nat +============================================================================= diff --git a/tlatools/test-model/pcal/FairSeq.cfg b/tlatools/test-model/pcal/FairSeq.cfg new file mode 100644 index 0000000000000000000000000000000000000000..eef01cc22c6ccc68e3ccebb6338999b0382ecbbc --- /dev/null +++ b/tlatools/test-model/pcal/FairSeq.cfg @@ -0,0 +1,4 @@ +SPECIFICATION Spec +PROPERTY Termination +\* Add statements after this line. +PROPERTY Termination diff --git a/tlatools/test-model/pcal/FairSeq.tla b/tlatools/test-model/pcal/FairSeq.tla new file mode 100644 index 0000000000000000000000000000000000000000..4eb048733aecb388e413825e685d891bbfb5d9dd --- /dev/null +++ b/tlatools/test-model/pcal/FairSeq.tla @@ -0,0 +1,44 @@ +------------------------------ MODULE FairSeq ------------------------------ +EXTENDS Integers +(*************************************************************************** +PlusCal options (version 1.5) +--algorithm FairSeq { + variable x = 0 ; + fair { while (x < 10) { + x := x+1; + } + } +} + ***************************************************************************) +\* BEGIN TRANSLATION - the hash of the PCal code: PCal-07e0e68497291a78b07d8fb9d5597180 +VARIABLES x, pc + +vars == << x, pc >> + +Init == (* Global variables *) + /\ x = 0 + /\ pc = "Lbl_1" + +Lbl_1 == /\ pc = "Lbl_1" + /\ IF x < 10 + THEN /\ x' = x+1 + /\ pc' = "Lbl_1" + ELSE /\ pc' = "Done" + /\ x' = x + +(* Allow infinite stuttering to prevent deadlock on termination. *) +Terminating == pc = "Done" /\ UNCHANGED vars + +Next == Lbl_1 + \/ Terminating + +Spec == /\ Init /\ [][Next]_vars + /\ WF_vars(Next) + +Termination == <>(pc = "Done") + +\* END TRANSLATION - the hash of the generated TLA code (remove to silence divergence warnings): TLA-8d9de25f8162fd0489585bc374dff964 +============================================================================= +\* Modification History +\* Last modified Sun Mar 20 10:13:11 PDT 2011 by lamport +\* Created Sun Mar 20 10:10:54 PDT 2011 by lamport diff --git a/tlatools/test-model/pcal/FairSeq2.tla b/tlatools/test-model/pcal/FairSeq2.tla new file mode 100644 index 0000000000000000000000000000000000000000..e17d76d811ef7e0d76bbe19f8a1111515187f8c0 --- /dev/null +++ b/tlatools/test-model/pcal/FairSeq2.tla @@ -0,0 +1,44 @@ +------------------------------ MODULE FairSeq2 ------------------------------ +EXTENDS Integers +(*************************************************************************** +PlusCal options (version 1.5) +--fair algorithm FairSeq { + variable x = 0 ; + fair { while (x < 10) { + x := x+1; + } + } +} + ***************************************************************************) +\* BEGIN TRANSLATION - the hash of the PCal code: PCal-07e0e68497291a78b07d8fb9d5597180 +VARIABLES x, pc + +vars == << x, pc >> + +Init == (* Global variables *) + /\ x = 0 + /\ pc = "Lbl_1" + +Lbl_1 == /\ pc = "Lbl_1" + /\ IF x < 10 + THEN /\ x' = x+1 + /\ pc' = "Lbl_1" + ELSE /\ pc' = "Done" + /\ x' = x + +(* Allow infinite stuttering to prevent deadlock on termination. *) +Terminating == pc = "Done" /\ UNCHANGED vars + +Next == Lbl_1 + \/ Terminating + +Spec == /\ Init /\ [][Next]_vars + /\ WF_vars(Next) + +Termination == <>(pc = "Done") + +\* END TRANSLATION - the hash of the generated TLA code (remove to silence divergence warnings): TLA-8d9de25f8162fd0489585bc374dff964 +============================================================================= +\* Modification History +\* Last modified Sun Mar 20 10:13:11 PDT 2011 by lamport +\* Created Sun Mar 20 10:10:54 PDT 2011 by lamport diff --git a/tlatools/test-model/pcal/FastMutex.cfg b/tlatools/test-model/pcal/FastMutex.cfg new file mode 100644 index 0000000000000000000000000000000000000000..0e9b97c14b32cf64a2bec7b2ab69133ff609a772 --- /dev/null +++ b/tlatools/test-model/pcal/FastMutex.cfg @@ -0,0 +1,6 @@ +SPECIFICATION Spec +CONSTANT defaultInitValue = defaultInitValue +\* Add statements after this line. +CONSTANT N = 2 +INVARIANT Invariant +PROPERTY Liveness diff --git a/tlatools/test-model/pcal/FastMutex.tla b/tlatools/test-model/pcal/FastMutex.tla new file mode 100644 index 0000000000000000000000000000000000000000..2c67388a8736b09879c3ca9fe0da4562206fbea4 --- /dev/null +++ b/tlatools/test-model/pcal/FastMutex.tla @@ -0,0 +1,167 @@ +------------------------------ MODULE FastMutex ----------------------------- + +EXTENDS Naturals + +CONSTANT N + +ASSUME N \in Nat + +(********************** +--algorithm FastMutex + variables x ; y = 0 ; b = [i \in 1..N |-> FALSE] ; +process Proc \in 1..N +variables j = 0 ; failed = FALSE ; +begin +start : while TRUE + do l1 : b[self] := TRUE ; + l2 : x := self ; + l3 : if y # 0 + then l4 : b[self] := FALSE ; + l5 : when y = 0 ; skip ; + else l6 : y := self ; + l7 : if x # self + then l8 : b[self] := FALSE ; + j := 1 ; + l9 : while (j \leq N) + do when ~b[j] ; + j := j+1 ; + end while ; + l10 : if y # self + then when y = 0 ; + failed := TRUE ; + end if; + end if ; + cs : if ~ failed + then skip ; \* the critical section + l11 : y := 0 ; + l12 : b[self] := FALSE ; + else failed := FALSE ; + end if ; + end if ; + end while ; +end process +end algorithm + +***********************) + +\* BEGIN TRANSLATION - the hash of the PCal code: PCal-0bb63b56a09bbe2e9360c99d30162534 +CONSTANT defaultInitValue +VARIABLES x, y, b, pc, j, failed + +vars == << x, y, b, pc, j, failed >> + +ProcSet == (1..N) + +Init == (* Global variables *) + /\ x = defaultInitValue + /\ y = 0 + /\ b = [i \in 1..N |-> FALSE] + (* Process Proc *) + /\ j = [self \in 1..N |-> 0] + /\ failed = [self \in 1..N |-> FALSE] + /\ pc = [self \in ProcSet |-> "start"] + +start(self) == /\ pc[self] = "start" + /\ pc' = [pc EXCEPT ![self] = "l1"] + /\ UNCHANGED << x, y, b, j, failed >> + +l1(self) == /\ pc[self] = "l1" + /\ b' = [b EXCEPT ![self] = TRUE] + /\ pc' = [pc EXCEPT ![self] = "l2"] + /\ UNCHANGED << x, y, j, failed >> + +l2(self) == /\ pc[self] = "l2" + /\ x' = self + /\ pc' = [pc EXCEPT ![self] = "l3"] + /\ UNCHANGED << y, b, j, failed >> + +l3(self) == /\ pc[self] = "l3" + /\ IF y # 0 + THEN /\ pc' = [pc EXCEPT ![self] = "l4"] + ELSE /\ pc' = [pc EXCEPT ![self] = "l6"] + /\ UNCHANGED << x, y, b, j, failed >> + +l4(self) == /\ pc[self] = "l4" + /\ b' = [b EXCEPT ![self] = FALSE] + /\ pc' = [pc EXCEPT ![self] = "l5"] + /\ UNCHANGED << x, y, j, failed >> + +l5(self) == /\ pc[self] = "l5" + /\ y = 0 + /\ TRUE + /\ pc' = [pc EXCEPT ![self] = "start"] + /\ UNCHANGED << x, y, b, j, failed >> + +l6(self) == /\ pc[self] = "l6" + /\ y' = self + /\ pc' = [pc EXCEPT ![self] = "l7"] + /\ UNCHANGED << x, b, j, failed >> + +l7(self) == /\ pc[self] = "l7" + /\ IF x # self + THEN /\ pc' = [pc EXCEPT ![self] = "l8"] + ELSE /\ pc' = [pc EXCEPT ![self] = "cs"] + /\ UNCHANGED << x, y, b, j, failed >> + +l8(self) == /\ pc[self] = "l8" + /\ b' = [b EXCEPT ![self] = FALSE] + /\ j' = [j EXCEPT ![self] = 1] + /\ pc' = [pc EXCEPT ![self] = "l9"] + /\ UNCHANGED << x, y, failed >> + +l9(self) == /\ pc[self] = "l9" + /\ IF (j[self] \leq N) + THEN /\ ~b[j[self]] + /\ j' = [j EXCEPT ![self] = j[self]+1] + /\ pc' = [pc EXCEPT ![self] = "l9"] + ELSE /\ pc' = [pc EXCEPT ![self] = "l10"] + /\ j' = j + /\ UNCHANGED << x, y, b, failed >> + +l10(self) == /\ pc[self] = "l10" + /\ IF y # self + THEN /\ y = 0 + /\ failed' = [failed EXCEPT ![self] = TRUE] + ELSE /\ TRUE + /\ UNCHANGED failed + /\ pc' = [pc EXCEPT ![self] = "cs"] + /\ UNCHANGED << x, y, b, j >> + +cs(self) == /\ pc[self] = "cs" + /\ IF ~ failed[self] + THEN /\ TRUE + /\ pc' = [pc EXCEPT ![self] = "l11"] + /\ UNCHANGED failed + ELSE /\ failed' = [failed EXCEPT ![self] = FALSE] + /\ pc' = [pc EXCEPT ![self] = "start"] + /\ UNCHANGED << x, y, b, j >> + +l11(self) == /\ pc[self] = "l11" + /\ y' = 0 + /\ pc' = [pc EXCEPT ![self] = "l12"] + /\ UNCHANGED << x, b, j, failed >> + +l12(self) == /\ pc[self] = "l12" + /\ b' = [b EXCEPT ![self] = FALSE] + /\ pc' = [pc EXCEPT ![self] = "start"] + /\ UNCHANGED << x, y, j, failed >> + +Proc(self) == start(self) \/ l1(self) \/ l2(self) \/ l3(self) \/ l4(self) + \/ l5(self) \/ l6(self) \/ l7(self) \/ l8(self) + \/ l9(self) \/ l10(self) \/ cs(self) \/ l11(self) + \/ l12(self) + +Next == (\E self \in 1..N: Proc(self)) + +Spec == /\ Init /\ [][Next]_vars + /\ \A self \in 1..N : WF_vars(Proc(self)) + +\* END TRANSLATION - the hash of the generated TLA code (remove to silence divergence warnings): TLA-7588f44b8dba7eba3195a1299d84da82 + +inCS(i) == (pc[i] = "cs") /\ (~failed[i]) + +Invariant == \A i, k \in 1..N : (i # k) => ~ (inCS(i) /\ inCS(k)) + + +Liveness == []<> \E i \in 1..N : inCS(i) +============================================================================= diff --git a/tlatools/test-model/pcal/FastMutex2.cfg b/tlatools/test-model/pcal/FastMutex2.cfg new file mode 100644 index 0000000000000000000000000000000000000000..d8d7506fe9585e7c018adb246dc9ae7229148ea6 --- /dev/null +++ b/tlatools/test-model/pcal/FastMutex2.cfg @@ -0,0 +1,5 @@ +SPECIFICATION Spec +\* Add statements after this line. +CONSTANT N = 2 M = 1 +INVARIANT Invariant +PROPERTY Liveness diff --git a/tlatools/test-model/pcal/FastMutex2.tla b/tlatools/test-model/pcal/FastMutex2.tla new file mode 100644 index 0000000000000000000000000000000000000000..1bae411bdf5778c6cf5f5ebe8fc7f0bc9d0eb521 --- /dev/null +++ b/tlatools/test-model/pcal/FastMutex2.tla @@ -0,0 +1,297 @@ +----------------------------- MODULE FastMutex2 ----------------------------- + +\* This tests the translation when there are two different processes + +EXTENDS Naturals, TLC + +CONSTANT N, M + +ASSUME (N \in Nat) /\ (M \in Nat) + +(********************** +--algorithm FastMutex + variables x = 0 ; y = 0 ; b = [i \in 1..N |-> FALSE] ; +process Proc1 \in 1..M +variables j = 0 ; failed = FALSE ; +begin +start : while TRUE + do l1 : b[self] := TRUE ; + l2 : x := self ; + l3 : if y # 0 + then l4 : b[self] := FALSE ; + l5 : when y = 0 ; skip ; + else l6 : y := self ; + l7 : if x # self + then l8 : b[self] := FALSE ; + j := 1 ; + l9 : while (j \leq N) + do when ~b[j] ; + j := j+1 ; + end while ; + l10 : if y # self + then when y = 0 ; + failed := TRUE ; + end if; + end if ; + cs : if ~ failed + then skip ; \* the critical section + l11 : y := 0 ; + l12 : b[self] := FALSE ; + else failed := FALSE ; + end if ; + end if ; + end while ; +end process +process Proc2 \in (M+1)..N +variables j2 = 0 ; failed2 = FALSE ; +begin +2start : while TRUE + do 2l1 : b[self] := TRUE ; + 2l2 : x := self ; + 2l3 : if y # 0 + then 2l4 : b[self] := FALSE ; + 2l5 : when y = 0 ; skip ; + else 2l6 : y := self ; + 2l7 : if x # self + then 2l8 : b[self] := FALSE ; + j2 := 1 ; + 2l9 : while (j2 \leq N) + do when ~b[j2] ; + j2 := j2+1 ; + end while ; + 2l10 : if y # self + then when y = 0 ; + failed2 := TRUE ; + end if; + end if ; + 2cs : if ~ failed2 + then skip ; \* the critical section + 2l11 : y := 0 ; + 2l12 : b[self] := FALSE ; + else failed2 := FALSE ; + end if ; + end if ; + end while ; +end process +end algorithm + +***********************) + +\* BEGIN TRANSLATION - the hash of the PCal code: PCal-cc9279d759058abea504462b4ff084d7 +VARIABLES x, y, b, pc, j, failed, j2, failed2 + +vars == << x, y, b, pc, j, failed, j2, failed2 >> + +ProcSet == (1..M) \cup ((M+1)..N) + +Init == (* Global variables *) + /\ x = 0 + /\ y = 0 + /\ b = [i \in 1..N |-> FALSE] + (* Process Proc1 *) + /\ j = [self \in 1..M |-> 0] + /\ failed = [self \in 1..M |-> FALSE] + (* Process Proc2 *) + /\ j2 = [self \in (M+1)..N |-> 0] + /\ failed2 = [self \in (M+1)..N |-> FALSE] + /\ pc = [self \in ProcSet |-> CASE self \in 1..M -> "start" + [] self \in (M+1)..N -> "2start"] + +start(self) == /\ pc[self] = "start" + /\ pc' = [pc EXCEPT ![self] = "l1"] + /\ UNCHANGED << x, y, b, j, failed, j2, failed2 >> + +l1(self) == /\ pc[self] = "l1" + /\ b' = [b EXCEPT ![self] = TRUE] + /\ pc' = [pc EXCEPT ![self] = "l2"] + /\ UNCHANGED << x, y, j, failed, j2, failed2 >> + +l2(self) == /\ pc[self] = "l2" + /\ x' = self + /\ pc' = [pc EXCEPT ![self] = "l3"] + /\ UNCHANGED << y, b, j, failed, j2, failed2 >> + +l3(self) == /\ pc[self] = "l3" + /\ IF y # 0 + THEN /\ pc' = [pc EXCEPT ![self] = "l4"] + ELSE /\ pc' = [pc EXCEPT ![self] = "l6"] + /\ UNCHANGED << x, y, b, j, failed, j2, failed2 >> + +l4(self) == /\ pc[self] = "l4" + /\ b' = [b EXCEPT ![self] = FALSE] + /\ pc' = [pc EXCEPT ![self] = "l5"] + /\ UNCHANGED << x, y, j, failed, j2, failed2 >> + +l5(self) == /\ pc[self] = "l5" + /\ y = 0 + /\ TRUE + /\ pc' = [pc EXCEPT ![self] = "start"] + /\ UNCHANGED << x, y, b, j, failed, j2, failed2 >> + +l6(self) == /\ pc[self] = "l6" + /\ y' = self + /\ pc' = [pc EXCEPT ![self] = "l7"] + /\ UNCHANGED << x, b, j, failed, j2, failed2 >> + +l7(self) == /\ pc[self] = "l7" + /\ IF x # self + THEN /\ pc' = [pc EXCEPT ![self] = "l8"] + ELSE /\ pc' = [pc EXCEPT ![self] = "cs"] + /\ UNCHANGED << x, y, b, j, failed, j2, failed2 >> + +l8(self) == /\ pc[self] = "l8" + /\ b' = [b EXCEPT ![self] = FALSE] + /\ j' = [j EXCEPT ![self] = 1] + /\ pc' = [pc EXCEPT ![self] = "l9"] + /\ UNCHANGED << x, y, failed, j2, failed2 >> + +l9(self) == /\ pc[self] = "l9" + /\ IF (j[self] \leq N) + THEN /\ ~b[j[self]] + /\ j' = [j EXCEPT ![self] = j[self]+1] + /\ pc' = [pc EXCEPT ![self] = "l9"] + ELSE /\ pc' = [pc EXCEPT ![self] = "l10"] + /\ j' = j + /\ UNCHANGED << x, y, b, failed, j2, failed2 >> + +l10(self) == /\ pc[self] = "l10" + /\ IF y # self + THEN /\ y = 0 + /\ failed' = [failed EXCEPT ![self] = TRUE] + ELSE /\ TRUE + /\ UNCHANGED failed + /\ pc' = [pc EXCEPT ![self] = "cs"] + /\ UNCHANGED << x, y, b, j, j2, failed2 >> + +cs(self) == /\ pc[self] = "cs" + /\ IF ~ failed[self] + THEN /\ TRUE + /\ pc' = [pc EXCEPT ![self] = "l11"] + /\ UNCHANGED failed + ELSE /\ failed' = [failed EXCEPT ![self] = FALSE] + /\ pc' = [pc EXCEPT ![self] = "start"] + /\ UNCHANGED << x, y, b, j, j2, failed2 >> + +l11(self) == /\ pc[self] = "l11" + /\ y' = 0 + /\ pc' = [pc EXCEPT ![self] = "l12"] + /\ UNCHANGED << x, b, j, failed, j2, failed2 >> + +l12(self) == /\ pc[self] = "l12" + /\ b' = [b EXCEPT ![self] = FALSE] + /\ pc' = [pc EXCEPT ![self] = "start"] + /\ UNCHANGED << x, y, j, failed, j2, failed2 >> + +Proc1(self) == start(self) \/ l1(self) \/ l2(self) \/ l3(self) \/ l4(self) + \/ l5(self) \/ l6(self) \/ l7(self) \/ l8(self) + \/ l9(self) \/ l10(self) \/ cs(self) \/ l11(self) + \/ l12(self) + +2start(self) == /\ pc[self] = "2start" + /\ pc' = [pc EXCEPT ![self] = "2l1"] + /\ UNCHANGED << x, y, b, j, failed, j2, failed2 >> + +2l1(self) == /\ pc[self] = "2l1" + /\ b' = [b EXCEPT ![self] = TRUE] + /\ pc' = [pc EXCEPT ![self] = "2l2"] + /\ UNCHANGED << x, y, j, failed, j2, failed2 >> + +2l2(self) == /\ pc[self] = "2l2" + /\ x' = self + /\ pc' = [pc EXCEPT ![self] = "2l3"] + /\ UNCHANGED << y, b, j, failed, j2, failed2 >> + +2l3(self) == /\ pc[self] = "2l3" + /\ IF y # 0 + THEN /\ pc' = [pc EXCEPT ![self] = "2l4"] + ELSE /\ pc' = [pc EXCEPT ![self] = "2l6"] + /\ UNCHANGED << x, y, b, j, failed, j2, failed2 >> + +2l4(self) == /\ pc[self] = "2l4" + /\ b' = [b EXCEPT ![self] = FALSE] + /\ pc' = [pc EXCEPT ![self] = "2l5"] + /\ UNCHANGED << x, y, j, failed, j2, failed2 >> + +2l5(self) == /\ pc[self] = "2l5" + /\ y = 0 + /\ TRUE + /\ pc' = [pc EXCEPT ![self] = "2start"] + /\ UNCHANGED << x, y, b, j, failed, j2, failed2 >> + +2l6(self) == /\ pc[self] = "2l6" + /\ y' = self + /\ pc' = [pc EXCEPT ![self] = "2l7"] + /\ UNCHANGED << x, b, j, failed, j2, failed2 >> + +2l7(self) == /\ pc[self] = "2l7" + /\ IF x # self + THEN /\ pc' = [pc EXCEPT ![self] = "2l8"] + ELSE /\ pc' = [pc EXCEPT ![self] = "2cs"] + /\ UNCHANGED << x, y, b, j, failed, j2, failed2 >> + +2l8(self) == /\ pc[self] = "2l8" + /\ b' = [b EXCEPT ![self] = FALSE] + /\ j2' = [j2 EXCEPT ![self] = 1] + /\ pc' = [pc EXCEPT ![self] = "2l9"] + /\ UNCHANGED << x, y, j, failed, failed2 >> + +2l9(self) == /\ pc[self] = "2l9" + /\ IF (j2[self] \leq N) + THEN /\ ~b[j2[self]] + /\ j2' = [j2 EXCEPT ![self] = j2[self]+1] + /\ pc' = [pc EXCEPT ![self] = "2l9"] + ELSE /\ pc' = [pc EXCEPT ![self] = "2l10"] + /\ j2' = j2 + /\ UNCHANGED << x, y, b, j, failed, failed2 >> + +2l10(self) == /\ pc[self] = "2l10" + /\ IF y # self + THEN /\ y = 0 + /\ failed2' = [failed2 EXCEPT ![self] = TRUE] + ELSE /\ TRUE + /\ UNCHANGED failed2 + /\ pc' = [pc EXCEPT ![self] = "2cs"] + /\ UNCHANGED << x, y, b, j, failed, j2 >> + +2cs(self) == /\ pc[self] = "2cs" + /\ IF ~ failed2[self] + THEN /\ TRUE + /\ pc' = [pc EXCEPT ![self] = "2l11"] + /\ UNCHANGED failed2 + ELSE /\ failed2' = [failed2 EXCEPT ![self] = FALSE] + /\ pc' = [pc EXCEPT ![self] = "2start"] + /\ UNCHANGED << x, y, b, j, failed, j2 >> + +2l11(self) == /\ pc[self] = "2l11" + /\ y' = 0 + /\ pc' = [pc EXCEPT ![self] = "2l12"] + /\ UNCHANGED << x, b, j, failed, j2, failed2 >> + +2l12(self) == /\ pc[self] = "2l12" + /\ b' = [b EXCEPT ![self] = FALSE] + /\ pc' = [pc EXCEPT ![self] = "2start"] + /\ UNCHANGED << x, y, j, failed, j2, failed2 >> + +Proc2(self) == 2start(self) \/ 2l1(self) \/ 2l2(self) \/ 2l3(self) + \/ 2l4(self) \/ 2l5(self) \/ 2l6(self) \/ 2l7(self) + \/ 2l8(self) \/ 2l9(self) \/ 2l10(self) \/ 2cs(self) + \/ 2l11(self) \/ 2l12(self) + +Next == (\E self \in 1..M: Proc1(self)) + \/ (\E self \in (M+1)..N: Proc2(self)) + +Spec == /\ Init /\ [][Next]_vars + /\ \A self \in 1..M : WF_vars(Proc1(self)) + /\ \A self \in (M+1)..N : WF_vars(Proc2(self)) + +\* END TRANSLATION - the hash of the generated TLA code (remove to silence divergence warnings): TLA-3862da72da966d861f548da308a59a9e + +ASSUME Print(<<"ProcSet =" , ProcSet>>, TRUE) +inCS(i) == IF i \in 1..M + THEN (pc[i] = "cs") /\ (~failed[i]) + ELSE (pc[i] = "2cs") /\ (~failed2[i]) + +Invariant == \A i, k \in 1..N : (i # k) => ~ (inCS(i) /\ inCS(k)) + +Liveness == []<> \E i \in 1..N : inCS(i) +============================================================================= diff --git a/tlatools/test-model/pcal/FastMutex3.cfg b/tlatools/test-model/pcal/FastMutex3.cfg new file mode 100644 index 0000000000000000000000000000000000000000..06b6866abd1c5a3c865f733fc2dc62af47317824 --- /dev/null +++ b/tlatools/test-model/pcal/FastMutex3.cfg @@ -0,0 +1,5 @@ +SPECIFICATION Spec +\* Add statements after this line. +CONSTANT N = 2 +INVARIANT Invariant +PROPERTY Liveness diff --git a/tlatools/test-model/pcal/FastMutex3.tla b/tlatools/test-model/pcal/FastMutex3.tla new file mode 100644 index 0000000000000000000000000000000000000000..b18f8c509d586d505a943539fe877b33bd5d4ce9 --- /dev/null +++ b/tlatools/test-model/pcal/FastMutex3.tla @@ -0,0 +1,293 @@ +----------------------------- MODULE FastMutex3 ----------------------------- + +\* This tests the translation when there are two different processes + +EXTENDS Naturals, TLC + +CONSTANT N + +ASSUME (N \in Nat) + +(********************** +--algorithm FastMutex + variables x = 0 ; y = 0 ; b = [i \in 1..N |-> FALSE] ; +process Proc1 = 1 +variables j = 0 ; failed = FALSE ; +begin +start : while TRUE + do l1 : b[1] := TRUE ; + l2 : x := 1 ; + l3 : if y # 0 + then l4 : b[1] := FALSE ; + l5 : when y = 0 ; skip ; + else l6 : y := 1 ; + l7 : if x # 1 + then l8 : b[1] := FALSE ; + j := 1 ; + l9 : while (j \leq N) + do when ~b[j] ; + j := j+1 ; + end while ; + l10 : if y # 1 + then when y = 0 ; + failed := TRUE ; + end if; + end if ; + cs : if ~ failed + then skip ; \* the critical section + l11 : y := 0 ; + l12 : b[1] := FALSE ; + else failed := FALSE ; + end if ; + end if ; + end while ; +end process +process Proc2 \in 2..N +variables j2 = 0 ; failed2 = FALSE ; +begin +2start : while TRUE + do 2l1 : b[self] := TRUE ; + 2l2 : x := self ; + 2l3 : if y # 0 + then 2l4 : b[self] := FALSE ; + 2l5 : when y = 0 ; skip ; + else 2l6 : y := self ; + 2l7 : if x # self + then 2l8 : b[self] := FALSE ; + j2 := 1 ; + 2l9 : while (j2 \leq N) + do when ~b[j2] ; + j2 := j2+1 ; + end while ; + 2l10 : if y # self + then when y = 0 ; + failed2 := TRUE ; + end if; + end if ; + 2cs : if ~ failed2 + then skip ; \* the critical section + 2l11 : y := 0 ; + 2l12 : b[self] := FALSE ; + else failed2 := FALSE ; + end if ; + end if ; + end while ; +end process +end algorithm + +***********************) + +\* BEGIN TRANSLATION - the hash of the PCal code: PCal-1901ed6bf685f13ebc84143ab4ef5b13 +VARIABLES x, y, b, pc, j, failed, j2, failed2 + +vars == << x, y, b, pc, j, failed, j2, failed2 >> + +ProcSet == {1} \cup (2..N) + +Init == (* Global variables *) + /\ x = 0 + /\ y = 0 + /\ b = [i \in 1..N |-> FALSE] + (* Process Proc1 *) + /\ j = 0 + /\ failed = FALSE + (* Process Proc2 *) + /\ j2 = [self \in 2..N |-> 0] + /\ failed2 = [self \in 2..N |-> FALSE] + /\ pc = [self \in ProcSet |-> CASE self = 1 -> "start" + [] self \in 2..N -> "2start"] + +start == /\ pc[1] = "start" + /\ pc' = [pc EXCEPT ![1] = "l1"] + /\ UNCHANGED << x, y, b, j, failed, j2, failed2 >> + +l1 == /\ pc[1] = "l1" + /\ b' = [b EXCEPT ![1] = TRUE] + /\ pc' = [pc EXCEPT ![1] = "l2"] + /\ UNCHANGED << x, y, j, failed, j2, failed2 >> + +l2 == /\ pc[1] = "l2" + /\ x' = 1 + /\ pc' = [pc EXCEPT ![1] = "l3"] + /\ UNCHANGED << y, b, j, failed, j2, failed2 >> + +l3 == /\ pc[1] = "l3" + /\ IF y # 0 + THEN /\ pc' = [pc EXCEPT ![1] = "l4"] + ELSE /\ pc' = [pc EXCEPT ![1] = "l6"] + /\ UNCHANGED << x, y, b, j, failed, j2, failed2 >> + +l4 == /\ pc[1] = "l4" + /\ b' = [b EXCEPT ![1] = FALSE] + /\ pc' = [pc EXCEPT ![1] = "l5"] + /\ UNCHANGED << x, y, j, failed, j2, failed2 >> + +l5 == /\ pc[1] = "l5" + /\ y = 0 + /\ TRUE + /\ pc' = [pc EXCEPT ![1] = "start"] + /\ UNCHANGED << x, y, b, j, failed, j2, failed2 >> + +l6 == /\ pc[1] = "l6" + /\ y' = 1 + /\ pc' = [pc EXCEPT ![1] = "l7"] + /\ UNCHANGED << x, b, j, failed, j2, failed2 >> + +l7 == /\ pc[1] = "l7" + /\ IF x # 1 + THEN /\ pc' = [pc EXCEPT ![1] = "l8"] + ELSE /\ pc' = [pc EXCEPT ![1] = "cs"] + /\ UNCHANGED << x, y, b, j, failed, j2, failed2 >> + +l8 == /\ pc[1] = "l8" + /\ b' = [b EXCEPT ![1] = FALSE] + /\ j' = 1 + /\ pc' = [pc EXCEPT ![1] = "l9"] + /\ UNCHANGED << x, y, failed, j2, failed2 >> + +l9 == /\ pc[1] = "l9" + /\ IF (j \leq N) + THEN /\ ~b[j] + /\ j' = j+1 + /\ pc' = [pc EXCEPT ![1] = "l9"] + ELSE /\ pc' = [pc EXCEPT ![1] = "l10"] + /\ j' = j + /\ UNCHANGED << x, y, b, failed, j2, failed2 >> + +l10 == /\ pc[1] = "l10" + /\ IF y # 1 + THEN /\ y = 0 + /\ failed' = TRUE + ELSE /\ TRUE + /\ UNCHANGED failed + /\ pc' = [pc EXCEPT ![1] = "cs"] + /\ UNCHANGED << x, y, b, j, j2, failed2 >> + +cs == /\ pc[1] = "cs" + /\ IF ~ failed + THEN /\ TRUE + /\ pc' = [pc EXCEPT ![1] = "l11"] + /\ UNCHANGED failed + ELSE /\ failed' = FALSE + /\ pc' = [pc EXCEPT ![1] = "start"] + /\ UNCHANGED << x, y, b, j, j2, failed2 >> + +l11 == /\ pc[1] = "l11" + /\ y' = 0 + /\ pc' = [pc EXCEPT ![1] = "l12"] + /\ UNCHANGED << x, b, j, failed, j2, failed2 >> + +l12 == /\ pc[1] = "l12" + /\ b' = [b EXCEPT ![1] = FALSE] + /\ pc' = [pc EXCEPT ![1] = "start"] + /\ UNCHANGED << x, y, j, failed, j2, failed2 >> + +Proc1 == start \/ l1 \/ l2 \/ l3 \/ l4 \/ l5 \/ l6 \/ l7 \/ l8 \/ l9 \/ l10 + \/ cs \/ l11 \/ l12 + +2start(self) == /\ pc[self] = "2start" + /\ pc' = [pc EXCEPT ![self] = "2l1"] + /\ UNCHANGED << x, y, b, j, failed, j2, failed2 >> + +2l1(self) == /\ pc[self] = "2l1" + /\ b' = [b EXCEPT ![self] = TRUE] + /\ pc' = [pc EXCEPT ![self] = "2l2"] + /\ UNCHANGED << x, y, j, failed, j2, failed2 >> + +2l2(self) == /\ pc[self] = "2l2" + /\ x' = self + /\ pc' = [pc EXCEPT ![self] = "2l3"] + /\ UNCHANGED << y, b, j, failed, j2, failed2 >> + +2l3(self) == /\ pc[self] = "2l3" + /\ IF y # 0 + THEN /\ pc' = [pc EXCEPT ![self] = "2l4"] + ELSE /\ pc' = [pc EXCEPT ![self] = "2l6"] + /\ UNCHANGED << x, y, b, j, failed, j2, failed2 >> + +2l4(self) == /\ pc[self] = "2l4" + /\ b' = [b EXCEPT ![self] = FALSE] + /\ pc' = [pc EXCEPT ![self] = "2l5"] + /\ UNCHANGED << x, y, j, failed, j2, failed2 >> + +2l5(self) == /\ pc[self] = "2l5" + /\ y = 0 + /\ TRUE + /\ pc' = [pc EXCEPT ![self] = "2start"] + /\ UNCHANGED << x, y, b, j, failed, j2, failed2 >> + +2l6(self) == /\ pc[self] = "2l6" + /\ y' = self + /\ pc' = [pc EXCEPT ![self] = "2l7"] + /\ UNCHANGED << x, b, j, failed, j2, failed2 >> + +2l7(self) == /\ pc[self] = "2l7" + /\ IF x # self + THEN /\ pc' = [pc EXCEPT ![self] = "2l8"] + ELSE /\ pc' = [pc EXCEPT ![self] = "2cs"] + /\ UNCHANGED << x, y, b, j, failed, j2, failed2 >> + +2l8(self) == /\ pc[self] = "2l8" + /\ b' = [b EXCEPT ![self] = FALSE] + /\ j2' = [j2 EXCEPT ![self] = 1] + /\ pc' = [pc EXCEPT ![self] = "2l9"] + /\ UNCHANGED << x, y, j, failed, failed2 >> + +2l9(self) == /\ pc[self] = "2l9" + /\ IF (j2[self] \leq N) + THEN /\ ~b[j2[self]] + /\ j2' = [j2 EXCEPT ![self] = j2[self]+1] + /\ pc' = [pc EXCEPT ![self] = "2l9"] + ELSE /\ pc' = [pc EXCEPT ![self] = "2l10"] + /\ j2' = j2 + /\ UNCHANGED << x, y, b, j, failed, failed2 >> + +2l10(self) == /\ pc[self] = "2l10" + /\ IF y # self + THEN /\ y = 0 + /\ failed2' = [failed2 EXCEPT ![self] = TRUE] + ELSE /\ TRUE + /\ UNCHANGED failed2 + /\ pc' = [pc EXCEPT ![self] = "2cs"] + /\ UNCHANGED << x, y, b, j, failed, j2 >> + +2cs(self) == /\ pc[self] = "2cs" + /\ IF ~ failed2[self] + THEN /\ TRUE + /\ pc' = [pc EXCEPT ![self] = "2l11"] + /\ UNCHANGED failed2 + ELSE /\ failed2' = [failed2 EXCEPT ![self] = FALSE] + /\ pc' = [pc EXCEPT ![self] = "2start"] + /\ UNCHANGED << x, y, b, j, failed, j2 >> + +2l11(self) == /\ pc[self] = "2l11" + /\ y' = 0 + /\ pc' = [pc EXCEPT ![self] = "2l12"] + /\ UNCHANGED << x, b, j, failed, j2, failed2 >> + +2l12(self) == /\ pc[self] = "2l12" + /\ b' = [b EXCEPT ![self] = FALSE] + /\ pc' = [pc EXCEPT ![self] = "2start"] + /\ UNCHANGED << x, y, j, failed, j2, failed2 >> + +Proc2(self) == 2start(self) \/ 2l1(self) \/ 2l2(self) \/ 2l3(self) + \/ 2l4(self) \/ 2l5(self) \/ 2l6(self) \/ 2l7(self) + \/ 2l8(self) \/ 2l9(self) \/ 2l10(self) \/ 2cs(self) + \/ 2l11(self) \/ 2l12(self) + +Next == Proc1 + \/ (\E self \in 2..N: Proc2(self)) + +Spec == /\ Init /\ [][Next]_vars + /\ WF_vars(Proc1) + /\ \A self \in 2..N : WF_vars(Proc2(self)) + +\* END TRANSLATION - the hash of the generated TLA code (remove to silence divergence warnings): TLA-35555ac3cdd01367ca89de83ceeebd64 + +ASSUME Print(<<"ProcSet =" , ProcSet>>, TRUE) +inCS(i) == IF i = 1 THEN (pc[i] = "cs") /\ (~failed) + ELSE (pc[i] = "2cs") /\ (~failed2[i]) +Invariant == \A i, k \in 1..N : (i # k) => ~ (inCS(i) /\ inCS(k)) + +Liveness == []<> \E i \in 1..N : inCS(i) +============================================================================= diff --git a/tlatools/test-model/pcal/FastMutexWithGoto.cfg b/tlatools/test-model/pcal/FastMutexWithGoto.cfg new file mode 100644 index 0000000000000000000000000000000000000000..cd016e674cbb13c109259f05e6e587fb82d6a4ae --- /dev/null +++ b/tlatools/test-model/pcal/FastMutexWithGoto.cfg @@ -0,0 +1,5 @@ +\* Add statements after this line. +CONSTANT N = 2 +INVARIANT Invariant +PROPERTY CondLiveness +SPECIFICATION FairSpec diff --git a/tlatools/test-model/pcal/FastMutexWithGoto.tla b/tlatools/test-model/pcal/FastMutexWithGoto.tla new file mode 100644 index 0000000000000000000000000000000000000000..a39b53e28de250b045bb9971a2f725a584465b2e --- /dev/null +++ b/tlatools/test-model/pcal/FastMutexWithGoto.tla @@ -0,0 +1,179 @@ +------------------------- MODULE FastMutexWithGoto -------------------------- + +EXTENDS Naturals + +CONSTANT N + +ASSUME N \in Nat + +(********************** +--algorithm FastMutex +variables x = 0 ; y = 0 ; b = [i \in 1..N |-> FALSE] ; + +process Proc \in 1..N + variables j = 0 ; + begin + ncs: while TRUE do + skip ; \* Noncritical section. + start: b[self] := TRUE ; + l1: x := self ; + l2: if y # 0 + then l3: b[self] := FALSE ; + l4: when y = 0 ; + goto start ; + end if ; + l5: y := self ; + l6: if x # self + then l7: b[self] := FALSE ; + j := 1 ; + l8: while j \leq N do + when ~b[j] ; + j := j+1 ; + end while ; + l9: if y # self then l10: when y = 0 ; + goto start ; + end if ; + end if; + cs: skip ; \* the critical section + l11: y := 0 ; + l12: b[self] := FALSE ; + end while ; +end process + +end algorithm + +***********************) + +\* BEGIN TRANSLATION - the hash of the PCal code: PCal-765a4c9fba722b8f4115732919fba3a0 +VARIABLES x, y, b, pc, j + +vars == << x, y, b, pc, j >> + +ProcSet == (1..N) + +Init == (* Global variables *) + /\ x = 0 + /\ y = 0 + /\ b = [i \in 1..N |-> FALSE] + (* Process Proc *) + /\ j = [self \in 1..N |-> 0] + /\ pc = [self \in ProcSet |-> "ncs"] + +ncs(self) == /\ pc[self] = "ncs" + /\ TRUE + /\ pc' = [pc EXCEPT ![self] = "start"] + /\ UNCHANGED << x, y, b, j >> + +start(self) == /\ pc[self] = "start" + /\ b' = [b EXCEPT ![self] = TRUE] + /\ pc' = [pc EXCEPT ![self] = "l1"] + /\ UNCHANGED << x, y, j >> + +l1(self) == /\ pc[self] = "l1" + /\ x' = self + /\ pc' = [pc EXCEPT ![self] = "l2"] + /\ UNCHANGED << y, b, j >> + +l2(self) == /\ pc[self] = "l2" + /\ IF y # 0 + THEN /\ pc' = [pc EXCEPT ![self] = "l3"] + ELSE /\ pc' = [pc EXCEPT ![self] = "l5"] + /\ UNCHANGED << x, y, b, j >> + +l3(self) == /\ pc[self] = "l3" + /\ b' = [b EXCEPT ![self] = FALSE] + /\ pc' = [pc EXCEPT ![self] = "l4"] + /\ UNCHANGED << x, y, j >> + +l4(self) == /\ pc[self] = "l4" + /\ y = 0 + /\ pc' = [pc EXCEPT ![self] = "start"] + /\ UNCHANGED << x, y, b, j >> + +l5(self) == /\ pc[self] = "l5" + /\ y' = self + /\ pc' = [pc EXCEPT ![self] = "l6"] + /\ UNCHANGED << x, b, j >> + +l6(self) == /\ pc[self] = "l6" + /\ IF x # self + THEN /\ pc' = [pc EXCEPT ![self] = "l7"] + ELSE /\ pc' = [pc EXCEPT ![self] = "cs"] + /\ UNCHANGED << x, y, b, j >> + +l7(self) == /\ pc[self] = "l7" + /\ b' = [b EXCEPT ![self] = FALSE] + /\ j' = [j EXCEPT ![self] = 1] + /\ pc' = [pc EXCEPT ![self] = "l8"] + /\ UNCHANGED << x, y >> + +l8(self) == /\ pc[self] = "l8" + /\ IF j[self] \leq N + THEN /\ ~b[j[self]] + /\ j' = [j EXCEPT ![self] = j[self]+1] + /\ pc' = [pc EXCEPT ![self] = "l8"] + ELSE /\ pc' = [pc EXCEPT ![self] = "l9"] + /\ j' = j + /\ UNCHANGED << x, y, b >> + +l9(self) == /\ pc[self] = "l9" + /\ IF y # self + THEN /\ pc' = [pc EXCEPT ![self] = "l10"] + ELSE /\ pc' = [pc EXCEPT ![self] = "cs"] + /\ UNCHANGED << x, y, b, j >> + +l10(self) == /\ pc[self] = "l10" + /\ y = 0 + /\ pc' = [pc EXCEPT ![self] = "start"] + /\ UNCHANGED << x, y, b, j >> + +cs(self) == /\ pc[self] = "cs" + /\ TRUE + /\ pc' = [pc EXCEPT ![self] = "l11"] + /\ UNCHANGED << x, y, b, j >> + +l11(self) == /\ pc[self] = "l11" + /\ y' = 0 + /\ pc' = [pc EXCEPT ![self] = "l12"] + /\ UNCHANGED << x, b, j >> + +l12(self) == /\ pc[self] = "l12" + /\ b' = [b EXCEPT ![self] = FALSE] + /\ pc' = [pc EXCEPT ![self] = "ncs"] + /\ UNCHANGED << x, y, j >> + +Proc(self) == ncs(self) \/ start(self) \/ l1(self) \/ l2(self) \/ l3(self) + \/ l4(self) \/ l5(self) \/ l6(self) \/ l7(self) + \/ l8(self) \/ l9(self) \/ l10(self) \/ cs(self) + \/ l11(self) \/ l12(self) + +Next == (\E self \in 1..N: Proc(self)) + +Spec == Init /\ [][Next]_vars + +\* END TRANSLATION - the hash of the generated TLA code (remove to silence divergence warnings): TLA-556f4fffd8fac96ddb09d8c9c984f878 + +inCS(i) == (pc[i] = "cs") + +Invariant == \A i, k \in 1..N: (i # k) => ~ (inCS(i) /\ inCS(k)) + +Liveness == []<> \E i \in 1..N: inCS(i) + +CondLiveness == ([]<> \E i \in 1..N : pc[i] # "ncs") => Liveness + +Fairness == \A i \in 1..N : + /\ WF_vars(start(i)) + /\ WF_vars(l1(i)) + /\ WF_vars(l2(i)) + /\ WF_vars(l3(i)) + /\ WF_vars(l4(i)) + /\ WF_vars(l5(i)) + /\ WF_vars(l6(i)) + /\ WF_vars(l7(i)) + /\ WF_vars(l8(i)) + /\ WF_vars(l9(i)) + /\ WF_vars(l10(i)) + /\ WF_vars(l11(i)) + /\ WF_vars(l12(i)) +FairSpec == Spec /\ Fairness +============================================================================= diff --git a/tlatools/test-model/pcal/FastMutexWithGoto2.cfg b/tlatools/test-model/pcal/FastMutexWithGoto2.cfg new file mode 100644 index 0000000000000000000000000000000000000000..de1000468b848d4daa608674a58a8b347b759671 --- /dev/null +++ b/tlatools/test-model/pcal/FastMutexWithGoto2.cfg @@ -0,0 +1,5 @@ +SPECIFICATION Spec +\* Add statements after this line. +CONSTANT N = 3 +INVARIANT Invariant +PROPERTY Liveness diff --git a/tlatools/test-model/pcal/FastMutexWithGoto2.tla b/tlatools/test-model/pcal/FastMutexWithGoto2.tla new file mode 100644 index 0000000000000000000000000000000000000000..cd98a524bd71a7899f4c6e3765b283c38e76b8b6 --- /dev/null +++ b/tlatools/test-model/pcal/FastMutexWithGoto2.tla @@ -0,0 +1,161 @@ +------------------------ MODULE FastMutexWithGoto2 -------------------------- + +EXTENDS Naturals + +CONSTANT N + +ASSUME N \in Nat + +(********************** +--algorithm FastMutex + variables x = 0 ; y = 0 ; b = [i \in 1..N |-> FALSE] ; +process Proc \in 1..N +variables S = {} ; +begin +start : while TRUE + do l1 : b[self] := TRUE ; + l2 : x := self ; + l3 : if y # 0 + then l4 : b[self] := FALSE ; + l5 : when y = 0 ; + goto start ; + end if ; + l6 : y := self ; + l7 : if x # self + then l8 : b[self] := FALSE ; + S := 1..N \ {self} ; + l9 : while S # {} do + with j \in S do when ~b[j] ; + S := S \ {j} + end with ; + end while ; + l10 : if y # self then l11 : when y = 0 ; + goto start ; + end if ; + end if; + cs : skip ; \* the critical section + l12 : y := 0 ; + l13 : b[self] := FALSE ; + end while ; +end process +end algorithm + +***********************) + +\* BEGIN TRANSLATION - the hash of the PCal code: PCal-d10cf42a8598db9135c9a3e8255fdc3c +VARIABLES x, y, b, pc, S + +vars == << x, y, b, pc, S >> + +ProcSet == (1..N) + +Init == (* Global variables *) + /\ x = 0 + /\ y = 0 + /\ b = [i \in 1..N |-> FALSE] + (* Process Proc *) + /\ S = [self \in 1..N |-> {}] + /\ pc = [self \in ProcSet |-> "start"] + +start(self) == /\ pc[self] = "start" + /\ pc' = [pc EXCEPT ![self] = "l1"] + /\ UNCHANGED << x, y, b, S >> + +l1(self) == /\ pc[self] = "l1" + /\ b' = [b EXCEPT ![self] = TRUE] + /\ pc' = [pc EXCEPT ![self] = "l2"] + /\ UNCHANGED << x, y, S >> + +l2(self) == /\ pc[self] = "l2" + /\ x' = self + /\ pc' = [pc EXCEPT ![self] = "l3"] + /\ UNCHANGED << y, b, S >> + +l3(self) == /\ pc[self] = "l3" + /\ IF y # 0 + THEN /\ pc' = [pc EXCEPT ![self] = "l4"] + ELSE /\ pc' = [pc EXCEPT ![self] = "l6"] + /\ UNCHANGED << x, y, b, S >> + +l4(self) == /\ pc[self] = "l4" + /\ b' = [b EXCEPT ![self] = FALSE] + /\ pc' = [pc EXCEPT ![self] = "l5"] + /\ UNCHANGED << x, y, S >> + +l5(self) == /\ pc[self] = "l5" + /\ y = 0 + /\ pc' = [pc EXCEPT ![self] = "start"] + /\ UNCHANGED << x, y, b, S >> + +l6(self) == /\ pc[self] = "l6" + /\ y' = self + /\ pc' = [pc EXCEPT ![self] = "l7"] + /\ UNCHANGED << x, b, S >> + +l7(self) == /\ pc[self] = "l7" + /\ IF x # self + THEN /\ pc' = [pc EXCEPT ![self] = "l8"] + ELSE /\ pc' = [pc EXCEPT ![self] = "cs"] + /\ UNCHANGED << x, y, b, S >> + +l8(self) == /\ pc[self] = "l8" + /\ b' = [b EXCEPT ![self] = FALSE] + /\ S' = [S EXCEPT ![self] = 1..N \ {self}] + /\ pc' = [pc EXCEPT ![self] = "l9"] + /\ UNCHANGED << x, y >> + +l9(self) == /\ pc[self] = "l9" + /\ IF S[self] # {} + THEN /\ \E j \in S[self]: + /\ ~b[j] + /\ S' = [S EXCEPT ![self] = S[self] \ {j}] + /\ pc' = [pc EXCEPT ![self] = "l9"] + ELSE /\ pc' = [pc EXCEPT ![self] = "l10"] + /\ S' = S + /\ UNCHANGED << x, y, b >> + +l10(self) == /\ pc[self] = "l10" + /\ IF y # self + THEN /\ pc' = [pc EXCEPT ![self] = "l11"] + ELSE /\ pc' = [pc EXCEPT ![self] = "cs"] + /\ UNCHANGED << x, y, b, S >> + +l11(self) == /\ pc[self] = "l11" + /\ y = 0 + /\ pc' = [pc EXCEPT ![self] = "start"] + /\ UNCHANGED << x, y, b, S >> + +cs(self) == /\ pc[self] = "cs" + /\ TRUE + /\ pc' = [pc EXCEPT ![self] = "l12"] + /\ UNCHANGED << x, y, b, S >> + +l12(self) == /\ pc[self] = "l12" + /\ y' = 0 + /\ pc' = [pc EXCEPT ![self] = "l13"] + /\ UNCHANGED << x, b, S >> + +l13(self) == /\ pc[self] = "l13" + /\ b' = [b EXCEPT ![self] = FALSE] + /\ pc' = [pc EXCEPT ![self] = "start"] + /\ UNCHANGED << x, y, S >> + +Proc(self) == start(self) \/ l1(self) \/ l2(self) \/ l3(self) \/ l4(self) + \/ l5(self) \/ l6(self) \/ l7(self) \/ l8(self) + \/ l9(self) \/ l10(self) \/ l11(self) \/ cs(self) + \/ l12(self) \/ l13(self) + +Next == (\E self \in 1..N: Proc(self)) + +Spec == /\ Init /\ [][Next]_vars + /\ WF_vars(Next) + +\* END TRANSLATION - the hash of the generated TLA code (remove to silence divergence warnings): TLA-71043efb021aa260bc5d04181cce7e16 + +inCS(i) == (pc[i] = "cs") + +Invariant == \A i, k \in 1..N : (i # k) => ~ (inCS(i) /\ inCS(k)) + + +Liveness == []<> \E i \in 1..N : inCS(i) +============================================================================= diff --git a/tlatools/test-model/pcal/Fischer.cfg b/tlatools/test-model/pcal/Fischer.cfg new file mode 100644 index 0000000000000000000000000000000000000000..699094a9583bbdf24c5155731fce87e5d9a56b92 --- /dev/null +++ b/tlatools/test-model/pcal/Fischer.cfg @@ -0,0 +1,5 @@ +SPECIFICATION Spec +\* Add statements after this line. +CONSTANT N = 3 Epsilon = 3 Delta = 2 +INVARIANT Invariant +PROPERTY Liveness diff --git a/tlatools/test-model/pcal/Fischer.tla b/tlatools/test-model/pcal/Fischer.tla new file mode 100644 index 0000000000000000000000000000000000000000..94f8697638adb3bf2d04c2434d69b960306e0e12 --- /dev/null +++ b/tlatools/test-model/pcal/Fischer.tla @@ -0,0 +1,137 @@ +Plus Cal options (-wf -spec PlusCal2) +----------------------------- MODULE Fischer ----------------------------- +EXTENDS Naturals, TLC +\* what's up +CONSTANT Delta, Epsilon \* The timing delays +CONSTANT N \* The number of synchronizing processes +\* CONSTANT defaultInitValue + +ASSUME /\ Print("Testing Fischer's Mutual Exclusion Algorithm", TRUE) + /\ Print(<<" Number of processes = ", N>>, TRUE) + /\ Print(<<" Delta = ", Delta>>, TRUE) + /\ Print(<<" Epsilon = ", Epsilon>>, TRUE) + /\ Print("Should find a bug if N > 1 and Delta >= Epsilon", TRUE) + + +Infinity == 9999999 + +(********************** +--algorithm Fischer + variables x = 0 ; timer = [i \in 1..N |-> Infinity] ; + process Proc \in 1..N + variable firstTime = TRUE ; + begin a : while TRUE + (**********************************************************************) + (* Note that the +cal syntax requires that both while statements be *) + (* labeled, adding a useless atomic action. The only ways I see to *) + (* eliminate that would be by adding a "goto" statement that could be *) + (* used to encode the inner "while" loop. *) + (**********************************************************************) + do b : while x # self + (***************************************************) + (* x can't equal i the first time through the loop *) + (***************************************************) + do c : when x = 0 ; + timer[self] := Delta ; + d : x := self ; + timer[self] := Epsilon ; + e : when timer[self] = 0 ; + timer[self] := Infinity ; + end while ; + cs : skip ; \* critical section + f : x := 0 ; + end while ; + end process + process Tick = 0 + begin t1 : while TRUE + do when \A i \in 1..N : timer[i] > 0 ; + timer := [i \in 1..N |-> IF timer[i] < Infinity + THEN timer[i] - 1 + ELSE timer[i] ] ; + end while ; + end process +end algorithm + +***********************) + +\* BEGIN TRANSLATION - the hash of the PCal code: PCal-fcd66548d87762ce6cd61f8b60ad89ec +VARIABLES x, timer, pc, firstTime + +vars == << x, timer, pc, firstTime >> + +ProcSet == (1..N) \cup {0} + +Init == (* Global variables *) + /\ x = 0 + /\ timer = [i \in 1..N |-> Infinity] + (* Process Proc *) + /\ firstTime = [self \in 1..N |-> TRUE] + /\ pc = [self \in ProcSet |-> CASE self \in 1..N -> "a" + [] self = 0 -> "t1"] + +a(self) == /\ pc[self] = "a" + /\ pc' = [pc EXCEPT ![self] = "b"] + /\ UNCHANGED << x, timer, firstTime >> + +b(self) == /\ pc[self] = "b" + /\ IF x # self + THEN /\ pc' = [pc EXCEPT ![self] = "c"] + ELSE /\ pc' = [pc EXCEPT ![self] = "cs"] + /\ UNCHANGED << x, timer, firstTime >> + +c(self) == /\ pc[self] = "c" + /\ x = 0 + /\ timer' = [timer EXCEPT ![self] = Delta] + /\ pc' = [pc EXCEPT ![self] = "d"] + /\ UNCHANGED << x, firstTime >> + +d(self) == /\ pc[self] = "d" + /\ x' = self + /\ timer' = [timer EXCEPT ![self] = Epsilon] + /\ pc' = [pc EXCEPT ![self] = "e"] + /\ UNCHANGED firstTime + +e(self) == /\ pc[self] = "e" + /\ timer[self] = 0 + /\ timer' = [timer EXCEPT ![self] = Infinity] + /\ pc' = [pc EXCEPT ![self] = "b"] + /\ UNCHANGED << x, firstTime >> + +cs(self) == /\ pc[self] = "cs" + /\ TRUE + /\ pc' = [pc EXCEPT ![self] = "f"] + /\ UNCHANGED << x, timer, firstTime >> + +f(self) == /\ pc[self] = "f" + /\ x' = 0 + /\ pc' = [pc EXCEPT ![self] = "a"] + /\ UNCHANGED << timer, firstTime >> + +Proc(self) == a(self) \/ b(self) \/ c(self) \/ d(self) \/ e(self) + \/ cs(self) \/ f(self) + +t1 == /\ pc[0] = "t1" + /\ \A i \in 1..N : timer[i] > 0 + /\ timer' = [i \in 1..N |-> IF timer[i] < Infinity + THEN timer[i] - 1 + ELSE timer[i] ] + /\ pc' = [pc EXCEPT ![0] = "t1"] + /\ UNCHANGED << x, firstTime >> + +Tick == t1 + +Next == Tick + \/ (\E self \in 1..N: Proc(self)) + +Spec == /\ Init /\ [][Next]_vars + /\ \A self \in 1..N : WF_vars(Proc(self)) + /\ WF_vars(Tick) + +\* END TRANSLATION - the hash of the generated TLA code (remove to silence divergence warnings): TLA-d8d38bac35efc64e68e3d99ac06a0837 + +inCS(i) == pc[i] = "cs" + +Invariant == \A i, k \in 1..N : (i # k) => ~ (inCS(i) /\ inCS(k)) + +Liveness == []<> \E i \in 1..N : inCS(i) +============================================================================= diff --git a/tlatools/test-model/pcal/InnerLabeledIf.tla b/tlatools/test-model/pcal/InnerLabeledIf.tla new file mode 100644 index 0000000000000000000000000000000000000000..a54b23102e1b97d993e92876aaac9531aa1d8d51 --- /dev/null +++ b/tlatools/test-model/pcal/InnerLabeledIf.tla @@ -0,0 +1,83 @@ +--------------------------- MODULE InnerLabeledIf ---------------------------- +EXTENDS Sequences, Naturals, TLC + +(* + --algorithm InnerLabeledIf + variable x \in 1..4 ; + begin a : if (x < 3) + then if (x = 1) + then skip ; + b : assert x = 1 + else c : assert x = 2 + end if ; + else if (x = 3) + then skip ; + d : assert x = 3 + else e : assert x = 4 ; + end if ; + end if ; + f : print("made it to end") ; + end algorithm +*) + +\* BEGIN TRANSLATION - the hash of the PCal code: PCal-8d605a76553c67cf0740a3d27b57470b +VARIABLES x, pc + +vars == << x, pc >> + +Init == (* Global variables *) + /\ x \in 1..4 + /\ pc = "a" + +a == /\ pc = "a" + /\ IF (x < 3) + THEN /\ IF (x = 1) + THEN /\ TRUE + /\ pc' = "b" + ELSE /\ pc' = "c" + ELSE /\ IF (x = 3) + THEN /\ TRUE + /\ pc' = "d" + ELSE /\ pc' = "e" + /\ x' = x + +b == /\ pc = "b" + /\ Assert(x = 1, "Failure of assertion at line 10, column 33.") + /\ pc' = "f" + /\ x' = x + +c == /\ pc = "c" + /\ Assert(x = 2, "Failure of assertion at line 11, column 33.") + /\ pc' = "f" + /\ x' = x + +d == /\ pc = "d" + /\ Assert(x = 3, "Failure of assertion at line 15, column 33.") + /\ pc' = "f" + /\ x' = x + +e == /\ pc = "e" + /\ Assert(x = 4, "Failure of assertion at line 16, column 33.") + /\ pc' = "f" + /\ x' = x + +f == /\ pc = "f" + /\ PrintT(("made it to end")) + /\ pc' = "Done" + /\ x' = x + +(* Allow infinite stuttering to prevent deadlock on termination. *) +Terminating == pc = "Done" /\ UNCHANGED vars + +Next == a \/ b \/ c \/ d \/ e \/ f + \/ Terminating + +Spec == /\ Init /\ [][Next]_vars + /\ WF_vars(Next) + +Termination == <>(pc = "Done") + +\* END TRANSLATION - the hash of the generated TLA code (remove to silence divergence warnings): TLA-c43944356bde2029519b5137bfd80466 + + +============================================================================= diff --git a/tlatools/test-model/pcal/MPFactorial.tla b/tlatools/test-model/pcal/MPFactorial.tla new file mode 100644 index 0000000000000000000000000000000000000000..99001e9dbad33069862700cf92da2fd71b97193c --- /dev/null +++ b/tlatools/test-model/pcal/MPFactorial.tla @@ -0,0 +1,120 @@ +----------------------------- MODULE MPFactorial ----------------------------- +EXTENDS Naturals, Sequences, TLC + +(*************************************************************************** +--algorithm Factorial + variable result = [i \in 1..3 |-> 1]; \* are comments ok? + procedure FactProc(arg1 (* = 0 *) ) (* are comments ok? *) + (* what about (* nested multi-line *) + comments? *) + variable u = 1 ; + begin p1 : if arg1 = 0 + then return; \* HERE IS A + else result[self] := result[self] * arg1; + call FactProc ( arg1 - 1) ; + return; + end if; + end procedure + process Main \in 1..2 + begin + a1 : call FactProc( 5 ) ; + a2 : assert result[self] = 120 ; + end process + process Minor = 3 + begin + b1 : call FactProc( 5 ) ; + b2 : assert result[3] = 120 ; + end process + end algorithm +***************************************************************************) + +\* BEGIN TRANSLATION - the hash of the PCal code: PCal-1dbfa1687c2aacb174fffdcfcb1e6b08 +CONSTANT defaultInitValue +VARIABLES result, pc, stack, arg1, u + +vars == << result, pc, stack, arg1, u >> + +ProcSet == (1..2) \cup {3} + +Init == (* Global variables *) + /\ result = [i \in 1..3 |-> 1] + (* Procedure FactProc *) + /\ arg1 = [ self \in ProcSet |-> defaultInitValue] + /\ u = [ self \in ProcSet |-> 1] + /\ stack = [self \in ProcSet |-> << >>] + /\ pc = [self \in ProcSet |-> CASE self \in 1..2 -> "a1" + [] self = 3 -> "b1"] + +p1(self) == /\ pc[self] = "p1" + /\ IF arg1[self] = 0 + THEN /\ pc' = [pc EXCEPT ![self] = Head(stack[self]).pc] + /\ u' = [u EXCEPT ![self] = Head(stack[self]).u] + /\ arg1' = [arg1 EXCEPT ![self] = Head(stack[self]).arg1] + /\ stack' = [stack EXCEPT ![self] = Tail(stack[self])] + /\ UNCHANGED result + ELSE /\ result' = [result EXCEPT ![self] = result[self] * arg1[self]] + /\ arg1' = [arg1 EXCEPT ![self] = arg1[self] - 1] + /\ u' = [u EXCEPT ![self] = 1] + /\ pc' = [pc EXCEPT ![self] = "p1"] + /\ stack' = stack + +FactProc(self) == p1(self) + +a1(self) == /\ pc[self] = "a1" + /\ /\ arg1' = [arg1 EXCEPT ![self] = 5] + /\ stack' = [stack EXCEPT ![self] = << [ procedure |-> "FactProc", + pc |-> "a2", + u |-> u[self], + arg1 |-> arg1[self] ] >> + \o stack[self]] + /\ u' = [u EXCEPT ![self] = 1] + /\ pc' = [pc EXCEPT ![self] = "p1"] + /\ UNCHANGED result + +a2(self) == /\ pc[self] = "a2" + /\ Assert(result[self] = 120, + "Failure of assertion at line 21, column 10.") + /\ pc' = [pc EXCEPT ![self] = "Done"] + /\ UNCHANGED << result, stack, arg1, u >> + +Main(self) == a1(self) \/ a2(self) + +b1 == /\ pc[3] = "b1" + /\ /\ arg1' = [arg1 EXCEPT ![3] = 5] + /\ stack' = [stack EXCEPT ![3] = << [ procedure |-> "FactProc", + pc |-> "b2", + u |-> u[3], + arg1 |-> arg1[3] ] >> + \o stack[3]] + /\ u' = [u EXCEPT ![3] = 1] + /\ pc' = [pc EXCEPT ![3] = "p1"] + /\ UNCHANGED result + +b2 == /\ pc[3] = "b2" + /\ Assert(result[3] = 120, + "Failure of assertion at line 26, column 10.") + /\ pc' = [pc EXCEPT ![3] = "Done"] + /\ UNCHANGED << result, stack, arg1, u >> + +Minor == b1 \/ b2 + +(* Allow infinite stuttering to prevent deadlock on termination. *) +Terminating == /\ \A self \in ProcSet: pc[self] = "Done" + /\ UNCHANGED vars + +Next == Minor + \/ (\E self \in ProcSet: FactProc(self)) + \/ (\E self \in 1..2: Main(self)) + \/ Terminating + +Spec == /\ Init /\ [][Next]_vars + /\ \A self \in 1..2 : WF_vars(Main(self)) /\ WF_vars(FactProc(self)) + /\ WF_vars(Minor) /\ WF_vars(FactProc(3)) + +Termination == <>(\A self \in ProcSet: pc[self] = "Done") + +\* END TRANSLATION - the hash of the generated TLA code (remove to silence divergence warnings): TLA-c530d8d4ac433df112955db00ba9f4a7 + + +Invariant == result \in Nat +============================================================================= diff --git a/tlatools/test-model/pcal/MPFactorial2.tla b/tlatools/test-model/pcal/MPFactorial2.tla new file mode 100644 index 0000000000000000000000000000000000000000..e0dd136dbf500ca5742603a140db4df97cc9bf51 --- /dev/null +++ b/tlatools/test-model/pcal/MPFactorial2.tla @@ -0,0 +1,164 @@ +----------------------------- MODULE MPFactorial2 ---------------------------- +EXTENDS Naturals, Sequences, TLC + +(*************************************************************************** +Factorial Algorithm with 2 procedures + +--algorithm Factorial + variable result = [i \in 1..3 |-> 1]; + procedure FactProc(arg1 = 0 ) + variable u = 1 ; + begin p1 : if arg1 = 0 + then return; + else result[self] := result[self] * arg1; + call FactProc2 ( arg1 - 1 ) ; + b: return; + end if; + end procedure + procedure FactProc2(arg2 = 0) + variable u2 = 1 ; + begin p12 : if arg2 = 0 + then return; + else result[self] := result[self] * arg2; + call FactProc ( arg2 - 1 ) ; + return; + end if; + end procedure + process Main \in 1..2 + begin + a1 : call FactProc( 5 ) ; + a2 : assert result[self] = 120 + end process + process Minor = 3 + begin + b1 : call FactProc( 5 ) ; + b2 : assert result[3] = 120 + end process + end algorithm +***************************************************************************) + +\* BEGIN TRANSLATION - the hash of the PCal code: PCal-b4a39db84a865a6dcb14d3b14f4d88b3 +VARIABLES result, pc, stack, arg1, u, arg2, u2 + +vars == << result, pc, stack, arg1, u, arg2, u2 >> + +ProcSet == (1..2) \cup {3} + +Init == (* Global variables *) + /\ result = [i \in 1..3 |-> 1] + (* Procedure FactProc *) + /\ arg1 = [ self \in ProcSet |-> 0] + /\ u = [ self \in ProcSet |-> 1] + (* Procedure FactProc2 *) + /\ arg2 = [ self \in ProcSet |-> 0] + /\ u2 = [ self \in ProcSet |-> 1] + /\ stack = [self \in ProcSet |-> << >>] + /\ pc = [self \in ProcSet |-> CASE self \in 1..2 -> "a1" + [] self = 3 -> "b1"] + +p1(self) == /\ pc[self] = "p1" + /\ IF arg1[self] = 0 + THEN /\ pc' = [pc EXCEPT ![self] = Head(stack[self]).pc] + /\ u' = [u EXCEPT ![self] = Head(stack[self]).u] + /\ arg1' = [arg1 EXCEPT ![self] = Head(stack[self]).arg1] + /\ stack' = [stack EXCEPT ![self] = Tail(stack[self])] + /\ UNCHANGED << result, arg2, u2 >> + ELSE /\ result' = [result EXCEPT ![self] = result[self] * arg1[self]] + /\ /\ arg2' = [arg2 EXCEPT ![self] = arg1[self] - 1] + /\ stack' = [stack EXCEPT ![self] = << [ procedure |-> "FactProc2", + pc |-> "b", + u2 |-> u2[self], + arg2 |-> arg2[self] ] >> + \o stack[self]] + /\ u2' = [u2 EXCEPT ![self] = 1] + /\ pc' = [pc EXCEPT ![self] = "p12"] + /\ UNCHANGED << arg1, u >> + +b(self) == /\ pc[self] = "b" + /\ pc' = [pc EXCEPT ![self] = Head(stack[self]).pc] + /\ u' = [u EXCEPT ![self] = Head(stack[self]).u] + /\ arg1' = [arg1 EXCEPT ![self] = Head(stack[self]).arg1] + /\ stack' = [stack EXCEPT ![self] = Tail(stack[self])] + /\ UNCHANGED << result, arg2, u2 >> + +FactProc(self) == p1(self) \/ b(self) + +p12(self) == /\ pc[self] = "p12" + /\ IF arg2[self] = 0 + THEN /\ pc' = [pc EXCEPT ![self] = Head(stack[self]).pc] + /\ u2' = [u2 EXCEPT ![self] = Head(stack[self]).u2] + /\ arg2' = [arg2 EXCEPT ![self] = Head(stack[self]).arg2] + /\ stack' = [stack EXCEPT ![self] = Tail(stack[self])] + /\ UNCHANGED << result, arg1, u >> + ELSE /\ result' = [result EXCEPT ![self] = result[self] * arg2[self]] + /\ /\ arg1' = [arg1 EXCEPT ![self] = arg2[self] - 1] + /\ stack' = [stack EXCEPT ![self] = << [ procedure |-> "FactProc", + pc |-> Head(stack[self]).pc, + u |-> u[self], + arg1 |-> arg1[self] ] >> + \o Tail(stack[self])] + /\ u2' = [u2 EXCEPT ![self] = Head(stack[self]).u2] + /\ u' = [u EXCEPT ![self] = 1] + /\ pc' = [pc EXCEPT ![self] = "p1"] + /\ arg2' = arg2 + +FactProc2(self) == p12(self) + +a1(self) == /\ pc[self] = "a1" + /\ /\ arg1' = [arg1 EXCEPT ![self] = 5] + /\ stack' = [stack EXCEPT ![self] = << [ procedure |-> "FactProc", + pc |-> "a2", + u |-> u[self], + arg1 |-> arg1[self] ] >> + \o stack[self]] + /\ u' = [u EXCEPT ![self] = 1] + /\ pc' = [pc EXCEPT ![self] = "p1"] + /\ UNCHANGED << result, arg2, u2 >> + +a2(self) == /\ pc[self] = "a2" + /\ Assert(result[self] = 120, + "Failure of assertion at line 30, column 10.") + /\ pc' = [pc EXCEPT ![self] = "Done"] + /\ UNCHANGED << result, stack, arg1, u, arg2, u2 >> + +Main(self) == a1(self) \/ a2(self) + +b1 == /\ pc[3] = "b1" + /\ /\ arg1' = [arg1 EXCEPT ![3] = 5] + /\ stack' = [stack EXCEPT ![3] = << [ procedure |-> "FactProc", + pc |-> "b2", + u |-> u[3], + arg1 |-> arg1[3] ] >> + \o stack[3]] + /\ u' = [u EXCEPT ![3] = 1] + /\ pc' = [pc EXCEPT ![3] = "p1"] + /\ UNCHANGED << result, arg2, u2 >> + +b2 == /\ pc[3] = "b2" + /\ Assert(result[3] = 120, + "Failure of assertion at line 35, column 10.") + /\ pc' = [pc EXCEPT ![3] = "Done"] + /\ UNCHANGED << result, stack, arg1, u, arg2, u2 >> + +Minor == b1 \/ b2 + +(* Allow infinite stuttering to prevent deadlock on termination. *) +Terminating == /\ \A self \in ProcSet: pc[self] = "Done" + /\ UNCHANGED vars + +Next == Minor + \/ (\E self \in ProcSet: FactProc(self) \/ FactProc2(self)) + \/ (\E self \in 1..2: Main(self)) + \/ Terminating + +Spec == /\ Init /\ [][Next]_vars + /\ \A self \in 1..2 : WF_vars(Main(self)) /\ WF_vars(FactProc(self)) /\ WF_vars(FactProc2(self)) + /\ WF_vars(Minor) /\ WF_vars(FactProc(3)) /\ WF_vars(FactProc2(3)) + +Termination == <>(\A self \in ProcSet: pc[self] = "Done") + +\* END TRANSLATION - the hash of the generated TLA code (remove to silence divergence warnings): TLA-81af4b8b8e1c44a573ab6b21fa7843b0 + + +Invariant == result \in Nat +============================================================================= diff --git a/tlatools/test-model/pcal/MPNoParams.tla b/tlatools/test-model/pcal/MPNoParams.tla new file mode 100644 index 0000000000000000000000000000000000000000..8f71899be5288284eee52236b4c609caa119ce60 --- /dev/null +++ b/tlatools/test-model/pcal/MPNoParams.tla @@ -0,0 +1,92 @@ +----------------------------- MODULE MPNoParams ------------------------------ +EXTENDS Sequences, Naturals, TLC + +(* +--algorithm MPNoParams + variables sum = 0; + + procedure Sum () + begin s1: sum := sum + 1; + return; + end procedure; + process P1 = 1 + begin p1 : call Sum(); + p2 : when sum = 4 ; + end process + process P2 \in 2..4 + begin + q1 : call Sum(); + q2 : when sum = 4 ; + end process + end algorithm + + +*) + +\* BEGIN TRANSLATION - the hash of the PCal code: PCal-deb6c76a28ce9358817b0dff2194ebab +VARIABLES sum, pc, stack + +vars == << sum, pc, stack >> + +ProcSet == {1} \cup (2..4) + +Init == (* Global variables *) + /\ sum = 0 + /\ stack = [self \in ProcSet |-> << >>] + /\ pc = [self \in ProcSet |-> CASE self = 1 -> "p1" + [] self \in 2..4 -> "q1"] + +s1(self) == /\ pc[self] = "s1" + /\ sum' = sum + 1 + /\ pc' = [pc EXCEPT ![self] = Head(stack[self]).pc] + /\ stack' = [stack EXCEPT ![self] = Tail(stack[self])] + +Sum(self) == s1(self) + +p1 == /\ pc[1] = "p1" + /\ stack' = [stack EXCEPT ![1] = << [ procedure |-> "Sum", + pc |-> "p2" ] >> + \o stack[1]] + /\ pc' = [pc EXCEPT ![1] = "s1"] + /\ sum' = sum + +p2 == /\ pc[1] = "p2" + /\ sum = 4 + /\ pc' = [pc EXCEPT ![1] = "Done"] + /\ UNCHANGED << sum, stack >> + +P1 == p1 \/ p2 + +q1(self) == /\ pc[self] = "q1" + /\ stack' = [stack EXCEPT ![self] = << [ procedure |-> "Sum", + pc |-> "q2" ] >> + \o stack[self]] + /\ pc' = [pc EXCEPT ![self] = "s1"] + /\ sum' = sum + +q2(self) == /\ pc[self] = "q2" + /\ sum = 4 + /\ pc' = [pc EXCEPT ![self] = "Done"] + /\ UNCHANGED << sum, stack >> + +P2(self) == q1(self) \/ q2(self) + +(* Allow infinite stuttering to prevent deadlock on termination. *) +Terminating == /\ \A self \in ProcSet: pc[self] = "Done" + /\ UNCHANGED vars + +Next == P1 + \/ (\E self \in ProcSet: Sum(self)) + \/ (\E self \in 2..4: P2(self)) + \/ Terminating + +Spec == /\ Init /\ [][Next]_vars + /\ WF_vars(P1) /\ WF_vars(Sum(1)) + /\ \A self \in 2..4 : WF_vars(P2(self)) /\ WF_vars(Sum(self)) + +Termination == <>(\A self \in ProcSet: pc[self] = "Done") + +\* END TRANSLATION - the hash of the generated TLA code (remove to silence divergence warnings): TLA-714bbad3547f7f99ec47cfca79fad75e + + +============================================================================= diff --git a/tlatools/test-model/pcal/MacroQuicksort.cfg b/tlatools/test-model/pcal/MacroQuicksort.cfg new file mode 100644 index 0000000000000000000000000000000000000000..a662e03b0eb6080164b084439f2edc97a0050a0d --- /dev/null +++ b/tlatools/test-model/pcal/MacroQuicksort.cfg @@ -0,0 +1,4 @@ +SPECIFICATION Spec +PROPERTY Termination +\* Add statements after this line. +CONSTANT ArrayLen = 4 diff --git a/tlatools/test-model/pcal/MacroQuicksort.tla b/tlatools/test-model/pcal/MacroQuicksort.tla new file mode 100644 index 0000000000000000000000000000000000000000..42a91b4aa35418a765a8204c0c9057c507711575 --- /dev/null +++ b/tlatools/test-model/pcal/MacroQuicksort.tla @@ -0,0 +1,146 @@ +---------------------------- MODULE MacroQuicksort --------------------------- +EXTENDS Naturals, Sequences, TLC + +CONSTANT ArrayLen + +ASSUME ArrayLen \in Nat + +PermsOf(Arr) == + LET Automorphism(S) == { f \in [S -> S] : + \A y \in S : \E x \in S : f[x] = y } + f ** g == [x \in DOMAIN g |-> f[g[x]]] + IN { Arr ** f : f \in Automorphism(DOMAIN Arr) } + +(* +--algorithm Quicksort + variables Ainit \in [1..ArrayLen -> 1..ArrayLen]; A = Ainit; + macro Partition(pivot, lo, hi) + begin with piv \in lo..(hi-1) + do pivot := piv ; + with Ap \in + {AA \in PermsOf(A) : + (\A i \in 1..(lo-1) : AA[i] = A[i]) + /\ (\A i \in (hi+1)..Len(A) : AA[i] = A[i]) + /\ (\A i \in lo..piv, j \in (piv+1)..hi : + AA[i] \leq AA[j])} + do A := Ap; + end with ; + end with; + end macro + procedure QS(qlo = 1, qhi = 1) + variable pivot = 1 ; + begin qs1 : if qlo < qhi + then Partition(pivot, qlo, qhi) ; + qs2 : call QS(qlo, pivot) ; + qs3 : call QS(pivot +1,qhi) ; + end if; + qs4 : return ; + end procedure + begin main : call QS(1, Len(A)) ; + test : assert A \in PermsOf(Ainit) + /\ \A i, j \in 1..ArrayLen : + (i < j) => A[i] \leq A[j] + end algorithm +*) + +\* BEGIN TRANSLATION +VARIABLES Ainit, A, pc, stack, qlo, qhi, pivot + +vars == << Ainit, A, pc, stack, qlo, qhi, pivot >> + +Init == (* Global variables *) + /\ Ainit \in [1..ArrayLen -> 1..ArrayLen] + /\ A = Ainit + (* Procedure QS *) + /\ qlo = 1 + /\ qhi = 1 + /\ pivot = 1 + /\ stack = << >> + /\ pc = "main" + +qs1 == /\ pc = "qs1" + /\ IF qlo < qhi + THEN /\ \E piv \in qlo..(qhi-1): + /\ pivot' = piv + /\ \E Ap \in {AA \in PermsOf(A) : + (\A i \in 1..(qlo-1) : AA[i] = A[i]) + /\ (\A i \in (qhi+1)..Len(A) : AA[i] = A[i]) + /\ (\A i \in qlo..piv, j \in (piv+1)..qhi : + AA[i] \leq AA[j])}: + A' = Ap + /\ pc' = "qs2" + ELSE /\ pc' = "qs4" + /\ UNCHANGED << A, pivot >> + /\ UNCHANGED << Ainit, stack, qlo, qhi >> + +qs2 == /\ pc = "qs2" + /\ /\ qhi' = pivot + /\ qlo' = qlo + /\ stack' = << [ procedure |-> "QS", + pc |-> "qs3", + pivot |-> pivot, + qlo |-> qlo, + qhi |-> qhi ] >> + \o stack + /\ pivot' = 1 + /\ pc' = "qs1" + /\ UNCHANGED << Ainit, A >> + +qs3 == /\ pc = "qs3" + /\ /\ qhi' = qhi + /\ qlo' = pivot +1 + /\ stack' = << [ procedure |-> "QS", + pc |-> "qs4", + pivot |-> pivot, + qlo |-> qlo, + qhi |-> qhi ] >> + \o stack + /\ pivot' = 1 + /\ pc' = "qs1" + /\ UNCHANGED << Ainit, A >> + +qs4 == /\ pc = "qs4" + /\ pc' = Head(stack).pc + /\ pivot' = Head(stack).pivot + /\ qlo' = Head(stack).qlo + /\ qhi' = Head(stack).qhi + /\ stack' = Tail(stack) + /\ UNCHANGED << Ainit, A >> + +QS == qs1 \/ qs2 \/ qs3 \/ qs4 + +main == /\ pc = "main" + /\ /\ qhi' = Len(A) + /\ qlo' = 1 + /\ stack' = << [ procedure |-> "QS", + pc |-> "test", + pivot |-> pivot, + qlo |-> qlo, + qhi |-> qhi ] >> + \o stack + /\ pivot' = 1 + /\ pc' = "qs1" + /\ UNCHANGED << Ainit, A >> + +test == /\ pc = "test" + /\ Assert( A \in PermsOf(Ainit) + /\ \A i, j \in 1..ArrayLen : + (i < j) => A[i] \leq A[j], + "Failure of assertion at line 40, column 17.") + /\ pc' = "Done" + /\ UNCHANGED << Ainit, A, stack, qlo, qhi, pivot >> + +Next == QS \/ main \/ test + \/ (* Disjunct to prevent deadlock on termination *) + (pc = "Done" /\ UNCHANGED vars) + +Spec == /\ Init /\ [][Next]_vars + /\ WF_vars(Next) + +Termination == <>(pc = "Done") + +\* END TRANSLATION +============================================================================= +Checked without termination on svc-lamport-2 with 2 workers: + arrayLen = 4 in 15 sec, 32280 states, depth 22 + arrayLen = 5 in 138min 17 sec 1529005 states, depth 28 diff --git a/tlatools/test-model/pcal/MacroRealQuicksort.cfg b/tlatools/test-model/pcal/MacroRealQuicksort.cfg new file mode 100644 index 0000000000000000000000000000000000000000..672c5ec21152f6c0135cca7850d5415e163e2259 --- /dev/null +++ b/tlatools/test-model/pcal/MacroRealQuicksort.cfg @@ -0,0 +1,4 @@ +SPECIFICATION Spec +PROPERTY Termination +\* Add statements after this line. +CONSTANT N = 4 diff --git a/tlatools/test-model/pcal/MacroRealQuicksort.tla b/tlatools/test-model/pcal/MacroRealQuicksort.tla new file mode 100644 index 0000000000000000000000000000000000000000..4c52cbc72a1d3a284942993e188479da9251f130 --- /dev/null +++ b/tlatools/test-model/pcal/MacroRealQuicksort.tla @@ -0,0 +1,90 @@ +-------------------------- MODULE MacroRealQuicksort ------------------------- +EXTENDS Naturals, Sequences, TLC + +CONSTANT N + +ASSUME N \in Nat + +PermsOf(Arr) == + LET Automorphism(S) == { f \in [S -> S] : + \A y \in S : \E x \in S : f[x] = y } + f ** g == [x \in DOMAIN g |-> f[g[x]]] + IN { Arr ** f : f \in Automorphism(DOMAIN Arr) } + +(* +--algorithm Quicksort + variables Ainit \in [1..N -> 1..N]; A = Ainit; + S = {<<1,N>>} ; pivot = 1 ; + macro Partition(pivot, lo, hi) + begin with piv \in lo..(hi-1) + do pivot := piv ; + with Ap \in + {AA \in PermsOf(A) : + (\A i \in 1..(lo-1) : AA[i] = A[i]) + /\ (\A i \in (hi+1)..Len(A) : AA[i] = A[i]) + /\ (\A i \in lo..piv, j \in (piv+1)..hi : + AA[i] \leq AA[j])} + do A := Ap; + end with ; + end with; + end macro + begin qs1 : while S # {} + do with I \in S + do if I[1] < I[2] + then Partition(pivot, I[1], I[2]) ; + S := (S \ {I}) + \cup {<<I[1], pivot>>, <<pivot+1, I[2]>>} + else S := (S \ {I}) + end if + end with + end while + end algorithm +*) + +\* BEGIN TRANSLATION +VARIABLES Ainit, A, S, pivot, pc + +vars == << Ainit, A, S, pivot, pc >> + +Init == (* Global variables *) + /\ Ainit \in [1..N -> 1..N] + /\ A = Ainit + /\ S = {<<1,N>>} + /\ pivot = 1 + /\ pc = "qs1" + +qs1 == /\ pc = "qs1" + /\ IF S # {} + THEN /\ \E I \in S: + IF I[1] < I[2] + THEN /\ \E piv \in (I[1])..((I[2])-1): + /\ pivot' = piv + /\ \E Ap \in {AA \in PermsOf(A) : + (\A i \in 1..((I[1])-1) : AA[i] = A[i]) + /\ (\A i \in ((I[2])+1)..Len(A) : AA[i] = A[i]) + /\ (\A i \in (I[1])..piv, j \in (piv+1)..(I[2]) : + AA[i] \leq AA[j])}: + A' = Ap + /\ S' = (S \ {I}) + \cup {<<I[1], pivot'>>, <<pivot'+1, I[2]>>} + ELSE /\ S' = (S \ {I}) + /\ UNCHANGED << A, pivot >> + /\ pc' = "qs1" + ELSE /\ pc' = "Done" + /\ UNCHANGED << A, S, pivot >> + /\ Ainit' = Ainit + +Next == qs1 + \/ (* Disjunct to prevent deadlock on termination *) + (pc = "Done" /\ UNCHANGED vars) + +Spec == /\ Init /\ [][Next]_vars + /\ WF_vars(Next) + +Termination == <>(pc = "Done") + +\* END TRANSLATION +============================================================================= +Checked without termination on svc-lamport-2 with 2 workers: + arrayLen = 4 in 15 sec, 32280 states, depth 22 + arrayLen = 5 in 138min 17 sec 1529005 states, depth 28 diff --git a/tlatools/test-model/pcal/MergeSort.cfg b/tlatools/test-model/pcal/MergeSort.cfg new file mode 100644 index 0000000000000000000000000000000000000000..fef7332dcd82181639a8ebe35c5a84e7458712e0 --- /dev/null +++ b/tlatools/test-model/pcal/MergeSort.cfg @@ -0,0 +1,6 @@ +SPECIFICATION Spec +PROPERTY Termination +CONSTANT defaultInitValue = defaultInitValue +\* Add statements after this line. +CONSTANTS ArrayLen = 2 +INVARIANT Invariant diff --git a/tlatools/test-model/pcal/MergeSort.tla b/tlatools/test-model/pcal/MergeSort.tla new file mode 100644 index 0000000000000000000000000000000000000000..a9df3645e7660041a3759abc9ab418acb1cab780 --- /dev/null +++ b/tlatools/test-model/pcal/MergeSort.tla @@ -0,0 +1,248 @@ +------------------------------- MODULE MergeSort ----------------------------- +EXTENDS Naturals, Sequences, TLC + +CONSTANT ArrayLen + +ASSUME ArrayLen \in Nat + +ASSUME Print(<<"Testing Mergesort on all arrays of length <= ", ArrayLen>>, + TRUE) + +PermsOf(Arr) == + LET Automorphism(S) == { f \in [S -> S] : + \A y \in S : \E x \in S : f[x] = y } + f ** g == [x \in DOMAIN g |-> f[g[x]]] + IN { Arr ** f : f \in Automorphism(DOMAIN Arr) } + +(* + +Copied from page 166 of the 2nd edition of Robert Sedgewick's "Algorithms". + +--algorithm Mergesort + variables a \in UNION {[1..N -> 1..N] : N \in 0..ArrayLen} ; + b = [x \in DOMAIN a |-> 99] \* ; + procedure mergesort(l, r) + variables i ; j ; k ; m \* ; + begin l1: if r - l > 0 + then m := (r + l) \div 2 ; + l2 : call mergesort(l, m) ; + l3 : call mergesort(m+1, r) ; + l4 : i := m ; + l5 : while i \geq l + do b[i] := a[i]; + i := i - 1 \* ; + end while ; + (*************************************************) + (* I don't know what the Pascal statement *) + (* *) + (* for i := m downto l ... *) + (* *) + (* is supposed to set i to if m < l, so the *) + (* algorithm reports an error and stops in this *) + (*************************************************) + if m \geq l + then i := l ; + else print "not sure of semantics of Pascal" ; + i := CHOOSE x \in {} : FALSE ; + end if ; + j := m + 1 ; + l6 : while j \leq r + do b[r + m + 1 - j] := a[j] ; + j := j + 1 ; + end while ; + (*************************************************) + (* I don't know what the Pascal statement *) + (* *) + (* for j := m+1 to r ... *) + (* *) + (* is supposed to set j to if m+1 < r, so the *) + (* algorithm reports an error and stops in this *) + (*************************************************) + if m+1 \leq r + then j := r ; + else print "not sure of semantics of Pascal" ; + i := CHOOSE x \in {} : FALSE ; + end if ; + k := l ; + l7 : while k \leq r + do if b[i] < b[j] + then a[k] := b[i] ; + i := i + 1 ; + else a[k] := b[j] ; + j := j - 1 ; + end if ; + k := k + 1 ; + end while ; + end if ; + l8: return ; + end procedure + begin main : call mergesort (1, Len(a)) ; + end algorithm +*) + +\* BEGIN TRANSLATION - the hash of the PCal code: PCal-3f24d15c1de7a8bf341d55378b5a76c0 +CONSTANT defaultInitValue +VARIABLES a, b, pc, stack, l, r, i, j, k, m + +vars == << a, b, pc, stack, l, r, i, j, k, m >> + +Init == (* Global variables *) + /\ a \in UNION {[1..N -> 1..N] : N \in 0..ArrayLen} + /\ b = [x \in DOMAIN a |-> 99] + (* Procedure mergesort *) + /\ l = defaultInitValue + /\ r = defaultInitValue + /\ i = defaultInitValue + /\ j = defaultInitValue + /\ k = defaultInitValue + /\ m = defaultInitValue + /\ stack = << >> + /\ pc = "main" + +l1 == /\ pc = "l1" + /\ IF r - l > 0 + THEN /\ m' = ((r + l) \div 2) + /\ pc' = "l2" + ELSE /\ pc' = "l8" + /\ m' = m + /\ UNCHANGED << a, b, stack, l, r, i, j, k >> + +l2 == /\ pc = "l2" + /\ /\ l' = l + /\ r' = m + /\ stack' = << [ procedure |-> "mergesort", + pc |-> "l3", + i |-> i, + j |-> j, + k |-> k, + m |-> m, + l |-> l, + r |-> r ] >> + \o stack + /\ i' = defaultInitValue + /\ j' = defaultInitValue + /\ k' = defaultInitValue + /\ m' = defaultInitValue + /\ pc' = "l1" + /\ UNCHANGED << a, b >> + +l3 == /\ pc = "l3" + /\ /\ l' = m+1 + /\ r' = r + /\ stack' = << [ procedure |-> "mergesort", + pc |-> "l4", + i |-> i, + j |-> j, + k |-> k, + m |-> m, + l |-> l, + r |-> r ] >> + \o stack + /\ i' = defaultInitValue + /\ j' = defaultInitValue + /\ k' = defaultInitValue + /\ m' = defaultInitValue + /\ pc' = "l1" + /\ UNCHANGED << a, b >> + +l4 == /\ pc = "l4" + /\ i' = m + /\ pc' = "l5" + /\ UNCHANGED << a, b, stack, l, r, j, k, m >> + +l5 == /\ pc = "l5" + /\ IF i \geq l + THEN /\ b' = [b EXCEPT ![i] = a[i]] + /\ i' = i - 1 + /\ pc' = "l5" + /\ j' = j + ELSE /\ IF m \geq l + THEN /\ i' = l + ELSE /\ PrintT("not sure of semantics of Pascal") + /\ i' = (CHOOSE x \in {} : FALSE) + /\ j' = m + 1 + /\ pc' = "l6" + /\ b' = b + /\ UNCHANGED << a, stack, l, r, k, m >> + +l6 == /\ pc = "l6" + /\ IF j \leq r + THEN /\ b' = [b EXCEPT ![r + m + 1 - j] = a[j]] + /\ j' = j + 1 + /\ pc' = "l6" + /\ UNCHANGED << i, k >> + ELSE /\ IF m+1 \leq r + THEN /\ j' = r + /\ i' = i + ELSE /\ PrintT("not sure of semantics of Pascal") + /\ i' = (CHOOSE x \in {} : FALSE) + /\ j' = j + /\ k' = l + /\ pc' = "l7" + /\ b' = b + /\ UNCHANGED << a, stack, l, r, m >> + +l7 == /\ pc = "l7" + /\ IF k \leq r + THEN /\ IF b[i] < b[j] + THEN /\ a' = [a EXCEPT ![k] = b[i]] + /\ i' = i + 1 + /\ j' = j + ELSE /\ a' = [a EXCEPT ![k] = b[j]] + /\ j' = j - 1 + /\ i' = i + /\ k' = k + 1 + /\ pc' = "l7" + ELSE /\ pc' = "l8" + /\ UNCHANGED << a, i, j, k >> + /\ UNCHANGED << b, stack, l, r, m >> + +l8 == /\ pc = "l8" + /\ pc' = Head(stack).pc + /\ i' = Head(stack).i + /\ j' = Head(stack).j + /\ k' = Head(stack).k + /\ m' = Head(stack).m + /\ l' = Head(stack).l + /\ r' = Head(stack).r + /\ stack' = Tail(stack) + /\ UNCHANGED << a, b >> + +mergesort == l1 \/ l2 \/ l3 \/ l4 \/ l5 \/ l6 \/ l7 \/ l8 + +main == /\ pc = "main" + /\ /\ l' = 1 + /\ r' = Len(a) + /\ stack' = << [ procedure |-> "mergesort", + pc |-> "Done", + i |-> i, + j |-> j, + k |-> k, + m |-> m, + l |-> l, + r |-> r ] >> + \o stack + /\ i' = defaultInitValue + /\ j' = defaultInitValue + /\ k' = defaultInitValue + /\ m' = defaultInitValue + /\ pc' = "l1" + /\ UNCHANGED << a, b >> + +(* Allow infinite stuttering to prevent deadlock on termination. *) +Terminating == pc = "Done" /\ UNCHANGED vars + +Next == mergesort \/ main + \/ Terminating + +Spec == /\ Init /\ [][Next]_vars + /\ WF_vars(Next) + +Termination == <>(pc = "Done") + +\* END TRANSLATION - the hash of the generated TLA code (remove to silence divergence warnings): TLA-ff6c7e89b9c9baac60a504f5cbacb5a2 + +Invariant == + (pc = "Done") => \A x, y \in DOMAIN a : + (x < y) => a[x] \leq a[y] +============================================================================= diff --git a/tlatools/test-model/pcal/MultiAssignment.tla b/tlatools/test-model/pcal/MultiAssignment.tla new file mode 100644 index 0000000000000000000000000000000000000000..2f2732c089d1af1913d7cd66c05b7cd5b4355224 --- /dev/null +++ b/tlatools/test-model/pcal/MultiAssignment.tla @@ -0,0 +1,65 @@ +--------------------------- MODULE MultiAssignment -------------------------- +(***************************************************************************) +(* A test of multi-assignment statements with arrays. *) +(***************************************************************************) + +EXTENDS Naturals, TLC + + + +(***** + + +--algorithm MultiAssignment + process Proc \in 1..3 + variables A = [i \in 1..5 |-> i] ; x = 0 ; + begin a : A[1] := A[3] || x := 7 || A[3] := A[1] ; + assert <<3 , 1>> = <<A[1], A[3]>> ; + b : assert <<3 , 1>> = <<A[1], A[3]>> ; + end process + end algorithm + +****) + +\* BEGIN TRANSLATION - the hash of the PCal code: PCal-922bd40bbb66854d6ea0cf6dbef935cb +VARIABLES pc, A, x + +vars == << pc, A, x >> + +ProcSet == (1..3) + +Init == (* Process Proc *) + /\ A = [self \in 1..3 |-> [i \in 1..5 |-> i]] + /\ x = [self \in 1..3 |-> 0] + /\ pc = [self \in ProcSet |-> "a"] + +a(self) == /\ pc[self] = "a" + /\ /\ A' = [A EXCEPT ![self][1] = A[self][3], + ![self][3] = A[self][1]] + /\ x' = [x EXCEPT ![self] = 7] + /\ Assert(<<3 , 1>> = <<A'[self][1], A'[self][3]>>, + "Failure of assertion at line 17, column 13.") + /\ pc' = [pc EXCEPT ![self] = "b"] + +b(self) == /\ pc[self] = "b" + /\ Assert(<<3 , 1>> = <<A[self][1], A[self][3]>>, + "Failure of assertion at line 18, column 13.") + /\ pc' = [pc EXCEPT ![self] = "Done"] + /\ UNCHANGED << A, x >> + +Proc(self) == a(self) \/ b(self) + +(* Allow infinite stuttering to prevent deadlock on termination. *) +Terminating == /\ \A self \in ProcSet: pc[self] = "Done" + /\ UNCHANGED vars + +Next == (\E self \in 1..3: Proc(self)) + \/ Terminating + +Spec == /\ Init /\ [][Next]_vars + /\ \A self \in 1..3 : WF_vars(Proc(self)) + +Termination == <>(\A self \in ProcSet: pc[self] = "Done") + +\* END TRANSLATION - the hash of the generated TLA code (remove to silence divergence warnings): TLA-6c5ff91175ea2e80dc22b1dde5479e58 +============================================================================= diff --git a/tlatools/test-model/pcal/MultiProc2.tla b/tlatools/test-model/pcal/MultiProc2.tla new file mode 100644 index 0000000000000000000000000000000000000000..136c0c3110b13afedcb0b59737fbedcbd1a43f56 --- /dev/null +++ b/tlatools/test-model/pcal/MultiProc2.tla @@ -0,0 +1,131 @@ +----------------------------- MODULE MultiProc2 ---------------------------- +EXTENDS Sequences, Naturals, TLC + +(* + + --algorithm MultiProc2 + variables + x = [i \in ProcSet |-> CASE i = 41 -> 1 [] + i = 42 -> 2 [] + i = 43 -> 3]; + sum = 0 ; + done = {}; + procedure Sum(arg = 0) + variable u = 1 ; + begin p1 : u := u + arg ; + p2 : sum := sum + u; + return; + end procedure + process ProcA = 41 + variable y = 0 ; + begin a1 : call Sum( x [ 41 ] + y ) ; + a2 : done := done \cup { 41 } ; + a3 : when done = { 41, 42, 43 } ; + when Print ( sum , TRUE ) ; + end process + process ProcB \in {42, 43} + variable z \in {2, 3} ; + begin b1 : call Sum ( x [ self ] + z ) ; + b2 : done := done \cup { self } ; + end process + end algorithm +*) + +\* BEGIN TRANSLATION - the hash of the PCal code: PCal-6a3f53faa6a08b14086cf6736a14399e +VARIABLES x, sum, done, pc, stack, arg, u, y, z + +vars == << x, sum, done, pc, stack, arg, u, y, z >> + +ProcSet == {41} \cup ({42, 43}) + +Init == (* Global variables *) + /\ x = [i \in ProcSet |-> CASE i = 41 -> 1 [] + i = 42 -> 2 [] + i = 43 -> 3] + /\ sum = 0 + /\ done = {} + (* Procedure Sum *) + /\ arg = [ self \in ProcSet |-> 0] + /\ u = [ self \in ProcSet |-> 1] + (* Process ProcA *) + /\ y = 0 + (* Process ProcB *) + /\ z \in [{42, 43} -> {2, 3}] + /\ stack = [self \in ProcSet |-> << >>] + /\ pc = [self \in ProcSet |-> CASE self = 41 -> "a1" + [] self \in {42, 43} -> "b1"] + +p1(self) == /\ pc[self] = "p1" + /\ u' = [u EXCEPT ![self] = u[self] + arg[self]] + /\ pc' = [pc EXCEPT ![self] = "p2"] + /\ UNCHANGED << x, sum, done, stack, arg, y, z >> + +p2(self) == /\ pc[self] = "p2" + /\ sum' = sum + u[self] + /\ pc' = [pc EXCEPT ![self] = Head(stack[self]).pc] + /\ u' = [u EXCEPT ![self] = Head(stack[self]).u] + /\ arg' = [arg EXCEPT ![self] = Head(stack[self]).arg] + /\ stack' = [stack EXCEPT ![self] = Tail(stack[self])] + /\ UNCHANGED << x, done, y, z >> + +Sum(self) == p1(self) \/ p2(self) + +a1 == /\ pc[41] = "a1" + /\ /\ arg' = [arg EXCEPT ![41] = x [ 41 ] + y] + /\ stack' = [stack EXCEPT ![41] = << [ procedure |-> "Sum", + pc |-> "a2", + u |-> u[41], + arg |-> arg[41] ] >> + \o stack[41]] + /\ u' = [u EXCEPT ![41] = 1] + /\ pc' = [pc EXCEPT ![41] = "p1"] + /\ UNCHANGED << x, sum, done, y, z >> + +a2 == /\ pc[41] = "a2" + /\ done' = (done \cup { 41 }) + /\ pc' = [pc EXCEPT ![41] = "a3"] + /\ UNCHANGED << x, sum, stack, arg, u, y, z >> + +a3 == /\ pc[41] = "a3" + /\ done = { 41, 42, 43 } + /\ Print ( sum , TRUE ) + /\ pc' = [pc EXCEPT ![41] = "Done"] + /\ UNCHANGED << x, sum, done, stack, arg, u, y, z >> + +ProcA == a1 \/ a2 \/ a3 + +b1(self) == /\ pc[self] = "b1" + /\ /\ arg' = [arg EXCEPT ![self] = x [ self ] + z[self]] + /\ stack' = [stack EXCEPT ![self] = << [ procedure |-> "Sum", + pc |-> "b2", + u |-> u[self], + arg |-> arg[self] ] >> + \o stack[self]] + /\ u' = [u EXCEPT ![self] = 1] + /\ pc' = [pc EXCEPT ![self] = "p1"] + /\ UNCHANGED << x, sum, done, y, z >> + +b2(self) == /\ pc[self] = "b2" + /\ done' = (done \cup { self }) + /\ pc' = [pc EXCEPT ![self] = "Done"] + /\ UNCHANGED << x, sum, stack, arg, u, y, z >> + +ProcB(self) == b1(self) \/ b2(self) + +(* Allow infinite stuttering to prevent deadlock on termination. *) +Terminating == /\ \A self \in ProcSet: pc[self] = "Done" + /\ UNCHANGED vars + +Next == ProcA + \/ (\E self \in ProcSet: Sum(self)) + \/ (\E self \in {42, 43}: ProcB(self)) + \/ Terminating + +Spec == /\ Init /\ [][Next]_vars + /\ WF_vars(ProcA) /\ WF_vars(Sum(41)) + /\ \A self \in {42, 43} : WF_vars(ProcB(self)) /\ WF_vars(Sum(self)) + +Termination == <>(\A self \in ProcSet: pc[self] = "Done") + +\* END TRANSLATION - the hash of the generated TLA code (remove to silence divergence warnings): TLA-3f17bd1aee44cca22b14ef7de16f1390 +============================================================================= diff --git a/tlatools/test-model/pcal/MultiprocDefine.tla b/tlatools/test-model/pcal/MultiprocDefine.tla new file mode 100644 index 0000000000000000000000000000000000000000..535bbde59c7a17b18e82800b5dbfe3a863ffab55 --- /dev/null +++ b/tlatools/test-model/pcal/MultiprocDefine.tla @@ -0,0 +1,59 @@ +---------------------------- MODULE MultiprocDefine -------------------------- +EXTENDS Naturals, Sequences, TLC + +(* +--algorithm MultiprocDefine + variables n = 0 ; + define nplus1 == n + 1 + nplus2 == nplus1 + 1 + end define ; + process Proc \in {1, 2, 3} + variables i ; + begin main : i := nplus2 ; + assert i = 2 ; + end process + end algorithm +*) + +\* BEGIN TRANSLATION - the hash of the PCal code: PCal-255a10565612be8265861699149ea328 +CONSTANT defaultInitValue +VARIABLES n, pc + +(* define statement *) +nplus1 == n + 1 +nplus2 == nplus1 + 1 + +VARIABLE i + +vars == << n, pc, i >> + +ProcSet == ({1, 2, 3}) + +Init == (* Global variables *) + /\ n = 0 + (* Process Proc *) + /\ i = [self \in {1, 2, 3} |-> defaultInitValue] + /\ pc = [self \in ProcSet |-> "main"] + +main(self) == /\ pc[self] = "main" + /\ i' = [i EXCEPT ![self] = nplus2] + /\ Assert(i'[self] = 2, + "Failure of assertion at line 13, column 17.") + /\ pc' = [pc EXCEPT ![self] = "Done"] + /\ n' = n + +Proc(self) == main(self) + +(* Allow infinite stuttering to prevent deadlock on termination. *) +Terminating == /\ \A self \in ProcSet: pc[self] = "Done" + /\ UNCHANGED vars + +Next == (\E self \in {1, 2, 3}: Proc(self)) + \/ Terminating + +Spec == Init /\ [][Next]_vars + +Termination == <>(\A self \in ProcSet: pc[self] = "Done") + +\* END TRANSLATION - the hash of the generated TLA code (remove to silence divergence warnings): TLA-0c8f6e25783592e29e1972ef47f12501 +============================================================================= diff --git a/tlatools/test-model/pcal/NestedMacros.tla b/tlatools/test-model/pcal/NestedMacros.tla new file mode 100644 index 0000000000000000000000000000000000000000..b03dee89a8f9867321b4ff5679c1dba9b0709330 --- /dev/null +++ b/tlatools/test-model/pcal/NestedMacros.tla @@ -0,0 +1,72 @@ +----------------------------- MODULE NestedMacros --------------------------- +EXTENDS Naturals, TLC + +\* PlusCal options(label) +(*************************************************************************** +--algorithm Test { + variables x, y ; + + macro ff(a, b) { + a := b + } + macro foo(a) { + ff(z,a); + y := a + } + + macro bar(b) { + x := b; + foo(22) + } + process (foob \in {1,2}) + variable z ; + { l1 : z := 0 ; + l2 : bar(42); + assert z = 22 /\ x = 42 + } +} + ***************************************************************************) +\* BEGIN TRANSLATION - the hash of the PCal code: PCal-45eef21f51ec0ad28fe46e4b9a7b10ce +CONSTANT defaultInitValue +VARIABLES x, y, pc, z + +vars == << x, y, pc, z >> + +ProcSet == ({1,2}) + +Init == (* Global variables *) + /\ x = defaultInitValue + /\ y = defaultInitValue + (* Process foob *) + /\ z = [self \in {1,2} |-> defaultInitValue] + /\ pc = [self \in ProcSet |-> "l1"] + +l1(self) == /\ pc[self] = "l1" + /\ z' = [z EXCEPT ![self] = 0] + /\ pc' = [pc EXCEPT ![self] = "l2"] + /\ UNCHANGED << x, y >> + +l2(self) == /\ pc[self] = "l2" + /\ x' = 42 + /\ z' = [z EXCEPT ![self] = 22] + /\ y' = 22 + /\ Assert(z'[self] = 22 /\ x' = 42, + "Failure of assertion at line 25, column 11.") + /\ pc' = [pc EXCEPT ![self] = "Done"] + +foob(self) == l1(self) \/ l2(self) + +(* Allow infinite stuttering to prevent deadlock on termination. *) +Terminating == /\ \A self \in ProcSet: pc[self] = "Done" + /\ UNCHANGED vars + +Next == (\E self \in {1,2}: foob(self)) + \/ Terminating + +Spec == /\ Init /\ [][Next]_vars + /\ \A self \in {1,2} : WF_vars(foob(self)) + +Termination == <>(\A self \in ProcSet: pc[self] = "Done") + +\* END TRANSLATION - the hash of the generated TLA code (remove to silence divergence warnings): TLA-5f6bcf611a462be83724e9522b1f4062 +============================================================================= diff --git a/tlatools/test-model/pcal/NoLoop.tla b/tlatools/test-model/pcal/NoLoop.tla new file mode 100644 index 0000000000000000000000000000000000000000..89678c0db0c904a3002c3f3d670827a0e517e5b5 --- /dev/null +++ b/tlatools/test-model/pcal/NoLoop.tla @@ -0,0 +1,56 @@ +------------------------------- MODULE NoLoop ------------------------------- +EXTENDS Sequences, Naturals, TLC + +(* + --algorithm NoLoop + variable x = 0; y = 0 ; + begin a : with i = 3 do x := i ; end with; + b : with j \in { 1 , 2 } do y := j ; x := x + y ; end with ; + c : if y = 1 then x := x + 1 ; else x := x + y; end if; + when Print ( x , TRUE ); + end algorithm +*) + +\* BEGIN TRANSLATION - the hash of the PCal code: PCal-688f592294a3ad1b9996ea0f884f9746 +VARIABLES x, y, pc + +vars == << x, y, pc >> + +Init == (* Global variables *) + /\ x = 0 + /\ y = 0 + /\ pc = "a" + +a == /\ pc = "a" + /\ LET i == 3 IN + x' = i + /\ pc' = "b" + /\ y' = y + +b == /\ pc = "b" + /\ \E j \in { 1 , 2 }: + /\ y' = j + /\ x' = x + y' + /\ pc' = "c" + +c == /\ pc = "c" + /\ IF y = 1 + THEN /\ x' = x + 1 + ELSE /\ x' = x + y + /\ Print ( x' , TRUE ) + /\ pc' = "Done" + /\ y' = y + +(* Allow infinite stuttering to prevent deadlock on termination. *) +Terminating == pc = "Done" /\ UNCHANGED vars + +Next == a \/ b \/ c + \/ Terminating + +Spec == /\ Init /\ [][Next]_vars + /\ WF_vars(Next) + +Termination == <>(pc = "Done") + +\* END TRANSLATION - the hash of the generated TLA code (remove to silence divergence warnings): TLA-e78115b3384d72d09de2ad789d3620a6 +============================================================================= diff --git a/tlatools/test-model/pcal/NoLoop2.tla b/tlatools/test-model/pcal/NoLoop2.tla new file mode 100644 index 0000000000000000000000000000000000000000..b155e5aa7a428169bf86ae56279b73477caaa5f5 --- /dev/null +++ b/tlatools/test-model/pcal/NoLoop2.tla @@ -0,0 +1,70 @@ +------------------------------- MODULE NoLoop2 ------------------------------- +EXTENDS Naturals, TLC + +(* + + --algorithm NoLoop2 + variable x = 0; y = 0 ; + begin a : with i = 3 do x := i ; end with; + b : with j \in { 1 , 2 } do y := j ; x := x + y ; end with; + c : if y = 1 then x := x + 1 ; d : x := 2 * x ; + else x := x + y ; end if; + e : when Print ( x , TRUE ); + end algorithm + \* should print out 10 or 7 + +*) + +\* BEGIN TRANSLATION - the hash of the PCal code: PCal-a111e49229d06e75e660ee8777442285 +VARIABLES x, y, pc + +vars == << x, y, pc >> + +Init == (* Global variables *) + /\ x = 0 + /\ y = 0 + /\ pc = "a" + +a == /\ pc = "a" + /\ LET i == 3 IN + x' = i + /\ pc' = "b" + /\ y' = y + +b == /\ pc = "b" + /\ \E j \in { 1 , 2 }: + /\ y' = j + /\ x' = x + y' + /\ pc' = "c" + +c == /\ pc = "c" + /\ IF y = 1 + THEN /\ x' = x + 1 + /\ pc' = "d" + ELSE /\ x' = x + y + /\ pc' = "e" + /\ y' = y + +d == /\ pc = "d" + /\ x' = 2 * x + /\ pc' = "e" + /\ y' = y + +e == /\ pc = "e" + /\ Print ( x , TRUE ) + /\ pc' = "Done" + /\ UNCHANGED << x, y >> + +(* Allow infinite stuttering to prevent deadlock on termination. *) +Terminating == pc = "Done" /\ UNCHANGED vars + +Next == a \/ b \/ c \/ d \/ e + \/ Terminating + +Spec == /\ Init /\ [][Next]_vars + /\ WF_vars(Next) + +Termination == <>(pc = "Done") + +\* END TRANSLATION - the hash of the generated TLA code (remove to silence divergence warnings): TLA-7bfbfdcfed4c1aea23c410420c1f4f9d +============================================================================= diff --git a/tlatools/test-model/pcal/NoParams.tla b/tlatools/test-model/pcal/NoParams.tla new file mode 100644 index 0000000000000000000000000000000000000000..ccb98c820452e034f16a870154adf2381414fe26 --- /dev/null +++ b/tlatools/test-model/pcal/NoParams.tla @@ -0,0 +1,69 @@ +----------------------------- MODULE NoParams ------------------------------ +EXTENDS Sequences, Naturals, TLC + +(* + +--algorithm NoParams + variables sum = 0 ; + procedure Sum() + begin s1: sum := sum + 1; + return; + end procedure; + begin m1 : call Sum(); + m2 : call Sum(); + m3 : when Print(sum, TRUE) ; + end algorithm + + +*) + +\* BEGIN TRANSLATION - the hash of the PCal code: PCal-053c2fa748febe4e17ba5f50b599466c +VARIABLES sum, pc, stack + +vars == << sum, pc, stack >> + +Init == (* Global variables *) + /\ sum = 0 + /\ stack = << >> + /\ pc = "m1" + +s1 == /\ pc = "s1" + /\ sum' = sum + 1 + /\ pc' = Head(stack).pc + /\ stack' = Tail(stack) + +Sum == s1 + +m1 == /\ pc = "m1" + /\ stack' = << [ procedure |-> "Sum", + pc |-> "m2" ] >> + \o stack + /\ pc' = "s1" + /\ sum' = sum + +m2 == /\ pc = "m2" + /\ stack' = << [ procedure |-> "Sum", + pc |-> "m3" ] >> + \o stack + /\ pc' = "s1" + /\ sum' = sum + +m3 == /\ pc = "m3" + /\ Print(sum, TRUE) + /\ pc' = "Done" + /\ UNCHANGED << sum, stack >> + +(* Allow infinite stuttering to prevent deadlock on termination. *) +Terminating == pc = "Done" /\ UNCHANGED vars + +Next == Sum \/ m1 \/ m2 \/ m3 + \/ Terminating + +Spec == /\ Init /\ [][Next]_vars + /\ WF_vars(Next) + +Termination == <>(pc = "Done") + +\* END TRANSLATION - the hash of the generated TLA code (remove to silence divergence warnings): TLA-621925aa8b4c5ddf90178ffc7da092a5 + +============================================================================= diff --git a/tlatools/test-model/pcal/NotSoSimpleLoop.tla b/tlatools/test-model/pcal/NotSoSimpleLoop.tla new file mode 100644 index 0000000000000000000000000000000000000000..335e5a9bb18ea23539defbcb553d8a92cd9fbd8e --- /dev/null +++ b/tlatools/test-model/pcal/NotSoSimpleLoop.tla @@ -0,0 +1,55 @@ +--------------------------- MODULE NotSoSimpleLoop -------------------------- +EXTENDS Naturals, TLC + +(* --algorithm NotSoSimpleLoop + variable x = 0; + begin a : while x < 10 + do x := x+1 ; + skip ; + assert x \in 1..10 + end while ; + x := 4*x ; + assert x = 40 ; + b : assert 2 * x = 80; + end algorithm *) + + +\* BEGIN TRANSLATION - the hash of the PCal code: PCal-50f6f9a537b91874ce70a2eef129eb7f +VARIABLES x, pc + +vars == << x, pc >> + +Init == (* Global variables *) + /\ x = 0 + /\ pc = "a" + +a == /\ pc = "a" + /\ IF x < 10 + THEN /\ x' = x+1 + /\ TRUE + /\ Assert(x' \in 1..10, + "Failure of assertion at line 9, column 21.") + /\ pc' = "a" + ELSE /\ x' = 4*x + /\ Assert(x' = 40, + "Failure of assertion at line 12, column 16.") + /\ pc' = "b" + +b == /\ pc = "b" + /\ Assert(2 * x = 80, "Failure of assertion at line 13, column 16.") + /\ pc' = "Done" + /\ x' = x + +(* Allow infinite stuttering to prevent deadlock on termination. *) +Terminating == pc = "Done" /\ UNCHANGED vars + +Next == a \/ b + \/ Terminating + +Spec == /\ Init /\ [][Next]_vars + /\ WF_vars(Next) + +Termination == <>(pc = "Done") + +\* END TRANSLATION - the hash of the generated TLA code (remove to silence divergence warnings): TLA-0694bcde67f647179b7e22184415182a +============================================================================= diff --git a/tlatools/test-model/pcal/PcalPaxos.cfg b/tlatools/test-model/pcal/PcalPaxos.cfg new file mode 100644 index 0000000000000000000000000000000000000000..8537dc757c18205aadb1e072561098ec298d10b1 --- /dev/null +++ b/tlatools/test-model/pcal/PcalPaxos.cfg @@ -0,0 +1,19 @@ +SPECIFICATION Spec +\* Add statements after this line. + +CONSTANTS + MaxBallot = 2 + Acceptor <- MCAcceptor + Leader <- MCLeader + Learner <- MCLearner + Ballot <- MCBallot + LeaderOf <- MCLeaderOf + Command <- MCCommand + Majority <- MCMajority + a1 = a1 a2 = a2 a3 = a3 + ldr1 = ldr1 ldr2 = ldr2 + lrn1 = lrn1 lrn2 = lrn2 + cmd1 = cmd1 cmd2 = cmd2 + NotACmd = NotACmd + +INVARIANTS TypeOK Safety diff --git a/tlatools/test-model/pcal/PcalPaxos.tla b/tlatools/test-model/pcal/PcalPaxos.tla new file mode 100644 index 0000000000000000000000000000000000000000..186785d1610d36369582ae765e04cfdf9c3dfccf --- /dev/null +++ b/tlatools/test-model/pcal/PcalPaxos.tla @@ -0,0 +1,220 @@ +------------------------------ MODULE PcalPaxos ----------------------------- +EXTENDS Integers, TLC, FiniteSets + +CONSTANTS Acceptor, Leader, Learner, Ballot, LeaderOf, Command, Majority + +ASSUME /\ LeaderOf \in [Ballot -> Leader] + /\ Ballot \subseteq Nat + /\ 0 \in Ballot + /\ Majority \subseteq SUBSET Acceptor + /\ \A M1, M2 \in Majority : M1 \cap M2 # {} + +NotACmd == CHOOSE c : c \notin Command + +BallotsOf(ldr) == {b \in Ballot : LeaderOf[b] = ldr} + +Message == [type : {"1a"}, bal : Ballot] \cup + [type : {"1b"}, bal : Ballot, acc : Acceptor, + mbal : Ballot \cup {-1}, mcmd : Command \cup {NotACmd}] + \cup + [type : {"2a"}, bal : Ballot, cmd : Command] \cup + [type : {"2b"}, bal : Ballot, cmd : Command, acc : Acceptor] + +(* +--algorithm Paxos + variables msgs = {}; + macro Send(m) + begin msgs := msgs \cup {m} + end macro + + process Ldr \in Leader + variables ldrBal = -1 ; + ldrPhs = 2 + begin L: + while TRUE do + with b \in {bb \in Ballot : LeaderOf[bb] = self} + do either when ldrBal < b; + ldrBal := b ; + ldrPhs := 1 ; + Send([type |-> "1a", bal |-> b]) + + or when (ldrBal = b) /\ (ldrPhs = 1) ; + with M = {m \in msgs : (m.type = "1b") /\ (m.bal = b)}; + A = {m.acc : m \in M} ; + mmsg \in {m \in M : + \A m2 \in M : m.mbal \geq m2.mbal} + do when A \in Majority ; + if mmsg.mbal > -1 + then Send([type |-> "2a", bal |-> b, + cmd |-> mmsg.mcmd]) + else with c \in Command + do Send([type |-> "2a", bal |-> b, + cmd |-> c]) + end with + end if ; + end with ; + ldrPhs := 2 + end either + end with + end while + end process + + process Acc \in Acceptor + variables bal = -1 ; mbal = -1 ; mcmd = NotACmd + begin A: + while TRUE do + with m \in msgs + do either when (m.type = "1a") /\ (m.bal > bal) ; + bal := m.bal ; + Send([type |-> "1b", bal |-> m.bal, acc |-> self, + mbal |-> mbal, mcmd |-> mcmd]) + or when (m.type = "2a") + /\ (m.bal \geq bal) + /\ (m.bal > mbal); + bal := m.bal ; + mbal := m.bal ; + mcmd := m.cmd ; + Send([type |-> "2b", bal |-> m.bal, acc |-> self, + cmd |-> m.cmd]) + end either + end with + end while + end process + + + process Lrn \in Learner + variable learned = NotACmd + begin N: with b \in Ballot ; + 2bMsgs = {m \in msgs : (m.type = "2b") /\ (m.bal = b)} + do when {m.acc : m \in 2bMsgs} \in Majority ; + with m \in 2bMsgs + do learned := m.cmd + end with + end with ; + end process +end algorithm +*) + +\* BEGIN TRANSLATION +VARIABLES msgs, pc, ldrBal, ldrPhs, bal, mbal, mcmd, learned + +vars == << msgs, pc, ldrBal, ldrPhs, bal, mbal, mcmd, learned >> + +ProcSet == (Leader) \cup (Acceptor) \cup (Learner) + +Init == (* Global variables *) + /\ msgs = {} + (* Process Ldr *) + /\ ldrBal = [self \in Leader |-> -1] + /\ ldrPhs = [self \in Leader |-> 2] + (* Process Acc *) + /\ bal = [self \in Acceptor |-> -1] + /\ mbal = [self \in Acceptor |-> -1] + /\ mcmd = [self \in Acceptor |-> NotACmd] + (* Process Lrn *) + /\ learned = [self \in Learner |-> NotACmd] + /\ pc = [self \in ProcSet |-> CASE self \in Leader -> "L" + [] self \in Acceptor -> "A" + [] self \in Learner -> "N"] + +L(self) == /\ pc[self] = "L" + /\ \E b \in {bb \in Ballot : LeaderOf[bb] = self}: + \/ /\ ldrBal[self] < b + /\ ldrBal' = [ldrBal EXCEPT ![self] = b] + /\ ldrPhs' = [ldrPhs EXCEPT ![self] = 1] + /\ msgs' = (msgs \cup {([type |-> "1a", bal |-> b])}) + \/ /\ (ldrBal[self] = b) /\ (ldrPhs[self] = 1) + /\ LET M == {m \in msgs : (m.type = "1b") /\ (m.bal = b)} IN + LET A == {m.acc : m \in M} IN + \E mmsg \in {m \in M : + \A m2 \in M : m.mbal \geq m2.mbal}: + /\ A \in Majority + /\ IF mmsg.mbal > -1 + THEN /\ msgs' = (msgs \cup {([type |-> "2a", bal |-> b, + cmd |-> mmsg.mcmd])}) + ELSE /\ \E c \in Command: + msgs' = (msgs \cup {([type |-> "2a", bal |-> b, + cmd |-> c])}) + /\ ldrPhs' = [ldrPhs EXCEPT ![self] = 2] + /\ UNCHANGED ldrBal + /\ pc' = [pc EXCEPT ![self] = "L"] + /\ UNCHANGED << bal, mbal, mcmd, learned >> + +Ldr(self) == L(self) + +A(self) == /\ pc[self] = "A" + /\ \E m \in msgs: + \/ /\ (m.type = "1a") /\ (m.bal > bal[self]) + /\ bal' = [bal EXCEPT ![self] = m.bal] + /\ msgs' = (msgs \cup {([type |-> "1b", bal |-> m.bal, acc |-> self, + mbal |-> mbal[self], mcmd |-> mcmd[self]])}) + /\ UNCHANGED <<mbal, mcmd>> + \/ /\ (m.type = "2a") + /\ (m.bal \geq bal[self]) + /\ (m.bal > mbal[self]) + /\ bal' = [bal EXCEPT ![self] = m.bal] + /\ mbal' = [mbal EXCEPT ![self] = m.bal] + /\ mcmd' = [mcmd EXCEPT ![self] = m.cmd] + /\ msgs' = (msgs \cup {([type |-> "2b", bal |-> m.bal, acc |-> self, + cmd |-> m.cmd])}) + /\ pc' = [pc EXCEPT ![self] = "A"] + /\ UNCHANGED << ldrBal, ldrPhs, learned >> + +Acc(self) == A(self) + +N(self) == /\ pc[self] = "N" + /\ \E b \in Ballot: + LET 2bMsgs == {m \in msgs : (m.type = "2b") /\ (m.bal = b)} IN + /\ {m.acc : m \in 2bMsgs} \in Majority + /\ \E m \in 2bMsgs: + learned' = [learned EXCEPT ![self] = m.cmd] + /\ pc' = [pc EXCEPT ![self] = "Done"] + /\ UNCHANGED << msgs, ldrBal, ldrPhs, bal, mbal, mcmd >> + +Lrn(self) == N(self) + +Next == (\E self \in Leader: Ldr(self)) + \/ (\E self \in Acceptor: Acc(self)) + \/ (\E self \in Learner: Lrn(self)) + +Spec == Init /\ [][Next]_vars + +\* END TRANSLATION + +TypeOK == /\ msgs \subseteq Message + /\ learned \in [Learner -> Command \cup {NotACmd}] + /\ ldrBal \in [Leader -> Ballot \cup {-1}] + /\ ldrPhs \in [Leader -> {1, 2}] + /\ bal \in [Acceptor -> Ballot \cup {-1}] + /\ mbal \in [Acceptor -> Ballot \cup {-1}] + /\ mcmd \in [Acceptor -> Command \cup {NotACmd}] +\* /\ \A b \in Ballot : +\* Cardinality({m \in msgs : (m.type = "1b") /\ (m.bal = b)}) +\* < 2 + +Safety == \A l1, l2 \in Learner : + \/ learned[l2] = NotACmd + \/ learned[l1] \in {learned[l2], NotACmd} + +CONSTANTS MaxBallot, a1, a2, a3, ldr1, ldr2, lrn1, lrn2, cmd1, cmd2 + +MCAcceptor == {a1, a2, a3} \* {a1} +MCLeader == {ldr1} +MCLearner == {lrn1, lrn2} \* {lrn1} +MCLeaderOf == [i \in Ballot |-> ldr1] +MCCommand == {cmd1, cmd2} +MCMajority == {{a1, a3}, {a1, a2}, {a2, a3}} \* {{a1}} + +MCBallot == 0..MaxBallot +============================================================================= +With + MaxBallot = 2 + MCAcceptor == {a1, a2, a3} + MCLeader == {ldr1} + MCLearner == {lrn1, lrn2} + MCLeaderOf == [i \in Ballot |-> ldr1] + MCCommand == {cmd1, cmd2} + MCMajority == {{a1, a3}, {a1, a2}, {a2, a3}} + +592889 distinct states found in Paxos and PcalPaxos + diff --git a/tlatools/test-model/pcal/Peterson.cfg b/tlatools/test-model/pcal/Peterson.cfg new file mode 100644 index 0000000000000000000000000000000000000000..a99527de5a0215e987814884692dda8c78004f27 --- /dev/null +++ b/tlatools/test-model/pcal/Peterson.cfg @@ -0,0 +1,9 @@ +\* Add statements after this line. + +SPECIFICATION Spec +INVARIANT Inv +\** INVARIANT MutualExclusion +\** PROPERTY InfinitelyOftenInCS + +\* SPECIFICATION ISpec +\* INVARIANT MutualExclusion Inv diff --git a/tlatools/test-model/pcal/Peterson.tla b/tlatools/test-model/pcal/Peterson.tla new file mode 100644 index 0000000000000000000000000000000000000000..6c4bbb5aeb975899d291fb592b45c4e83cb4bb26 --- /dev/null +++ b/tlatools/test-model/pcal/Peterson.tla @@ -0,0 +1,191 @@ +------------------------------ MODULE Peterson ------------------------------ + +(***************************************************************************) +(* This specification describes Peterson's algorithm. It is a modification *) +(* modification of one found on Wikipedia, written in terms of a single *) +(* parameterized process, instantiated with parameter self = 0 and 1. *) +(***************************************************************************) + +(***************************************************************************) +(* We define Not(i) to be the process number other than i, so Not(0) = 1 *) +(* and Not(1) = 0. *) +(***************************************************************************) +Not(i) == IF i = 0 THEN 1 ELSE 0 + +(************************************************************************* +Here is the algorithm in +Cal (using the C syntax): + +--algorithm Peterson { + variables flag = [i \in {0, 1} |-> FALSE], turn = 0; + process (proc \in {0,1}) { + a0: while (TRUE) { + a1: flag[self] := TRUE; + a2: turn := Not(self); + a3: while (flag[Not(self)] /\ turn = Not(self)) { + skip }; + cs: skip; \* critical section + a4: flag[self] := FALSE; + } \* end while + } \* end process + } +************************************************************************) + +(***************************************************************************) +(* Here is the TLA+ translation of the +Cal code, obtained by running pcal *) +(* with the -wf option, which defines Spec to be a specification that *) +(* assumes weak fairness of the next-state actions of both processes. *) +(* This fairness assumption is discussed below. *) +(***************************************************************************) +\* BEGIN TRANSLATION - the hash of the PCal code: PCal-9255d446219a56b97b6d3847e8a2c94c +VARIABLES flag, turn, pc + +vars == << flag, turn, pc >> + +ProcSet == ({0,1}) + +Init == (* Global variables *) + /\ flag = [i \in {0, 1} |-> FALSE] + /\ turn = 0 + /\ pc = [self \in ProcSet |-> "a0"] + +a0(self) == /\ pc[self] = "a0" + /\ pc' = [pc EXCEPT ![self] = "a1"] + /\ UNCHANGED << flag, turn >> + +a1(self) == /\ pc[self] = "a1" + /\ flag' = [flag EXCEPT ![self] = TRUE] + /\ pc' = [pc EXCEPT ![self] = "a2"] + /\ turn' = turn + +a2(self) == /\ pc[self] = "a2" + /\ turn' = Not(self) + /\ pc' = [pc EXCEPT ![self] = "a3"] + /\ flag' = flag + +a3(self) == /\ pc[self] = "a3" + /\ IF flag[Not(self)] /\ turn = Not(self) + THEN /\ TRUE + /\ pc' = [pc EXCEPT ![self] = "a3"] + ELSE /\ pc' = [pc EXCEPT ![self] = "cs"] + /\ UNCHANGED << flag, turn >> + +cs(self) == /\ pc[self] = "cs" + /\ TRUE + /\ pc' = [pc EXCEPT ![self] = "a4"] + /\ UNCHANGED << flag, turn >> + +a4(self) == /\ pc[self] = "a4" + /\ flag' = [flag EXCEPT ![self] = FALSE] + /\ pc' = [pc EXCEPT ![self] = "a0"] + /\ turn' = turn + +proc(self) == a0(self) \/ a1(self) \/ a2(self) \/ a3(self) \/ cs(self) + \/ a4(self) + +Next == (\E self \in {0,1}: proc(self)) + +Spec == Init /\ [][Next]_vars + +\* END TRANSLATION - the hash of the generated TLA code (remove to silence divergence warnings): TLA-3c75243f0ddf7b933ebf70f115d692f3 + +(***************************************************************************) +(* Here is the invariant property that the algorithm should satisfy. It *) +(* asserts that at least one process is not in its critical section., *) +(***************************************************************************) +MutualExclusion == \E i \in {0, 1} : pc[i] # "cs" + +(***************************************************************************) +(* It's a good idea to check an algorithm for liveness, to make sure that *) +(* safety isn't ensured by an error that prevents anything interesting *) +(* from happening. Peterson's algorithm is supposed to be starvation *) +(* free. The easiest and best way to check that no process is ever *) +(* starved is to assume that both processes keep trying to enter their *) +(* critical sections and check that both of them do enter it infinitely *) +(* often. This is done by assuming weak fairness of both processes next *) +(* state actions and checking that the following formula is satisfied. *) +(***************************************************************************) +InfinitelyOftenInCS == \A i \in {0, 1} : []<>(pc[i] = "cs") + +(***************************************************************************) +(* The spec of a starvation free mutual exclusion algorithm asserts that, *) +(* if any process wants to enter its critical section, then it eventually *) +(* does. A process indicates that it wants to enter by leaving its *) +(* noncritical section. In this representation of the algorithm, process *) +(* i is in its noncritical section iff pc[i] = "a0". It leaves the *) +(* noncritical section by executing an a0(i) step. that sets pc[i] to *) +(* "a1". So we assume weak fairness of the action proc(i) /\ pc[i] # *) +(* "a0", the next-state action of process i when it is not in its *) +(* noncritical section, for each i. This gives us the spec. *) +(***************************************************************************) +FairSpec == /\ Init + /\ [][Next]_vars + /\ \A self \in {0,1}: WF_vars(proc(self) /\ (pc[self] # "a0")) + +(***************************************************************************) +(* We check that this spec satisfies the following condition, which *) +(* asserts that whenever a process i leaves the noncritical section (by *) +(* setting pc[i] to "a1"), it eventually enters its critical section. *) +(***************************************************************************) +DeadlockFree == \A i \in {0,1} : (pc[i] = "a1") ~> (pc[i] = "cs") + +----------------------------------------------------------------------------- +(***************************************************************************) +(* To prove mutual exclusion, we find an inductive invariant Inv that is *) +(* true initially and implies mutual exclusion. In other words, we must *) +(* find an Inv that satisfies *) +(* *) +(* THEOREM /\ Init => Inv *) +(* /\ Inv /\ [Next]_vars => Inv' *) +(* /\ Inv => MutualExclusion *) +(* *) +(* The inductive invariant Inv will have the form TypeOK /\ I *) +(* where TypeOK just asserts type-correctness and I is the interesting *) +(* part. For Peterson's algorithm, TypeOK is: *) +(***************************************************************************) +TypeOK == /\ pc \in [{0,1} -> {"a0", "a1", "a2", "a3", "cs", "a4"}] + /\ turn \in {0, 1} + /\ flag \in [{0,1} -> BOOLEAN] + +(***************************************************************************) +(* It turns out that we can use the following I. (I explain below how I *) +(* found I.) *) +(***************************************************************************) +I == \A i \in {0, 1} : + /\ (pc[i] \in {"a2", "a3", "cs", "a4"} ) => flag[i] + /\ (pc[i] \in {"cs", "a4"}) => /\ pc[Not(i)] \notin {"cs", "a4"} + /\ (pc[Not(i)] = "a3") => (turn = i) + +(***************************************************************************) +(* so we have: *) +(***************************************************************************) +Inv == TypeOK /\ I + +(***************************************************************************) +(* We can easily use TLC to check Init => Inv by checking that Inv is an *) +(* invariant of the specification. We can check that Inv satisfies *) +(* *) +(* Inv /\ [Next]_vars => Inv' *) +(* *) +(* by checking that Inv is an invariant of the following specification. *) +(***************************************************************************) +ISpec == Inv /\ [][Next]_vars + +(***************************************************************************) +(* This works only because Inv is written as the conjunction TypeOK and *) +(* something else, where TypeOK has the form it does. (Otherwise, TLC *) +(* would not be able to handle Inv as an initial predicate.) Moreover, to *) +(* compute the initial states, TLC must calculate all states that satisfy *) +(* the type-correctness invariant. For most specs, this is an enormous *) +(* number of states except perhaps for the tiniest of models, so this *) +(* seldom works. However, Peterson's algorithm is so simple that it works *) +(* fine. In fact, I discovered the invariant I by trial and error, using *) +(* TLC to discover the problems with I that did not make Inv an invariant *) +(* of ISpec. *) +(* *) +(* Finally, we can check *) +(* *) +(* Inv => MutualExclusion *) +(* *) +(* by having TLC check that MutualExclusion is an invariant of ISpec. *) +(***************************************************************************) +============================================================================= diff --git a/tlatools/test-model/pcal/Quicksort.tla b/tlatools/test-model/pcal/Quicksort.tla new file mode 100644 index 0000000000000000000000000000000000000000..4b8b7b46ad6561b9c4b98049fc455ef8c719e3ac --- /dev/null +++ b/tlatools/test-model/pcal/Quicksort.tla @@ -0,0 +1,169 @@ +------------------------------- MODULE Quicksort ----------------------------- +EXTENDS Naturals, Sequences, TLC + +CONSTANT ArrayLen + +ASSUME ArrayLen \in Nat + +PermsOf(Arr) == + LET Automorphism(S) == { f \in [S -> S] : + \A y \in S : \E x \in S : f[x] = y } + f ** g == [x \in DOMAIN g |-> f[g[x]]] + IN { Arr ** f : f \in Automorphism(DOMAIN Arr) } + +(* +--algorithm Quicksort + variables Ainit \in [1..ArrayLen -> 1..ArrayLen]; A = Ainit; + returnVal = 1 + procedure Partition(lo, hi ) + begin pt1 : with piv \in lo..(hi-1) + do returnVal := piv ; + with Ap \in + {AA \in PermsOf(A) : + (\A i \in 1..(lo-1) : AA[i] = A[i]) + /\ (\A i \in (hi+1)..Len(A) : AA[i] = A[i]) + /\ (\A i \in lo..piv, j \in (piv+1)..hi : + AA[i] \leq AA[j])} + do A := Ap; + return ; + end with ; + end with; + end procedure + procedure QS(qlo, qhi ) + variable pivot ; + begin qs1 : if qlo < qhi + then call Partition(qlo, qhi) ; + qs2 : pivot := returnVal ; + qs3 : call QS(qlo, pivot) ; + qs4 : call QS(pivot +1,qhi) ; + return ; + else return; + end if; + end procedure + begin main : call QS(1, Len(A)) ; + test : assert A \in PermsOf(Ainit) + /\ \A i, j \in 1..ArrayLen : + (i < j) => A[i] \leq A[j] + end algorithm +*) + +\* BEGIN TRANSLATION - the hash of the PCal code: PCal-9360dabbf03d04fb938a53e6f43a0dc3 +CONSTANT defaultInitValue +VARIABLES Ainit, A, returnVal, pc, stack, lo, hi, qlo, qhi, pivot + +vars == << Ainit, A, returnVal, pc, stack, lo, hi, qlo, qhi, pivot >> + +Init == (* Global variables *) + /\ Ainit \in [1..ArrayLen -> 1..ArrayLen] + /\ A = Ainit + /\ returnVal = 1 + (* Procedure Partition *) + /\ lo = defaultInitValue + /\ hi = defaultInitValue + (* Procedure QS *) + /\ qlo = defaultInitValue + /\ qhi = defaultInitValue + /\ pivot = defaultInitValue + /\ stack = << >> + /\ pc = "main" + +pt1 == /\ pc = "pt1" + /\ \E piv \in lo..(hi-1): + /\ returnVal' = piv + /\ \E Ap \in {AA \in PermsOf(A) : + (\A i \in 1..(lo-1) : AA[i] = A[i]) + /\ (\A i \in (hi+1)..Len(A) : AA[i] = A[i]) + /\ (\A i \in lo..piv, j \in (piv+1)..hi : + AA[i] \leq AA[j])}: + /\ A' = Ap + /\ pc' = Head(stack).pc + /\ lo' = Head(stack).lo + /\ hi' = Head(stack).hi + /\ stack' = Tail(stack) + /\ UNCHANGED << Ainit, qlo, qhi, pivot >> + +Partition == pt1 + +qs1 == /\ pc = "qs1" + /\ IF qlo < qhi + THEN /\ /\ hi' = qhi + /\ lo' = qlo + /\ stack' = << [ procedure |-> "Partition", + pc |-> "qs2", + lo |-> lo, + hi |-> hi ] >> + \o stack + /\ pc' = "pt1" + /\ UNCHANGED << qlo, qhi, pivot >> + ELSE /\ pc' = Head(stack).pc + /\ pivot' = Head(stack).pivot + /\ qlo' = Head(stack).qlo + /\ qhi' = Head(stack).qhi + /\ stack' = Tail(stack) + /\ UNCHANGED << lo, hi >> + /\ UNCHANGED << Ainit, A, returnVal >> + +qs2 == /\ pc = "qs2" + /\ pivot' = returnVal + /\ pc' = "qs3" + /\ UNCHANGED << Ainit, A, returnVal, stack, lo, hi, qlo, qhi >> + +qs3 == /\ pc = "qs3" + /\ /\ qhi' = pivot + /\ qlo' = qlo + /\ stack' = << [ procedure |-> "QS", + pc |-> "qs4", + pivot |-> pivot, + qlo |-> qlo, + qhi |-> qhi ] >> + \o stack + /\ pivot' = defaultInitValue + /\ pc' = "qs1" + /\ UNCHANGED << Ainit, A, returnVal, lo, hi >> + +qs4 == /\ pc = "qs4" + /\ /\ qhi' = qhi + /\ qlo' = pivot +1 + /\ pivot' = defaultInitValue + /\ pc' = "qs1" + /\ UNCHANGED << Ainit, A, returnVal, stack, lo, hi >> + +QS == qs1 \/ qs2 \/ qs3 \/ qs4 + +main == /\ pc = "main" + /\ /\ qhi' = Len(A) + /\ qlo' = 1 + /\ stack' = << [ procedure |-> "QS", + pc |-> "test", + pivot |-> pivot, + qlo |-> qlo, + qhi |-> qhi ] >> + \o stack + /\ pivot' = defaultInitValue + /\ pc' = "qs1" + /\ UNCHANGED << Ainit, A, returnVal, lo, hi >> + +test == /\ pc = "test" + /\ Assert( A \in PermsOf(Ainit) + /\ \A i, j \in 1..ArrayLen : + (i < j) => A[i] \leq A[j], + "Failure of assertion at line 44, column 17.") + /\ pc' = "Done" + /\ UNCHANGED << Ainit, A, returnVal, stack, lo, hi, qlo, qhi, pivot >> + +(* Allow infinite stuttering to prevent deadlock on termination. *) +Terminating == pc = "Done" /\ UNCHANGED vars + +Next == Partition \/ QS \/ main \/ test + \/ Terminating + +Spec == /\ Init /\ [][Next]_vars + /\ WF_vars(Next) + +Termination == <>(pc = "Done") + +\* END TRANSLATION - the hash of the generated TLA code (remove to silence divergence warnings): TLA-c890d98935ff53e5f234680508ac16aa +============================================================================= +Checked without termination on svc-lamport-2 with 2 workers: + arrayLen = 4 in 15 sec, 32280 states, depth 22 + arrayLen = 5 in 138min 17 sec 1529005 states, depth 28 diff --git a/tlatools/test-model/pcal/Quicksort2Procs.cfg b/tlatools/test-model/pcal/Quicksort2Procs.cfg new file mode 100644 index 0000000000000000000000000000000000000000..8e5945333cd1398dc09ad5bbbab24c46e8121b21 --- /dev/null +++ b/tlatools/test-model/pcal/Quicksort2Procs.cfg @@ -0,0 +1,6 @@ +SPECIFICATION Spec +PROPERTY Termination +\* Add statements after this line. +CONSTANTS ArrayLen = 3 +INVARIANT Invariant + diff --git a/tlatools/test-model/pcal/Quicksort2Procs.tla b/tlatools/test-model/pcal/Quicksort2Procs.tla new file mode 100644 index 0000000000000000000000000000000000000000..b0e2d4ff7bf1ae9bec80323fc77bcf39a4b5bed7 --- /dev/null +++ b/tlatools/test-model/pcal/Quicksort2Procs.tla @@ -0,0 +1,243 @@ +--------------------------- MODULE Quicksort2Procs --------------------------- +EXTENDS Naturals, Sequences + +CONSTANT ArrayLen + +ASSUME ArrayLen \in Nat + +PermsOf(Arr) == + LET Automorphism(S) == { f \in [S -> S] : + \A y \in S : \E x \in S : f[x] = y } + f ** g == [x \in DOMAIN g |-> f[g[x]]] + IN { Arr ** f : f \in Automorphism(DOMAIN Arr) } + +(* This algorithm uses two different copies of the QS procedure + to test whether tail recursion works when calling a different + procedure. + +--algorithm Quicksort + variables A \in [1..ArrayLen -> 1..ArrayLen]; + returnVal = 99; + procedure Partition(lo = 99, hi = 99) + begin pt1 : with piv \in lo..(hi-1) + do returnVal := piv ; + with Ap \in + {AA \in PermsOf(A) : + (\A i \in 1..(lo-1) : AA[i] = A[i]) + /\ (\A i \in (hi+1)..Len(A) : AA[i] = A[i]) + /\ (\A i \in lo..piv, j \in (piv+1)..hi : + AA[i] \leq AA[j])} + do A := Ap; + return ; + end with ; + end with; + end procedure + procedure QS(qlo = 99, qhi = 99) + variable pivot = 99 ; + begin qs1 : if qlo < qhi + then call Partition(qlo, qhi) ; + qs2 : pivot := returnVal ; + qs3 : call QS2(qlo, pivot) ; + qs4 : call QS2(pivot +1,qhi) ; + return ; + else return; + end if; + end procedure + procedure QS2(qlo2 = 99, qhi2 = 99) + variable pivot2 = 99 ; + begin 2qs1 : if qlo2 < qhi2 + then call Partition(qlo2, qhi2) ; + 2qs2 : pivot2 := returnVal ; + 2qs3 : call QS(qlo2, pivot2) ; + 2qs4 : call QS(pivot2 +1,qhi2) ; + return ; + else return; + end if; + end procedure + begin main : call QS(1, Len(A)) ; + end algorithm +*) + +\* BEGIN TRANSLATION - the hash of the PCal code: PCal-041626606cd9c50bd2700d7f84bf1f9c +VARIABLES A, returnVal, pc, stack, lo, hi, qlo, qhi, pivot, qlo2, qhi2, + pivot2 + +vars == << A, returnVal, pc, stack, lo, hi, qlo, qhi, pivot, qlo2, qhi2, + pivot2 >> + +Init == (* Global variables *) + /\ A \in [1..ArrayLen -> 1..ArrayLen] + /\ returnVal = 99 + (* Procedure Partition *) + /\ lo = 99 + /\ hi = 99 + (* Procedure QS *) + /\ qlo = 99 + /\ qhi = 99 + /\ pivot = 99 + (* Procedure QS2 *) + /\ qlo2 = 99 + /\ qhi2 = 99 + /\ pivot2 = 99 + /\ stack = << >> + /\ pc = "main" + +pt1 == /\ pc = "pt1" + /\ \E piv \in lo..(hi-1): + /\ returnVal' = piv + /\ \E Ap \in {AA \in PermsOf(A) : + (\A i \in 1..(lo-1) : AA[i] = A[i]) + /\ (\A i \in (hi+1)..Len(A) : AA[i] = A[i]) + /\ (\A i \in lo..piv, j \in (piv+1)..hi : + AA[i] \leq AA[j])}: + /\ A' = Ap + /\ pc' = Head(stack).pc + /\ lo' = Head(stack).lo + /\ hi' = Head(stack).hi + /\ stack' = Tail(stack) + /\ UNCHANGED << qlo, qhi, pivot, qlo2, qhi2, pivot2 >> + +Partition == pt1 + +qs1 == /\ pc = "qs1" + /\ IF qlo < qhi + THEN /\ /\ hi' = qhi + /\ lo' = qlo + /\ stack' = << [ procedure |-> "Partition", + pc |-> "qs2", + lo |-> lo, + hi |-> hi ] >> + \o stack + /\ pc' = "pt1" + /\ UNCHANGED << qlo, qhi, pivot >> + ELSE /\ pc' = Head(stack).pc + /\ pivot' = Head(stack).pivot + /\ qlo' = Head(stack).qlo + /\ qhi' = Head(stack).qhi + /\ stack' = Tail(stack) + /\ UNCHANGED << lo, hi >> + /\ UNCHANGED << A, returnVal, qlo2, qhi2, pivot2 >> + +qs2 == /\ pc = "qs2" + /\ pivot' = returnVal + /\ pc' = "qs3" + /\ UNCHANGED << A, returnVal, stack, lo, hi, qlo, qhi, qlo2, qhi2, + pivot2 >> + +qs3 == /\ pc = "qs3" + /\ /\ qhi2' = pivot + /\ qlo2' = qlo + /\ stack' = << [ procedure |-> "QS2", + pc |-> "qs4", + pivot2 |-> pivot2, + qlo2 |-> qlo2, + qhi2 |-> qhi2 ] >> + \o stack + /\ pivot2' = 99 + /\ pc' = "2qs1" + /\ UNCHANGED << A, returnVal, lo, hi, qlo, qhi, pivot >> + +qs4 == /\ pc = "qs4" + /\ /\ pivot' = Head(stack).pivot + /\ qhi2' = qhi + /\ qlo2' = pivot +1 + /\ stack' = << [ procedure |-> "QS2", + pc |-> Head(stack).pc, + pivot2 |-> pivot2, + qlo2 |-> qlo2, + qhi2 |-> qhi2 ] >> + \o Tail(stack) + /\ pivot2' = 99 + /\ pc' = "2qs1" + /\ UNCHANGED << A, returnVal, lo, hi, qlo, qhi >> + +QS == qs1 \/ qs2 \/ qs3 \/ qs4 + +2qs1 == /\ pc = "2qs1" + /\ IF qlo2 < qhi2 + THEN /\ /\ hi' = qhi2 + /\ lo' = qlo2 + /\ stack' = << [ procedure |-> "Partition", + pc |-> "2qs2", + lo |-> lo, + hi |-> hi ] >> + \o stack + /\ pc' = "pt1" + /\ UNCHANGED << qlo2, qhi2, pivot2 >> + ELSE /\ pc' = Head(stack).pc + /\ pivot2' = Head(stack).pivot2 + /\ qlo2' = Head(stack).qlo2 + /\ qhi2' = Head(stack).qhi2 + /\ stack' = Tail(stack) + /\ UNCHANGED << lo, hi >> + /\ UNCHANGED << A, returnVal, qlo, qhi, pivot >> + +2qs2 == /\ pc = "2qs2" + /\ pivot2' = returnVal + /\ pc' = "2qs3" + /\ UNCHANGED << A, returnVal, stack, lo, hi, qlo, qhi, pivot, qlo2, + qhi2 >> + +2qs3 == /\ pc = "2qs3" + /\ /\ qhi' = pivot2 + /\ qlo' = qlo2 + /\ stack' = << [ procedure |-> "QS", + pc |-> "2qs4", + pivot |-> pivot, + qlo |-> qlo, + qhi |-> qhi ] >> + \o stack + /\ pivot' = 99 + /\ pc' = "qs1" + /\ UNCHANGED << A, returnVal, lo, hi, qlo2, qhi2, pivot2 >> + +2qs4 == /\ pc = "2qs4" + /\ /\ pivot2' = Head(stack).pivot2 + /\ qhi' = qhi2 + /\ qlo' = pivot2 +1 + /\ stack' = << [ procedure |-> "QS", + pc |-> Head(stack).pc, + pivot |-> pivot, + qlo |-> qlo, + qhi |-> qhi ] >> + \o Tail(stack) + /\ pivot' = 99 + /\ pc' = "qs1" + /\ UNCHANGED << A, returnVal, lo, hi, qlo2, qhi2 >> + +QS2 == 2qs1 \/ 2qs2 \/ 2qs3 \/ 2qs4 + +main == /\ pc = "main" + /\ /\ qhi' = Len(A) + /\ qlo' = 1 + /\ stack' = << [ procedure |-> "QS", + pc |-> "Done", + pivot |-> pivot, + qlo |-> qlo, + qhi |-> qhi ] >> + \o stack + /\ pivot' = 99 + /\ pc' = "qs1" + /\ UNCHANGED << A, returnVal, lo, hi, qlo2, qhi2, pivot2 >> + +(* Allow infinite stuttering to prevent deadlock on termination. *) +Terminating == pc = "Done" /\ UNCHANGED vars + +Next == Partition \/ QS \/ QS2 \/ main + \/ Terminating + +Spec == /\ Init /\ [][Next]_vars + /\ WF_vars(Next) + +Termination == <>(pc = "Done") + +\* END TRANSLATION - the hash of the generated TLA code (remove to silence divergence warnings): TLA-da193a603aa89ac5bf05f91d5ffaf922 + +Invariant == + (pc = "Done") => \A i, j \in 1..ArrayLen : + (i < j) => A[i] \leq A[j] + +\* Tested on 28 June 2005 with arrayLen = 5 in +\* 2 hours 9 min 6.5 seconds on SVC-Lamport-3 + +============================================================================= diff --git a/tlatools/test-model/pcal/QuicksortMacro.cfg b/tlatools/test-model/pcal/QuicksortMacro.cfg new file mode 100644 index 0000000000000000000000000000000000000000..0ebf8fe12d66c62cbd54cf80207efc9dda94587b --- /dev/null +++ b/tlatools/test-model/pcal/QuicksortMacro.cfg @@ -0,0 +1,5 @@ +SPECIFICATION Spec +PROPERTY Termination +\* Add statements after this line. +CONSTANTS ArrayLen = 3 +INVARIANT Invariant diff --git a/tlatools/test-model/pcal/QuicksortMacro.tla b/tlatools/test-model/pcal/QuicksortMacro.tla new file mode 100644 index 0000000000000000000000000000000000000000..ce284e9145de3934ad94bdee20943bbfe9b60476 --- /dev/null +++ b/tlatools/test-model/pcal/QuicksortMacro.tla @@ -0,0 +1,138 @@ +---------------------------- MODULE QuicksortMacro --------------------------- +EXTENDS Naturals, Sequences + +CONSTANT ArrayLen + +ASSUME ArrayLen \in Nat + +PermsOf(Arr) == + LET Automorphism(S) == { f \in [S -> S] : + \A y \in S : \E x \in S : f[x] = y } + f ** g == [x \in DOMAIN g |-> f[g[x]]] + IN { Arr ** f : f \in Automorphism(DOMAIN Arr) } + +(* +--algorithm QuicksortMacro + variables A \in [1..ArrayLen -> 1..ArrayLen]; + returnVal = 1; + macro Partition(lo, hi) + begin with piv \in lo..(hi-1) + do returnVal := piv ; + with Ap \in + {AA \in PermsOf(A) : + (\A i \in 1..(lo-1) : AA[i] = A[i]) + /\ (\A i \in (hi+1)..Len(A) : AA[i] = A[i]) + /\ (\A i \in lo..piv, j \in (piv+1)..hi : + AA[i] \leq AA[j])} + do A := Ap; + end with ; + end with; + end macro + procedure QS(qlo = 1, qhi = 1) + variable pivot = 1 ; + begin qs1 : if qlo < qhi + then Partition(qlo, qhi) ; + qs2 : pivot := returnVal ; + qs3 : call QS(qlo, pivot) ; + qs4 : call QS(pivot +1,qhi) ; + return ; + else return; + end if; + end procedure + begin main : call QS(1, Len(A)) ; + end algorithm +*) + +\* BEGIN TRANSLATION - the hash of the PCal code: PCal-f3c86b18c12d46dfb100d83f44bd15cc +VARIABLES A, returnVal, pc, stack, qlo, qhi, pivot + +vars == << A, returnVal, pc, stack, qlo, qhi, pivot >> + +Init == (* Global variables *) + /\ A \in [1..ArrayLen -> 1..ArrayLen] + /\ returnVal = 1 + (* Procedure QS *) + /\ qlo = 1 + /\ qhi = 1 + /\ pivot = 1 + /\ stack = << >> + /\ pc = "main" + +qs1 == /\ pc = "qs1" + /\ IF qlo < qhi + THEN /\ \E piv \in qlo..(qhi-1): + /\ returnVal' = piv + /\ \E Ap \in {AA \in PermsOf(A) : + (\A i \in 1..(qlo-1) : AA[i] = A[i]) + /\ (\A i \in (qhi+1)..Len(A) : AA[i] = A[i]) + /\ (\A i \in qlo..piv, j \in (piv+1)..qhi : + AA[i] \leq AA[j])}: + A' = Ap + /\ pc' = "qs2" + /\ UNCHANGED << stack, qlo, qhi, pivot >> + ELSE /\ pc' = Head(stack).pc + /\ pivot' = Head(stack).pivot + /\ qlo' = Head(stack).qlo + /\ qhi' = Head(stack).qhi + /\ stack' = Tail(stack) + /\ UNCHANGED << A, returnVal >> + +qs2 == /\ pc = "qs2" + /\ pivot' = returnVal + /\ pc' = "qs3" + /\ UNCHANGED << A, returnVal, stack, qlo, qhi >> + +qs3 == /\ pc = "qs3" + /\ /\ qhi' = pivot + /\ qlo' = qlo + /\ stack' = << [ procedure |-> "QS", + pc |-> "qs4", + pivot |-> pivot, + qlo |-> qlo, + qhi |-> qhi ] >> + \o stack + /\ pivot' = 1 + /\ pc' = "qs1" + /\ UNCHANGED << A, returnVal >> + +qs4 == /\ pc = "qs4" + /\ /\ qhi' = qhi + /\ qlo' = pivot +1 + /\ pivot' = 1 + /\ pc' = "qs1" + /\ UNCHANGED << A, returnVal, stack >> + +QS == qs1 \/ qs2 \/ qs3 \/ qs4 + +main == /\ pc = "main" + /\ /\ qhi' = Len(A) + /\ qlo' = 1 + /\ stack' = << [ procedure |-> "QS", + pc |-> "Done", + pivot |-> pivot, + qlo |-> qlo, + qhi |-> qhi ] >> + \o stack + /\ pivot' = 1 + /\ pc' = "qs1" + /\ UNCHANGED << A, returnVal >> + +(* Allow infinite stuttering to prevent deadlock on termination. *) +Terminating == pc = "Done" /\ UNCHANGED vars + +Next == QS \/ main + \/ Terminating + +Spec == /\ Init /\ [][Next]_vars + /\ WF_vars(Next) + +Termination == <>(pc = "Done") + +\* END TRANSLATION - the hash of the generated TLA code (remove to silence divergence warnings): TLA-f36591cc8efd54110514a4b1f13d3500 + +Invariant == + (pc = "Done") => \A i, j \in 1..ArrayLen : + (i < j) => A[i] \leq A[j] + + +============================================================================= diff --git a/tlatools/test-model/pcal/RAB.cfg b/tlatools/test-model/pcal/RAB.cfg new file mode 100644 index 0000000000000000000000000000000000000000..31078b2505852cc4c10f20c6b1643aef28d53a24 --- /dev/null +++ b/tlatools/test-model/pcal/RAB.cfg @@ -0,0 +1,5 @@ +SPECIFICATION Spec +\* Add statements after this line. +CONSTANT Pid = { p0, p1 } +INVARIANT Consistency + diff --git a/tlatools/test-model/pcal/RAB.tla b/tlatools/test-model/pcal/RAB.tla new file mode 100644 index 0000000000000000000000000000000000000000..4d920d96784b29b8da65505ca364931390bba4f3 --- /dev/null +++ b/tlatools/test-model/pcal/RAB.tla @@ -0,0 +1,208 @@ +------------------------------- MODULE RAB ------------------------------- + +EXTENDS Naturals, Sequences, TLC + + +(**************************************************************************) +(* The Remoting Attributes bug. *) +(* *) +(* *) +(* Simplified version of the c program (comment in the original): *) +(* *) +(* *) +(* class Attributes { *) +(* int flags = 0; *) +(* *) +(* boolean CalcA () { ... } *) +(* boolean CalcB () { ... } *) +(* *) +(* // We are not protecting against a race. *) +(* // If there is a race while setting flags we *) +(* // will have to compute the result again, but *) +(* // we will always return the correct result. *) +(* *) +(* boolean isA () { *) +(* if ((flags & 0x02) == 0) { *) +(* int temp = (CalcA() ? 0x03 : 0x02); *) +(* flags |= temp; *) +(* } *) +(* return (flags & 0x01) != 0; *) +(* } *) +(* *) +(* boolean isB () { *) +(* if ((flags & 0x20) == 0) { *) +(* int temp = (CalcB() ? 0x30 : 0x20); *) +(* flags |= temp; *) +(* } *) +(* return (flags & 0x10) != 0; *) +(* } *) +(* } *) +(* *) +(* *) +(* CalcA and CalcB are assumed to be functions that each always returns *) +(* the same value; we just do not know what value that is. We model this *) +(* using an "oracle" global variable named calc. *) +(* *) +(* We model the environment as a set of processes Pid. Each process runs *) +(* in a loop, selecting a random attribute ("A" or "B") to access on each *) +(* iteration. *) +(* *) +(**************************************************************************) + +CONSTANT Pid \* set of process ids + +Attr == { "A", "B" } \* what are the attributes +Boolean == { FALSE, TRUE } + +Flags == [ Attr -> [ valid: Boolean, value: Boolean ]] + + + +(**************************************************************************) +(* How to compute the "bitwise or" of two Flags. *) +(**************************************************************************) +f | g == [ a \in DOMAIN f |-> [ n \in DOMAIN f[a] |-> f[a][n] \/ g[a][n] ]] + + + + + + + +(*--algorithm rab + + variables + (****************************************************************) + (* Global variable containing flags for all attributes. The *) + (* initial state has all valid and value bits as FALSE. *) + (****************************************************************) + flags = [ a \in Attr |-> [ valid |-> FALSE, value |-> FALSE ]]; + + + (****************************************************************) + (* Oracle that says what the value is for each attribute. *) + (* Technically this is a variable, but we never change it. *) + (****************************************************************) + calc \in [ Attr -> { FALSE, TRUE } ]; + + + + + process work \in Pid + variables + (************************************************************) + (* Arbitrary initial values of the correct type. *) + (************************************************************) + temp = CHOOSE f \in Flags : TRUE; + myattr = CHOOSE a \in Attr : TRUE; + begin + Loop: + while TRUE do + (************************************************************) + (* Choose an attribute to access. *) + (************************************************************) + with attr \in Attr do myattr := attr; end with; + + if \lnot flags[myattr].valid then + (********************************************************) + (* My component of the global flags variable is not *) + (* valid. Compute the temporary. *) + (********************************************************) + temp := + [ a \in Attr |-> + IF a = myattr + THEN [ valid |-> TRUE, value |-> calc[myattr] ] + ELSE [ valid |-> FALSE, value |-> FALSE ] + ]; + + FetchFlags: + (********************************************************) + (* Fetch the global flags variable and "bitwise or" it *) + (* into the temporary. *) + (********************************************************) + temp := flags | temp; + + StoreFlags: + (********************************************************) + (* Store the temporary back into the global flags *) + (* variable. *) + (********************************************************) + + flags := temp; + end if; + + ReadFlags: + (************************************************************) + (* Read my component of the global flags variable. It is *) + (* supposed to be consistent with the oracle. *) + (************************************************************) + \* assert flags[myattr].value = calc[myattr]; + skip; + end while; + end process; +end algorithm + +*) + + +\* BEGIN TRANSLATION - the hash of the PCal code: PCal-36470f24ec099c641e8894d14dc1be7a +VARIABLES flags, calc, pc, temp, myattr + +vars == << flags, calc, pc, temp, myattr >> + +ProcSet == (Pid) + +Init == (* Global variables *) + /\ flags = [ a \in Attr |-> [ valid |-> FALSE, value |-> FALSE ]] + /\ calc \in [ Attr -> { FALSE, TRUE } ] + (* Process work *) + /\ temp = [self \in Pid |-> CHOOSE f \in Flags : TRUE] + /\ myattr = [self \in Pid |-> CHOOSE a \in Attr : TRUE] + /\ pc = [self \in ProcSet |-> "Loop"] + +Loop(self) == /\ pc[self] = "Loop" + /\ \E attr \in Attr: + myattr' = [myattr EXCEPT ![self] = attr] + /\ IF \lnot flags[myattr'[self]].valid + THEN /\ temp' = [temp EXCEPT ![self] = [ a \in Attr |-> + IF a = myattr'[self] + THEN [ valid |-> TRUE, value |-> calc[myattr'[self]] ] + ELSE [ valid |-> FALSE, value |-> FALSE ] + ]] + /\ pc' = [pc EXCEPT ![self] = "FetchFlags"] + ELSE /\ pc' = [pc EXCEPT ![self] = "ReadFlags"] + /\ temp' = temp + /\ UNCHANGED << flags, calc >> + +ReadFlags(self) == /\ pc[self] = "ReadFlags" + /\ TRUE + /\ pc' = [pc EXCEPT ![self] = "Loop"] + /\ UNCHANGED << flags, calc, temp, myattr >> + +FetchFlags(self) == /\ pc[self] = "FetchFlags" + /\ temp' = [temp EXCEPT ![self] = flags | temp[self]] + /\ pc' = [pc EXCEPT ![self] = "StoreFlags"] + /\ UNCHANGED << flags, calc, myattr >> + +StoreFlags(self) == /\ pc[self] = "StoreFlags" + /\ flags' = temp[self] + /\ pc' = [pc EXCEPT ![self] = "ReadFlags"] + /\ UNCHANGED << calc, temp, myattr >> + +work(self) == Loop(self) \/ ReadFlags(self) \/ FetchFlags(self) + \/ StoreFlags(self) + +Next == (\E self \in Pid: work(self)) + +Spec == Init /\ [][Next]_vars + +\* END TRANSLATION - the hash of the generated TLA code (remove to silence divergence warnings): TLA-e0d61e0fb8a75ee2eb592e9a5b0f5f46 + + +Consistency == + \A self \in ProcSet : + pc[self] = "ReadFlags" => + flags[myattr[self]].value = calc[myattr[self]] + + +============================================================================ diff --git a/tlatools/test-model/pcal/RealQuicksort.cfg b/tlatools/test-model/pcal/RealQuicksort.cfg new file mode 100644 index 0000000000000000000000000000000000000000..105b80b88f83103a719c8d296f999f4d9a8c580a --- /dev/null +++ b/tlatools/test-model/pcal/RealQuicksort.cfg @@ -0,0 +1,7 @@ +SPECIFICATION Spec +PROPERTY Termination +CONSTANT defaultInitValue = defaultInitValue +\* Add statements after this line. +CONSTANTS MaxLen = 3 +INVARIANT Invariant + diff --git a/tlatools/test-model/pcal/RealQuicksort.tla b/tlatools/test-model/pcal/RealQuicksort.tla new file mode 100644 index 0000000000000000000000000000000000000000..3392236c96d5d5d470f179d399623a8be2a63dd8 --- /dev/null +++ b/tlatools/test-model/pcal/RealQuicksort.tla @@ -0,0 +1,124 @@ +----------------------------- MODULE RealQuicksort --------------------------- +EXTENDS Naturals, Sequences, FiniteSets + +CONSTANT MaxLen + +ASSUME MaxLen \in Nat + +PermsOf(Arr) == + LET Automorphism(S) == { f \in [S -> S] : + \A y \in S : \E x \in S : f[x] = y } + f ** g == [x \in DOMAIN g |-> f[g[x]]] + IN { Arr ** f : f \in Automorphism(DOMAIN Arr) } + +Min(S) == CHOOSE i \in S : \A j \in S : i \leq j +Max(S) == CHOOSE i \in S : \A j \in S : i \geq j + +(* + +--algorithm RealQuicksort + variables A \in UNION {[1..N -> 1..N] : N \in 0..MaxLen}; + Uns = {1..Len(A)} ; + new = {} ; + procedure Part(parg) + begin pt1 : with new1 \in + {Min(parg)..piv : piv \in parg \ {Max(parg)}} do + with new2 = parg \ new1 do + new := {new1, new2} ; + with Ap \in + {AA \in PermsOf(A) : + (\A i \in 1..Len(A) \ parg : AA[i] = A[i]) + /\ (\A i \in new1, j \in new2 : + AA[i] \leq AA[j])} do + A := Ap; + return ; + end with ; + end with ; + end with; + end procedure + begin rqs : while Uns # {} + do with next \in Uns + do Uns := Uns \ {next}; + if Cardinality(next) > 1 + then call Part(next) ; + else new := {} ; + end if ; + end with ; + rqs2 : Uns := Uns \cup new ; + end while; + end algorithm + +*) + +\* BEGIN TRANSLATION - the hash of the PCal code: PCal-1f05a46c32a9d2bb1934b39aad724eed +CONSTANT defaultInitValue +VARIABLES A, Uns, new, pc, stack, parg + +vars == << A, Uns, new, pc, stack, parg >> + +Init == (* Global variables *) + /\ A \in UNION {[1..N -> 1..N] : N \in 0..MaxLen} + /\ Uns = {1..Len(A)} + /\ new = {} + (* Procedure Part *) + /\ parg = defaultInitValue + /\ stack = << >> + /\ pc = "rqs" + +pt1 == /\ pc = "pt1" + /\ \E new1 \in {Min(parg)..piv : piv \in parg \ {Max(parg)}}: + LET new2 == parg \ new1 IN + /\ new' = {new1, new2} + /\ \E Ap \in {AA \in PermsOf(A) : + (\A i \in 1..Len(A) \ parg : AA[i] = A[i]) + /\ (\A i \in new1, j \in new2 : + AA[i] \leq AA[j])}: + /\ A' = Ap + /\ pc' = Head(stack).pc + /\ parg' = Head(stack).parg + /\ stack' = Tail(stack) + /\ Uns' = Uns + +Part == pt1 + +rqs == /\ pc = "rqs" + /\ IF Uns # {} + THEN /\ \E next \in Uns: + /\ Uns' = Uns \ {next} + /\ IF Cardinality(next) > 1 + THEN /\ /\ parg' = next + /\ stack' = << [ procedure |-> "Part", + pc |-> "rqs2", + parg |-> parg ] >> + \o stack + /\ pc' = "pt1" + /\ new' = new + ELSE /\ new' = {} + /\ pc' = "rqs2" + /\ UNCHANGED << stack, parg >> + ELSE /\ pc' = "Done" + /\ UNCHANGED << Uns, new, stack, parg >> + /\ A' = A + +rqs2 == /\ pc = "rqs2" + /\ Uns' = (Uns \cup new) + /\ pc' = "rqs" + /\ UNCHANGED << A, new, stack, parg >> + +(* Allow infinite stuttering to prevent deadlock on termination. *) +Terminating == pc = "Done" /\ UNCHANGED vars + +Next == Part \/ rqs \/ rqs2 + \/ Terminating + +Spec == /\ Init /\ [][Next]_vars + /\ WF_vars(Next) + +Termination == <>(pc = "Done") + +\* END TRANSLATION - the hash of the generated TLA code (remove to silence divergence warnings): TLA-4de5033005d037379ed702fb72c5f705 + +Invariant == + (pc = "Done") => \A i, j \in 1..Len(A) : + (i < j) => A[i] \leq A[j] +============================================================================= diff --git a/tlatools/test-model/pcal/RealQuicksort2.cfg b/tlatools/test-model/pcal/RealQuicksort2.cfg new file mode 100644 index 0000000000000000000000000000000000000000..11946ee160e9c567089c7a04b4e059fdf484a7a1 --- /dev/null +++ b/tlatools/test-model/pcal/RealQuicksort2.cfg @@ -0,0 +1,6 @@ +SPECIFICATION Spec +PROPERTY Termination +CONSTANT defaultInitValue = defaultInitValue +\* Add statements after this line. +CONSTANTS MaxLen = 3 +INVARIANT Invariant diff --git a/tlatools/test-model/pcal/RealQuicksort2.tla b/tlatools/test-model/pcal/RealQuicksort2.tla new file mode 100644 index 0000000000000000000000000000000000000000..c30fb7d371fe3cdf540e8fcef096b352600abd20 --- /dev/null +++ b/tlatools/test-model/pcal/RealQuicksort2.tla @@ -0,0 +1,124 @@ +---------------------------- MODULE RealQuicksort2 -------------------------- +EXTENDS Naturals, Sequences, FiniteSets + +CONSTANT MaxLen + +ASSUME MaxLen \in Nat + +PermsOf(Arr) == + LET Automorphism(S) == { f \in [S -> S] : + \A y \in S : \E x \in S : f[x] = y } + f ** g == [x \in DOMAIN g |-> f[g[x]]] + IN { Arr ** f : f \in Automorphism(DOMAIN Arr) } + +Min(S) == CHOOSE i \in S : \A j \in S : i \leq j +Max(S) == CHOOSE i \in S : \A j \in S : i \geq j + +(* + +--algorithm RealQuicksort2 + variables A \in UNION {[1..N -> 1..N] : N \in 0..MaxLen}; + Uns = {1..Len(A)} ; + new = {} ; + next = {} ; + procedure Part(parg) + begin pt1 : with new1 \in + {Min(parg)..piv : piv \in parg \ {Max(parg)}} do + with new2 = parg \ new1 do + new := {new1, new2} ; + with Ap \in + {AA \in PermsOf(A) : + (\A i \in 1..Len(A) \ parg : AA[i] = A[i]) + /\ (\A i \in new1, j \in new2 : + AA[i] \leq AA[j])} do + A := Ap; + return ; + end with ; + end with ; + end with; + end procedure; + begin rqs : while Uns # {} + do with nxt \in Uns do next := nxt ; + end with ; + Uns := Uns \ {next}; + if Cardinality(next) > 1 + then call Part(next) ; + rqs2 : Uns := Uns \cup new ; + end if ; + end while; + end algorithm + +*) + +\* BEGIN TRANSLATION - the hash of the PCal code: PCal-23d3f8a2c70b06b61c3d312c6981bc33 +CONSTANT defaultInitValue +VARIABLES A, Uns, new, next, pc, stack, parg + +vars == << A, Uns, new, next, pc, stack, parg >> + +Init == (* Global variables *) + /\ A \in UNION {[1..N -> 1..N] : N \in 0..MaxLen} + /\ Uns = {1..Len(A)} + /\ new = {} + /\ next = {} + (* Procedure Part *) + /\ parg = defaultInitValue + /\ stack = << >> + /\ pc = "rqs" + +pt1 == /\ pc = "pt1" + /\ \E new1 \in {Min(parg)..piv : piv \in parg \ {Max(parg)}}: + LET new2 == parg \ new1 IN + /\ new' = {new1, new2} + /\ \E Ap \in {AA \in PermsOf(A) : + (\A i \in 1..Len(A) \ parg : AA[i] = A[i]) + /\ (\A i \in new1, j \in new2 : + AA[i] \leq AA[j])}: + /\ A' = Ap + /\ pc' = Head(stack).pc + /\ parg' = Head(stack).parg + /\ stack' = Tail(stack) + /\ UNCHANGED << Uns, next >> + +Part == pt1 + +rqs == /\ pc = "rqs" + /\ IF Uns # {} + THEN /\ \E nxt \in Uns: + next' = nxt + /\ Uns' = Uns \ {next'} + /\ IF Cardinality(next') > 1 + THEN /\ /\ parg' = next' + /\ stack' = << [ procedure |-> "Part", + pc |-> "rqs2", + parg |-> parg ] >> + \o stack + /\ pc' = "pt1" + ELSE /\ pc' = "rqs" + /\ UNCHANGED << stack, parg >> + ELSE /\ pc' = "Done" + /\ UNCHANGED << Uns, next, stack, parg >> + /\ UNCHANGED << A, new >> + +rqs2 == /\ pc = "rqs2" + /\ Uns' = (Uns \cup new) + /\ pc' = "rqs" + /\ UNCHANGED << A, new, next, stack, parg >> + +(* Allow infinite stuttering to prevent deadlock on termination. *) +Terminating == pc = "Done" /\ UNCHANGED vars + +Next == Part \/ rqs \/ rqs2 + \/ Terminating + +Spec == /\ Init /\ [][Next]_vars + /\ WF_vars(Next) + +Termination == <>(pc = "Done") + +\* END TRANSLATION - the hash of the generated TLA code (remove to silence divergence warnings): TLA-86e50a32c6061aae8bbd3a4da8513951 + +Invariant == + (pc = "Done") => \A i, j \in 1..Len(A) : + (i < j) => A[i] \leq A[j] +============================================================================= diff --git a/tlatools/test-model/pcal/ReallySimpleMultiProc.tla b/tlatools/test-model/pcal/ReallySimpleMultiProc.tla new file mode 100644 index 0000000000000000000000000000000000000000..29501d33d7111b3f6840a6d11a65815e21105241 --- /dev/null +++ b/tlatools/test-model/pcal/ReallySimpleMultiProc.tla @@ -0,0 +1,89 @@ +--------------------------- MODULE ReallySimpleMultiProc -------------------------- +EXTENDS Naturals, TLC + +(* +--algorithm SimpleMultiProc + variables + x = [i \in ProcSet |-> CASE i = 41 -> 1 [] + i = 42 -> 2 [] + i = 43 -> 3]; + sum = 0 ; + done = {}; + process ProcA = 41 + variable y = 0; + begin a1 : sum := sum + y + x [ 41 ] || + done := done \cup { 41 } ; + a2 : when done = { 41, 42, 43 } ; + when Print ( sum , TRUE ) ; + end process + process ProcB \in {42, 43} + variable z \in {2, 3} ; + begin b1 : sum := sum + z + x [ self ] ; + b2 : done := done \cup { self } ; + end process + end algorithm + +*) + +\* BEGIN TRANSLATION - the hash of the PCal code: PCal-489e6572dfcfeaa9e2ac64d8c5013ef0 +VARIABLES x, sum, done, pc, y, z + +vars == << x, sum, done, pc, y, z >> + +ProcSet == {41} \cup ({42, 43}) + +Init == (* Global variables *) + /\ x = [i \in ProcSet |-> CASE i = 41 -> 1 [] + i = 42 -> 2 [] + i = 43 -> 3] + /\ sum = 0 + /\ done = {} + (* Process ProcA *) + /\ y = 0 + (* Process ProcB *) + /\ z \in [{42, 43} -> {2, 3}] + /\ pc = [self \in ProcSet |-> CASE self = 41 -> "a1" + [] self \in {42, 43} -> "b1"] + +a1 == /\ pc[41] = "a1" + /\ /\ done' = (done \cup { 41 }) + /\ sum' = sum + y + x [ 41 ] + /\ pc' = [pc EXCEPT ![41] = "a2"] + /\ UNCHANGED << x, y, z >> + +a2 == /\ pc[41] = "a2" + /\ done = { 41, 42, 43 } + /\ Print ( sum , TRUE ) + /\ pc' = [pc EXCEPT ![41] = "Done"] + /\ UNCHANGED << x, sum, done, y, z >> + +ProcA == a1 \/ a2 + +b1(self) == /\ pc[self] = "b1" + /\ sum' = sum + z[self] + x [ self ] + /\ pc' = [pc EXCEPT ![self] = "b2"] + /\ UNCHANGED << x, done, y, z >> + +b2(self) == /\ pc[self] = "b2" + /\ done' = (done \cup { self }) + /\ pc' = [pc EXCEPT ![self] = "Done"] + /\ UNCHANGED << x, sum, y, z >> + +ProcB(self) == b1(self) \/ b2(self) + +(* Allow infinite stuttering to prevent deadlock on termination. *) +Terminating == /\ \A self \in ProcSet: pc[self] = "Done" + /\ UNCHANGED vars + +Next == ProcA + \/ (\E self \in {42, 43}: ProcB(self)) + \/ Terminating + +Spec == /\ Init /\ [][Next]_vars + /\ WF_vars(ProcA) + /\ \A self \in {42, 43} : WF_vars(ProcB(self)) + +Termination == <>(\A self \in ProcSet: pc[self] = "Done") + +\* END TRANSLATION - the hash of the generated TLA code (remove to silence divergence warnings): TLA-9c6ea370932e887119161ccedba84a47 +============================================================================= diff --git a/tlatools/test-model/pcal/SBB.cfg b/tlatools/test-model/pcal/SBB.cfg new file mode 100644 index 0000000000000000000000000000000000000000..9acb2839f67c4a2f76f25b5185c0309d20896a7a --- /dev/null +++ b/tlatools/test-model/pcal/SBB.cfg @@ -0,0 +1,11 @@ +SPECIFICATION Spec +\* Add statements after this line. +CONSTANT Pid = { p0, p1 } +CONSTANT NoPid = NoPid + +CONSTANT Buf = { b0, b1, b2, b3 } +CONSTANT NoBuf = NoBuf + +INVARIANT Immutability + +CONSTRAINT Constraint diff --git a/tlatools/test-model/pcal/SBB.tla b/tlatools/test-model/pcal/SBB.tla new file mode 100644 index 0000000000000000000000000000000000000000..9bb98be275aa2ee48342de98a3c649a054c5ab1f --- /dev/null +++ b/tlatools/test-model/pcal/SBB.tla @@ -0,0 +1,154 @@ +------------------------------- MODULE SBB ------------------------------- + +EXTENDS Naturals, Sequences, TLC + + +(**************************************************************************) +(* The StringBuilder bug. *) +(**************************************************************************) + +CONSTANT Pid \* set of process ids +NoPid == CHOOSE p : p \notin Pid + +CONSTANT Buf \* set of buffers +NoBuf == CHOOSE b : b \notin Buf + + + +(*--algorithm sbb + + variables + sb = [ owner |-> NoPid, buf |-> CHOOSE b \in Buf : TRUE ]; + availablebuffers = Buf \ {sb.buf}; + publishedbuffers = {}; + + + process work \in Pid + variable + buf = NoBuf; + op = {}; + begin + Loop: + while TRUE do + with lop \in { "Publish", "Modify" } do + op := lop; + end with; + + if (op = "Publish") then + buf := sb.buf; + Publish1: + if sb.owner # self /\ sb.owner # NoPid then + buf := CHOOSE b \in availablebuffers : TRUE; + availablebuffers := availablebuffers \ {buf}; + else + Publish2: + sb.owner := NoPid; + end if; + Publish3: + publishedbuffers := publishedbuffers \cup {buf}; + + + else + buf := sb.buf; + Modify1: + if sb.owner # self then + buf := CHOOSE b \in availablebuffers : TRUE; + availablebuffers := availablebuffers \ {buf}; + end if; + Modify2: + \* assert buf \notin publishedbuffers; + sb.owner := self; + Modify3: + sb.buf := buf; + end if + end while; + end process; +end algorithm + +*) + + +\* BEGIN TRANSLATION - the hash of the PCal code: PCal-28938b6571490e0f82ac59c63d6f47e4 +VARIABLES sb, availablebuffers, publishedbuffers, pc, buf, op + +vars == << sb, availablebuffers, publishedbuffers, pc, buf, op >> + +ProcSet == (Pid) + +Init == (* Global variables *) + /\ sb = [ owner |-> NoPid, buf |-> CHOOSE b \in Buf : TRUE ] + /\ availablebuffers = Buf \ {sb.buf} + /\ publishedbuffers = {} + (* Process work *) + /\ buf = [self \in Pid |-> NoBuf] + /\ op = [self \in Pid |-> {}] + /\ pc = [self \in ProcSet |-> "Loop"] + +Loop(self) == /\ pc[self] = "Loop" + /\ \E lop \in { "Publish", "Modify" }: + op' = [op EXCEPT ![self] = lop] + /\ IF (op'[self] = "Publish") + THEN /\ buf' = [buf EXCEPT ![self] = sb.buf] + /\ pc' = [pc EXCEPT ![self] = "Publish1"] + ELSE /\ buf' = [buf EXCEPT ![self] = sb.buf] + /\ pc' = [pc EXCEPT ![self] = "Modify1"] + /\ UNCHANGED << sb, availablebuffers, publishedbuffers >> + +Publish1(self) == /\ pc[self] = "Publish1" + /\ IF sb.owner # self /\ sb.owner # NoPid + THEN /\ buf' = [buf EXCEPT ![self] = CHOOSE b \in availablebuffers : TRUE] + /\ availablebuffers' = availablebuffers \ {buf'[self]} + /\ pc' = [pc EXCEPT ![self] = "Publish3"] + ELSE /\ pc' = [pc EXCEPT ![self] = "Publish2"] + /\ UNCHANGED << availablebuffers, buf >> + /\ UNCHANGED << sb, publishedbuffers, op >> + +Publish2(self) == /\ pc[self] = "Publish2" + /\ sb' = [sb EXCEPT !.owner = NoPid] + /\ pc' = [pc EXCEPT ![self] = "Publish3"] + /\ UNCHANGED << availablebuffers, publishedbuffers, buf, op >> + +Publish3(self) == /\ pc[self] = "Publish3" + /\ publishedbuffers' = (publishedbuffers \cup {buf[self]}) + /\ pc' = [pc EXCEPT ![self] = "Loop"] + /\ UNCHANGED << sb, availablebuffers, buf, op >> + +Modify1(self) == /\ pc[self] = "Modify1" + /\ IF sb.owner # self + THEN /\ buf' = [buf EXCEPT ![self] = CHOOSE b \in availablebuffers : TRUE] + /\ availablebuffers' = availablebuffers \ {buf'[self]} + ELSE /\ TRUE + /\ UNCHANGED << availablebuffers, buf >> + /\ pc' = [pc EXCEPT ![self] = "Modify2"] + /\ UNCHANGED << sb, publishedbuffers, op >> + +Modify2(self) == /\ pc[self] = "Modify2" + /\ sb' = [sb EXCEPT !.owner = self] + /\ pc' = [pc EXCEPT ![self] = "Modify3"] + /\ UNCHANGED << availablebuffers, publishedbuffers, buf, op >> + +Modify3(self) == /\ pc[self] = "Modify3" + /\ sb' = [sb EXCEPT !.buf = buf[self]] + /\ pc' = [pc EXCEPT ![self] = "Loop"] + /\ UNCHANGED << availablebuffers, publishedbuffers, buf, op >> + +work(self) == Loop(self) \/ Publish1(self) \/ Publish2(self) + \/ Publish3(self) \/ Modify1(self) \/ Modify2(self) + \/ Modify3(self) + +Next == (\E self \in Pid: work(self)) + +Spec == Init /\ [][Next]_vars + +\* END TRANSLATION - the hash of the generated TLA code (remove to silence divergence warnings): TLA-0476e94d7836683a2a4a6e344c5a0a7b + + +Immutability == + \A self \in ProcSet : + pc[self] = "Modify2" => buf[self] \notin publishedbuffers + +Constraint == + availablebuffers # {} + + +============================================================================ diff --git a/tlatools/test-model/pcal/SemaphoreMutex.cfg b/tlatools/test-model/pcal/SemaphoreMutex.cfg new file mode 100644 index 0000000000000000000000000000000000000000..de1000468b848d4daa608674a58a8b347b759671 --- /dev/null +++ b/tlatools/test-model/pcal/SemaphoreMutex.cfg @@ -0,0 +1,5 @@ +SPECIFICATION Spec +\* Add statements after this line. +CONSTANT N = 3 +INVARIANT Invariant +PROPERTY Liveness diff --git a/tlatools/test-model/pcal/SemaphoreMutex.tla b/tlatools/test-model/pcal/SemaphoreMutex.tla new file mode 100644 index 0000000000000000000000000000000000000000..d7332cbc3dbd083329985041c4b9ac6930f91ff3 --- /dev/null +++ b/tlatools/test-model/pcal/SemaphoreMutex.tla @@ -0,0 +1,75 @@ +---------------------------- MODULE SemaphoreMutex -------------------------- + +EXTENDS Naturals + +CONSTANT N + +ASSUME N \in Nat + +(********************** +--algorithm SemaphoreMutex +variables sem = 1 ; +macro P(s) begin when s > 0 ; + s := s - 1 ; +end macro + +macro V(s) begin s := s + 1 ; +end macro + +process Proc \in 1..N +begin +start : while TRUE + do enter : P(sem) ; + cs : skip ; + exit : V(sem) ; + end while ; +end process +end algorithm + + +***********************) + +\* BEGIN TRANSLATION - the hash of the PCal code: PCal-44c6b54854f894e836bedeb86826fe34 +VARIABLES sem, pc + +vars == << sem, pc >> + +ProcSet == (1..N) + +Init == (* Global variables *) + /\ sem = 1 + /\ pc = [self \in ProcSet |-> "start"] + +start(self) == /\ pc[self] = "start" + /\ pc' = [pc EXCEPT ![self] = "enter"] + /\ sem' = sem + +enter(self) == /\ pc[self] = "enter" + /\ sem > 0 + /\ sem' = sem - 1 + /\ pc' = [pc EXCEPT ![self] = "cs"] + +cs(self) == /\ pc[self] = "cs" + /\ TRUE + /\ pc' = [pc EXCEPT ![self] = "exit"] + /\ sem' = sem + +exit(self) == /\ pc[self] = "exit" + /\ sem' = sem + 1 + /\ pc' = [pc EXCEPT ![self] = "start"] + +Proc(self) == start(self) \/ enter(self) \/ cs(self) \/ exit(self) + +Next == (\E self \in 1..N: Proc(self)) + +Spec == /\ Init /\ [][Next]_vars + /\ \A self \in 1..N : SF_vars(Proc(self)) + +\* END TRANSLATION - the hash of the generated TLA code (remove to silence divergence warnings): TLA-30232ad9c07579ad742b84411a6a279a + +inCS(i) == (pc[i] = "cs") + +Invariant == \A i, k \in 1..N : (i # k) => ~ (inCS(i) /\ inCS(k)) + +Liveness == \A i \in 1..N : []<> inCS(i) +============================================================================= diff --git a/tlatools/test-model/pcal/SimpleLoop.tla b/tlatools/test-model/pcal/SimpleLoop.tla new file mode 100644 index 0000000000000000000000000000000000000000..53e5993f1429a48993c45f292576e69c15f78048 --- /dev/null +++ b/tlatools/test-model/pcal/SimpleLoop.tla @@ -0,0 +1,46 @@ +----------------------------- MODULE SimpleLoop ----------------------------- +EXTENDS Naturals, TLC +(* + + --algorithm SimpleLoop + variable x = 0; + begin a : while x < 10 + do x := x+1 ; + skip ; + assert x \in 1..10; + end while ; + end algorithm +*) + +\* BEGIN TRANSLATION - the hash of the PCal code: PCal-33bbd786fd9a21815a80c1c8d918b5c0 +VARIABLES x, pc + +vars == << x, pc >> + +Init == (* Global variables *) + /\ x = 0 + /\ pc = "a" + +a == /\ pc = "a" + /\ IF x < 10 + THEN /\ x' = x+1 + /\ TRUE + /\ Assert(x' \in 1..10, + "Failure of assertion at line 10, column 21.") + /\ pc' = "a" + ELSE /\ pc' = "Done" + /\ x' = x + +(* Allow infinite stuttering to prevent deadlock on termination. *) +Terminating == pc = "Done" /\ UNCHANGED vars + +Next == a + \/ Terminating + +Spec == /\ Init /\ [][Next]_vars + /\ WF_vars(Next) + +Termination == <>(pc = "Done") + +\* END TRANSLATION - the hash of the generated TLA code (remove to silence divergence warnings): TLA-d8f87f4745585bbecc6137f522211233 +============================================================================= diff --git a/tlatools/test-model/pcal/SimpleLoopWithProcedure.tla b/tlatools/test-model/pcal/SimpleLoopWithProcedure.tla new file mode 100644 index 0000000000000000000000000000000000000000..b69c62963c3830ea211473710cb900d28b19cd86 --- /dev/null +++ b/tlatools/test-model/pcal/SimpleLoopWithProcedure.tla @@ -0,0 +1,81 @@ +---------------------- MODULE SimpleLoopWithProcedure ---------------------- +EXTENDS Naturals, Sequences, TLC + +(* + +--algorithm SimpleLoopWithProcedure + variable x = 0; y \in {1, 2}; n = 0; i = 0; + procedure Incr(incr = 0) + variable z = 2; + begin i1 : x := incr + z + x; + i2 : return; + end procedure + begin a : while i < 10 + do when Print(x, TRUE); + i := i + 1 ; + call Incr(y) ; + end while ; + end algorithm + +*) + + +\* BEGIN TRANSLATION - the hash of the PCal code: PCal-925d38ef233a8a8c33b34bb25ba0c30d +VARIABLES x, y, n, i, pc, stack, incr, z + +vars == << x, y, n, i, pc, stack, incr, z >> + +Init == (* Global variables *) + /\ x = 0 + /\ y \in {1, 2} + /\ n = 0 + /\ i = 0 + (* Procedure Incr *) + /\ incr = 0 + /\ z = 2 + /\ stack = << >> + /\ pc = "a" + +i1 == /\ pc = "i1" + /\ x' = incr + z + x + /\ pc' = "i2" + /\ UNCHANGED << y, n, i, stack, incr, z >> + +i2 == /\ pc = "i2" + /\ pc' = Head(stack).pc + /\ z' = Head(stack).z + /\ incr' = Head(stack).incr + /\ stack' = Tail(stack) + /\ UNCHANGED << x, y, n, i >> + +Incr == i1 \/ i2 + +a == /\ pc = "a" + /\ IF i < 10 + THEN /\ Print(x, TRUE) + /\ i' = i + 1 + /\ /\ incr' = y + /\ stack' = << [ procedure |-> "Incr", + pc |-> "a", + z |-> z, + incr |-> incr ] >> + \o stack + /\ z' = 2 + /\ pc' = "i1" + ELSE /\ pc' = "Done" + /\ UNCHANGED << i, stack, incr, z >> + /\ UNCHANGED << x, y, n >> + +(* Allow infinite stuttering to prevent deadlock on termination. *) +Terminating == pc = "Done" /\ UNCHANGED vars + +Next == Incr \/ a + \/ Terminating + +Spec == /\ Init /\ [][Next]_vars + /\ WF_vars(Next) + +Termination == <>(pc = "Done") + +\* END TRANSLATION - the hash of the generated TLA code (remove to silence divergence warnings): TLA-1145b37773754e0c960b38596f8082a0 +============================================================================= diff --git a/tlatools/test-model/pcal/SimpleMultiProc.tla b/tlatools/test-model/pcal/SimpleMultiProc.tla new file mode 100644 index 0000000000000000000000000000000000000000..5c2a3165b83729971a9a55114e1eb8b026fb52e0 --- /dev/null +++ b/tlatools/test-model/pcal/SimpleMultiProc.tla @@ -0,0 +1,162 @@ +--------------------------- MODULE SimpleMultiProc -------------------------- +EXTENDS Naturals, Sequences, TLC + +(* +--algorithm SimpleMultiProc + variables + x = [i \in ProcSet |-> CASE i = 41 -> 1 [] + i = 42 -> 2 [] + i = 43 -> 3 [] + i = 44 -> 4 [] + i = 45 -> 5]; + sum = 0 ; + done = {}; + procedure AddMe(me = 0) + variable y = 0; + begin am: done := done \cup { me } ; + return ; + end procedure + process ProcA = 41 + variable y = 0; + begin a1 : sum := sum + y + x [ 41 ] || + y := sum ; + a2 : call AddMe(41) ; + a3 : when done = { 41, 42, 43, 44, 45 } ; + end process + process ProcB \in 42 .. 43 + variable z \in {2, 3} ; + begin b1 : sum := sum + z + x [ self ] ; + b2 : call AddMe(self); + end process + process ProcC \in { 44, + 45 } + variable z \in {4, 5} ; + begin c1 : sum := sum + z + x [ self ] ; + c2 : call AddMe(self) ; + end process + end algorithm + +*) + +\* BEGIN TRANSLATION +\* Process variable y of process ProcA at line 20 col 17 changed to y_ +\* Process variable z of process ProcB at line 27 col 17 changed to z_ +VARIABLES x, sum, done, pc, stack, me, y, y_, z_, z + +vars == << x, sum, done, pc, stack, me, y, y_, z_, z >> + +ProcSet == {41} \cup (42 .. 43) \cup ({ 44, + 45 }) + +Init == (* Global variables *) + /\ x = [i \in ProcSet |-> CASE i = 41 -> 1 [] + i = 42 -> 2 [] + i = 43 -> 3 [] + i = 44 -> 4 [] + i = 45 -> 5] + /\ sum = 0 + /\ done = {} + (* Procedure AddMe *) + /\ me = [ self \in ProcSet |-> 0] + /\ y = [ self \in ProcSet |-> 0] + (* Process ProcA *) + /\ y_ = 0 + (* Process ProcB *) + /\ z_ \in [42 .. 43 -> {2, 3}] + (* Process ProcC *) + /\ z \in [{ 44, + 45 } -> {4, 5}] + /\ stack = [self \in ProcSet |-> << >>] + /\ pc = [self \in ProcSet |-> CASE self = 41 -> "a1" + [] self \in 42 .. 43 -> "b1" + [] self \in { 44, + 45 } -> "c1"] + +am(self) == /\ pc[self] = "am" + /\ done' = (done \cup { me[self] }) + /\ pc' = [pc EXCEPT ![self] = Head(stack[self]).pc] + /\ y' = [y EXCEPT ![self] = Head(stack[self]).y] + /\ me' = [me EXCEPT ![self] = Head(stack[self]).me] + /\ stack' = [stack EXCEPT ![self] = Tail(stack[self])] + /\ UNCHANGED << x, sum, y_, z_, z >> + +AddMe(self) == am(self) + +a1 == /\ pc[41] = "a1" + /\ /\ sum' = sum + y_ + x [ 41 ] + /\ y_' = sum + /\ pc' = [pc EXCEPT ![41] = "a2"] + /\ UNCHANGED << x, done, stack, me, y, z_, z >> + +a2 == /\ pc[41] = "a2" + /\ /\ me' = [me EXCEPT ![41] = 41] + /\ stack' = [stack EXCEPT ![41] = << [ procedure |-> "AddMe", + pc |-> "a3", + y |-> y[41], + me |-> me[41] ] >> + \o stack[41]] + /\ y' = [y EXCEPT ![41] = 0] + /\ pc' = [pc EXCEPT ![41] = "am"] + /\ UNCHANGED << x, sum, done, y_, z_, z >> + +a3 == /\ pc[41] = "a3" + /\ done = { 41, 42, 43, 44, 45 } + /\ pc' = [pc EXCEPT ![41] = "Done"] + /\ UNCHANGED << x, sum, done, stack, me, y, y_, z_, z >> + +ProcA == a1 \/ a2 \/ a3 + +b1(self) == /\ pc[self] = "b1" + /\ sum' = sum + z_[self] + x [ self ] + /\ pc' = [pc EXCEPT ![self] = "b2"] + /\ UNCHANGED << x, done, stack, me, y, y_, z_, z >> + +b2(self) == /\ pc[self] = "b2" + /\ /\ me' = [me EXCEPT ![self] = self] + /\ stack' = [stack EXCEPT ![self] = << [ procedure |-> "AddMe", + pc |-> "Done", + y |-> y[self], + me |-> me[self] ] >> + \o stack[self]] + /\ y' = [y EXCEPT ![self] = 0] + /\ pc' = [pc EXCEPT ![self] = "am"] + /\ UNCHANGED << x, sum, done, y_, z_, z >> + +ProcB(self) == b1(self) \/ b2(self) + +c1(self) == /\ pc[self] = "c1" + /\ sum' = sum + z[self] + x [ self ] + /\ pc' = [pc EXCEPT ![self] = "c2"] + /\ UNCHANGED << x, done, stack, me, y, y_, z_, z >> + +c2(self) == /\ pc[self] = "c2" + /\ /\ me' = [me EXCEPT ![self] = self] + /\ stack' = [stack EXCEPT ![self] = << [ procedure |-> "AddMe", + pc |-> "Done", + y |-> y[self], + me |-> me[self] ] >> + \o stack[self]] + /\ y' = [y EXCEPT ![self] = 0] + /\ pc' = [pc EXCEPT ![self] = "am"] + /\ UNCHANGED << x, sum, done, y_, z_, z >> + +ProcC(self) == c1(self) \/ c2(self) + +Next == ProcA + \/ (\E self \in ProcSet: AddMe(self)) + \/ (\E self \in 42 .. 43: ProcB(self)) + \/ (\E self \in { 44, + 45 }: ProcC(self)) + \/ (* Disjunct to prevent deadlock on termination *) + ((\A self \in ProcSet: pc[self] = "Done") /\ UNCHANGED vars) + +Spec == /\ Init /\ [][Next]_vars + /\ WF_vars(ProcA) /\ WF_vars(AddMe(41)) + /\ \A self \in 42 .. 43 : WF_vars(ProcB(self)) /\ WF_vars(AddMe(self)) + /\ \A self \in { 44, + 45 } : WF_vars(ProcC(self)) /\ WF_vars(AddMe(self)) + +Termination == <>(\A self \in ProcSet: pc[self] = "Done") + +\* END TRANSLATION +============================================================================= diff --git a/tlatools/test-model/pcal/StackTest.tla b/tlatools/test-model/pcal/StackTest.tla new file mode 100644 index 0000000000000000000000000000000000000000..85250e43848213aa4222f403709e0cea9370df27 --- /dev/null +++ b/tlatools/test-model/pcal/StackTest.tla @@ -0,0 +1,73 @@ +Bug: Currently putting an extra `self' subscript in assignment to + `stack'. + + + +----------- MODULE StackTest ----------- +EXTENDS Sequences, Naturals, TLC + +(*************************************************************************** +--algorithm StackAndPCTest + procedure P(a=42) + begin P1: stack[self][1].a := stack[self][1].a + 1; + assert Head(stack[self]).a = 43 ; + assert pc[self] = "P1" ; + P2: return + end procedure + process Q \in 0..2 + begin Q1: assert \A i \in ProcSet : pc[i] \in {"P1", "P2", "Q1", "Done"}; + call P(22) ; + end process; + end algorithm + ***************************************************************************) +\* BEGIN TRANSLATION +VARIABLES pc, stack, a + +vars == << pc, stack, a >> + +ProcSet == (0..2) + +Init == (* Procedure P *) + /\ a = [ self \in ProcSet |-> 42] + /\ stack = [self \in ProcSet |-> << >>] + /\ pc = [self \in ProcSet |-> "Q1"] + +P1(self) == /\ pc[self] = "P1" + /\ stack' = [stack EXCEPT ![self][self][1].a = stack[self][1].a + 1] + /\ Assert(Head(stack'[self]).a = 43, + "Failure of assertion at line 13, column 17.") + /\ Assert(pc[self] = "P1", + "Failure of assertion at line 14, column 17.") + /\ pc' = [pc EXCEPT ![self] = "P2"] + /\ a' = a + +P2(self) == /\ pc[self] = "P2" + /\ pc' = [pc EXCEPT ![self] = Head(stack[self]).pc] + /\ a' = [a EXCEPT ![self] = Head(stack[self]).a] + /\ stack' = [stack EXCEPT ![self] = Tail(stack[self])] + +P(self) == P1(self) \/ P2(self) + +Q1(self) == /\ pc[self] = "Q1" + /\ Assert(\A i \in ProcSet : pc[i] \in {"P1", "P2", "Q1", "Done"}, + "Failure of assertion at line 18, column 16.") + /\ /\ a' = [a EXCEPT ![self] = 22] + /\ stack' = [stack EXCEPT ![self] = << [ procedure |-> "P", + pc |-> "Done", + a |-> a[self] ] >> + \o stack[self]] + /\ pc' = [pc EXCEPT ![self] = "P1"] + +Q(self) == Q1(self) + +Next == (\E self \in ProcSet: P(self)) + \/ (\E self \in 0..2: Q(self)) + \/ (* Disjunct to prevent deadlock on termination *) + ((\A self \in ProcSet: pc[self] = "Done") /\ UNCHANGED vars) + +Spec == Init /\ [][Next]_vars + +Termination == <>(\A self \in ProcSet: pc[self] = "Done") + +\* END TRANSLATION +======================================== diff --git a/tlatools/test-model/pcal/StarkMutex.cfg b/tlatools/test-model/pcal/StarkMutex.cfg new file mode 100644 index 0000000000000000000000000000000000000000..8459e52f22b00841a0b8d2bdaff8bd8581e93e04 --- /dev/null +++ b/tlatools/test-model/pcal/StarkMutex.cfg @@ -0,0 +1,5 @@ +\* Add statements after this line. +CONSTANT N = 3 +SPECIFICATION StarkSpec \* A comment +INVARIANT Mutex +PROPERTY StarvationFree diff --git a/tlatools/test-model/pcal/StarkMutex.tla b/tlatools/test-model/pcal/StarkMutex.tla new file mode 100644 index 0000000000000000000000000000000000000000..09087d4ff76786afa174bc584157f49325f587be --- /dev/null +++ b/tlatools/test-model/pcal/StarkMutex.tla @@ -0,0 +1,208 @@ +--algorithm StarkMutex + variables flag = [i \in 1..N |-> FALSE]; + next = 1; + empty = TRUE; + mutex = 1; + weakSem = [i \in 1..N |-> 0]; + process i \in 1..N + variables first = FALSE; + j = 1; + begin + in1: while (TRUE) do + flag[self] := TRUE; + first := FALSE; + in2: when mutex = 1; mutex := 0; + in3: if empty then + in4: empty := FALSE; + first := TRUE; + end if; + in5: mutex := 1; + in6: if ~ first then + when weakSem[self] = 1; weakSem[self] := 0; + end if; + cs: skip; + ex1: flag[self] := FALSE; + ex2: when mutex = 1; mutex := 0; + ex3: j := 1; + empty := TRUE; + ex4: while j \leq N do + if flag[IF next + j > N THEN next + j - N ELSE next + j] then + ex5: with n = IF next + j > N THEN next + j - N ELSE next + j do + next := n; + weakSem[n] := 1; + j := N + 1; + end with; + ex6: empty := FALSE; + else + ex7: j := j + 1; + end if; + end while; + ex8: mutex := 1; + nc: skip; + end while; + end process + end algorithm + + + +------------------ MODULE StarkMutex ------------------ + +EXTENDS Naturals, Sequences +CONSTANT N + +------------------------------------------------------- + + + +\* BEGIN TRANSLATION +VARIABLES flag, next, empty, mutex, weakSem, pc, first, j + +vars == << flag, next, empty, mutex, weakSem, pc, first, j >> + +ProcSet == (1..N) + +Init == (* Global variables *) + /\ flag = [i \in 1..N |-> FALSE] + /\ next = 1 + /\ empty = TRUE + /\ mutex = 1 + /\ weakSem = [i \in 1..N |-> 0] + (* Process i *) + /\ first = [self \in 1..N |-> FALSE] + /\ j = [self \in 1..N |-> 1] + /\ pc = [self \in ProcSet |-> "in1"] + +in1(self) == /\ pc[self] = "in1" + /\ flag' = [flag EXCEPT ![self] = TRUE] + /\ first' = [first EXCEPT ![self] = FALSE] + /\ pc' = [pc EXCEPT ![self] = "in2"] + /\ UNCHANGED << next, empty, mutex, weakSem, j >> + +in2(self) == /\ pc[self] = "in2" + /\ mutex = 1 + /\ mutex' = 0 + /\ pc' = [pc EXCEPT ![self] = "in3"] + /\ UNCHANGED << flag, next, empty, weakSem, first, j >> + +in3(self) == /\ pc[self] = "in3" + /\ IF empty + THEN /\ pc' = [pc EXCEPT ![self] = "in4"] + ELSE /\ pc' = [pc EXCEPT ![self] = "in5"] + /\ UNCHANGED << flag, next, empty, mutex, weakSem, first, j >> + +in4(self) == /\ pc[self] = "in4" + /\ empty' = FALSE + /\ first' = [first EXCEPT ![self] = TRUE] + /\ pc' = [pc EXCEPT ![self] = "in5"] + /\ UNCHANGED << flag, next, mutex, weakSem, j >> + +in5(self) == /\ pc[self] = "in5" + /\ mutex' = 1 + /\ pc' = [pc EXCEPT ![self] = "in6"] + /\ UNCHANGED << flag, next, empty, weakSem, first, j >> + +in6(self) == /\ pc[self] = "in6" + /\ IF ~ first[self] + THEN /\ weakSem[self] = 1 + /\ weakSem' = [weakSem EXCEPT ![self] = 0] + ELSE /\ TRUE + /\ UNCHANGED weakSem + /\ pc' = [pc EXCEPT ![self] = "cs"] + /\ UNCHANGED << flag, next, empty, mutex, first, j >> + +cs(self) == /\ pc[self] = "cs" + /\ TRUE + /\ pc' = [pc EXCEPT ![self] = "ex1"] + /\ UNCHANGED << flag, next, empty, mutex, weakSem, first, j >> + +ex1(self) == /\ pc[self] = "ex1" + /\ flag' = [flag EXCEPT ![self] = FALSE] + /\ pc' = [pc EXCEPT ![self] = "ex2"] + /\ UNCHANGED << next, empty, mutex, weakSem, first, j >> + +ex2(self) == /\ pc[self] = "ex2" + /\ mutex = 1 + /\ mutex' = 0 + /\ pc' = [pc EXCEPT ![self] = "ex3"] + /\ UNCHANGED << flag, next, empty, weakSem, first, j >> + +ex3(self) == /\ pc[self] = "ex3" + /\ j' = [j EXCEPT ![self] = 1] + /\ empty' = TRUE + /\ pc' = [pc EXCEPT ![self] = "ex4"] + /\ UNCHANGED << flag, next, mutex, weakSem, first >> + +ex4(self) == /\ pc[self] = "ex4" + /\ IF j[self] \leq N + THEN /\ IF flag[IF next + j[self] > N THEN next + j[self] - N ELSE next + j[self]] + THEN /\ pc' = [pc EXCEPT ![self] = "ex5"] + ELSE /\ pc' = [pc EXCEPT ![self] = "ex7"] + ELSE /\ pc' = [pc EXCEPT ![self] = "ex8"] + /\ UNCHANGED << flag, next, empty, mutex, weakSem, first, j >> + +ex5(self) == /\ pc[self] = "ex5" + /\ LET n == IF next + j[self] > N THEN next + j[self] - N ELSE next + j[self] IN + /\ next' = n + /\ weakSem' = [weakSem EXCEPT ![n] = 1] + /\ j' = [j EXCEPT ![self] = N + 1] + /\ pc' = [pc EXCEPT ![self] = "ex6"] + /\ UNCHANGED << flag, empty, mutex, first >> + +ex6(self) == /\ pc[self] = "ex6" + /\ empty' = FALSE + /\ pc' = [pc EXCEPT ![self] = "ex4"] + /\ UNCHANGED << flag, next, mutex, weakSem, first, j >> + +ex7(self) == /\ pc[self] = "ex7" + /\ j' = [j EXCEPT ![self] = j[self] + 1] + /\ pc' = [pc EXCEPT ![self] = "ex4"] + /\ UNCHANGED << flag, next, empty, mutex, weakSem, first >> + +ex8(self) == /\ pc[self] = "ex8" + /\ mutex' = 1 + /\ pc' = [pc EXCEPT ![self] = "nc"] + /\ UNCHANGED << flag, next, empty, weakSem, first, j >> + +nc(self) == /\ pc[self] = "nc" + /\ TRUE + /\ pc' = [pc EXCEPT ![self] = "in1"] + /\ UNCHANGED << flag, next, empty, mutex, weakSem, first, j >> + +i(self) == in1(self) \/ in2(self) \/ in3(self) \/ in4(self) \/ in5(self) + \/ in6(self) \/ cs(self) \/ ex1(self) \/ ex2(self) + \/ ex3(self) \/ ex4(self) \/ ex5(self) \/ ex6(self) + \/ ex7(self) \/ ex8(self) \/ nc(self) + +Next == (\E self \in 1..N: i(self)) + \/ (* Disjunct to prevent deadlock on termination *) + ((\A self \in ProcSet: pc[self] = "Done") /\ UNCHANGED vars) + +Spec == Init /\ [][Next]_vars + +Termination == <>(\A self \in ProcSet: pc[self] = "Done") + +\* END TRANSLATION + +StarkSpec == /\ Spec + /\ \A self \in ProcSet: + /\ WF_vars(in1(self)) + /\ WF_vars(in2(self)) + /\ WF_vars(in3(self)) + /\ WF_vars(in4(self)) + /\ WF_vars(in5(self)) + /\ WF_vars(in6(self)) + /\ WF_vars(ex1(self)) + /\ WF_vars(ex2(self)) + /\ WF_vars(ex3(self)) + /\ WF_vars(ex4(self)) + /\ WF_vars(ex5(self)) + /\ WF_vars(ex6(self)) + /\ WF_vars(ex7(self)) + /\ WF_vars(ex8(self)) + /\ WF_vars(cs(self)) + +Mutex == \A p1, p2 \in ProcSet: (pc[p1] = "cs" /\ pc[p2] = "pc") => (p1 = p2) + +StarvationFree == \A p \in ProcSet: pc[p] = "in1" ~> pc[p] = "cs" +======================================================= + diff --git a/tlatools/test-model/pcal/SubSub.tla b/tlatools/test-model/pcal/SubSub.tla new file mode 100644 index 0000000000000000000000000000000000000000..fff5f6a594b5b94f0dbb7e5a664cce38951d4d17 --- /dev/null +++ b/tlatools/test-model/pcal/SubSub.tla @@ -0,0 +1,70 @@ +Test that "[self]" subscripts are added to expressions within subscripts. + +------------------------- MODULE SubSub ---------------------------- +EXTENDS Naturals, TLC + +ServerID == {1} +ObjectID == {1} + +-------------------------------------------------------------------------- +(********* +--algorithm SubSub + process proc \in 1..3 + variables x = [i \in {"a", "b"} |-> 0] , + y = [i \in 5..6 |-> "a"] , + z + begin + lab : z := 5 ; + y[z] := "b" ; + x[y[z]] := 1 ; + assert x[y[z]] = 1 ; + assert y[z] = "b" + end process + +end algorithm +*****) + +\* BEGIN TRANSLATION - the hash of the PCal code: PCal-e4495880aa3211ed2f4c6f44d3918dc7 +CONSTANT defaultInitValue +VARIABLES pc, x, y, z + +vars == << pc, x, y, z >> + +ProcSet == (1..3) + +Init == (* Process proc *) + /\ x = [self \in 1..3 |-> [i \in {"a", "b"} |-> 0]] + /\ y = [self \in 1..3 |-> [i \in 5..6 |-> "a"]] + /\ z = [self \in 1..3 |-> defaultInitValue] + /\ pc = [self \in ProcSet |-> "lab"] + +lab(self) == /\ pc[self] = "lab" + /\ z' = [z EXCEPT ![self] = 5] + /\ y' = [y EXCEPT ![self][z'[self]] = "b"] + /\ x' = [x EXCEPT ![self][y'[self][z'[self]]] = 1] + /\ Assert(x'[self][y'[self][z'[self]]] = 1, + "Failure of assertion at line 20, column 12.") + /\ Assert(y'[self][z'[self]] = "b", + "Failure of assertion at line 21, column 12.") + /\ pc' = [pc EXCEPT ![self] = "Done"] + +proc(self) == lab(self) + +(* Allow infinite stuttering to prevent deadlock on termination. *) +Terminating == /\ \A self \in ProcSet: pc[self] = "Done" + /\ UNCHANGED vars + +Next == (\E self \in 1..3: proc(self)) + \/ Terminating + +Spec == Init /\ [][Next]_vars + +Termination == <>(\A self \in ProcSet: pc[self] = "Done") + +\* END TRANSLATION - the hash of the generated TLA code (remove to silence divergence warnings): TLA-4eda2626bbd263ed274c0990ecc17263 + +-------------------------------------------------------------------------- + + + +========================================================================== diff --git a/tlatools/test-model/pcal/SyncCons.cfg b/tlatools/test-model/pcal/SyncCons.cfg new file mode 100644 index 0000000000000000000000000000000000000000..a0e5c59a498af864a06c1a9b2ebc8143516de156 --- /dev/null +++ b/tlatools/test-model/pcal/SyncCons.cfg @@ -0,0 +1,8 @@ +SPECIFICATION Spec +\* Add statements after this line. +CONSTANTS N = 2 + t = 1 + Data = {0, 1} + bot = bot +INVARIANT C1 +INVARIANT C2 diff --git a/tlatools/test-model/pcal/SyncCons.tla b/tlatools/test-model/pcal/SyncCons.tla new file mode 100644 index 0000000000000000000000000000000000000000..63f32e354cbef760d5162bb0ae07dbde9b7d1d76 --- /dev/null +++ b/tlatools/test-model/pcal/SyncCons.tla @@ -0,0 +1,264 @@ +--algorithm SyncCons + variables clock = 0; + input \in Data; + round = [i \in 1..N |-> 0]; + buffer= { }; + crashed = { }; + +\***** Macros for sending and receiving messages + macro Send(i, j, msg) + begin + buffer := buffer \cup + {[from |-> i, + to |-> j, + msg |-> IF i \in crashed + THEN bot + ELSE msg]}; + end macro + + macro Receive(i, j, msg) + begin + when [from |-> i, + to |-> j, + msg |-> msg] \in buffer; + buffer := buffer + \ {[from |-> i, + to |-> j, + msg |-> msg]}; + end macro + +\***** Synchronous consensus protocol for crash failures +\***** A crashed process sends "bot" messages, which model timeouts + process Participant \in 1..N + variables output = bot; + procs = { }; + value = IF self = 1 + THEN input + ELSE bot; + recd = { }; + begin +s1: while round[self] < t + 1 do + when round[self] = clock; + procs := 1..N; +s2: while procs # { } do + with dest \in procs do + Send(self, dest, value); + procs := procs \ {dest}; + end with + end while; +s3: if self \notin crashed then + procs := 1..N; + recd := { }; +s4: while procs # { } do + with source \in Proc do + with data \in Data \cup {bot} do + Receive(source, self, data); + recd := recd \cup {data}; + procs := procs \ {source}; + end with + end with + end while; +s5: if recd \cap Data = { } + then value := bot; + else value := CHOOSE i \in Data: + i \in recd \cap Data; + end if; + end if; +s6: round[self] := round[self] + 1; + end while; + output := IF value = bot + THEN 0 + ELSE value; + end process; + +\***** Model of clock: ticks when all processes finish the current round + process Clock = N + 1 + begin +clock: while clock < t + 1 do + when \A i \in 1..N: round[i] = clock + 1; + clock := clock + 1; + end while; + end process; + +\***** Crashing processes + process Crash = N + 2 + begin +crash: while Cardinality(crashed) < t do + with x \in (1..N) \ crashed do + crashed := crashed \cup {x} + end with + end while + end process; + +end algorithm + +-------------------------------- MODULE SyncCons -------------------------------- +EXTENDS Naturals, FiniteSets, TLC + +CONSTANTS N, t, Data + +ASSUME N > t + +bot == CHOOSE v: v \notin Data + +Proc == 1..N + +\* BEGIN TRANSLATION - the hash of the PCal code: PCal-6c62906a7a71f4a60f9b9f0eecf7e59e +\* Label clock of process Clock at line 77 col 12 changed to clock_ +VARIABLES clock, input, round, buffer, crashed, pc, output, procs, value, + recd + +vars == << clock, input, round, buffer, crashed, pc, output, procs, value, + recd >> + +ProcSet == (1..N) \cup {N + 1} \cup {N + 2} + +Init == (* Global variables *) + /\ clock = 0 + /\ input \in Data + /\ round = [i \in 1..N |-> 0] + /\ buffer = { } + /\ crashed = { } + (* Process Participant *) + /\ output = [self \in 1..N |-> bot] + /\ procs = [self \in 1..N |-> { }] + /\ value = [self \in 1..N |-> IF self = 1 + THEN input + ELSE bot] + /\ recd = [self \in 1..N |-> { }] + /\ pc = [self \in ProcSet |-> CASE self \in 1..N -> "s1" + [] self = N + 1 -> "clock_" + [] self = N + 2 -> "crash"] + +s1(self) == /\ pc[self] = "s1" + /\ IF round[self] < t + 1 + THEN /\ round[self] = clock + /\ procs' = [procs EXCEPT ![self] = 1..N] + /\ pc' = [pc EXCEPT ![self] = "s2"] + /\ UNCHANGED output + ELSE /\ output' = [output EXCEPT ![self] = IF value[self] = bot + THEN 0 + ELSE value[self]] + /\ pc' = [pc EXCEPT ![self] = "Done"] + /\ procs' = procs + /\ UNCHANGED << clock, input, round, buffer, crashed, value, recd >> + +s2(self) == /\ pc[self] = "s2" + /\ IF procs[self] # { } + THEN /\ \E dest \in procs[self]: + /\ buffer' = ( buffer \cup + {[from |-> self, + to |-> dest, + msg |-> IF self \in crashed + THEN bot + ELSE value[self]]}) + /\ procs' = [procs EXCEPT ![self] = procs[self] \ {dest}] + /\ pc' = [pc EXCEPT ![self] = "s2"] + ELSE /\ pc' = [pc EXCEPT ![self] = "s3"] + /\ UNCHANGED << buffer, procs >> + /\ UNCHANGED << clock, input, round, crashed, output, value, recd >> + +s3(self) == /\ pc[self] = "s3" + /\ IF self \notin crashed + THEN /\ procs' = [procs EXCEPT ![self] = 1..N] + /\ recd' = [recd EXCEPT ![self] = { }] + /\ pc' = [pc EXCEPT ![self] = "s4"] + ELSE /\ pc' = [pc EXCEPT ![self] = "s6"] + /\ UNCHANGED << procs, recd >> + /\ UNCHANGED << clock, input, round, buffer, crashed, output, + value >> + +s4(self) == /\ pc[self] = "s4" + /\ IF procs[self] # { } + THEN /\ \E source \in Proc: + \E data \in Data \cup {bot}: + /\ [from |-> source, + to |-> self, + msg |-> data] \in buffer + /\ buffer' = buffer + \ {[from |-> source, + to |-> self, + msg |-> data]} + /\ recd' = [recd EXCEPT ![self] = recd[self] \cup {data}] + /\ procs' = [procs EXCEPT ![self] = procs[self] \ {source}] + /\ pc' = [pc EXCEPT ![self] = "s4"] + ELSE /\ pc' = [pc EXCEPT ![self] = "s5"] + /\ UNCHANGED << buffer, procs, recd >> + /\ UNCHANGED << clock, input, round, crashed, output, value >> + +s5(self) == /\ pc[self] = "s5" + /\ IF recd[self] \cap Data = { } + THEN /\ value' = [value EXCEPT ![self] = bot] + ELSE /\ value' = [value EXCEPT ![self] = CHOOSE i \in Data: + i \in recd[self] \cap Data] + /\ pc' = [pc EXCEPT ![self] = "s6"] + /\ UNCHANGED << clock, input, round, buffer, crashed, output, + procs, recd >> + +s6(self) == /\ pc[self] = "s6" + /\ round' = [round EXCEPT ![self] = round[self] + 1] + /\ pc' = [pc EXCEPT ![self] = "s1"] + /\ UNCHANGED << clock, input, buffer, crashed, output, procs, + value, recd >> + +Participant(self) == s1(self) \/ s2(self) \/ s3(self) \/ s4(self) + \/ s5(self) \/ s6(self) + +clock_ == /\ pc[N + 1] = "clock_" + /\ IF clock < t + 1 + THEN /\ \A i \in 1..N: round[i] = clock + 1 + /\ clock' = clock + 1 + /\ pc' = [pc EXCEPT ![N + 1] = "clock_"] + ELSE /\ pc' = [pc EXCEPT ![N + 1] = "Done"] + /\ clock' = clock + /\ UNCHANGED << input, round, buffer, crashed, output, procs, value, + recd >> + +Clock == clock_ + +crash == /\ pc[N + 2] = "crash" + /\ IF Cardinality(crashed) < t + THEN /\ \E x \in (1..N) \ crashed: + crashed' = (crashed \cup {x}) + /\ pc' = [pc EXCEPT ![N + 2] = "crash"] + ELSE /\ pc' = [pc EXCEPT ![N + 2] = "Done"] + /\ UNCHANGED crashed + /\ UNCHANGED << clock, input, round, buffer, output, procs, value, + recd >> + +Crash == crash + +(* Allow infinite stuttering to prevent deadlock on termination. *) +Terminating == /\ \A self \in ProcSet: pc[self] = "Done" + /\ UNCHANGED vars + +Next == Clock \/ Crash + \/ (\E self \in 1..N: Participant(self)) + \/ Terminating + +Spec == Init /\ [][Next]_vars + +Termination == <>(\A self \in ProcSet: pc[self] = "Done") + +\* END TRANSLATION - the hash of the generated TLA code (remove to silence divergence warnings): TLA-166884a9c4ec2ab81b93439149dfdc07 + +C1 == \A p1, p2 \in (1..N) \ crashed: + (pc[p1] = "Done" /\ pc[p2] = "Done") => (output[p1] = output[p2]) + +C2 == \A p \in (1..N) \ crashed: + ( /\ 1 \notin crashed + /\ pc[1] = "Done" + /\ pc[p] = "Done" ) => (output[p] = input) + +THEOREM Spec => [] C1 /\ [] C2 +================================================================================= +On derosa.ucsd.edu (dual processor G5) +Model checking completed. No error has been found. + Estimates of the probability that TLC did not check all reachable states + because two distinct states had the same fingerprint: + calculated (optimistic): 8.312778857584233E-8 + based on the actual fingerprints: 8.652893187423173E-9 +3093758 states generated, 619842 distinct states found, 0 states left on queue. +The depth of the complete state graph search is 81. +181.963u 2.589s 2:47.51 110.1% 0+0k 2+119io 0pf+0w + diff --git a/tlatools/test-model/pcal/Test.tla b/tlatools/test-model/pcal/Test.tla new file mode 100644 index 0000000000000000000000000000000000000000..cb68cd3b3f9ba05b447bf7428a293b9693cd8ea4 --- /dev/null +++ b/tlatools/test-model/pcal/Test.tla @@ -0,0 +1,105 @@ + +--algorithm bug +variables x = 0 ; y = 0 ; +macro foo() + begin if TRUE then if TRUE then y := 22 ; + else y := 42 end if + else with a = 47 ; b = 77 ; do + y := 27 + end with end if + end macro +procedure Bar() + begin Q: skip ; + foo() ; + return + end procedure +begin L1 : y := 1 ; + L3 : skip ; + if x > 0 then foo() + else x := 17 end if; + L2 : assert x = 17 ; + foo() ; + assert y = 22 +end algorithm + +--------- MODULE Test ------- +(* \* xxxx *) + +\* (* *) +EXTENDS Sequences, Naturals, TLC + + +\* BEGIN TRANSLATION - the hash of the PCal code: PCal-648cb8f5989caacb85bf4050478c7a20 +VARIABLES x, y, pc, stack + +vars == << x, y, pc, stack >> + +Init == (* Global variables *) + /\ x = 0 + /\ y = 0 + /\ stack = << >> + /\ pc = "L1" + +Q == /\ pc = "Q" + /\ TRUE + /\ IF TRUE + THEN /\ IF TRUE + THEN /\ y' = 22 + ELSE /\ y' = 42 + ELSE /\ LET a == 47 IN + LET b == 77 IN + y' = 27 + /\ pc' = Head(stack).pc + /\ stack' = Tail(stack) + /\ x' = x + +Bar == Q + +L1 == /\ pc = "L1" + /\ y' = 1 + /\ pc' = "L3" + /\ UNCHANGED << x, stack >> + +L3 == /\ pc = "L3" + /\ TRUE + /\ IF x > 0 + THEN /\ IF TRUE + THEN /\ IF TRUE + THEN /\ y' = 22 + ELSE /\ y' = 42 + ELSE /\ LET a == 47 IN + LET b == 77 IN + y' = 27 + /\ x' = x + ELSE /\ x' = 17 + /\ y' = y + /\ pc' = "L2" + /\ stack' = stack + +L2 == /\ pc = "L2" + /\ Assert(x = 17, "Failure of assertion at line 20, column 13.") + /\ IF TRUE + THEN /\ IF TRUE + THEN /\ y' = 22 + ELSE /\ y' = 42 + ELSE /\ LET a == 47 IN + LET b == 77 IN + y' = 27 + /\ Assert(y' = 22, "Failure of assertion at line 22, column 13.") + /\ pc' = "Done" + /\ UNCHANGED << x, stack >> + +(* Allow infinite stuttering to prevent deadlock on termination. *) +Terminating == pc = "Done" /\ UNCHANGED vars + +Next == Bar \/ L1 \/ L3 \/ L2 + \/ Terminating + +Spec == /\ Init /\ [][Next]_vars + /\ WF_vars(Next) + +Termination == <>(pc = "Done") + +\* END TRANSLATION - the hash of the generated TLA code (remove to silence divergence warnings): TLA-025c418c95de365cc2a03da9bdd7e60c + +============================================ diff --git a/tlatools/test-model/pcal/TestReplace.tla b/tlatools/test-model/pcal/TestReplace.tla new file mode 100644 index 0000000000000000000000000000000000000000..742eb25a62b72745aebaa295783d1d4655ce3c09 --- /dev/null +++ b/tlatools/test-model/pcal/TestReplace.tla @@ -0,0 +1,251 @@ +------------------ MODULE TestReplace ----------------- +EXTENDS Naturals, TLC, Sequences + + +(********************** +--algorithm TestReplace +macro IncrementX(u) + begin X := X + u + end macro + +procedure Bar(u, v) + begin a: assert u = v; + return; + end procedure + +procedure Foo1(u, v) + variable X = 0; Y = X ; + begin + a : assert Y = X ; + b : while Y = X do + Y := Y - 1; + end while ; + assert Y = X - 1; + with id = X do assert id = 0 end with ; + if X = 0 + then c: call Bar(X, 0) + else assert FALSE + end if ; + d : print <<X, " = 0">> ; + IncrementX(X+1) ; + assert X = 1 ; + e: return +end procedure + +procedure Foo2(u, v) + variable X = 9; Z = X ; + begin + a : assert Z = X ; + b : while Z = X do + Z := Z - 1; + end while ; + assert Z = X - 1; + with id = X do assert id = 9 end with ; + if X = 9 + then c: call Bar(X, 9) + + else assert FALSE + end if ; + d : print <<X, " = 9">> ; + IncrementX(X+1) ; + assert X = 19 ; + e: return +end procedure + +begin +start : call Foo1(1, 2) ; +b : call Foo2(1, 2) ; +end algorithm + +***********************) + +\* BEGIN TRANSLATION - the hash of the PCal code: PCal-a373e52549e281416ed7cb70fa982188 +\* Label a of procedure Bar at line 12 col 11 changed to a_ +\* Label a of procedure Foo1 at line 19 col 6 changed to a_F +\* Label b of procedure Foo1 at line 20 col 6 changed to b_ +\* Label c of procedure Foo1 at line 26 col 17 changed to c_ +\* Label d of procedure Foo1 at line 29 col 6 changed to d_ +\* Label e of procedure Foo1 at line 32 col 6 changed to e_ +\* Label b of procedure Foo2 at line 39 col 6 changed to b_F +\* Procedure variable X of procedure Foo1 at line 17 col 11 changed to X_ +\* Parameter u of procedure Bar at line 11 col 15 changed to u_ +\* Parameter v of procedure Bar at line 11 col 18 changed to v_ +\* Parameter u of procedure Foo1 at line 16 col 16 changed to u_F +\* Parameter v of procedure Foo1 at line 16 col 19 changed to v_F +CONSTANT defaultInitValue +VARIABLES pc, stack, u_, v_, u_F, v_F, X_, Y, u, v, X, Z + +vars == << pc, stack, u_, v_, u_F, v_F, X_, Y, u, v, X, Z >> + +Init == (* Procedure Bar *) + /\ u_ = defaultInitValue + /\ v_ = defaultInitValue + (* Procedure Foo1 *) + /\ u_F = defaultInitValue + /\ v_F = defaultInitValue + /\ X_ = 0 + /\ Y = X_ + (* Procedure Foo2 *) + /\ u = defaultInitValue + /\ v = defaultInitValue + /\ X = 9 + /\ Z = X + /\ stack = << >> + /\ pc = "start" + +a_ == /\ pc = "a_" + /\ Assert(u_ = v_, "Failure of assertion at line 12, column 11.") + /\ pc' = Head(stack).pc + /\ u_' = Head(stack).u_ + /\ v_' = Head(stack).v_ + /\ stack' = Tail(stack) + /\ UNCHANGED << u_F, v_F, X_, Y, u, v, X, Z >> + +Bar == a_ + +a_F == /\ pc = "a_F" + /\ Assert(Y = X_, "Failure of assertion at line 19, column 6.") + /\ pc' = "b_" + /\ UNCHANGED << stack, u_, v_, u_F, v_F, X_, Y, u, v, X, Z >> + +b_ == /\ pc = "b_" + /\ IF Y = X_ + THEN /\ Y' = Y - 1 + /\ pc' = "b_" + ELSE /\ Assert(Y = X_ - 1, + "Failure of assertion at line 23, column 6.") + /\ LET id == X_ IN + Assert(id = 0, + "Failure of assertion at line 24, column 21.") + /\ IF X_ = 0 + THEN /\ pc' = "c_" + ELSE /\ Assert(FALSE, + "Failure of assertion at line 27, column 14.") + /\ pc' = "d_" + /\ Y' = Y + /\ UNCHANGED << stack, u_, v_, u_F, v_F, X_, u, v, X, Z >> + +c_ == /\ pc = "c_" + /\ /\ stack' = << [ procedure |-> "Bar", + pc |-> "d_", + u_ |-> u_, + v_ |-> v_ ] >> + \o stack + /\ u_' = X_ + /\ v_' = 0 + /\ pc' = "a_" + /\ UNCHANGED << u_F, v_F, X_, Y, u, v, X, Z >> + +d_ == /\ pc = "d_" + /\ PrintT(<<X_, " = 0">>) + /\ X_' = X_ + (X_+1) + /\ Assert(X_' = 1, "Failure of assertion at line 31, column 6.") + /\ pc' = "e_" + /\ UNCHANGED << stack, u_, v_, u_F, v_F, Y, u, v, X, Z >> + +e_ == /\ pc = "e_" + /\ pc' = Head(stack).pc + /\ X_' = Head(stack).X_ + /\ Y' = Head(stack).Y + /\ u_F' = Head(stack).u_F + /\ v_F' = Head(stack).v_F + /\ stack' = Tail(stack) + /\ UNCHANGED << u_, v_, u, v, X, Z >> + +Foo1 == a_F \/ b_ \/ c_ \/ d_ \/ e_ + +a == /\ pc = "a" + /\ Assert(Z = X, "Failure of assertion at line 38, column 6.") + /\ pc' = "b_F" + /\ UNCHANGED << stack, u_, v_, u_F, v_F, X_, Y, u, v, X, Z >> + +b_F == /\ pc = "b_F" + /\ IF Z = X + THEN /\ Z' = Z - 1 + /\ pc' = "b_F" + ELSE /\ Assert(Z = X - 1, + "Failure of assertion at line 42, column 6.") + /\ LET id == X IN + Assert(id = 9, + "Failure of assertion at line 43, column 21.") + /\ IF X = 9 + THEN /\ pc' = "c" + ELSE /\ Assert(FALSE, + "Failure of assertion at line 47, column 14.") + /\ pc' = "d" + /\ Z' = Z + /\ UNCHANGED << stack, u_, v_, u_F, v_F, X_, Y, u, v, X >> + +c == /\ pc = "c" + /\ /\ stack' = << [ procedure |-> "Bar", + pc |-> "d", + u_ |-> u_, + v_ |-> v_ ] >> + \o stack + /\ u_' = X + /\ v_' = 9 + /\ pc' = "a_" + /\ UNCHANGED << u_F, v_F, X_, Y, u, v, X, Z >> + +d == /\ pc = "d" + /\ PrintT(<<X, " = 9">>) + /\ X' = X + (X+1) + /\ Assert(X' = 19, "Failure of assertion at line 51, column 6.") + /\ pc' = "e" + /\ UNCHANGED << stack, u_, v_, u_F, v_F, X_, Y, u, v, Z >> + +e == /\ pc = "e" + /\ pc' = Head(stack).pc + /\ X' = Head(stack).X + /\ Z' = Head(stack).Z + /\ u' = Head(stack).u + /\ v' = Head(stack).v + /\ stack' = Tail(stack) + /\ UNCHANGED << u_, v_, u_F, v_F, X_, Y >> + +Foo2 == a \/ b_F \/ c \/ d \/ e + +start == /\ pc = "start" + /\ /\ stack' = << [ procedure |-> "Foo1", + pc |-> "b", + X_ |-> X_, + Y |-> Y, + u_F |-> u_F, + v_F |-> v_F ] >> + \o stack + /\ u_F' = 1 + /\ v_F' = 2 + /\ X_' = 0 + /\ Y' = X_' + /\ pc' = "a_F" + /\ UNCHANGED << u_, v_, u, v, X, Z >> + +b == /\ pc = "b" + /\ /\ stack' = << [ procedure |-> "Foo2", + pc |-> "Done", + X |-> X, + Z |-> Z, + u |-> u, + v |-> v ] >> + \o stack + /\ u' = 1 + /\ v' = 2 + /\ X' = 9 + /\ Z' = X' + /\ pc' = "a" + /\ UNCHANGED << u_, v_, u_F, v_F, X_, Y >> + +(* Allow infinite stuttering to prevent deadlock on termination. *) +Terminating == pc = "Done" /\ UNCHANGED vars + +Next == Bar \/ Foo1 \/ Foo2 \/ start \/ b + \/ Terminating + +Spec == Init /\ [][Next]_vars + +Termination == <>(pc = "Done") + +\* END TRANSLATION - the hash of the generated TLA code (remove to silence divergence warnings): TLA-424be35f7792f0f2c25e136cc792e4c5 + +================================================== + diff --git a/tlatools/test-model/pcal/TestTabs.tla b/tlatools/test-model/pcal/TestTabs.tla new file mode 100644 index 0000000000000000000000000000000000000000..975cbbb3a07596aa22292bb2598c999728c34eaf --- /dev/null +++ b/tlatools/test-model/pcal/TestTabs.tla @@ -0,0 +1,51 @@ +------------------------------ MODULE TestTabs ------------------------------ +\* Warning: there are evil tabs in this file. +EXTENDS Naturals, TLC +(* +--algorithm TestTabs + variables x = 0 ; + begin +l: x := IF /\ \A i \in {1} : 1 + 1 = 2 + /\ \A i \in {1} : 2 + 2 = 4 + /\ \/ \A i \in {1} : + 1 = 0 + \/ \A i \in {1} : 1 = 2 + \/ \A i \in {1} : 1 = 1 + THEN 1 + ELSE 0 ; + assert x = 1 ; + end algorithm +*) +\* BEGIN TRANSLATION - the hash of the PCal code: PCal-df86b294351f95a8207d12196b3732f0 +VARIABLES x, pc + +vars == << x, pc >> + +Init == (* Global variables *) + /\ x = 0 + /\ pc = "l" + +l == /\ pc = "l" + /\ x' = (IF /\ \A i \in {1} : 1 + 1 = 2 + /\ \A i \in {1} : 2 + 2 = 4 + /\ \/ \A i \in {1} : + 1 = 0 + \/ \A i \in {1} : 1 = 2 + \/ \A i \in {1} : 1 = 1 + THEN 1 + ELSE 0) + /\ Assert(x' = 1, "Failure of assertion at line 16, column 5.") + /\ pc' = "Done" + +(* Allow infinite stuttering to prevent deadlock on termination. *) +Terminating == pc = "Done" /\ UNCHANGED vars + +Next == l + \/ Terminating + +Spec == Init /\ [][Next]_vars + +Termination == <>(pc = "Done") + +\* END TRANSLATION - the hash of the generated TLA code (remove to silence divergence warnings): TLA-ec3da753015679c262e945265fc700f9 +============================================================================= diff --git a/tlatools/test-model/pcal/TreeBarrier.cfg b/tlatools/test-model/pcal/TreeBarrier.cfg new file mode 100644 index 0000000000000000000000000000000000000000..ef9c9f9df75ac1792e7e68265b19e81d981b7b21 --- /dev/null +++ b/tlatools/test-model/pcal/TreeBarrier.cfg @@ -0,0 +1,5 @@ +SPECIFICATION Spec +\* Add statements after this line. +CONSTANT exp = 2 +INVARIANT SafeBarrier +PROPERTY LiveBarrier diff --git a/tlatools/test-model/pcal/TreeBarrier.tla b/tlatools/test-model/pcal/TreeBarrier.tla new file mode 100644 index 0000000000000000000000000000000000000000..a84d857a4fd0b8afe40e60fc640b0812233f0711 --- /dev/null +++ b/tlatools/test-model/pcal/TreeBarrier.tla @@ -0,0 +1,140 @@ +--algorithm TreeBarrier + variables arrived = [i \in 1..2 |-> [j \in 1..N |-> 0]]; + proceed = [i \in 1..2 |-> [j \in 1..N |-> 0]]; + process i \in 1..N + variables b = 1; + p = 0; + begin +prc: while (TRUE) do +comp: skip; +b1: if 2*self \leq N then +b2: when arrived[b][2*self] = 1; arrived[b][2*self] := 0; +b3: when arrived[b][2*self + 1] = 1; arrived[b][2*self + 1] := 0; + end if; +b4: arrived[b][self] := 1; +b5: if self = 1 then + p := 1; +b6: while (p \leq N) do +b7: proceed[b][p] := 1; + p := p + 1; + end while; + end if; +b8: when proceed[b][self] = 1; proceed[b][self] := 0; +b9: b := IF b = 1 THEN 2 ELSE 1; + end while; + end process; +end algorithm + +------------------------ MODULE TreeBarrier ------------------------ + +EXTENDS Naturals, Sequences +CONSTANT exp +N == 2^exp-1 + +-------------------------------------------------------------------- + +\* BEGIN TRANSLATION - the hash of the PCal code: PCal-f8296f0cc6166098bc809c33e090e03e +VARIABLES arrived, proceed, pc, b, p + +vars == << arrived, proceed, pc, b, p >> + +ProcSet == (1..N) + +Init == (* Global variables *) + /\ arrived = [i \in 1..2 |-> [j \in 1..N |-> 0]] + /\ proceed = [i \in 1..2 |-> [j \in 1..N |-> 0]] + (* Process i *) + /\ b = [self \in 1..N |-> 1] + /\ p = [self \in 1..N |-> 0] + /\ pc = [self \in ProcSet |-> "prc"] + +prc(self) == /\ pc[self] = "prc" + /\ pc' = [pc EXCEPT ![self] = "comp"] + /\ UNCHANGED << arrived, proceed, b, p >> + +comp(self) == /\ pc[self] = "comp" + /\ TRUE + /\ pc' = [pc EXCEPT ![self] = "b1"] + /\ UNCHANGED << arrived, proceed, b, p >> + +b1(self) == /\ pc[self] = "b1" + /\ IF 2*self \leq N + THEN /\ pc' = [pc EXCEPT ![self] = "b2"] + ELSE /\ pc' = [pc EXCEPT ![self] = "b4"] + /\ UNCHANGED << arrived, proceed, b, p >> + +b2(self) == /\ pc[self] = "b2" + /\ arrived[b[self]][2*self] = 1 + /\ arrived' = [arrived EXCEPT ![b[self]][2*self] = 0] + /\ pc' = [pc EXCEPT ![self] = "b3"] + /\ UNCHANGED << proceed, b, p >> + +b3(self) == /\ pc[self] = "b3" + /\ arrived[b[self]][2*self + 1] = 1 + /\ arrived' = [arrived EXCEPT ![b[self]][2*self + 1] = 0] + /\ pc' = [pc EXCEPT ![self] = "b4"] + /\ UNCHANGED << proceed, b, p >> + +b4(self) == /\ pc[self] = "b4" + /\ arrived' = [arrived EXCEPT ![b[self]][self] = 1] + /\ pc' = [pc EXCEPT ![self] = "b5"] + /\ UNCHANGED << proceed, b, p >> + +b5(self) == /\ pc[self] = "b5" + /\ IF self = 1 + THEN /\ p' = [p EXCEPT ![self] = 1] + /\ pc' = [pc EXCEPT ![self] = "b6"] + ELSE /\ pc' = [pc EXCEPT ![self] = "b8"] + /\ p' = p + /\ UNCHANGED << arrived, proceed, b >> + +b6(self) == /\ pc[self] = "b6" + /\ IF (p[self] \leq N) + THEN /\ pc' = [pc EXCEPT ![self] = "b7"] + ELSE /\ pc' = [pc EXCEPT ![self] = "b8"] + /\ UNCHANGED << arrived, proceed, b, p >> + +b7(self) == /\ pc[self] = "b7" + /\ proceed' = [proceed EXCEPT ![b[self]][p[self]] = 1] + /\ p' = [p EXCEPT ![self] = p[self] + 1] + /\ pc' = [pc EXCEPT ![self] = "b6"] + /\ UNCHANGED << arrived, b >> + +b8(self) == /\ pc[self] = "b8" + /\ proceed[b[self]][self] = 1 + /\ proceed' = [proceed EXCEPT ![b[self]][self] = 0] + /\ pc' = [pc EXCEPT ![self] = "b9"] + /\ UNCHANGED << arrived, b, p >> + +b9(self) == /\ pc[self] = "b9" + /\ b' = [b EXCEPT ![self] = IF b[self] = 1 THEN 2 ELSE 1] + /\ pc' = [pc EXCEPT ![self] = "prc"] + /\ UNCHANGED << arrived, proceed, p >> + +i(self) == prc(self) \/ comp(self) \/ b1(self) \/ b2(self) \/ b3(self) + \/ b4(self) \/ b5(self) \/ b6(self) \/ b7(self) \/ b8(self) + \/ b9(self) + +(* Allow infinite stuttering to prevent deadlock on termination. *) +Terminating == /\ \A self \in ProcSet: pc[self] = "Done" + /\ UNCHANGED vars + +Next == (\E self \in 1..N: i(self)) + \/ Terminating + +Spec == /\ Init /\ [][Next]_vars + /\ WF_vars(Next) + +Termination == <>(\A self \in ProcSet: pc[self] = "Done") + +\* END TRANSLATION - the hash of the generated TLA code (remove to silence divergence warnings): TLA-cbb736d8ea82daa6b485e32bbe870840 + + +SafeBarrier == \A p1, p2 \in ProcSet: + ((pc[p1] = "comp") /\ (pc[p2] = "comp")) => (b[p1] = b[p2]) + +LiveBarrier == \A p1 \in ProcSet: + /\ (b[p1] = 1) ~> (b[p1] = 2) + /\ (b[p1] = 2) ~> (b[p1] = 1) + +==================================================================== diff --git a/tlatools/test-model/pcal/ULBakery.cfg b/tlatools/test-model/pcal/ULBakery.cfg new file mode 100644 index 0000000000000000000000000000000000000000..9209a00c6288aab93977bdf9e85b204efd16702f --- /dev/null +++ b/tlatools/test-model/pcal/ULBakery.cfg @@ -0,0 +1,7 @@ +SPECIFICATION Spec +\* Add statements after this line. +CONSTANTS NumProcs = 2 + MaxNum = 3 +INVARIANT Invariant +CONSTRAINT Constraint + diff --git a/tlatools/test-model/pcal/ULCallReturn1.tla b/tlatools/test-model/pcal/ULCallReturn1.tla new file mode 100644 index 0000000000000000000000000000000000000000..20328512f989245d5215a1dd6d58388966d3dbe9 --- /dev/null +++ b/tlatools/test-model/pcal/ULCallReturn1.tla @@ -0,0 +1,124 @@ +------------------------------ MODULE ULCallReturn1 ------------------------- +EXTENDS Sequences, Naturals, TLC + +(* + --algorithm CallReturn1 + procedure Proc1(arg1 = 0) + variable u = 1 ; + begin (*p1 :*) u := 2 ; + call Proc2 ( 2 * u ) ; + (*p2 :*) assert u = 2; + assert arg1 = 4 ; + call Proc2 ( 2 * u + 1 ) ; + return ; + end procedure + procedure Proc2(arg2 = 0) + variable v = 42 ; + begin (*q1 :*) assert v = 42; + assert arg2 \in {4, 5} ; + call Proc3 ( v + arg2 ) ; + return ; + end procedure + procedure Proc3(arg3 = 0) + begin (*r1 :*) assert arg3 \in {46, 47} ; + return ; + end procedure + begin + (*a1 :*) call Proc1( 4 ) ; + end algorithm + +*) + +\* BEGIN TRANSLATION - the hash of the PCal code: PCal-a699498c8513ed4e4423d961c35407b2 +VARIABLES pc, stack, arg1, u, arg2, v, arg3 + +vars == << pc, stack, arg1, u, arg2, v, arg3 >> + +Init == (* Procedure Proc1 *) + /\ arg1 = 0 + /\ u = 1 + (* Procedure Proc2 *) + /\ arg2 = 0 + /\ v = 42 + (* Procedure Proc3 *) + /\ arg3 = 0 + /\ stack = << >> + /\ pc = "Lbl_5" + +Lbl_1 == /\ pc = "Lbl_1" + /\ u' = 2 + /\ /\ arg2' = 2 * u' + /\ stack' = << [ procedure |-> "Proc2", + pc |-> "Lbl_2", + v |-> v, + arg2 |-> arg2 ] >> + \o stack + /\ v' = 42 + /\ pc' = "Lbl_3" + /\ UNCHANGED << arg1, arg3 >> + +Lbl_2 == /\ pc = "Lbl_2" + /\ Assert(u = 2, "Failure of assertion at line 10, column 22.") + /\ Assert(arg1 = 4, "Failure of assertion at line 11, column 18.") + /\ /\ arg2' = 2 * u + 1 + /\ stack' = << [ procedure |-> "Proc2", + pc |-> Head(stack).pc, + v |-> v, + arg2 |-> arg2 ] >> + \o Tail(stack) + /\ u' = Head(stack).u + /\ v' = 42 + /\ pc' = "Lbl_3" + /\ UNCHANGED << arg1, arg3 >> + +Proc1 == Lbl_1 \/ Lbl_2 + +Lbl_3 == /\ pc = "Lbl_3" + /\ Assert(v = 42, "Failure of assertion at line 17, column 22.") + /\ Assert(arg2 \in {4, 5}, + "Failure of assertion at line 18, column 18.") + /\ /\ arg3' = v + arg2 + /\ stack' = << [ procedure |-> "Proc3", + pc |-> Head(stack).pc, + arg3 |-> arg3 ] >> + \o Tail(stack) + /\ v' = Head(stack).v + /\ pc' = "Lbl_4" + /\ UNCHANGED << arg1, u, arg2 >> + +Proc2 == Lbl_3 + +Lbl_4 == /\ pc = "Lbl_4" + /\ Assert(arg3 \in {46, 47}, + "Failure of assertion at line 23, column 22.") + /\ pc' = Head(stack).pc + /\ arg3' = Head(stack).arg3 + /\ stack' = Tail(stack) + /\ UNCHANGED << arg1, u, arg2, v >> + +Proc3 == Lbl_4 + +Lbl_5 == /\ pc = "Lbl_5" + /\ /\ arg1' = 4 + /\ stack' = << [ procedure |-> "Proc1", + pc |-> "Done", + u |-> u, + arg1 |-> arg1 ] >> + \o stack + /\ u' = 1 + /\ pc' = "Lbl_1" + /\ UNCHANGED << arg2, v, arg3 >> + +(* Allow infinite stuttering to prevent deadlock on termination. *) +Terminating == pc = "Done" /\ UNCHANGED vars + +Next == Proc1 \/ Proc2 \/ Proc3 \/ Lbl_5 + \/ Terminating + +Spec == /\ Init /\ [][Next]_vars + /\ WF_vars(Next) + +Termination == <>(pc = "Done") + +\* END TRANSLATION - the hash of the generated TLA code (remove to silence divergence warnings): TLA-542b3dd4f4eee627dff4b0b877928865 +============================================================================= diff --git a/tlatools/test-model/pcal/ULEuclid.cfg b/tlatools/test-model/pcal/ULEuclid.cfg new file mode 100644 index 0000000000000000000000000000000000000000..d3f674e358ba428b8a060e8a4d4eccc010228b50 --- /dev/null +++ b/tlatools/test-model/pcal/ULEuclid.cfg @@ -0,0 +1,6 @@ +SPECIFICATION Spec +PROPERTY Termination +\* Add statements after this line. +INVARIANT Invariant +CONSTANTS MaxNum = 20 + diff --git a/tlatools/test-model/pcal/ULEuclid.tla b/tlatools/test-model/pcal/ULEuclid.tla new file mode 100644 index 0000000000000000000000000000000000000000..53b0d05cd0106d40b220e174555e35b18314b24e --- /dev/null +++ b/tlatools/test-model/pcal/ULEuclid.tla @@ -0,0 +1,92 @@ +------------------------------ MODULE ULEuclid ------------------------------- +(***************************************************************************) +(* Euclid's algorithm. *) +(***************************************************************************) + +EXTENDS Naturals, TLC + +CONSTANT MaxNum + +ASSUME MaxNum > 0 + +ASSUME /\ Print(<<"Testing Euclid's algorithm on all numbers between 1 and ", + MaxNum>>, TRUE) + /\ Print("Most time spent evaluating naive definition of GCD for test", + TRUE) + +(****** +Adapted from page 8 of the 2nd edition of Robert Sedgewick's "Algorithms". + +--algorithm Euclid + variables u_ini \in 1 .. MaxNum ; + v_ini \in 1 .. MaxNum ; + u = u_ini ; v = v_ini ; + begin (*a :*) while u # 0 + do if u < v then u := v || v := u ; end if ; + (* b: *) u := u - v; + end while ; + assert v = GCD(u_ini, v_ini) ; + \* print <<"gcd of ", u_ini, v_ini, " equals ", v >> ; + end algorithm + +*) + +GCD(x, y) == CHOOSE i \in (1..x) \cap (1..y) : + /\ x % i = 0 + /\ y % i = 0 + /\ \A j \in (1..x) \cap (1..y) : + /\ x % j = 0 + /\ y % j = 0 + => i \geq j + + + +\* BEGIN TRANSLATION - the hash of the PCal code: PCal-15a96a62d4bb94349f0b7e97b7877563 +VARIABLES u_ini, v_ini, u, v, pc + +vars == << u_ini, v_ini, u, v, pc >> + +Init == (* Global variables *) + /\ u_ini \in 1 .. MaxNum + /\ v_ini \in 1 .. MaxNum + /\ u = u_ini + /\ v = v_ini + /\ pc = "Lbl_1" + +Lbl_1 == /\ pc = "Lbl_1" + /\ IF u # 0 + THEN /\ IF u < v + THEN /\ /\ u' = v + /\ v' = u + ELSE /\ TRUE + /\ UNCHANGED << u, v >> + /\ pc' = "Lbl_2" + ELSE /\ Assert(v = GCD(u_ini, v_ini), + "Failure of assertion at line 28, column 13.") + /\ pc' = "Done" + /\ UNCHANGED << u, v >> + /\ UNCHANGED << u_ini, v_ini >> + +Lbl_2 == /\ pc = "Lbl_2" + /\ u' = u - v + /\ pc' = "Lbl_1" + /\ UNCHANGED << u_ini, v_ini, v >> + +(* Allow infinite stuttering to prevent deadlock on termination. *) +Terminating == pc = "Done" /\ UNCHANGED vars + +Next == Lbl_1 \/ Lbl_2 + \/ Terminating + +Spec == /\ Init /\ [][Next]_vars + /\ WF_vars(Next) + +Termination == <>(pc = "Done") + +\* END TRANSLATION - the hash of the generated TLA code (remove to silence divergence warnings): TLA-42ac9bbadc686601495c6cb5a86d3d55 + + +Invariant == + (pc = "Done") => (v = GCD(u_ini, v_ini)) + +============================================================================= diff --git a/tlatools/test-model/pcal/ULEvenOdd.cfg b/tlatools/test-model/pcal/ULEvenOdd.cfg new file mode 100644 index 0000000000000000000000000000000000000000..88725dab9155d12e1fbca4531e4f30063b7dc81e --- /dev/null +++ b/tlatools/test-model/pcal/ULEvenOdd.cfg @@ -0,0 +1,5 @@ +SPECIFICATION Spec +PROPERTY Termination +\* Add statements after this line. +CONSTANT N = 6 + diff --git a/tlatools/test-model/pcal/ULEvenOdd.tla b/tlatools/test-model/pcal/ULEvenOdd.tla new file mode 100644 index 0000000000000000000000000000000000000000..0c3b6cdf01e7d69756c3ea47521c891085d1033d --- /dev/null +++ b/tlatools/test-model/pcal/ULEvenOdd.tla @@ -0,0 +1,116 @@ +--algorithm EvenOdd +variable result = FALSE; +procedure Even (xEven = 0) +begin + (*Even1:*) if xEven = 0 then + result := TRUE; + return; + else + call Odd(xEven - 1); + return; + end if; + end procedure +procedure Odd (xOdd = 0) +begin + (*Odd1:*) if xOdd = 0 then result := FALSE; + else call Even(xOdd - 1); + end if; + (*Odd2:*) return; + end procedure +begin + (*a1:*) call Even(N); + (*a2:*) print result; +end algorithm + +--------------- MODULE ULEvenOdd --------------- + +EXTENDS Naturals, Sequences, TLC + +CONSTANT N + +---------------------------------------------- + + +\* BEGIN TRANSLATION - the hash of the PCal code: PCal-ead0453df43345041c6d46c21bad39b3 +VARIABLES result, pc, stack, xEven, xOdd + +vars == << result, pc, stack, xEven, xOdd >> + +Init == (* Global variables *) + /\ result = FALSE + (* Procedure Even *) + /\ xEven = 0 + (* Procedure Odd *) + /\ xOdd = 0 + /\ stack = << >> + /\ pc = "Lbl_4" + +Lbl_1 == /\ pc = "Lbl_1" + /\ IF xEven = 0 + THEN /\ result' = TRUE + /\ pc' = Head(stack).pc + /\ xEven' = Head(stack).xEven + /\ stack' = Tail(stack) + /\ xOdd' = xOdd + ELSE /\ /\ stack' = << [ procedure |-> "Odd", + pc |-> Head(stack).pc, + xOdd |-> xOdd ] >> + \o Tail(stack) + /\ xOdd' = xEven - 1 + /\ pc' = "Lbl_2" + /\ UNCHANGED << result, xEven >> + +Even == Lbl_1 + +Lbl_2 == /\ pc = "Lbl_2" + /\ IF xOdd = 0 + THEN /\ result' = FALSE + /\ pc' = "Lbl_3" + /\ UNCHANGED << stack, xEven >> + ELSE /\ /\ stack' = << [ procedure |-> "Even", + pc |-> "Lbl_3", + xEven |-> xEven ] >> + \o stack + /\ xEven' = xOdd - 1 + /\ pc' = "Lbl_1" + /\ UNCHANGED result + /\ xOdd' = xOdd + +Lbl_3 == /\ pc = "Lbl_3" + /\ pc' = Head(stack).pc + /\ xOdd' = Head(stack).xOdd + /\ stack' = Tail(stack) + /\ UNCHANGED << result, xEven >> + +Odd == Lbl_2 \/ Lbl_3 + +Lbl_4 == /\ pc = "Lbl_4" + /\ /\ stack' = << [ procedure |-> "Even", + pc |-> "Lbl_5", + xEven |-> xEven ] >> + \o stack + /\ xEven' = N + /\ pc' = "Lbl_1" + /\ UNCHANGED << result, xOdd >> + +Lbl_5 == /\ pc = "Lbl_5" + /\ PrintT(result) + /\ pc' = "Done" + /\ UNCHANGED << result, stack, xEven, xOdd >> + +(* Allow infinite stuttering to prevent deadlock on termination. *) +Terminating == pc = "Done" /\ UNCHANGED vars + +Next == Even \/ Odd \/ Lbl_4 \/ Lbl_5 + \/ Terminating + +Spec == /\ Init /\ [][Next]_vars + /\ WF_vars(Next) + +Termination == <>(pc = "Done") + +\* END TRANSLATION - the hash of the generated TLA code (remove to silence divergence warnings): TLA-20632e604e66ef1ac373dca4eac3efb3 + +============================================== + + diff --git a/tlatools/test-model/pcal/ULFactorial2.tla b/tlatools/test-model/pcal/ULFactorial2.tla new file mode 100644 index 0000000000000000000000000000000000000000..3205ee538c10648f2caf9fd109ac01ca4c0d4774 --- /dev/null +++ b/tlatools/test-model/pcal/ULFactorial2.tla @@ -0,0 +1,126 @@ +-------------------------- MODULE ULFactorial2 --------------------------- +EXTENDS Naturals, Sequences, TLC + +(*************************************************************************** +Factorial Algorithm with 2 procedures + +--algorithm Factorial + variable result = 1; + procedure FactProc(arg1 = 0 ) + variable u = 1 ; + begin (*p1 :*) if arg1 = 0 + then return; + else result := result * arg1; + call FactProc2 ( arg1 - 1 ) ; + (*b:*) return; + end if; + end procedure + procedure FactProc2(arg2 = 0) + variable u2 = 1 ; + begin (*p12 :*) if arg2 = 0 + then return; + else result := result * arg2; + call FactProc ( arg2 - 1 ) ; + return; + end if; + end procedure + begin + (*a1 :*) call FactProc( 5 ) ; + (*a2 :*) if result = 120 then print <<"Correct =", 120>>; + else print <<"Error = ", result>> ; + end if; + end algorithm +***************************************************************************) + +\* BEGIN TRANSLATION - the hash of the PCal code: PCal-e543cee5733d328853b3f740967ffc6f +VARIABLES result, pc, stack, arg1, u, arg2, u2 + +vars == << result, pc, stack, arg1, u, arg2, u2 >> + +Init == (* Global variables *) + /\ result = 1 + (* Procedure FactProc *) + /\ arg1 = 0 + /\ u = 1 + (* Procedure FactProc2 *) + /\ arg2 = 0 + /\ u2 = 1 + /\ stack = << >> + /\ pc = "Lbl_3" + +Lbl_1 == /\ pc = "Lbl_1" + /\ IF arg1 = 0 + THEN /\ pc' = Head(stack).pc + /\ u' = Head(stack).u + /\ arg1' = Head(stack).arg1 + /\ stack' = Tail(stack) + /\ UNCHANGED << result, arg2, u2 >> + ELSE /\ result' = result * arg1 + /\ /\ arg2' = arg1 - 1 + /\ stack' = << [ procedure |-> "FactProc2", + pc |-> Head(stack).pc, + u2 |-> u2, + arg2 |-> arg2 ] >> + \o Tail(stack) + /\ u' = Head(stack).u + /\ u2' = 1 + /\ pc' = "Lbl_2" + /\ arg1' = arg1 + +FactProc == Lbl_1 + +Lbl_2 == /\ pc = "Lbl_2" + /\ IF arg2 = 0 + THEN /\ pc' = Head(stack).pc + /\ u2' = Head(stack).u2 + /\ arg2' = Head(stack).arg2 + /\ stack' = Tail(stack) + /\ UNCHANGED << result, arg1, u >> + ELSE /\ result' = result * arg2 + /\ /\ arg1' = arg2 - 1 + /\ stack' = << [ procedure |-> "FactProc", + pc |-> Head(stack).pc, + u |-> u, + arg1 |-> arg1 ] >> + \o Tail(stack) + /\ u2' = Head(stack).u2 + /\ u' = 1 + /\ pc' = "Lbl_1" + /\ arg2' = arg2 + +FactProc2 == Lbl_2 + +Lbl_3 == /\ pc = "Lbl_3" + /\ /\ arg1' = 5 + /\ stack' = << [ procedure |-> "FactProc", + pc |-> "Lbl_4", + u |-> u, + arg1 |-> arg1 ] >> + \o stack + /\ u' = 1 + /\ pc' = "Lbl_1" + /\ UNCHANGED << result, arg2, u2 >> + +Lbl_4 == /\ pc = "Lbl_4" + /\ IF result = 120 + THEN /\ PrintT(<<"Correct =", 120>>) + ELSE /\ PrintT(<<"Error = ", result>>) + /\ pc' = "Done" + /\ UNCHANGED << result, stack, arg1, u, arg2, u2 >> + +(* Allow infinite stuttering to prevent deadlock on termination. *) +Terminating == pc = "Done" /\ UNCHANGED vars + +Next == FactProc \/ FactProc2 \/ Lbl_3 \/ Lbl_4 + \/ Terminating + +Spec == /\ Init /\ [][Next]_vars + /\ WF_vars(Next) + +Termination == <>(pc = "Done") + +\* END TRANSLATION - the hash of the generated TLA code (remove to silence divergence warnings): TLA-13df185bb6632a27d39dd317f4d5a749 + + +Invariant == result \in Nat +============================================================================= diff --git a/tlatools/test-model/pcal/ULQuicksortMacro.cfg b/tlatools/test-model/pcal/ULQuicksortMacro.cfg new file mode 100644 index 0000000000000000000000000000000000000000..0ebf8fe12d66c62cbd54cf80207efc9dda94587b --- /dev/null +++ b/tlatools/test-model/pcal/ULQuicksortMacro.cfg @@ -0,0 +1,5 @@ +SPECIFICATION Spec +PROPERTY Termination +\* Add statements after this line. +CONSTANTS ArrayLen = 3 +INVARIANT Invariant diff --git a/tlatools/test-model/pcal/ULQuicksortMacro.tla b/tlatools/test-model/pcal/ULQuicksortMacro.tla new file mode 100644 index 0000000000000000000000000000000000000000..d99543dccafba75a8851a7581d5f2f3bfcc0cf8f --- /dev/null +++ b/tlatools/test-model/pcal/ULQuicksortMacro.tla @@ -0,0 +1,134 @@ +-------------------------- MODULE ULQuicksortMacro ------------------------- +EXTENDS Naturals, Sequences + +CONSTANT ArrayLen + +ASSUME ArrayLen \in Nat + +PermsOf(Arr) == + LET Automorphism(S) == { f \in [S -> S] : + \A y \in S : \E x \in S : f[x] = y } + f ** g == [x \in DOMAIN g |-> f[g[x]]] + IN { Arr ** f : f \in Automorphism(DOMAIN Arr) } + +(* +--algorithm QuicksortMacro + variables A \in [1..ArrayLen -> 1..ArrayLen]; + returnVal = 1; + macro Partition(lo, hi) + begin with piv \in lo..(hi-1) + do returnVal := piv ; + with Ap \in + {AA \in PermsOf(A) : + (\A i \in 1..(lo-1) : AA[i] = A[i]) + /\ (\A i \in (hi+1)..Len(A) : AA[i] = A[i]) + /\ (\A i \in lo..piv, j \in (piv+1)..hi : + AA[i] \leq AA[j])} + do A := Ap; + end with ; + end with; + end macro + procedure QS(qlo = 1, qhi = 1) + variable pivot = 1 ; + begin (*qs1 :*) if qlo < qhi + then Partition(qlo, qhi) ; + (*qs2 :*) pivot := returnVal ; + (*qs3 :*) call QS(qlo, pivot) ; + (*qs4 :*) call QS(pivot +1,qhi) ; + return ; + else return; + end if; + end procedure + begin (*main :*) call QS(1, Len(A)) ; + end algorithm +*) + +\* BEGIN TRANSLATION - the hash of the PCal code: PCal-749f221938b5b2dde2ae6c5d6a23511d +VARIABLES A, returnVal, pc, stack, qlo, qhi, pivot + +vars == << A, returnVal, pc, stack, qlo, qhi, pivot >> + +Init == (* Global variables *) + /\ A \in [1..ArrayLen -> 1..ArrayLen] + /\ returnVal = 1 + (* Procedure QS *) + /\ qlo = 1 + /\ qhi = 1 + /\ pivot = 1 + /\ stack = << >> + /\ pc = "Lbl_4" + +Lbl_1 == /\ pc = "Lbl_1" + /\ IF qlo < qhi + THEN /\ \E piv \in qlo..(qhi-1): + /\ returnVal' = piv + /\ \E Ap \in {AA \in PermsOf(A) : + (\A i \in 1..(qlo-1) : AA[i] = A[i]) + /\ (\A i \in (qhi+1)..Len(A) : AA[i] = A[i]) + /\ (\A i \in qlo..piv, j \in (piv+1)..qhi : + AA[i] \leq AA[j])}: + A' = Ap + /\ pivot' = returnVal' + /\ pc' = "Lbl_2" + /\ UNCHANGED << stack, qlo, qhi >> + ELSE /\ pc' = Head(stack).pc + /\ pivot' = Head(stack).pivot + /\ qlo' = Head(stack).qlo + /\ qhi' = Head(stack).qhi + /\ stack' = Tail(stack) + /\ UNCHANGED << A, returnVal >> + +Lbl_2 == /\ pc = "Lbl_2" + /\ /\ qhi' = pivot + /\ qlo' = qlo + /\ stack' = << [ procedure |-> "QS", + pc |-> "Lbl_3", + pivot |-> pivot, + qlo |-> qlo, + qhi |-> qhi ] >> + \o stack + /\ pivot' = 1 + /\ pc' = "Lbl_1" + /\ UNCHANGED << A, returnVal >> + +Lbl_3 == /\ pc = "Lbl_3" + /\ /\ qhi' = qhi + /\ qlo' = pivot +1 + /\ pivot' = 1 + /\ pc' = "Lbl_1" + /\ UNCHANGED << A, returnVal, stack >> + +QS == Lbl_1 \/ Lbl_2 \/ Lbl_3 + +Lbl_4 == /\ pc = "Lbl_4" + /\ /\ qhi' = Len(A) + /\ qlo' = 1 + /\ stack' = << [ procedure |-> "QS", + pc |-> "Done", + pivot |-> pivot, + qlo |-> qlo, + qhi |-> qhi ] >> + \o stack + /\ pivot' = 1 + /\ pc' = "Lbl_1" + /\ UNCHANGED << A, returnVal >> + +(* Allow infinite stuttering to prevent deadlock on termination. *) +Terminating == pc = "Done" /\ UNCHANGED vars + +Next == QS \/ Lbl_4 + \/ Terminating + +Spec == /\ Init /\ [][Next]_vars + /\ WF_vars(Next) + +Termination == <>(pc = "Done") + +\* END TRANSLATION - the hash of the generated TLA code (remove to silence divergence warnings): TLA-d263fef6f518e6f97b673c4d40ca9921 + +Invariant == + (pc = "Done") => \A i, j \in 1..ArrayLen : + (i < j) => A[i] \leq A[j] + + +============================================================================= diff --git a/tlatools/test-model/pcal/UniprocDefine.tla b/tlatools/test-model/pcal/UniprocDefine.tla new file mode 100644 index 0000000000000000000000000000000000000000..8d3e3a52a71b5c343a8a65d4c28041dcc7cccd5e --- /dev/null +++ b/tlatools/test-model/pcal/UniprocDefine.tla @@ -0,0 +1,76 @@ +----------------------------- MODULE UniprocDefine -------------------------- +EXTENDS Naturals, Sequences, TLC + +(* +--algorithm UniprocDefine + variables n = 0 ; + define nplus1 == n + 1 + nplus2 == nplus1 + 1 + end define ; + procedure Foo(a) + variable b = 2 ; + begin foo : n := nplus2 + a + b ; + return ; + end procedure ; + begin main : call Foo(2) ; + minor: assert n = 6 ; + end algorithm +*) + +\* BEGIN TRANSLATION - the hash of the PCal code: PCal-6a1ac8b08c9ed86b40d0f6e5ef5cbdb0 +CONSTANT defaultInitValue +VARIABLES n, pc, stack + +(* define statement *) +nplus1 == n + 1 +nplus2 == nplus1 + 1 + +VARIABLES a, b + +vars == << n, pc, stack, a, b >> + +Init == (* Global variables *) + /\ n = 0 + (* Procedure Foo *) + /\ a = defaultInitValue + /\ b = 2 + /\ stack = << >> + /\ pc = "main" + +foo == /\ pc = "foo" + /\ n' = nplus2 + a + b + /\ pc' = Head(stack).pc + /\ b' = Head(stack).b + /\ a' = Head(stack).a + /\ stack' = Tail(stack) + +Foo == foo + +main == /\ pc = "main" + /\ /\ a' = 2 + /\ stack' = << [ procedure |-> "Foo", + pc |-> "minor", + b |-> b, + a |-> a ] >> + \o stack + /\ b' = 2 + /\ pc' = "foo" + /\ n' = n + +minor == /\ pc = "minor" + /\ Assert(n = 6, "Failure of assertion at line 16, column 17.") + /\ pc' = "Done" + /\ UNCHANGED << n, stack, a, b >> + +(* Allow infinite stuttering to prevent deadlock on termination. *) +Terminating == pc = "Done" /\ UNCHANGED vars + +Next == Foo \/ main \/ minor + \/ Terminating + +Spec == Init /\ [][Next]_vars + +Termination == <>(pc = "Done") + +\* END TRANSLATION - the hash of the generated TLA code (remove to silence divergence warnings): TLA-bb2646c148c04705a11ba77b1f981f05 +============================================================================= diff --git a/tlatools/test-model/pcal/bug-05-12-10.tla b/tlatools/test-model/pcal/bug-05-12-10.tla new file mode 100644 index 0000000000000000000000000000000000000000..fd80b54f278e3e8d394f139e67da4d4005822f8c --- /dev/null +++ b/tlatools/test-model/pcal/bug-05-12-10.tla @@ -0,0 +1,43 @@ +CAUSES the translator to throw an exception + +last modified on Sun 11 Dec 2005 at 0:30:16 UT by lamport + +---------------------------- MODULE bug-05-12-10 ---------------------------- +EXTENDS Naturals, Sequences, FiniteSets , TLC +(* +--algorithm Pcal + procedure IsAlgorithm(alg) + variable res = FALSE ; i = 0 ; + begin IA1: assert \/ /\ alg.type = "uniprocess" + /\ DOMAIN alg = {"type", "name", "defs", + "decls", "prcds", "body"} + \/ /\ alg.type = "multiiprocess" + /\ DOMAIN alg = {"type", "name", "defs", + "decls", "prcds", "procs"}; + assert alg.name \in STRING ; + assert IsExpr(alg.defs) ; + assert IsSeq(alg.decls) ; + i := Len(alg.decls) ; + IA2: while i > 0 do call IsVarDecl(alg.decls[i]) end while ; + i := Len(alg.prcds) ; + IA3: while i > 0 do call IsProcedure(alg.prcds[i]) end while ; + if alg.type = "uniprocess" + then assert IsNonemptySeq(alg.body) ; + i := Len(alg.body) ; + IA4: while i > 0 do call IsLabeledStmt(alg.prcds[i]) + end while ; + else assert IsNonemptySeq(alg.procs) ; + i := Len(alg.procs) ; + IA5: while i > 0 do call IsLabeledStmt(alg.procs[i]) + end while ; + end if ; + IA6: return ; + end procedure + begin PC1: call IsAlgorithm(alg) ; + Pc2: print "IsAlgorithm(alg) = TRUE" + end algorithm +*) + +\* BEGIN TRANSLATION +\* END TRANSLATION +============================================================================= diff --git a/tlatools/test-model/pcal/bug_05_10_03.tla b/tlatools/test-model/pcal/bug_05_10_03.tla new file mode 100644 index 0000000000000000000000000000000000000000..71d4d55ca983a26ad603c5b03f21607284236410 --- /dev/null +++ b/tlatools/test-model/pcal/bug_05_10_03.tla @@ -0,0 +1,47 @@ +--algorithm showBug + +process Proc \in 1..2 +variables x=[f1 |-> 1 , f2|->self ]; + +begin +start: x.f1:=self; + assert x.f1 = self; +end process; + +end algorithm + +------------- MODULE bug_05_10_03 ------------ +EXTENDS Naturals, TLC, FiniteSets +------------------------------------------ +\* BEGIN TRANSLATION - the hash of the PCal code: PCal-b9dcdef1a5e6ce1735b61fe6aacca0f9 +VARIABLES pc, x + +vars == << pc, x >> + +ProcSet == (1..2) + +Init == (* Process Proc *) + /\ x = [self \in 1..2 |-> [f1 |-> 1 , f2|->self ]] + /\ pc = [self \in ProcSet |-> "start"] + +start(self) == /\ pc[self] = "start" + /\ x' = [x EXCEPT ![self].f1 = self] + /\ Assert(x'[self].f1 = self, + "Failure of assertion at line 8, column 8.") + /\ pc' = [pc EXCEPT ![self] = "Done"] + +Proc(self) == start(self) + +(* Allow infinite stuttering to prevent deadlock on termination. *) +Terminating == /\ \A self \in ProcSet: pc[self] = "Done" + /\ UNCHANGED vars + +Next == (\E self \in 1..2: Proc(self)) + \/ Terminating + +Spec == Init /\ [][Next]_vars + +Termination == <>(\A self \in ProcSet: pc[self] = "Done") + +\* END TRANSLATION - the hash of the generated TLA code (remove to silence divergence warnings): TLA-50c9ab33ebfd9eb7e6aeb12b5bbb6bd1 +========================================== diff --git a/tlatools/test-model/pcal/bug_05_12_10a.tla b/tlatools/test-model/pcal/bug_05_12_10a.tla new file mode 100644 index 0000000000000000000000000000000000000000..bb525e1e1181f2eee1855a7c7ad3743e1bf81e9c --- /dev/null +++ b/tlatools/test-model/pcal/bug_05_12_10a.tla @@ -0,0 +1,309 @@ +last modified on Thu 2 Feb 2006 at 15:40:27 PST by lamport + +The translator was renaming i to i_ in procedure IsAlgorithm, but +it failed to rename the i in the while test of statement IA3. + +--------------------------- MODULE bug_05_12_10a --------------------------- +EXTENDS Naturals, Sequences, FiniteSets , TLC +IsSeq(x) == DOMAIN x = 1..Len(x) +IsNonemptySeq(x) == IsSeq(x) /\ (Len(x) > 0) + +alg == [type |-> "uniprocess", + name |-> "Quicksort", + decls |-> << >>, + defs |-> << >>, + prcds |-> << >>, + body |-> <<1>>] + +(* +--algorithm Pcal + procedure IsAlgorithm(A) + variable res = FALSE ; i = 0 ; + begin IA1: assert \/ /\ A.type = "uniprocess" + /\ DOMAIN A = {"type", "name", "defs", + "decls", "prcds", "body"} + \/ /\ A.type = "multiiprocess" + /\ DOMAIN A = {"type", "name", "defs", + "decls", "prcds", "procs"}; + assert A.name \in STRING ; + call IsExpr(A.defs) ; + IA2: assert IsSeq(A.decls) ; + i := Len(A.decls) ; + IA3: while i > 0 do call IsVarDecl(A.decls[i]) ; + IA3a: i := i-1 + end while ; + i := Len(A.prcds) ; + IA4: while i > 0 do call IsProcedure(A.prcds[i]) ; + IA4a: i := i-1 + end while ; + if A.type = "uniprocess" + then assert IsNonemptySeq(A.body) ; + i := Len(A.body) ; + IA5: while i > 0 do call IsLabeledStmt(A.body[i]) ; + IA5a: i := i-1 + end while ; + else assert IsNonemptySeq(A.procs) ; + i := Len(A.procs) ; + IA6: while i > 0 do call IsProcess(A.procs[i]) ; + IA6a: i := i-1 + end while ; + end if ; + IA7: return ; + end procedure + procedure IsExpr(exp) + variable i ; + begin IE1 : assert IsSeq(exp) ; + i := Len(exp) ; + IE2: while i > 0 do assert exp[i] \in STRING ; + IA5a: i := i-1 + end while ; + return + end procedure + procedure IsVarDecl(vdcl) + begin IV1 : return + end procedure + procedure IsProcedure(prcdr) + begin IP1 : return + end procedure + procedure IsLabeledStmt(lstmt) + begin IL1 : return + end procedure + procedure IsProcess(proc) + begin IPr1 : return + end procedure + begin PC1: call IsAlgorithm(alg) ; + Pc2: print "IsAlgorithm(alg) = TRUE" + end algorithm +*) + +\* BEGIN TRANSLATION - the hash of the PCal code: PCal-019f5cd9d60f4235bafaf7162f8ed3b4 +\* Label IA5a of procedure IsAlgorithm at line 43 col 38 changed to IA5a_ +\* Procedure variable i of procedure IsAlgorithm at line 21 col 28 changed to i_ +CONSTANT defaultInitValue +VARIABLES pc, stack, A, res, i_, exp, i, vdcl, prcdr, lstmt, proc + +vars == << pc, stack, A, res, i_, exp, i, vdcl, prcdr, lstmt, proc >> + +Init == (* Procedure IsAlgorithm *) + /\ A = defaultInitValue + /\ res = FALSE + /\ i_ = 0 + (* Procedure IsExpr *) + /\ exp = defaultInitValue + /\ i = defaultInitValue + (* Procedure IsVarDecl *) + /\ vdcl = defaultInitValue + (* Procedure IsProcedure *) + /\ prcdr = defaultInitValue + (* Procedure IsLabeledStmt *) + /\ lstmt = defaultInitValue + (* Procedure IsProcess *) + /\ proc = defaultInitValue + /\ stack = << >> + /\ pc = "PC1" + +IA1 == /\ pc = "IA1" + /\ Assert(\/ /\ A.type = "uniprocess" + /\ DOMAIN A = {"type", "name", "defs", + "decls", "prcds", "body"} + \/ /\ A.type = "multiiprocess" + /\ DOMAIN A = {"type", "name", "defs", + "decls", "prcds", "procs"}, + "Failure of assertion at line 22, column 16.") + /\ Assert(A.name \in STRING, + "Failure of assertion at line 28, column 16.") + /\ /\ exp' = A.defs + /\ stack' = << [ procedure |-> "IsExpr", + pc |-> "IA2", + i |-> i, + exp |-> exp ] >> + \o stack + /\ i' = defaultInitValue + /\ pc' = "IE1" + /\ UNCHANGED << A, res, i_, vdcl, prcdr, lstmt, proc >> + +IA2 == /\ pc = "IA2" + /\ Assert(IsSeq(A.decls), + "Failure of assertion at line 30, column 16.") + /\ i_' = Len(A.decls) + /\ pc' = "IA3" + /\ UNCHANGED << stack, A, res, exp, i, vdcl, prcdr, lstmt, proc >> + +IA3 == /\ pc = "IA3" + /\ IF i_ > 0 + THEN /\ /\ stack' = << [ procedure |-> "IsVarDecl", + pc |-> "IA3a", + vdcl |-> vdcl ] >> + \o stack + /\ vdcl' = A.decls[i_] + /\ pc' = "IV1" + /\ i_' = i_ + ELSE /\ i_' = Len(A.prcds) + /\ pc' = "IA4" + /\ UNCHANGED << stack, vdcl >> + /\ UNCHANGED << A, res, exp, i, prcdr, lstmt, proc >> + +IA3a == /\ pc = "IA3a" + /\ i_' = i_-1 + /\ pc' = "IA3" + /\ UNCHANGED << stack, A, res, exp, i, vdcl, prcdr, lstmt, proc >> + +IA4 == /\ pc = "IA4" + /\ IF i_ > 0 + THEN /\ /\ prcdr' = A.prcds[i_] + /\ stack' = << [ procedure |-> "IsProcedure", + pc |-> "IA4a", + prcdr |-> prcdr ] >> + \o stack + /\ pc' = "IP1" + /\ i_' = i_ + ELSE /\ IF A.type = "uniprocess" + THEN /\ Assert(IsNonemptySeq(A.body), + "Failure of assertion at line 40, column 23.") + /\ i_' = Len(A.body) + /\ pc' = "IA5" + ELSE /\ Assert(IsNonemptySeq(A.procs), + "Failure of assertion at line 45, column 23.") + /\ i_' = Len(A.procs) + /\ pc' = "IA6" + /\ UNCHANGED << stack, prcdr >> + /\ UNCHANGED << A, res, exp, i, vdcl, lstmt, proc >> + +IA4a == /\ pc = "IA4a" + /\ i_' = i_-1 + /\ pc' = "IA4" + /\ UNCHANGED << stack, A, res, exp, i, vdcl, prcdr, lstmt, proc >> + +IA5 == /\ pc = "IA5" + /\ IF i_ > 0 + THEN /\ /\ lstmt' = A.body[i_] + /\ stack' = << [ procedure |-> "IsLabeledStmt", + pc |-> "IA5a_", + lstmt |-> lstmt ] >> + \o stack + /\ pc' = "IL1" + ELSE /\ pc' = "IA7" + /\ UNCHANGED << stack, lstmt >> + /\ UNCHANGED << A, res, i_, exp, i, vdcl, prcdr, proc >> + +IA5a_ == /\ pc = "IA5a_" + /\ i_' = i_-1 + /\ pc' = "IA5" + /\ UNCHANGED << stack, A, res, exp, i, vdcl, prcdr, lstmt, proc >> + +IA6 == /\ pc = "IA6" + /\ IF i_ > 0 + THEN /\ /\ proc' = A.procs[i_] + /\ stack' = << [ procedure |-> "IsProcess", + pc |-> "IA6a", + proc |-> proc ] >> + \o stack + /\ pc' = "IPr1" + ELSE /\ pc' = "IA7" + /\ UNCHANGED << stack, proc >> + /\ UNCHANGED << A, res, i_, exp, i, vdcl, prcdr, lstmt >> + +IA6a == /\ pc = "IA6a" + /\ i_' = i_-1 + /\ pc' = "IA6" + /\ UNCHANGED << stack, A, res, exp, i, vdcl, prcdr, lstmt, proc >> + +IA7 == /\ pc = "IA7" + /\ pc' = Head(stack).pc + /\ res' = Head(stack).res + /\ i_' = Head(stack).i_ + /\ A' = Head(stack).A + /\ stack' = Tail(stack) + /\ UNCHANGED << exp, i, vdcl, prcdr, lstmt, proc >> + +IsAlgorithm == IA1 \/ IA2 \/ IA3 \/ IA3a \/ IA4 \/ IA4a \/ IA5 \/ IA5a_ + \/ IA6 \/ IA6a \/ IA7 + +IE1 == /\ pc = "IE1" + /\ Assert(IsSeq(exp), "Failure of assertion at line 55, column 17.") + /\ i' = Len(exp) + /\ pc' = "IE2" + /\ UNCHANGED << stack, A, res, i_, exp, vdcl, prcdr, lstmt, proc >> + +IE2 == /\ pc = "IE2" + /\ IF i > 0 + THEN /\ Assert(exp[i] \in STRING, + "Failure of assertion at line 57, column 31.") + /\ pc' = "IA5a" + /\ UNCHANGED << stack, exp, i >> + ELSE /\ pc' = Head(stack).pc + /\ i' = Head(stack).i + /\ exp' = Head(stack).exp + /\ stack' = Tail(stack) + /\ UNCHANGED << A, res, i_, vdcl, prcdr, lstmt, proc >> + +IA5a == /\ pc = "IA5a" + /\ i' = i-1 + /\ pc' = "IE2" + /\ UNCHANGED << stack, A, res, i_, exp, vdcl, prcdr, lstmt, proc >> + +IsExpr == IE1 \/ IE2 \/ IA5a + +IV1 == /\ pc = "IV1" + /\ pc' = Head(stack).pc + /\ vdcl' = Head(stack).vdcl + /\ stack' = Tail(stack) + /\ UNCHANGED << A, res, i_, exp, i, prcdr, lstmt, proc >> + +IsVarDecl == IV1 + +IP1 == /\ pc = "IP1" + /\ pc' = Head(stack).pc + /\ prcdr' = Head(stack).prcdr + /\ stack' = Tail(stack) + /\ UNCHANGED << A, res, i_, exp, i, vdcl, lstmt, proc >> + +IsProcedure == IP1 + +IL1 == /\ pc = "IL1" + /\ pc' = Head(stack).pc + /\ lstmt' = Head(stack).lstmt + /\ stack' = Tail(stack) + /\ UNCHANGED << A, res, i_, exp, i, vdcl, prcdr, proc >> + +IsLabeledStmt == IL1 + +IPr1 == /\ pc = "IPr1" + /\ pc' = Head(stack).pc + /\ proc' = Head(stack).proc + /\ stack' = Tail(stack) + /\ UNCHANGED << A, res, i_, exp, i, vdcl, prcdr, lstmt >> + +IsProcess == IPr1 + +PC1 == /\ pc = "PC1" + /\ /\ A' = alg + /\ stack' = << [ procedure |-> "IsAlgorithm", + pc |-> "Pc2", + res |-> res, + i_ |-> i_, + A |-> A ] >> + \o stack + /\ res' = FALSE + /\ i_' = 0 + /\ pc' = "IA1" + /\ UNCHANGED << exp, i, vdcl, prcdr, lstmt, proc >> + +Pc2 == /\ pc = "Pc2" + /\ PrintT("IsAlgorithm(alg) = TRUE") + /\ pc' = "Done" + /\ UNCHANGED << stack, A, res, i_, exp, i, vdcl, prcdr, lstmt, proc >> + +(* Allow infinite stuttering to prevent deadlock on termination. *) +Terminating == pc = "Done" /\ UNCHANGED vars + +Next == IsAlgorithm \/ IsExpr \/ IsVarDecl \/ IsProcedure \/ IsLabeledStmt + \/ IsProcess \/ PC1 \/ Pc2 + \/ Terminating + +Spec == Init /\ [][Next]_vars + +Termination == <>(pc = "Done") + +\* END TRANSLATION - the hash of the generated TLA code (remove to silence divergence warnings): TLA-1e7f8dbaa6644662e3fa38b76045e01d +============================================================================= diff --git a/tlatools/test-model/pcal/bug_05_12_16b.tla b/tlatools/test-model/pcal/bug_05_12_16b.tla new file mode 100644 index 0000000000000000000000000000000000000000..9dcfff9d2890738f18223210558ea7d665839047 --- /dev/null +++ b/tlatools/test-model/pcal/bug_05_12_16b.tla @@ -0,0 +1,76 @@ +--algorithm Dijkstra1 + procedure Foo(a = 1) + variable x = 42 ; y = x + begin Foo1: return ; + end procedure + process P \in 1..3 + begin + P1: assert x = y ; + skip ; + P2: call Foo(17) ; + end process; + + end algorithm + +----------- MODULE bug_05_12_16b ----------- +EXTENDS Naturals, Sequences, TLC + +\* BEGIN TRANSLATION - the hash of the PCal code: PCal-7f01be0bd1e78f7ef9aee292dd8f66fc +VARIABLES pc, stack, a, x, y + +vars == << pc, stack, a, x, y >> + +ProcSet == (1..3) + +Init == (* Procedure Foo *) + /\ a = [ self \in ProcSet |-> 1] + /\ x = [ self \in ProcSet |-> 42] + /\ y = [ self \in ProcSet |-> x[self]] + /\ stack = [self \in ProcSet |-> << >>] + /\ pc = [self \in ProcSet |-> "P1"] + +Foo1(self) == /\ pc[self] = "Foo1" + /\ pc' = [pc EXCEPT ![self] = Head(stack[self]).pc] + /\ x' = [x EXCEPT ![self] = Head(stack[self]).x] + /\ y' = [y EXCEPT ![self] = Head(stack[self]).y] + /\ a' = [a EXCEPT ![self] = Head(stack[self]).a] + /\ stack' = [stack EXCEPT ![self] = Tail(stack[self])] + +Foo(self) == Foo1(self) + +P1(self) == /\ pc[self] = "P1" + /\ Assert(x[self] = y[self], + "Failure of assertion at line 8, column 9.") + /\ TRUE + /\ pc' = [pc EXCEPT ![self] = "P2"] + /\ UNCHANGED << stack, a, x, y >> + +P2(self) == /\ pc[self] = "P2" + /\ /\ a' = [a EXCEPT ![self] = 17] + /\ stack' = [stack EXCEPT ![self] = << [ procedure |-> "Foo", + pc |-> "Done", + x |-> x[self], + y |-> y[self], + a |-> a[self] ] >> + \o stack[self]] + /\ x' = [x EXCEPT ![self] = 42] + /\ y' = [y EXCEPT ![self] = x'[self]] + /\ pc' = [pc EXCEPT ![self] = "Foo1"] + +P(self) == P1(self) \/ P2(self) + +(* Allow infinite stuttering to prevent deadlock on termination. *) +Terminating == /\ \A self \in ProcSet: pc[self] = "Done" + /\ UNCHANGED vars + +Next == (\E self \in ProcSet: Foo(self)) + \/ (\E self \in 1..3: P(self)) + \/ Terminating + +Spec == Init /\ [][Next]_vars + +Termination == <>(\A self \in ProcSet: pc[self] = "Done") + +\* END TRANSLATION - the hash of the generated TLA code (remove to silence divergence warnings): TLA-e3a05375e3bf2d3d55b6d92003f1f2fa + +======================================== diff --git a/tlatools/test-model/pcal/bug_05_12_31.tla b/tlatools/test-model/pcal/bug_05_12_31.tla new file mode 100644 index 0000000000000000000000000000000000000000..79fbadc5e9de7a735bd9cac3f1b8679f1d75c268 --- /dev/null +++ b/tlatools/test-model/pcal/bug_05_12_31.tla @@ -0,0 +1,66 @@ + +------------------------------- MODULE bug_05_12_31 ----------------------------- +EXTENDS Naturals, Sequences, TLC + + +(* +--algorithm Test + procedure P(a = 7) + variable x = a ; y = x+1 ; + begin P1: assert a = 1; + assert x = a; + assert y = a+1; + return; + end procedure + begin A: call P(1) + end algorithm +*) + +\* BEGIN TRANSLATION - the hash of the PCal code: PCal-1f8e16f0d9c2b45f0271cd3d8415f9b8 +VARIABLES pc, stack, a, x, y + +vars == << pc, stack, a, x, y >> + +Init == (* Procedure P *) + /\ a = 7 + /\ x = a + /\ y = x+1 + /\ stack = << >> + /\ pc = "A" + +P1 == /\ pc = "P1" + /\ Assert(a = 1, "Failure of assertion at line 10, column 17.") + /\ Assert(x = a, "Failure of assertion at line 11, column 17.") + /\ Assert(y = a+1, "Failure of assertion at line 12, column 17.") + /\ pc' = Head(stack).pc + /\ x' = Head(stack).x + /\ y' = Head(stack).y + /\ a' = Head(stack).a + /\ stack' = Tail(stack) + +P == P1 + +A == /\ pc = "A" + /\ /\ a' = 1 + /\ stack' = << [ procedure |-> "P", + pc |-> "Done", + x |-> x, + y |-> y, + a |-> a ] >> + \o stack + /\ x' = a' + /\ y' = x'+1 + /\ pc' = "P1" + +(* Allow infinite stuttering to prevent deadlock on termination. *) +Terminating == pc = "Done" /\ UNCHANGED vars + +Next == P \/ A + \/ Terminating + +Spec == Init /\ [][Next]_vars + +Termination == <>(pc = "Done") + +\* END TRANSLATION - the hash of the generated TLA code (remove to silence divergence warnings): TLA-6df4c643b7934c47f1761b2b8aced244 +============================================================================= diff --git a/tlatools/test-model/pcal/bug_06_01_25.tla b/tlatools/test-model/pcal/bug_06_01_25.tla new file mode 100644 index 0000000000000000000000000000000000000000..5d52e83356e07bb122f08c19108898b4bd4f3709 --- /dev/null +++ b/tlatools/test-model/pcal/bug_06_01_25.tla @@ -0,0 +1,228 @@ + +--algorithm TestAlignment + variable x ; y ; z = /\ \A i \in {1} : i > 0 + /\ \A i \in {1} : i > 0 + /\ \A i \in {1} : i > 0 + define foo == /\ \A i \in {1} : i > 0 + /\ \A i \in {1} : i > 0 + /\ \A i \in {1} : i > 0 + end define; + macro Mac(a) begin x := \A i \in {1} : i > 0 ; + y := \A i \in {1} : i > 0 ; + z := \A i \in {1} : i > 0 ; + assert /\ \A i \in {1} : i > 0 + /\ \A i \in {1} : i > 0 + /\ \A i \in {1} : i > 0 ; + assert a + end macro; + + procedure P(a = /\ \A i \in {1} : i > 0 + /\ \A i \in {1} : i > 0 + /\ \A i \in {1} : i > 0) + begin P1: x := \A i \in {1} : i > 0 ; + y := \A i \in {1} : i > 0 ; + z := \A i \in {1} : i > 0 ; + return + end procedure + process Q \in {qq \in {1,2} : /\ \A i \in {1} : i > 0 + /\ \A j \in {1} : j > 0 + /\ \A k \in {1} : k > 0 } + variable w = /\ \A i \in {1} : i > 0 + /\ \A i \in {1} : i > 0 + /\ \A i \in {1} : i > 0 + begin P1: if /\ \A i \in {1} : i > 0 + /\ \A i \in {1} : i > 0 + /\ \A i \in {1} : i > 0 then x := \A i \in {1} : i > 0 ; + y := \A i \in {1} : i > 0 ; + z := \A i \in {1} : i > 0 + else x := \A i \in {1} : i > 0 ; + y := \A i \in {1} : i > 0 ; + z := \A i \in {1} : i > 0 + end if ; + P15: with k \in {kk \in {2,3} : /\ \A i \in {1} : i > 0 + /\ \A i \in {1} : i > 0 + /\ \A i \in {1} : i > 0 } do + with m = /\ \A i \in {1} : i > 0 + /\ \A i \in {1} : i > 0 + /\ \A i \in {1} : i > 0 do + x := \A i \in {1} : i > 0 ; + y := \A i \in {1} : i > 0 ; + z := \A i \in {1} : i > 0 + end with end with ; + P2: while /\ \A i \in {1} : i > 0 + /\ \A i \in {1} : i > 0 + /\ \A i \in {1} : i > 0 + /\ FALSE do + x := \A i \in {1} : i > 0 ; + y := \A i \in {1} : i > 0 ; + z := \A i \in {1} : i > 0 ; + w := /\ \A i \in {1} : i > 0 + /\ \A i \in {1} : i > 0 + /\ \A i \in {1} : i > 0 + end while ; + P3: call P(/\ \A i \in {1} : i > 0 + /\ \A i \in {1} : i > 0 + /\ \A i \in {1} : i > 0) ; + P4: assert /\ \A i \in {1} : i > 0 + /\ \A i \in {1} : i > 0 + /\ \A i \in {1} : i > 0 ; + print /\ \A i \in {1} : i > 0 + /\ \A i \in {1} : i > 0 + /\ \A i \in {1} : i > 0 ; + P5: Mac(/\ \A i \in {1} : i > 0 + /\ \A i \in {1} : i > 0 + /\ \A i \in {1} : i > 0 ) + end process + end algorithm + +----------- MODULE bug_06_01_25 ----------- +EXTENDS Sequences, Naturals, TLC + +ASSUME \forall i \in {} : i \geq 1 + +\* BEGIN TRANSLATION - the hash of the PCal code: PCal-9ef67f07b10dbbf43fc7d4cb2d8d7b02 +\* Label P1 of procedure P at line 22 col 17 changed to P1_ +CONSTANT defaultInitValue +VARIABLES x, y, z, pc, stack + +(* define statement *) +foo == /\ \A i \in {1} : i > 0 + /\ \A i \in {1} : i > 0 + /\ \A i \in {1} : i > 0 + +VARIABLES a, w + +vars == << x, y, z, pc, stack, a, w >> + +ProcSet == ({qq \in {1,2} : /\ \A i \in {1} : i > 0 + /\ \A j \in {1} : j > 0 + /\ \A k \in {1} : k > 0 }) + +Init == (* Global variables *) + /\ x = defaultInitValue + /\ y = defaultInitValue + /\ z = (/\ \A i \in {1} : i > 0 + /\ \A i \in {1} : i > 0 + /\ \A i \in {1} : i > 0) + (* Procedure P *) + /\ a = [ self \in ProcSet |-> /\ \A i \in {1} : i > 0 + /\ \A i \in {1} : i > 0 + /\ \A i \in {1} : i > 0] + (* Process Q *) + /\ w = [self \in {qq \in {1,2} : /\ \A i \in {1} : i > 0 + /\ \A j \in {1} : j > 0 + /\ \A k \in {1} : k > 0 } |-> /\ \A i \in {1} : i > 0 + /\ \A i \in {1} : i > 0 + /\ \A i \in {1} : i > 0] + /\ stack = [self \in ProcSet |-> << >>] + /\ pc = [self \in ProcSet |-> "P1"] + +P1_(self) == /\ pc[self] = "P1_" + /\ x' = (\A i \in {1} : i > 0) + /\ y' = (\A i \in {1} : i > 0) + /\ z' = (\A i \in {1} : i > 0) + /\ pc' = [pc EXCEPT ![self] = Head(stack[self]).pc] + /\ a' = [a EXCEPT ![self] = Head(stack[self]).a] + /\ stack' = [stack EXCEPT ![self] = Tail(stack[self])] + /\ w' = w + +P(self) == P1_(self) + +P1(self) == /\ pc[self] = "P1" + /\ IF /\ \A i \in {1} : i > 0 + /\ \A i \in {1} : i > 0 + /\ \A i \in {1} : i > 0 + THEN /\ x' = (\A i \in {1} : i > 0) + /\ y' = (\A i \in {1} : i > 0) + /\ z' = (\A i \in {1} : i > 0) + ELSE /\ x' = (\A i \in {1} : i > 0) + /\ y' = (\A i \in {1} : i > 0) + /\ z' = (\A i \in {1} : i > 0) + /\ pc' = [pc EXCEPT ![self] = "P15"] + /\ UNCHANGED << stack, a, w >> + +P15(self) == /\ pc[self] = "P15" + /\ \E k \in {kk \in {2,3} : /\ \A i \in {1} : i > 0 + /\ \A i \in {1} : i > 0 + /\ \A i \in {1} : i > 0 }: + LET m == /\ \A i \in {1} : i > 0 + /\ \A i \in {1} : i > 0 + /\ \A i \in {1} : i > 0 IN + /\ x' = (\A i \in {1} : i > 0) + /\ y' = (\A i \in {1} : i > 0) + /\ z' = (\A i \in {1} : i > 0) + /\ pc' = [pc EXCEPT ![self] = "P2"] + /\ UNCHANGED << stack, a, w >> + +P2(self) == /\ pc[self] = "P2" + /\ IF /\ \A i \in {1} : i > 0 + /\ \A i \in {1} : i > 0 + /\ \A i \in {1} : i > 0 + /\ FALSE + THEN /\ x' = (\A i \in {1} : i > 0) + /\ y' = (\A i \in {1} : i > 0) + /\ z' = (\A i \in {1} : i > 0) + /\ w' = [w EXCEPT ![self] = /\ \A i \in {1} : i > 0 + /\ \A i \in {1} : i > 0 + /\ \A i \in {1} : i > 0] + /\ pc' = [pc EXCEPT ![self] = "P2"] + ELSE /\ pc' = [pc EXCEPT ![self] = "P3"] + /\ UNCHANGED << x, y, z, w >> + /\ UNCHANGED << stack, a >> + +P3(self) == /\ pc[self] = "P3" + /\ /\ a' = [a EXCEPT ![self] = /\ \A i \in {1} : i > 0 + /\ \A i \in {1} : i > 0 + /\ \A i \in {1} : i > 0] + /\ stack' = [stack EXCEPT ![self] = << [ procedure |-> "P", + pc |-> "P4", + a |-> a[self] ] >> + \o stack[self]] + /\ pc' = [pc EXCEPT ![self] = "P1_"] + /\ UNCHANGED << x, y, z, w >> + +P4(self) == /\ pc[self] = "P4" + /\ Assert(/\ \A i \in {1} : i > 0 + /\ \A i \in {1} : i > 0 + /\ \A i \in {1} : i > 0, + "Failure of assertion at line 66, column 17.") + /\ PrintT(/\ \A i \in {1} : i > 0 + /\ \A i \in {1} : i > 0 + /\ \A i \in {1} : i > 0) + /\ pc' = [pc EXCEPT ![self] = "P5"] + /\ UNCHANGED << x, y, z, stack, a, w >> + +P5(self) == /\ pc[self] = "P5" + /\ x' = (\A i \in {1} : i > 0) + /\ y' = (\A i \in {1} : i > 0) + /\ z' = (\A i \in {1} : i > 0) + /\ Assert(/\ \A i \in {1} : i > 0 + /\ \A i \in {1} : i > 0 + /\ \A i \in {1} : i > 0, + "Failure of assertion at line 13, column 23 of macro called at line 72, column 17.") + /\ Assert(/\ \A i \in {1} : i > 0 + /\ \A i \in {1} : i > 0 + /\ \A i \in {1} : i > 0, + "Failure of assertion at line 16, column 23 of macro called at line 72, column 17.") + /\ pc' = [pc EXCEPT ![self] = "Done"] + /\ UNCHANGED << stack, a, w >> + +Q(self) == P1(self) \/ P15(self) \/ P2(self) \/ P3(self) \/ P4(self) + \/ P5(self) + +(* Allow infinite stuttering to prevent deadlock on termination. *) +Terminating == /\ \A self \in ProcSet: pc[self] = "Done" + /\ UNCHANGED vars + +Next == (\E self \in ProcSet: P(self)) + \/ (\E self \in {qq \in {1,2} : /\ \A i \in {1} : i > 0 + /\ \A j \in {1} : j > 0 + /\ \A k \in {1} : k > 0 }: Q(self)) + \/ Terminating + +Spec == Init /\ [][Next]_vars + +Termination == <>(\A self \in ProcSet: pc[self] = "Done") + +\* END TRANSLATION - the hash of the generated TLA code (remove to silence divergence warnings): TLA-c0ae8bdf7ad8a969ed412d1b64210cb9 +======================================== diff --git a/tlatools/test-model/simulation/BasicMultiTrace/BasicMultiTrace.tla b/tlatools/test-model/simulation/BasicMultiTrace/BasicMultiTrace.tla new file mode 100644 index 0000000000000000000000000000000000000000..c7d0ec58d042f7ed37c209571739538b8767ad8e --- /dev/null +++ b/tlatools/test-model/simulation/BasicMultiTrace/BasicMultiTrace.tla @@ -0,0 +1,63 @@ +------------------------------ MODULE BasicMultiTrace ------------------------------ +EXTENDS Integers + +\* +\* This spec attempts to define a simple state machine that has multiple behaviors. +\* Originally added to verify correctness of simulation mode in both single and multi-threaded mode. +\* + +VARIABLE branch, depth + +MaxDepth == 5 +Width == 10 + +Init == + /\ branch = 0 + /\ depth = 0 + +Next == + \* Pick a branch + \/ /\ branch = 0 + /\ branch' \in 1..Width /\ UNCHANGED depth + \* Traverse down that branch. + \/ /\ branch > 0 + /\ depth < MaxDepth + /\ depth' = depth + 1 /\ UNCHANGED branch + +Spec == Init /\ [][Next]_<<branch, depth>> + +UnderspecifiedNext == branch' = 0 +UnderspecifiedInit == branch = 0 + +UnderspecifiedInitSpec == UnderspecifiedInit /\ [][Next]_<<branch, depth>> +UnderspecifiedNextSpec == Init /\ [][UnderspecifiedNext]_<<branch, depth>> + +\* A valid invariant that should be violated by the initial state. +InvInitState == ~(branch = 0) + +\* A valid invariant that should be violated by the spec. +Inv == ~(depth = 2) + +\* A nonsensical invariant that will cause a runtime evaluation error on the initial state. +InvBadEvalInitState == (0 = "a") + +\* A nonsensical invariant that will cause a runtime evaluation error on a non-initial state. +InvBadEvalNonInitState == IF branch = 0 THEN TRUE ELSE (0 = "a") + +\* A valid action property that should be violated by the spec. +ActionProp == [][branch' > branch]_<<branch, depth>> + +\* A nonsensical action property that will cause a runtime evaluation error. +ActionPropBadEval == [][IF branch = 0 THEN TRUE ELSE 0 = "a"]_<<branch, depth>> + +\* A simple state and action constraint, and an invariant that should hold true +\* given either constraint. +StateConstraint == depth < 4 +ActionConstraint == ~(depth' = 4 /\ depth = 3) +InvConstraint == depth <= 4 + +\* A liveness property that should be violated. +LivenessProp == <>(branch = -1) + + +============================================================================= \ No newline at end of file diff --git a/tlatools/test-model/simulation/BasicMultiTrace/MC.cfg b/tlatools/test-model/simulation/BasicMultiTrace/MC.cfg new file mode 100644 index 0000000000000000000000000000000000000000..b763c9c194f8988812ce02ebc9f9012fe3a8b442 --- /dev/null +++ b/tlatools/test-model/simulation/BasicMultiTrace/MC.cfg @@ -0,0 +1,2 @@ +SPECIFICATION +Spec \ No newline at end of file diff --git a/tlatools/test-model/simulation/BasicMultiTrace/MCActionProp.cfg b/tlatools/test-model/simulation/BasicMultiTrace/MCActionProp.cfg new file mode 100644 index 0000000000000000000000000000000000000000..02c18eae9509cfeae0f88a0a8d500b6ae3da156e --- /dev/null +++ b/tlatools/test-model/simulation/BasicMultiTrace/MCActionProp.cfg @@ -0,0 +1,4 @@ +SPECIFICATION +Spec +PROPERTY +ActionProp diff --git a/tlatools/test-model/simulation/BasicMultiTrace/MCActionPropBadEval.cfg b/tlatools/test-model/simulation/BasicMultiTrace/MCActionPropBadEval.cfg new file mode 100644 index 0000000000000000000000000000000000000000..bba9f9740204b197987977900a83e634a9dc9927 --- /dev/null +++ b/tlatools/test-model/simulation/BasicMultiTrace/MCActionPropBadEval.cfg @@ -0,0 +1,4 @@ +SPECIFICATION +Spec +PROPERTY +ActionPropBadEval diff --git a/tlatools/test-model/simulation/BasicMultiTrace/MCBadInvInitState.cfg b/tlatools/test-model/simulation/BasicMultiTrace/MCBadInvInitState.cfg new file mode 100644 index 0000000000000000000000000000000000000000..d01b36afe2405e3a02bd8a911bd65a427edc8f36 --- /dev/null +++ b/tlatools/test-model/simulation/BasicMultiTrace/MCBadInvInitState.cfg @@ -0,0 +1,4 @@ +SPECIFICATION +Spec +INVARIANT +InvBadEvalInitState \ No newline at end of file diff --git a/tlatools/test-model/simulation/BasicMultiTrace/MCBadInvNonInitState.cfg b/tlatools/test-model/simulation/BasicMultiTrace/MCBadInvNonInitState.cfg new file mode 100644 index 0000000000000000000000000000000000000000..6b7abf91a8e841503ff4472fbac021640a9c6c6d --- /dev/null +++ b/tlatools/test-model/simulation/BasicMultiTrace/MCBadInvNonInitState.cfg @@ -0,0 +1,4 @@ +SPECIFICATION +Spec +INVARIANT +InvBadEvalNonInitState \ No newline at end of file diff --git a/tlatools/test-model/simulation/BasicMultiTrace/MCInv.cfg b/tlatools/test-model/simulation/BasicMultiTrace/MCInv.cfg new file mode 100644 index 0000000000000000000000000000000000000000..c8cffbe64c3dcc8bce148218f2e7b66a7537f573 --- /dev/null +++ b/tlatools/test-model/simulation/BasicMultiTrace/MCInv.cfg @@ -0,0 +1,4 @@ +SPECIFICATION +Spec +INVARIANT +Inv \ No newline at end of file diff --git a/tlatools/test-model/simulation/BasicMultiTrace/MCInvInitState.cfg b/tlatools/test-model/simulation/BasicMultiTrace/MCInvInitState.cfg new file mode 100644 index 0000000000000000000000000000000000000000..564eb2d977457b3b535e66584faa46db08cd978e --- /dev/null +++ b/tlatools/test-model/simulation/BasicMultiTrace/MCInvInitState.cfg @@ -0,0 +1,4 @@ +SPECIFICATION +Spec +INVARIANT +InvInitState \ No newline at end of file diff --git a/tlatools/test-model/simulation/BasicMultiTrace/MCLivenessProp.cfg b/tlatools/test-model/simulation/BasicMultiTrace/MCLivenessProp.cfg new file mode 100644 index 0000000000000000000000000000000000000000..2a5e4e9fb0e9b45e8355d4bf71d4f3f396cd7704 --- /dev/null +++ b/tlatools/test-model/simulation/BasicMultiTrace/MCLivenessProp.cfg @@ -0,0 +1,4 @@ +SPECIFICATION +Spec +PROPERTY +LivenessProp diff --git a/tlatools/test-model/simulation/BasicMultiTrace/MCUnderspecInit.cfg b/tlatools/test-model/simulation/BasicMultiTrace/MCUnderspecInit.cfg new file mode 100644 index 0000000000000000000000000000000000000000..b1919bb49034f496f5204a62eacb6990c13e1ced --- /dev/null +++ b/tlatools/test-model/simulation/BasicMultiTrace/MCUnderspecInit.cfg @@ -0,0 +1,4 @@ +SPECIFICATION +UnderspecifiedInitSpec +INVARIANT +Inv \ No newline at end of file diff --git a/tlatools/test-model/simulation/BasicMultiTrace/MCUnderspecNext.cfg b/tlatools/test-model/simulation/BasicMultiTrace/MCUnderspecNext.cfg new file mode 100644 index 0000000000000000000000000000000000000000..456896216d24793a361be6563c330eed792e0ab0 --- /dev/null +++ b/tlatools/test-model/simulation/BasicMultiTrace/MCUnderspecNext.cfg @@ -0,0 +1,4 @@ +SPECIFICATION +UnderspecifiedNextSpec +INVARIANT +Inv \ No newline at end of file diff --git a/tlatools/test-model/simulation/BasicMultiTrace/MCWithActionConstraint.cfg b/tlatools/test-model/simulation/BasicMultiTrace/MCWithActionConstraint.cfg new file mode 100644 index 0000000000000000000000000000000000000000..8e14f70964e994bb30e651bb896f0903de532b4b --- /dev/null +++ b/tlatools/test-model/simulation/BasicMultiTrace/MCWithActionConstraint.cfg @@ -0,0 +1,6 @@ +SPECIFICATION +Spec +INVARIANT +InvConstraint +ACTION_CONSTRAINT +ActionConstraint \ No newline at end of file diff --git a/tlatools/test-model/simulation/BasicMultiTrace/MCWithConstraint.cfg b/tlatools/test-model/simulation/BasicMultiTrace/MCWithConstraint.cfg new file mode 100644 index 0000000000000000000000000000000000000000..eb2ec2045cb216aca236f52b19e032281b7610c0 --- /dev/null +++ b/tlatools/test-model/simulation/BasicMultiTrace/MCWithConstraint.cfg @@ -0,0 +1,6 @@ +SPECIFICATION +Spec +INVARIANT +InvConstraint +CONSTRAINT +StateConstraint \ No newline at end of file diff --git a/tlatools/test-model/simulation/BenchmarkSpec/BenchmarkSpec.tla b/tlatools/test-model/simulation/BenchmarkSpec/BenchmarkSpec.tla new file mode 100644 index 0000000000000000000000000000000000000000..49eca6bb354bab0a5b5a450cbfded41e7c5d71d1 --- /dev/null +++ b/tlatools/test-model/simulation/BenchmarkSpec/BenchmarkSpec.tla @@ -0,0 +1,28 @@ +------------------------------ MODULE BenchmarkSpec ------------------------------ +EXTENDS Integers, Sequences + +\* +\* This spec produces a state space with a simple tree-like structure. The maximum depth and branching +\* factor of the state space tree can be controlled by setting the constant values of the spec. The goal +\* is to give a spec whose state space structure is easily understandable and controllable, for the sake +\* of running performance and/or correctness tests of TLC. +\* + +VARIABLE hist + +Init == hist = <<>> + +CONSTANTS Depth, BranchingFactor + +Next == + \E e \in 1..BranchingFactor : + hist' = Append(hist, e) + +Spec == Init /\ [][Next]_<<hist>> + +Constraint == Len(hist) < Depth + +\* An arbitrary state of max length. It's just a sequence filled with all 1's. +Inv == hist # [i \in 1..Depth |-> 1] + +============================================================================= \ No newline at end of file diff --git a/tlatools/test-model/simulation/BenchmarkSpec/MCInv.cfg b/tlatools/test-model/simulation/BenchmarkSpec/MCInv.cfg new file mode 100644 index 0000000000000000000000000000000000000000..4606d873bd81a8dc67cc6faf0a8337dd9c7e7421 --- /dev/null +++ b/tlatools/test-model/simulation/BenchmarkSpec/MCInv.cfg @@ -0,0 +1,9 @@ +SPECIFICATION +Spec +INVARIANT +Inv +CONSTANTS +BranchingFactor = 3 +Depth = 15 +CONSTRAINT +Constraint \ No newline at end of file diff --git a/tlatools/test-model/simulation/NQSpec/MC.cfg b/tlatools/test-model/simulation/NQSpec/MC.cfg new file mode 100644 index 0000000000000000000000000000000000000000..ed49c011a10f683b730d990530404af87d6889ee --- /dev/null +++ b/tlatools/test-model/simulation/NQSpec/MC.cfg @@ -0,0 +1,47 @@ +\* MV CONSTANT declarations +CONSTANTS +e1 = e1 +\* MV CONSTANT declarations +CONSTANTS +id1 = id1 +\* MV CONSTANT declarations +CONSTANTS +d1 = d1 +\* MV CONSTANT declarations +CONSTANTS +v1 = v1 +\* MV CONSTANT definitions +CONSTANT +EnQers <- const_14476073153412000 +\* MV CONSTANT definitions +CONSTANT +Ids <- const_14476073153513000 +\* MV CONSTANT definitions +CONSTANT +DeQers <- const_14476073153614000 +\* MV CONSTANT definitions +CONSTANT +Data <- const_14476073153715000 +\* CONSTANT declarations +CONSTANT Busy = Busy +\* CONSTANT declarations +CONSTANT NoData = NoData +\* CONSTANT definitions +CONSTANT +InitData <- const_14476073153816000 +\* CONSTANT definition +CONSTANT +Done = Done +NotAnId = NotAnId +NotAnElement = NotAnElement +top = top +\* SPECIFICATION definition +SPECIFICATION +spec_144760731543311000 +\* INVARIANT definition +INVARIANT +inv_144760731544312000 +\* PROPERTY definition +PROPERTY +prop_144760731545313000 +\* Generated on Sun Nov 15 18:08:35 CET 2015 \ No newline at end of file diff --git a/tlatools/test-model/simulation/NQSpec/MC.tla b/tlatools/test-model/simulation/NQSpec/MC.tla new file mode 100644 index 0000000000000000000000000000000000000000..88d4a9a0075de01213b05a9a2c52d6dafb5709c4 --- /dev/null +++ b/tlatools/test-model/simulation/NQSpec/MC.tla @@ -0,0 +1,63 @@ +---- MODULE MC ---- +EXTENDS NQSpecImpliesQSpec, TLC + +\* MV CONSTANT declarations@modelParameterConstants +CONSTANTS +e1 +---- + +\* MV CONSTANT declarations@modelParameterConstants +CONSTANTS +id1 +---- + +\* MV CONSTANT declarations@modelParameterConstants +CONSTANTS +d1 +---- + +\* MV CONSTANT declarations@modelParameterConstants +CONSTANTS +v1 +---- + +\* MV CONSTANT definitions EnQers +const_14476073153412000 == +{e1} +---- + +\* MV CONSTANT definitions Ids +const_14476073153513000 == +{id1} +---- + +\* MV CONSTANT definitions DeQers +const_14476073153614000 == +{d1} +---- + +\* MV CONSTANT definitions Data +const_14476073153715000 == +{v1} +---- + +\* CONSTANT definitions @modelParameterConstants:4InitData +const_14476073153816000 == +v1 +---- + +\* SPECIFICATION definition @modelBehaviorSpec:0 +spec_144760731543311000 == +SpecI +---- +\* INVARIANT definition @modelCorrectnessInvariants:0 +inv_144760731544312000 == +TypeOKI +---- +\* PROPERTY definition @modelCorrectnessProperties:0 +prop_144760731545313000 == +I!Spec +---- +============================================================================= +\* Modification History +\* Created Sun Nov 15 18:08:35 CET 2015 by markus diff --git a/tlatools/test-model/simulation/NQSpec/NQSpec.tla b/tlatools/test-model/simulation/NQSpec/NQSpec.tla new file mode 100644 index 0000000000000000000000000000000000000000..7affcc75a616acf5e1f1fd441f5eb4fbafc2da0d --- /dev/null +++ b/tlatools/test-model/simulation/NQSpec/NQSpec.tla @@ -0,0 +1,89 @@ +------------------------------- MODULE NQSpec ------------------------------- +EXTENDS Integers, Sequences, TLC + +CONSTANTS EnQers, DeQers, Data, InitData, Ids, Busy, NoData + +ASSUME InitData \in Data + +Done == CHOOSE D : D \notin Data +\*Busy == CHOOSE D : D \notin Data +NotAnId == CHOOSE i : i \notin Ids +\*NoData == CHOOSE D : D \notin Data +Elements == [data : Data, id : Ids] +NotAnElement == CHOOSE E : E \notin Elements + +VARIABLES enq, deq, after, adding +vars == <<enq, deq, after, adding>> + +elts == DOMAIN after + +doneElts == elts \ {adding[e] : e \in EnQers} + +TypeOK == /\ enq \in [EnQers -> Data \cup {Done}] + /\ deq \in [DeQers -> Data \cup {Busy}] + /\ elts \in SUBSET Elements + /\ after \in [elts -> SUBSET doneElts] + /\ adding \in [EnQers -> Elements \cup {NotAnElement}] + +Init == + /\ TLCSet(42, 1) + /\ enq = [e \in EnQers |-> Done] + /\ deq = [d \in DeQers |-> InitData] + /\ after = << >> + /\ adding = [e \in EnQers |-> NotAnElement] +----------------------------------------------------------------------------- + +Assign(var, idx, val) == var' = [var EXCEPT ![idx] = val] + +IdsBeingAdded == {adding[el].id : el \in {e \in EnQers : adding[e] # NotAnElement}} + +UnusedIds == (Ids \ {el.id : el \in elts}) \ IdsBeingAdded + + +AddElt(el) == + after' = [x \in elts \cup {el} |-> + IF x = el THEN doneElts + ELSE after[x] ] + +RemoveElt(el) == + after' = [x \in elts \ {el} |-> after[x] \ {el}] + +MinimalElts(After) == {el \in DOMAIN After : After[el] = {}} +minimalElts == MinimalElts(after) + +BeginEnq(e) == /\ enq[e] = Done + /\ \E D \in Data, id \in UnusedIds : + LET el == [data |-> D, id |-> id] + IN /\ Assign(enq, e, D) + /\ AddElt(el) + /\ Assign(adding, e, el) + /\ UNCHANGED deq + +EndEnq(e) == /\ enq[e] # Done + /\ Assign(enq, e, Done) + /\ Assign(adding, e, NotAnElement) + /\ UNCHANGED <<deq, after>> + +\* enq, deq, elts, after, adding +BeginDeq(d) == /\ deq[d] # Busy + /\ Assign(deq, d, Busy) + /\ UNCHANGED <<enq, after, adding>> + +EndDeq(d) == /\ deq[d] = Busy + /\ \E el \in minimalElts : + /\ RemoveElt(el) + /\ Assign(deq, d, el.data) + /\ UNCHANGED <<enq, adding>> + +Next == \/ \E e \in EnQers : BeginEnq(e) \/ EndEnq(e) + \/ \E d \in DeQers : BeginDeq(d) \/ EndDeq(d) + +Liveness == /\ \A e \in EnQers : WF_vars(BeginEnq(e) \/ EndEnq(e)) + /\ \A d \in DeQers : WF_vars(BeginDeq(d) \/ EndDeq(d)) + +Spec == Init /\ [][Next]_vars /\ Liveness + +============================================================================= +\* Modification History +\* Last modified Sat Nov 14 16:00:53 PST 2015 by lamport +\* Created Thu Nov 05 15:07:25 PST 2015 by lamport diff --git a/tlatools/test-model/simulation/NQSpec/NQSpecImpliesQSpec.tla b/tlatools/test-model/simulation/NQSpec/NQSpecImpliesQSpec.tla new file mode 100644 index 0000000000000000000000000000000000000000..2197c3620e3a2d900ef513d986a489b4c9e52543 --- /dev/null +++ b/tlatools/test-model/simulation/NQSpec/NQSpecImpliesQSpec.tla @@ -0,0 +1,212 @@ +------------------------- MODULE NQSpecImpliesQSpec ------------------------- +EXTENDS NQSpec, FiniteSets, TLC + +\*NoData == NotData +NatSetMax(S) == IF S = {} THEN 0 ELSE CHOOSE n \in S : \A m \in S : n >=m +VARIABLE p, \* The prophecy variable = sequence of next values read, + \* of length Cardinality(elts) + Q, \* The queue of QSpec, except as Elements with their Ids, + \* and except that it is changed only on the actions + \* of NQSpec. Added as a history variable. + s, + \* A stuttering variable to add the actions + \* corresponding to the DoEnq and DoDeq actions of QSpec + enqInner, deqInner + \* History variables implementing the corresponding variables + \* of QSpec, except deqInner gets set to an Element el instead + \* of el.data + +\* The order of adding the variables is: +\* s, p, Q, {queue, enqInner, deqInner} +varsI == <<vars, p, Q, s, enqInner, deqInner>> + +top == CHOOSE t : t \notin {} +bot == [EndEnq |-> TRUE, EndDeq |-> TRUE] + +TypeOKI == /\ TypeOK + /\ Q \in Seq(Elements) + /\ p \in Seq(Elements) + /\ s \in {top} + \cup [type : {"EndDeq"}, name : DeQers \X Elements, val : {FALSE, TRUE}] + \cup [type : {"EndEnq"}, name : EnQers, val : {TRUE}] + \cup [type : {"BeginEnq"}, name : EnQers, val : Seq(Elements)] + /\ enqInner \in [EnQers -> {Done, Busy}] + /\ deqInner \in [DeQers -> {NoData} \cup Elements] + +InitI == /\ Init + /\ Q = << >> + /\ enqInner = [e \in EnQers |-> Done] + /\ \E i \in Ids : deqInner = [d \in DeQers |-> [data |-> InitData, + id |-> i]] + /\ p = << >> + /\ s = top + /\ TLCSet(1, {}) + +RemoveFrom(E, After) == [x \in (DOMAIN After) \ E |-> After[x] \ E] +RECURSIVE IsConsistent(_, _) +IsConsistent(seq, After) == + IF seq = << >> + THEN TRUE + ELSE LET hd == Head(seq) + Elts == DOMAIN After + IN /\ (hd \in DOMAIN After) /\ (After[hd] = {}) + /\ IsConsistent(Tail(seq), + RemoveFrom({hd}, After)) + +IsBlocking(el, After) == LET Elts == DOMAIN After + MinElts == MinimalElts(After) + IN (el \notin MinElts) /\ (doneElts # {}) + + +AllEnabled == s = top + +PostStutterEnabled(type, name) == (s # top) /\ (s.type = type) /\ (s.name = name) +PreStutterEnabled(type, name) == (s # top) => + /\ (s.type = type) /\ (s.name = name) + /\ s.val # bot[s.type] +AfterPreStutterEnabled(type, name) == /\ s # top + /\ (s.type = type) /\ (s.name = name) + /\ s.val = bot[s.type] + +SetStutter(type, name, val) == + /\ (s # top) => Assert(s.type = type /\ s.name = name, + "SetStutter not executed by owner of stuttering variable") + /\ s' = [type |-> type, name |-> name, val |-> val] + +Prefix(n, seq) == [i \in 1..n |-> seq[i]] +Suffix(n, seq) == [i \in 1..(Len(seq)-n) |-> seq[i+n]] +SeqElts(seq) == {seq[i] : i \in 1..Len(seq)} + +PInv == /\ Cardinality(elts) = Len(p) + +QInv == /\ Cardinality(SeqElts(Q)) = Len(Q) + /\ SeqElts(Q) \subseteq elts + /\ IsConsistent(Q, after) + /\ LET n == NatSetMax({i \in 1..Len(Q) : Prefix(i, Q) = Prefix(i, p)}) + IN (n < Len(Q)) => IsBlocking(p[n+1], + RemoveFrom(SeqElts(Prefix(n, Q)), after)) + +Writer(elt) == CHOOSE e \in EnQers : adding[e] = elt + +BeginEnqI(e) == /\ AllEnabled + /\ BeginEnq(e) + /\ \E el \in Elements : p' = Append(p, el) +\* /\ TLCSet(1, {p'} \cup TLCGet(1)) +\* /\ IF (Elements \X Elements \subseteq TLCGet(1)) +\* THEN /\ PrintT(TLCGet(1)) +\* /\ Assert (FALSE, "There") +\* ELSE TRUE +\*\* /\ PrintT(<<e, p, p', after'>>) + /\ LET RECURSIVE ToAdd(_, _) + ToAdd(i, After) == + IF (i > Len(p')) \/ p'[i] \notin MinimalElts(After) + THEN << >> + ELSE <<p'[i]>> \o ToAdd(i+1, RemoveFrom({p'[i]}, After)) + TA == ToAdd(Len(Q)+1, RemoveFrom(SeqElts(Q), after')) + IN IF TA # << >> + THEN SetStutter("BeginEnq", e, TA) + ELSE s' = s + /\ Assign(enqInner, e, Busy) + /\ UNCHANGED <<Q, deqInner>> + +BeginEnqIDo(e) == /\ PostStutterEnabled("BeginEnq", e) + /\ Q' = Append(Q, Head(s.val)) + /\ Assign(enqInner, Writer(Head(s.val)), Done) + /\ IF Tail(s.val) # << >> THEN SetStutter("BeginEnq", e, Tail(s.val)) + ELSE s' = top + /\ UNCHANGED <<vars, p, deqInner>> + +DoEnqI(e) == /\ PreStutterEnabled("EndEnq", e) + /\ enq[e] # Done + /\ IF enqInner[e] # Done \* adding[e] \notin SeqElts(Q) + THEN /\ Q' = Append(Q, adding[e]) + /\ Assign(enqInner, e, Done) + ELSE UNCHANGED <<Q, enqInner>> + /\ SetStutter("EndEnq", e, TRUE) + /\ UNCHANGED <<vars, p, deqInner>> + +EndEnqI(e) == /\ AfterPreStutterEnabled("EndEnq", e) + /\ EndEnq(e) + /\ s' = top + /\ UNCHANGED << p, Q, enqInner, deqInner>> + +BeginDeqI(d) == /\ AllEnabled + /\ BeginDeq(d) + /\ Assign(deqInner, d, NoData) + /\ UNCHANGED <<p, Q, s, enqInner>> + +\* OLD DEFS: +\*DoDeqI(d) == /\ PreStutterEnabled("EndDeq", d) +\* /\ deq[d] = Busy +\* /\ minimalElts # {} +\* /\ IF s = top /\ Q = << >> +\* THEN /\ \E el \in minimalElts : +\* /\ Q' = <<el>> +\* /\ Assign(enqInner, Writer(el), Done) +\* /\ SetStutter("EndDeq", d, FALSE) +\* /\ UNCHANGED deqInner +\* ELSE /\ Q' = Tail(Q) +\* /\ SetStutter("EndDeq", d, TRUE) +\* /\ Assign(deqInner, d, Head(Q)) +\* /\ UNCHANGED enqInner +\* /\ UNCHANGED <<vars, p>> +\* +\*EndDeqI(d) == /\ AfterPreStutterEnabled("EndDeq", d) +\* /\ EndDeq(d) +\* /\ p = << >> \/ Head(p) = deqInner[d] +\* /\ p' = Tail(p) +\* /\ s' = top +\* /\ UNCHANGED <<Q, enqInner, deqInner>> +(*************************************************************************** +XEndDeq(d, el) defined so that + + EndDeq(d) == \E el \in Elements : XEndDeq(d, el) + ***************************************************************************) +XEndDeq(d, el) == /\ deq[d] = Busy + /\ el \in minimalElts + /\ RemoveElt(el) + /\ Assign(deq, d, el.data) + /\ UNCHANGED <<enq, adding>> + +DoDeqI(d, el) == /\ PreStutterEnabled("EndDeq", <<d, el>>) + /\ deq[d] = Busy + /\ el \in minimalElts + /\ \/ p = << >> /\ p' = << >> /\ Assert(FALSE, "shouldn't happen") + \/ Head(p) = el /\ p' = Tail(p) + /\ IF s = top /\ Q = << >> + THEN \* I believe this case should never occur + /\ Q' = <<el>> + /\ Assign(enqInner, Writer(el), Done) + /\ SetStutter("EndDeq", <<d, el>>, FALSE) + /\ UNCHANGED deqInner + ELSE /\ el = Head(Q) + /\ Q' = Tail(Q) + /\ SetStutter("EndDeq", <<d, el>>, TRUE) + /\ Assign(deqInner, d, Head(Q)) + /\ UNCHANGED enqInner + /\ UNCHANGED <<vars>> + +EndDeqI(d, el) == /\ AfterPreStutterEnabled("EndDeq", <<d, el>>) + /\ XEndDeq(d, el) +\* /\ p = << >> \/ Head(p) = deqInner[d] +\* /\ p' = Tail(p) + /\ s' = top + /\ UNCHANGED <<Q, enqInner, deqInner, p>> + +NextI == \/ \E e \in EnQers : BeginEnqI(e) \/ BeginEnqIDo(e) + \/ DoEnqI(e) \/ EndEnqI(e) + \/ \E d \in DeQers : \/ BeginDeqI(d) + \/ \E el \in Elements : DoDeqI(d, el) \/ EndDeqI(d, el) + +SpecI == InitI /\ [][NextI]_varsI +----------------------------------------------------------------------------- +queue == [i \in 1..Len(Q) |-> Q[i].data] + +deqInnerBar == [d \in DeQers |-> IF deqInner[d] = NoData THEN NoData + ELSE deqInner[d].data] +I == INSTANCE QSpec WITH deqInner <- deqInnerBar + +============================================================================= +\* Modification History +\* Last modified Sat Nov 14 17:54:40 PST 2015 by lamport +\* Created Fri Nov 06 11:05:33 PST 2015 by lamport diff --git a/tlatools/test-model/simulation/NQSpec/QSpec.tla b/tlatools/test-model/simulation/NQSpec/QSpec.tla new file mode 100644 index 0000000000000000000000000000000000000000..4d3a7962b5121e4e4d08f855a99c682921f1fad8 --- /dev/null +++ b/tlatools/test-model/simulation/NQSpec/QSpec.tla @@ -0,0 +1,68 @@ +------------------------------- MODULE QSpec ------------------------------- +EXTENDS Integers, Sequences, TLC + +CONSTANTS EnQers, DeQers, Data, Done, Busy, NoData, InitData + +ASSUME /\ Done \notin Data + /\ Busy \notin Data + /\ NoData \notin Data + /\ InitData \in Data + +VARIABLES enq, deq, queue, enqInner, deqInner +vars == <<enq, deq, queue, enqInner, deqInner>> + +TypeOK == /\ enq \in [EnQers -> Data \cup {Done}] + /\ deq \in [DeQers -> Data \cup {Busy}] + /\ queue \in Seq(Data) + /\ enqInner \in [EnQers -> {Done, Busy}] + /\ deqInner \in [DeQers -> {NoData} \cup Data] + +Init == \* /\ PrintT("begin") + /\ enq = [e \in EnQers |-> Done] + /\ deq = [d \in DeQers |-> InitData] + /\ queue = << >> + /\ enqInner = [e \in EnQers |-> Done] \* /\ PrintT("foo") + /\ deqInner = [d \in DeQers |-> InitData] \* /\ PrintT("bar") + +BeginEnq(e) == /\ enq[e] = Done + /\ \E D \in Data : enq' = [enq EXCEPT ![e] = D] + /\ enqInner' = [enqInner EXCEPT ![e] = Busy] + /\ UNCHANGED <<deq, queue, deqInner>> + +DoEnq(e) == /\ enqInner[e] = Busy + /\ queue' = Append(queue, enq[e]) + /\ enqInner' = [enqInner EXCEPT ![e] = Done] + /\ UNCHANGED <<deq, enq, deqInner>> + +EndEnq(e) == /\ enq[e] # Done + /\ enqInner[e] = Done + /\ enq' = [enq EXCEPT ![e] = Done] + /\ UNCHANGED <<deq, queue, enqInner, deqInner>> + +BeginDeq(d) == /\ deq[d] # Busy + /\ deq' = [deq EXCEPT ![d] = Busy] + /\ deqInner' = [deqInner EXCEPT ![d] = NoData] + /\ UNCHANGED <<enq, queue, enqInner>> + +DoDeq(d) == /\ deq[d] = Busy + /\ deqInner[d] = NoData + /\ queue # << >> + /\ deqInner' = [deqInner EXCEPT ![d] = Head(queue)] + /\ queue' = Tail(queue) + /\ UNCHANGED <<enq, deq, enqInner>> + +EndDeq(d) == /\ deq[d] = Busy + /\ deqInner[d] # NoData + /\ deq' = [deq EXCEPT ![d] = deqInner[d]] + /\ UNCHANGED <<enq, queue, enqInner, deqInner>> + +Next == + /\ TLCSet(42, TLCGet(42)) + /\ \/ \E e \in EnQers : BeginEnq(e) \/ DoEnq(e) \/ EndEnq(e) + \/ \E d \in DeQers : BeginDeq(d) \/ DoDeq(d) \/ EndDeq(d) + +Spec == Init /\ [][Next]_vars +============================================================================= +\* Modification History +\* Last modified Sat Nov 14 12:56:11 PST 2015 by lamport +\* Created Wed Nov 04 15:29:04 PST 2015 by lamport diff --git a/tlatools/test-model/suite/etest1.cfg b/tlatools/test-model/suite/etest1.cfg new file mode 100644 index 0000000000000000000000000000000000000000..b0ee260b28eef2026bbc2f6cb01c89fd3d01d947 --- /dev/null +++ b/tlatools/test-model/suite/etest1.cfg @@ -0,0 +1,8 @@ + INIT + Init + + NEXT + Next + + INVARIANT Inv + diff --git a/tlatools/test-model/suite/etest1.tla b/tlatools/test-model/suite/etest1.tla new file mode 100644 index 0000000000000000000000000000000000000000..9004a1858d319b2ec4505829f97471d35a64ae01 --- /dev/null +++ b/tlatools/test-model/suite/etest1.tla @@ -0,0 +1,20 @@ +--------------- MODULE etest1 ------------- + +(* Test that TLC complains if an operator is called with too many arguments. *) + +EXTENDS TLC + +VARIABLE x +Type == x \in BOOLEAN +Init == + /\ Print("Should complain that operator Foo is called with too many arguments", TRUE) + /\ x = TRUE + +Next == UNCHANGED x + + +Foo(a) == a + +Inv == Foo(x, x) + +========================================= diff --git a/tlatools/test-model/suite/etest10.cfg b/tlatools/test-model/suite/etest10.cfg new file mode 100644 index 0000000000000000000000000000000000000000..e3e8c3d2505a0163440775d92531a92d4321811c --- /dev/null +++ b/tlatools/test-model/suite/etest10.cfg @@ -0,0 +1,2 @@ +INIT Init +NEXT Next diff --git a/tlatools/test-model/suite/etest10.tla b/tlatools/test-model/suite/etest10.tla new file mode 100644 index 0000000000000000000000000000000000000000..0063d717a09aee8328ea1507c344499339974d35 --- /dev/null +++ b/tlatools/test-model/suite/etest10.tla @@ -0,0 +1,15 @@ +--------------- MODULE etest10 ------------- + +\* Test that TLC doesn't accept x \in Int if x not a number + +EXTENDS TLC, Integers + +VARIABLE x + +Init == /\ x = "a" + /\ Print("Should report error here", TRUE) + /\ Print(x \in Int, TRUE) + +Next == /\ x'=x + /\ Print("Test failed--error not reported", TRUE) +========================================= diff --git a/tlatools/test-model/suite/etest11.cfg b/tlatools/test-model/suite/etest11.cfg new file mode 100644 index 0000000000000000000000000000000000000000..e3e8c3d2505a0163440775d92531a92d4321811c --- /dev/null +++ b/tlatools/test-model/suite/etest11.cfg @@ -0,0 +1,2 @@ +INIT Init +NEXT Next diff --git a/tlatools/test-model/suite/etest11.tla b/tlatools/test-model/suite/etest11.tla new file mode 100644 index 0000000000000000000000000000000000000000..f948b4cc37429f5897bf4142704b2527200bb863 --- /dev/null +++ b/tlatools/test-model/suite/etest11.tla @@ -0,0 +1,15 @@ +--------------- MODULE etest11 ------------- + +\* Test that TLC doesn't accept x \in STRING if x not a string + +EXTENDS TLC + +VARIABLE x + +Init == /\ x = 1 + /\ Print("Should report error here", TRUE) + /\ Print(x \in STRING, TRUE) + +Next == /\ x'=x + /\ Print("Test failed--error not reported", TRUE) +========================================= diff --git a/tlatools/test-model/suite/etest12.cfg b/tlatools/test-model/suite/etest12.cfg new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/tlatools/test-model/suite/etest12.tla b/tlatools/test-model/suite/etest12.tla new file mode 100644 index 0000000000000000000000000000000000000000..d471244d2690616b9468300c04402d238a99224f --- /dev/null +++ b/tlatools/test-model/suite/etest12.tla @@ -0,0 +1,9 @@ +-------------------------- MODULE etest12 -------------------------------- +EXTENDS Naturals, TLC, FiniteSets +\* Test of error reporting when computing Cardinality of a huge set. + +ASSUME + IF Cardinality ([0..2 -> 1..2000]) = Cardinality ([2..4 -> 1..2000]) + THEN Print("Test 22 OK", TRUE) + ELSE Assert(FALSE, "Test 22 Failed") +========================================================================== \ No newline at end of file diff --git a/tlatools/test-model/suite/etest13.cfg b/tlatools/test-model/suite/etest13.cfg new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/tlatools/test-model/suite/etest13.tla b/tlatools/test-model/suite/etest13.tla new file mode 100644 index 0000000000000000000000000000000000000000..e0d4e62c966367ddb93813ddb332358a98cea9e3 --- /dev/null +++ b/tlatools/test-model/suite/etest13.tla @@ -0,0 +1,9 @@ +-------------------------- MODULE etest13 -------------------------------- +EXTENDS Naturals, TLC, FiniteSets +\* Test of error reporting when computing Cardinality of a huge set. + +ASSUME + IF Cardinality ([0..2 -> 1..2000]) = Cardinality ([2..4 -> 1..2000]) + THEN Print("Test 22 OK", TRUE) + ELSE Assert(FALSE, "Test 22 Failed") +========================================================================== \ No newline at end of file diff --git a/tlatools/test-model/suite/etest14.cfg b/tlatools/test-model/suite/etest14.cfg new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/tlatools/test-model/suite/etest14.tla b/tlatools/test-model/suite/etest14.tla new file mode 100644 index 0000000000000000000000000000000000000000..e93ce4a819c7dc16c6c52de0b521146af97d4f8d --- /dev/null +++ b/tlatools/test-model/suite/etest14.tla @@ -0,0 +1,6 @@ +---------------------- MODULE etest14 ----------------------------- + +\* Test of CHOOSEing element of empty set. +ASSUME + /\ (CHOOSE y \in {} : TRUE) = (CHOOSE y \in {} : TRUE) +===================================================================== diff --git a/tlatools/test-model/suite/etest15.cfg b/tlatools/test-model/suite/etest15.cfg new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/tlatools/test-model/suite/etest15.tla b/tlatools/test-model/suite/etest15.tla new file mode 100644 index 0000000000000000000000000000000000000000..af25c8cc9469be17cbb43b5e8a906a9aeaf2e9a8 --- /dev/null +++ b/tlatools/test-model/suite/etest15.tla @@ -0,0 +1,10 @@ +---------------------- MODULE etest15 ----------------------------- +\* Test that TLC distinguishes <<x>> from x in function expressions. + +EXTENDS Naturals + +S == {<<y>> : y \in {1, 2, 3, 4}} + +ASSUME [<<y>> \in S |-> y+1][2] = [<<y>> \in S |-> y+1][2] + +===================================================================== diff --git a/tlatools/test-model/suite/etest16.cfg b/tlatools/test-model/suite/etest16.cfg new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/tlatools/test-model/suite/etest16.tla b/tlatools/test-model/suite/etest16.tla new file mode 100644 index 0000000000000000000000000000000000000000..d0d388ae3858ddef0d3abccf3e991eb0f0c0df5d --- /dev/null +++ b/tlatools/test-model/suite/etest16.tla @@ -0,0 +1,11 @@ +--------------- MODULE etest16 ----------- + +\* Test of detecting duplicate record elements + +Foo == [a |-> 1, b |-> 2, a |-> 1] + +Bar == [a : {1}, b : {2}, a : {1}] +============================================================================= + + + diff --git a/tlatools/test-model/suite/etest2.cfg b/tlatools/test-model/suite/etest2.cfg new file mode 100644 index 0000000000000000000000000000000000000000..b0ee260b28eef2026bbc2f6cb01c89fd3d01d947 --- /dev/null +++ b/tlatools/test-model/suite/etest2.cfg @@ -0,0 +1,8 @@ + INIT + Init + + NEXT + Next + + INVARIANT Inv + diff --git a/tlatools/test-model/suite/etest2.tla b/tlatools/test-model/suite/etest2.tla new file mode 100644 index 0000000000000000000000000000000000000000..3b133bf9277d08b23ca8ca201d014ca207dee145 --- /dev/null +++ b/tlatools/test-model/suite/etest2.tla @@ -0,0 +1,20 @@ +--------------- MODULE etest2 ------------- + +(* Test that TLC complains if a operator is called with too few arguments. *) + +EXTENDS TLC + +VARIABLE x +Type == x \in BOOLEAN +Init == + /\ Print("Should complain that operator Foo is called with too few arguments", TRUE) + /\ x = TRUE + +Next == UNCHANGED x + + +Foo(a, b) == a /\ b + +Inv == Foo(x) + +========================================= diff --git a/tlatools/test-model/suite/etest3.cfg b/tlatools/test-model/suite/etest3.cfg new file mode 100644 index 0000000000000000000000000000000000000000..c73ef51536cac7f74a6e6d78154edf3a7c89d042 --- /dev/null +++ b/tlatools/test-model/suite/etest3.cfg @@ -0,0 +1,9 @@ + INIT + Init + + NEXT + Next + + INVARIANT Inv + + \ No newline at end of file diff --git a/tlatools/test-model/suite/etest3.tla b/tlatools/test-model/suite/etest3.tla new file mode 100644 index 0000000000000000000000000000000000000000..5172acf98e888eee5f38b2b90fcea7151ececdd4 --- /dev/null +++ b/tlatools/test-model/suite/etest3.tla @@ -0,0 +1,22 @@ +--------------- MODULE etest3 ------------- + +(* Testing deadlock and number of states found. *) + +EXTENDS TLC, Naturals, Sequences, FiniteSets + +VARIABLE x, y, z + +Init == /\ Print("This test should deadlock after finding two states", TRUE) + /\ x = {} + /\ y = {} + /\ z = 1 + +Next == /\ x = {} + /\ x' = {1} + /\ y' \in SUBSET x' + /\ z' = z + /\ y' = {} + +Inv == TRUE + +========================================= diff --git a/tlatools/test-model/suite/etest4.cfg b/tlatools/test-model/suite/etest4.cfg new file mode 100644 index 0000000000000000000000000000000000000000..c95fce6e2a4a0a1137f1e83ec8efe3d039acfede --- /dev/null +++ b/tlatools/test-model/suite/etest4.cfg @@ -0,0 +1,11 @@ + INIT + Init + + NEXT + Next + + INVARIANT Inv + + CONSTANT + A = ModelValue + \ No newline at end of file diff --git a/tlatools/test-model/suite/etest4.tla b/tlatools/test-model/suite/etest4.tla new file mode 100644 index 0000000000000000000000000000000000000000..e6b4fbdff6637f772a9dfd18a2295c79e5323e74 --- /dev/null +++ b/tlatools/test-model/suite/etest4.tla @@ -0,0 +1,22 @@ +--------------- MODULE etest4 ------------- + +(* Evaluates m[v] for model-value m. *) + +EXTENDS TLC, Naturals, Sequences, FiniteSets + +VARIABLE x +CONSTANT A + + + +Req == [ mask |-> A ] +Init == /\ x = 1 + /\ Print("Should report error for applying model value to arg", TRUE) + /\ Req.mask[1] = 1 + /\ Print("Should never get this far", TRUE) + +Next == UNCHANGED x + +Inv == TRUE + +========================================= diff --git a/tlatools/test-model/suite/etest5.cfg b/tlatools/test-model/suite/etest5.cfg new file mode 100644 index 0000000000000000000000000000000000000000..c73ef51536cac7f74a6e6d78154edf3a7c89d042 --- /dev/null +++ b/tlatools/test-model/suite/etest5.cfg @@ -0,0 +1,9 @@ + INIT + Init + + NEXT + Next + + INVARIANT Inv + + \ No newline at end of file diff --git a/tlatools/test-model/suite/etest5.tla b/tlatools/test-model/suite/etest5.tla new file mode 100644 index 0000000000000000000000000000000000000000..a3c736d7c1c70ebe9e14583e700c205d931e48b3 --- /dev/null +++ b/tlatools/test-model/suite/etest5.tla @@ -0,0 +1,19 @@ +--------------- MODULE etest5 ------------- + +(* M!Ident for nonexistent module M. *) + +EXTENDS TLC, Naturals + +VARIABLE x + +Init == /\ x = 1 + /\ Print("Should report error for nonexistent module M", TRUE) + +Next == /\ UNCHANGED x + /\ IF M!Init THEN TRUE + ELSE TRUE + /\ Print("Should not get this far", TRUE) + +Inv == TRUE + +========================================= diff --git a/tlatools/test-model/suite/etest6.cfg b/tlatools/test-model/suite/etest6.cfg new file mode 100644 index 0000000000000000000000000000000000000000..0cc6c2124ed19c2b3250dc1dfa1ab569eca3c252 --- /dev/null +++ b/tlatools/test-model/suite/etest6.cfg @@ -0,0 +1,13 @@ + INIT + Init + + NEXT + Next + + CONSTRAINT Constraint + + INVARIANT Inv + + + + \ No newline at end of file diff --git a/tlatools/test-model/suite/etest6.tla b/tlatools/test-model/suite/etest6.tla new file mode 100644 index 0000000000000000000000000000000000000000..73b610103641896adb0d5ef427f26a33cc4b03aa --- /dev/null +++ b/tlatools/test-model/suite/etest6.tla @@ -0,0 +1,25 @@ +--------------- MODULE etest6 ------------- + +(* Defining x' in terms of x' *) + +EXTENDS TLC, Integers, Sequences, FiniteSets + +VARIABLE x + +Init == /\ Print("Should complain about undefined value of x.", TRUE) + /\ x = [i \in {1,2} |-> i] + + +Action(c) == c' = [c EXCEPT ![1] = (c[2])'] + +Next == \/ /\ x[1] = 1 + /\ Action(x) + /\ Print("Should not get this far", TRUE) + + \/ UNCHANGED x + +Inv == TRUE + + +Constraint == TRUE +========================================= diff --git a/tlatools/test-model/suite/etest7.cfg b/tlatools/test-model/suite/etest7.cfg new file mode 100644 index 0000000000000000000000000000000000000000..8f3f0653e8f0d314ebb13afe19cfb1158ca370f1 --- /dev/null +++ b/tlatools/test-model/suite/etest7.cfg @@ -0,0 +1,11 @@ + INIT + Init + + NEXT + Next + + INVARIANT Inv + + CONSTANT + C <- Foo + diff --git a/tlatools/test-model/suite/etest7.tla b/tlatools/test-model/suite/etest7.tla new file mode 100644 index 0000000000000000000000000000000000000000..862ef69ee0d4c48e3772bfb3432ad66eebadd673 --- /dev/null +++ b/tlatools/test-model/suite/etest7.tla @@ -0,0 +1,21 @@ +--------------- MODULE etest7 ------------- + +(* Test replacement of constant with nonconstant operator. *) + +EXTENDS TLC, Naturals, Sequences + +CONSTANT C + +VARIABLE x + +Foo == x + +Next == x' = C + +Init == /\ Print("TLC should complain about replacing the constant C", TRUE) + /\ Print("by the nonconstant Foo", TRUE) + /\ x = 2 + +Inv == x = 2 + +========================================= diff --git a/tlatools/test-model/suite/etest8.cfg b/tlatools/test-model/suite/etest8.cfg new file mode 100644 index 0000000000000000000000000000000000000000..e3e8c3d2505a0163440775d92531a92d4321811c --- /dev/null +++ b/tlatools/test-model/suite/etest8.cfg @@ -0,0 +1,2 @@ +INIT Init +NEXT Next diff --git a/tlatools/test-model/suite/etest8.tla b/tlatools/test-model/suite/etest8.tla new file mode 100644 index 0000000000000000000000000000000000000000..11bbfb68d7a2b4c28e72ca5878954274d9304c39 --- /dev/null +++ b/tlatools/test-model/suite/etest8.tla @@ -0,0 +1,21 @@ +--------------- MODULE etest8 ------------- + +(* Test that simulation mode doesn't check for deadlock. *) + +EXTENDS TLC + + +VARIABLE x + +Init == /\ x \in {"a", "b"} + /\ Print("This spec can deadlock, but shouldn't.", TRUE) + +Next == \/ /\ x = "b" + /\ x' = "c" + \/ /\ x = "c" + /\ x' = "d" + \/ /\ x = "d" + /\ x' = "d" + /\ Assert(FALSE, "Test successfully completed.") + +========================================= diff --git a/tlatools/test-model/suite/etest9.cfg b/tlatools/test-model/suite/etest9.cfg new file mode 100644 index 0000000000000000000000000000000000000000..e3e8c3d2505a0163440775d92531a92d4321811c --- /dev/null +++ b/tlatools/test-model/suite/etest9.cfg @@ -0,0 +1,2 @@ +INIT Init +NEXT Next diff --git a/tlatools/test-model/suite/etest9.tla b/tlatools/test-model/suite/etest9.tla new file mode 100644 index 0000000000000000000000000000000000000000..c860dd62aa9aeda4765ab0c9dbdbe0a4dea38bb8 --- /dev/null +++ b/tlatools/test-model/suite/etest9.tla @@ -0,0 +1,15 @@ +--------------- MODULE etest9 ------------- + +\* Test that TLC doesn't accept x \in Nat if x not a number + +EXTENDS TLC, Naturals + +VARIABLE x + +Init == /\ x = "a" + /\ Print("Should report error here", TRUE) + /\ Print(x \in Nat, TRUE) + +Next == /\ x'=x + /\ Print("Test failed--error not reported", TRUE) +========================================= diff --git a/tlatools/test-model/suite/test.cfg b/tlatools/test-model/suite/test.cfg new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/tlatools/test-model/suite/test.tla b/tlatools/test-model/suite/test.tla new file mode 100644 index 0000000000000000000000000000000000000000..bd4950a64a48d49da9591248c5f03ad3c26cb57d --- /dev/null +++ b/tlatools/test-model/suite/test.tla @@ -0,0 +1,9 @@ +------------- MODULE Foo -------------- +EXTENDS Naturals + +Foo == \A x \in {1} : lab(x) :: x = 1 + +Bar == Foo!lab(1) + +ASSUME Bar +============================================= diff --git a/tlatools/test-model/suite/test1.cfg b/tlatools/test-model/suite/test1.cfg new file mode 100644 index 0000000000000000000000000000000000000000..b0ee260b28eef2026bbc2f6cb01c89fd3d01d947 --- /dev/null +++ b/tlatools/test-model/suite/test1.cfg @@ -0,0 +1,8 @@ + INIT + Init + + NEXT + Next + + INVARIANT Inv + diff --git a/tlatools/test-model/suite/test1.tla b/tlatools/test-model/suite/test1.tla new file mode 100644 index 0000000000000000000000000000000000000000..9bc722cc36f846eb80debae15b06d5af65d95a44 --- /dev/null +++ b/tlatools/test-model/suite/test1.tla @@ -0,0 +1,77 @@ +--------------- MODULE test1 ------------- + +(* test equality of various representations of sets. *) + +EXTENDS Naturals, TLC + +VARIABLE x + + +Type == x \in {1} +Inv == + /\ IF {1, 2, 3} # {3, 2, 2, 1} + THEN Assert(FALSE, "Failed Test 1") + ELSE Print("Test 1 OK", TRUE) + + /\ IF {1, 2, 3} # {i \in {5, 4, 3, 2, 1} : i < 4} + THEN Assert(FALSE, "Failed Test 2") + ELSE Print("Test 2 OK", TRUE) + + /\ IF {1, 2, 3} # {i-3 : i \in {4, 6, 5, 6, 5, 6}} + THEN Assert(FALSE, "Failed Test 3") + ELSE Print("Test 3 OK", TRUE) + + /\ IF SUBSET {1, 2, 3} # {{}, {1}, {2,1,2}, {3,1}, {2}, {3}, {3,2,3}, {1,2,3}} + THEN Assert(FALSE, "Failed Test 4") + ELSE Print("Test 4 OK", TRUE) + + /\ IF {1,2,3} # UNION (SUBSET {1, 2, 3}) + THEN Assert(FALSE, "Failed Test 5") + ELSE Print("Test 5 OK", TRUE) + + /\ IF {1,2,3} # UNION {{1}, {2}, {3, 2, 1}} + THEN Assert(FALSE, "Failed Test 6") + ELSE Print("Test 6 OK", TRUE) + + /\ IF {1,2,3} # {1} \cup {3, 2} \cup {} + THEN Assert(FALSE, "Failed Test 7") + ELSE Print("Test 7 OK", TRUE) + + /\ IF {1,2,3} # {5, 4, 3, 2, 1} \cap {1, 3, 0, 2} + THEN Assert(FALSE, "Failed Test 8") + ELSE Print("Test 8 OK", TRUE) + + /\ IF {1,2,3} \subseteq {5, 3, 3, 4, 2} + THEN Assert(FALSE, "Failed Test 9") + ELSE Print("Test 9 OK", TRUE) + + /\ IF {1,2,3} # {5, 3, 3, 4, 2,1} \ {4, 5} + THEN Assert(FALSE, "Failed Test 10") + ELSE Print("Test 10 OK", TRUE) + + /\ IF {1,2,3} # 1..3 + THEN Assert(FALSE, "Failed Test 11") + ELSE Print("Test 11 OK", TRUE) + + /\ IF {1,2,3} # DOMAIN <<"a", "b", "c">> + THEN Assert(FALSE, "Failed Test 12") + ELSE Print("Test 12 OK", TRUE) + + /\ IF {1,2,3} # DOMAIN [i \in {3,2,2,1} |-> i+1] + THEN Assert(FALSE, "Failed Test 13") + ELSE Print("Test 13 OK", TRUE) + + /\ IF {"a", "b", "c"} # DOMAIN [a |-> 1, b |-> 2, c |-> 3] + THEN Assert(FALSE, "Failed Test 14") + ELSE Print("Test 14 OK", TRUE) + + /\ IF {} # [{1, 2, 3} -> {}] + THEN Assert(FALSE, "Failed Test 15") + ELSE Print("Test 15 OK", TRUE) + + /\ Print("Test Completed", TRUE) + +Init == x = 1 + +Next == UNCHANGED x +========================================= diff --git a/tlatools/test-model/suite/test10.cfg b/tlatools/test-model/suite/test10.cfg new file mode 100644 index 0000000000000000000000000000000000000000..b0ee260b28eef2026bbc2f6cb01c89fd3d01d947 --- /dev/null +++ b/tlatools/test-model/suite/test10.cfg @@ -0,0 +1,8 @@ + INIT + Init + + NEXT + Next + + INVARIANT Inv + diff --git a/tlatools/test-model/suite/test10.tla b/tlatools/test-model/suite/test10.tla new file mode 100644 index 0000000000000000000000000000000000000000..7e35ec086d4fab2dbef27399250da6487a85467d --- /dev/null +++ b/tlatools/test-model/suite/test10.tla @@ -0,0 +1,160 @@ +--------------- MODULE test10 ------------- + +(* Test of function definition and application *) + +EXTENDS Integers, TLC + +VARIABLE x +Type == x \in BOOLEAN +Init == x = TRUE +Next == UNCHANGED x + +f1[i \in Int] == i+2 +f2[i, j \in Int] == i+j +f3[i, j \in Int, <<k,l>> \in Int \X Int, m \in {5, 7,8}] == + i + 2*j + 3*k + 4*l + 5*m +f4[i, j \in Int] == [a |-> i, b |-> j] + +f5[i, j, k \in Int] == i+j+k + +f6[i, j, k \in Int, <<l, m, n>> \in Int \X Int \X Int, p \in {5,7,8}] == + i + 2*j + 3*k + 4*l + 5*m + 6*n + 7*p + +Inv == + +LET r1 == [a |-> f1, b |-> f2] + g1 == [f4 EXCEPT ![1,2].a = 22, ![1,3].b = 33] + g2 == [[<<i, j>> \in (1..10) \X (1..10) + |-> [a |-> i, b |-> j]] EXCEPT ![1,2].a = 22, ![1,3].b = 33] +IN + /\ IF f1[3] # 5 + THEN Assert(FALSE, "Test 1 Failed") + ELSE Print("Test 1 OK", TRUE) + + /\ IF f2[3,4] # 7 + THEN Assert(FALSE, "Test 2 Failed") + ELSE Print("Test 2 OK", TRUE) + + /\ IF f2[<<3,4>>] # 7 + THEN Assert(FALSE, "Test 3 Failed") + ELSE Print("Test 3 OK", TRUE) + + /\ IF f3[1, 2, <<3, 4>>, 5] # 55 + THEN Assert(FALSE, "Test 4 Failed") + ELSE Print("Test 4 OK", TRUE) + + /\ IF f3[<<1, 2, <<3, 4>>, 5>>] # 55 + THEN Assert(FALSE, "Test 5 Failed") + ELSE Print("Test 5 OK", TRUE) + + + /\ IF [i \in Int |-> i+2][3] # 5 + THEN Assert(FALSE, "Test 6 Failed") + ELSE Print("Test 6 OK", TRUE) + + /\ IF [i, j \in Int |-> i+j][3,4] # 7 + THEN Assert(FALSE, "Test 7 Failed") + ELSE Print("Test 7 OK", TRUE) + + /\ IF [i, j \in Int |-> i+j][<<3,4>>] # 7 + THEN Assert(FALSE, "Test 8 Failed") + ELSE Print("Test 8 OK", TRUE) + + /\ IF [i, j \in Int, <<k,l>> \in Int \X Int, m \in {5,7,8} + |-> i + 2*j + 3*k + 4*l + 5*m][1, 2, <<3, 4>>, 5] # 55 + THEN Assert(FALSE, "Test 9 Failed") + ELSE Print("Test 9 OK", TRUE) + + /\ IF [i, j \in Int, <<k,l>> \in Int \X Int, m \in {5,7,8} + |-> i + 2*j + 3*k + 4*l + 5*m][<<1, 2, <<3, 4>>, 5>>] # 55 + THEN Assert(FALSE, "Test 10 Failed") + ELSE Print("Test 10 OK", TRUE) + + /\ IF [f1 EXCEPT ![3] = [i \in Int |-> @ + i]][3][7] # 12 + THEN Assert(FALSE, "Test 11 Failed") + ELSE Print("Test 11 OK", TRUE) + + /\ IF [f1 EXCEPT ![3] = [i \in Int |-> @ + i], ![3][6] = 44][3][6] # 44 + THEN Assert(FALSE, "Test 12 Failed") + ELSE Print("Test 12 OK", TRUE) + + /\ IF [f1 EXCEPT ![3] = [i \in Int |-> @ + i], ![3][6] = 44][3][7] # 12 + THEN Assert(FALSE, "Test 13 Failed") + ELSE Print("Test 13 OK", TRUE) + + /\ IF f4[3,4].a # 3 + THEN Assert(FALSE, "Test 14 Failed") + ELSE Print("Test 14 OK", TRUE) + + /\ IF [f4 EXCEPT ![3,4].b = 17][3,4].b # 17 + THEN Assert(FALSE, "Test 15 Failed") + ELSE Print("Test 15 OK", TRUE) + + /\ IF [f4 EXCEPT ![3,4].b = 17][3,4].a # 3 + THEN Assert(FALSE, "Test 16 Failed") + ELSE Print("Test 16 OK", TRUE) + + /\ IF [f4 EXCEPT ![3,4].b = 17][2,4].b # 4 + THEN Assert(FALSE, "Test 17 Failed") + ELSE Print("Test 17 OK", TRUE) + + /\ IF [f4 EXCEPT ![3,4].b = 17, ![3,4].a = 18][3,4].b # 17 + THEN Assert(FALSE, "Test 18 Failed") + ELSE Print("Test 18 OK", TRUE) + + /\ IF [f4 EXCEPT ![3,4].b = 17, ![3,4].a = 18][3,4].a # 18 + THEN Assert(FALSE, "Test 19 Failed") + ELSE Print("Test 19 OK", TRUE) + + /\ IF [f4 EXCEPT ![3,4].b = 17, ![3,4].a = 18, ![3,4].b= @+5][3,4].b # 22 + THEN Assert(FALSE, "Test 20 Failed") + ELSE Print("Test 20 OK", TRUE) + + /\ IF [f4 EXCEPT ![3,4].b = 17, ![3,4].a = 18, ![<<3,4>>].b= @+5][3,4].b # 22 + THEN Assert(FALSE, "Test 21 Failed") + ELSE Print("Test 21 OK", TRUE) + + /\ IF g1[1,3].a # 1 \/ g1[1,3].a # 1 + THEN Assert(FALSE, "Test 22 Failed") + ELSE Print("Test 22 OK", TRUE) + + /\ IF g2[1,3].a # 1 \/ g2[1,3].a # 1 + THEN Assert(FALSE, "Test 23 Failed") + ELSE Print("Test 23 OK", TRUE) + + /\ IF g1[1,2].a # 22 \/ g1[1,2].a # 22 + THEN Assert(FALSE, "Test 24 Failed") + ELSE Print("Test 24 OK", TRUE) + + /\ IF g2[1,2].a # 22 \/ g2[1,2].a # 22 + THEN Assert(FALSE, "Test 25 Failed") + ELSE Print("Test 25 OK", TRUE) + + /\ IF f5[3,4,5] # 12 + THEN Assert(FALSE, "Test 26 Failed") + ELSE Print("Test 26 OK", TRUE) + + /\ IF f5[<<3,4,5>>] # 12 + THEN Assert(FALSE, "Test 27 Failed") + ELSE Print("Test 27 OK", TRUE) + + /\ IF f6[1, 2, 3, <<4,5,6>>, 7] # 140 + THEN Assert(FALSE, "Test 28 Failed") + ELSE Print("Test 28 OK", TRUE) + + /\ IF f6[<<1, 2, 3, <<4,5,6>>, 7>>] # 140 + THEN Assert(FALSE, "Test 29 Failed") + ELSE Print("Test 29 OK", TRUE) + + /\ IF [i, j, k \in Int |-> i+j+k][3,4,5] # 12 + THEN Assert(FALSE, "Test 30 Failed") + ELSE Print("Test 30 OK", TRUE) + + /\ IF [i, j, k \in Int |-> i+j+k][<<3,4,5>>] # 12 + THEN Assert(FALSE, "Test 31 Failed") + ELSE Print("Test 31 OK", TRUE) + + + + +========================================= diff --git a/tlatools/test-model/suite/test11.cfg b/tlatools/test-model/suite/test11.cfg new file mode 100644 index 0000000000000000000000000000000000000000..b0ee260b28eef2026bbc2f6cb01c89fd3d01d947 --- /dev/null +++ b/tlatools/test-model/suite/test11.cfg @@ -0,0 +1,8 @@ + INIT + Init + + NEXT + Next + + INVARIANT Inv + diff --git a/tlatools/test-model/suite/test11.tla b/tlatools/test-model/suite/test11.tla new file mode 100644 index 0000000000000000000000000000000000000000..b388dd57d86dc71f7dc5c24b7fd2aeb8f9eb52cd --- /dev/null +++ b/tlatools/test-model/suite/test11.tla @@ -0,0 +1,120 @@ +--------------- MODULE test11 ------------- + +(* Test of DOMAIN and Function Sets *) + +EXTENDS Integers, TLC, FiniteSets + + +VARIABLE x +Type == x \in BOOLEAN +Init == x = TRUE +Next == UNCHANGED x + +f1[i \in Int] == i+3 + +S == [1..4 -> Nat] + +Inv == + /\ IF 5 \notin DOMAIN f1 + THEN Assert(FALSE, "Test 1 Failed") + ELSE Print("Test 1 OK", TRUE) + + /\ IF 5 \notin DOMAIN [i \in Int |-> i+3] + THEN Assert(FALSE, "Test 2 Failed") + ELSE Print("Test 2 OK", TRUE) + + /\ IF {1,2,3} \notin DOMAIN [T \in SUBSET Int |-> T \cup {1}] + THEN Assert(FALSE, "Test 3 Failed") + ELSE Print("Test 3 OK", TRUE) + + /\ IF Cardinality(DOMAIN [i \in {1,3,5} |-> i+7]) # 3 + THEN Assert(FALSE, "Test 4 Failed") + ELSE Print("Test 4 OK", TRUE) + + /\ IF Cardinality(DOMAIN [f \in [{1,3,5} -> {1,2}] |-> f[3]]) # 8 + THEN Assert(FALSE, "Test 5 Failed") + ELSE Print("Test 5 OK", TRUE) + + /\ IF Cardinality(DOMAIN [f \in {g \in [{1,3,5} -> {1,2}] : g[3]=1} + |-> f[3]]) # 4 + THEN Assert(FALSE, "Test 6 Failed") + ELSE Print("Test 6 OK", TRUE) + + /\ IF [i \in {1,3, 5} |-> i-2] \notin + {g \in [{1,3,5} -> -2..3] : g[3]=1} + THEN Assert(FALSE, "Test 7 Failed") + ELSE Print("Test 7 OK", TRUE) + + /\ IF ~ ((DOMAIN [a |-> 1, b |-> 2]) \subseteq {"a", "b", "cde"}) + THEN Assert(FALSE, "Test 8 Failed") + ELSE Print("Test 8 OK", TRUE) + + /\ IF <<1, 2, 3, 4>> \notin S + THEN Assert(FALSE, "Test 9 Failed") + ELSE Print("Test 9 OK", TRUE) + + /\ IF <<1, 2, -3, 4>> \in S + THEN Assert(FALSE, "Test 10 Failed") + ELSE Print("Test 10 OK", TRUE) + + /\ IF <<1, 2, 4>> \in S + THEN Assert(FALSE, "Test 11 Failed") + ELSE Print("Test 11 OK", TRUE) + + /\ IF [i \in {1, 2} |-> i] \in S + THEN Assert(FALSE, "Test 12 Failed") + ELSE Print("Test 12 OK", TRUE) + + /\ IF [i \in {1, 2, 3, 4} |-> i+1] \notin S + THEN Assert(FALSE, "Test 13 Failed") + ELSE Print("Test 13 OK", TRUE) + + /\ IF [i \in {1, 2, 3, 4} |-> i-2] \in S + THEN Assert(FALSE, "Test 14 Failed") + ELSE Print("Test 14 OK", TRUE) + + /\ IF [i \in 1..100 |-> i+1] \notin [1..100 -> Nat] + THEN Assert(FALSE, "Test 15 Failed") + ELSE Print("Test 15 OK", TRUE) + + /\ IF [i \in 1..10 |-> i+1] \in [1..100 -> Nat] + THEN Assert(FALSE, "Test 16 Failed") + ELSE Print("Test 16 OK", TRUE) + + /\ IF [i \in {99, 100} |-> i+1] \in [1..100 -> Nat] + THEN Assert(FALSE, "Test 17 Failed") + ELSE Print("Test 17 OK", TRUE) + + /\ IF [i \in {"a", "b"} |-> i] \in [1..100 -> Nat] + THEN Assert(FALSE, "Test 18 Failed") + ELSE Print("Test 18 OK", TRUE) + + /\ IF [a |-> 1, b |-> 2] \in [1..100 -> Nat] + THEN Assert(FALSE, "Test 19 Failed") + ELSE Print("Test 19 OK", TRUE) + + /\ Print("Test 20: Enumeration of [{1+1,2} -> {3-1,2,1+1}]", TRUE) + /\ \A f \in [{1+1,2} -> {3-1,2,1+1}] : Print(f, TRUE) + + /\ Print(<<"Test 21: evaluation of [{1+1,2} -> {3-1,2,1+1}] = ", + [{1+1,2} -> {3-1,2,1+1}]>>, TRUE) + + /\ IF /\ Cardinality ([0..1 -> 1..2000]) = 4000000 + /\ Cardinality (1..8099) = 8099 + THEN Print("Test 22 OK", TRUE) + ELSE Assert(FALSE, "Test 22 Failed") + + /\ IF DOMAIN [i \in {1,2}, j \in {3,4} |-> i+j] # {1,2} \X {3, 4} + THEN Assert(FALSE, "Test 23 Failed") + ELSE Print("Test 23 OK", TRUE) + + /\ IF DOMAIN [i \in {1,2}, j \in {3,4}, k \in {5,6} |-> i+j+k] + # {1,2} \X {3, 4} \X {5, 6} + THEN Assert(FALSE, "Test 24 Failed") + ELSE Print("Test 24 OK", TRUE) + + /\ IF <<1,3,5>> \notin + DOMAIN [i \in {1,2}, j \in {3,4}, k \in {5,6} |-> i+j+k] + THEN Assert(FALSE, "Test 25 Failed") + ELSE Print("Test 25 OK", TRUE) +========================================= diff --git a/tlatools/test-model/suite/test12.cfg b/tlatools/test-model/suite/test12.cfg new file mode 100644 index 0000000000000000000000000000000000000000..b0ee260b28eef2026bbc2f6cb01c89fd3d01d947 --- /dev/null +++ b/tlatools/test-model/suite/test12.cfg @@ -0,0 +1,8 @@ + INIT + Init + + NEXT + Next + + INVARIANT Inv + diff --git a/tlatools/test-model/suite/test12.tla b/tlatools/test-model/suite/test12.tla new file mode 100644 index 0000000000000000000000000000000000000000..cde8c843e67a637bb2314e823570bf6336f20153 --- /dev/null +++ b/tlatools/test-model/suite/test12.tla @@ -0,0 +1,120 @@ +--------------- MODULE test12 ------------- + +(* Test of Record constructs. *) + +EXTENDS Integers, TLC, FiniteSets + + +VARIABLE x +Type == x \in BOOLEAN +Init == x = TRUE +Next == UNCHANGED x + + +Inv == +LET r1 == [l1 |-> 1, l2 |-> 2, l3 |-> 3] + r2 == [l1 |-> r1, l2 |-> [r1 EXCEPT !.l2 = 22], l3 |-> [r1 EXCEPT !.l3 = 33]] + r3 == [l1 |-> [i \in Int |-> i+1], + l2 |-> [i , j \in Int |-> [m1 |->i+2, m2 |-> j+3]]] + + r4 == [a |-> [i \in 1..1 |-> i], + b |-> [i \in 1..1 |-> 2]] + + S1 == [l1 : Int, l2 : [{1,2} -> {i \in Int : i > 5}]] + S2(j) == [l1 : {1,3}, l2 : {f \in [{1,2} -> 1..10] : f[1] > j /\ f[2] > j}] + S3 == [l1 : Int, l2 : SUBSET [{1,2} -> {2,3}]] + +IN + /\ IF r1.l1 # 1 \/ r1.l1 # 1 + THEN Assert(FALSE, "Test 1 Failed") + ELSE Print("Test 1 OK", TRUE) + + /\ IF r1["l1"] # 1 \/ r1["l1"] # 1 + THEN Assert(FALSE, "Test 2 Failed") + ELSE Print("Test 2 OK", TRUE) + + /\ IF [l1 |-> 1, l2 |-> 2, l3 |-> 3].l1 # 1 + THEN Assert(FALSE, "Test 3 Failed") + ELSE Print("Test 3 OK", TRUE) + + /\ IF [l1 |-> 1, l2 |-> 2, l3 |-> 3]["l1"] # 1 + THEN Assert(FALSE, "Test 4 Failed") + ELSE Print("Test 4 OK", TRUE) + + /\ IF [1l |-> 1, 2l |-> 2, 3l |-> 3]["2l"] # 2 + THEN Assert(FALSE, "Test 5 Failed") + ELSE Print("Test 5 OK", TRUE) + + /\ IF r2.l1 # r1 \/ r2.l1 # r1 + THEN Assert(FALSE, "Test 6 Failed") + ELSE Print("Test 6 OK", TRUE) + + /\ IF r2.l2.l2 # 22 \/ r2.l2.l2 # 22 + THEN Assert(FALSE, "Test 7 Failed") + ELSE Print("Test 7 OK", TRUE) + + /\ IF r2.l2.l1 # 1 \/ r2.l2.l1 # 1 + THEN Assert(FALSE, "Test 8 Failed") + ELSE Print("Test 8 OK", TRUE) + + /\ IF [r2 EXCEPT !.l2.l1 = 44, !.l2.l3=55].l2.l1 # 44 + THEN Assert(FALSE, "Test 9 Failed") + ELSE Print("Test 9 OK", TRUE) + + /\ IF [r2 EXCEPT !.l2.l1 = 44, !.l2.l3=55].l2.l2 # 22 + THEN Assert(FALSE, "Test 10 Failed") + ELSE Print("Test 10 OK", TRUE) + + /\ IF r3.l2[5,6].m1 # 7 \/ r3.l2[5,6].m1 # 7 + THEN Assert(FALSE, "Test 11 Failed") + ELSE Print("Test 11 OK", TRUE) + + /\ IF [i , j \in Int |-> [m1 |->i+2, m2 |-> j+3]][5,6].m2 # 9 + THEN Assert(FALSE, "Test 12 Failed") + ELSE Print("Test 12 OK", TRUE) + + /\ IF r3.l2[5,6].m2 # 9 \/ r3.l2[5,6].m2 # 9 + THEN Assert(FALSE, "Test 14 Failed") + ELSE Print("Test 13 OK", TRUE) + + /\ IF [l1 |-> 1, l2 |-> [i \in {1,2} |-> i+5]] \notin S1 + THEN Assert(FALSE, "Test 14 Failed") + ELSE Print("Test 14 OK", TRUE) + + /\ IF [l1 |-> 1, l2 |-> [i \in {1,2} |-> i+4]] \in S1 + THEN Assert(FALSE, "Test 15 Failed") + ELSE Print("Test 15 OK", TRUE) + + /\ IF ~(S2(5) \subseteq S1) \/ ~(S2(5) \subseteq S1) + THEN Assert(FALSE, "Test 16 Failed") + ELSE Print("Test 16 OK", TRUE) + + /\ IF S2(4) \subseteq S1 \/ S2(4) \subseteq S1 + THEN Assert(FALSE, "Test 17 Failed") + ELSE Print("Test 17 OK", TRUE) + /\ IF Cardinality(S2(5)) # 50 \/ Cardinality(S2(5)) # 50 + THEN Assert(FALSE, "Test 18 Failed") + ELSE Print("Test 18 OK", TRUE) + + /\ IF Cardinality([l1 : {1,2}, + l2 : {1, 2, 3, 4, 4, 4, 4}, l3 : {3, 3, 2, 1}]) # 24 + THEN Assert(FALSE, "Test 19 Failed") + ELSE Print("Test 19 OK", TRUE) + + /\ IF ~(\A r \in S2(5) : r.l2[1] > 5) \/ ~(\A r \in S2(5) : r.l2[1] > 5) + THEN Assert(FALSE, "Test 20 Failed") + ELSE Print("Test 20 OK", TRUE) + + /\ IF [l1 |-> -4, l2 |-> {<<3,2>>}] \notin S3 + THEN Assert(FALSE, "Test 21 Failed") + ELSE Print("Test 21 OK", TRUE) + + /\ IF [l1 |-> -4, l2 |-> {<<3,4>>}] \in S3 + THEN Assert(FALSE, "Test 22 Failed") + ELSE Print("Test 22 OK", TRUE) + + /\ IF [r4 EXCEPT !["a"][1] = 2]["a"][1] # 2 + THEN Assert(FALSE, "Test 23 Failed") + ELSE Print("Test 23 OK", TRUE) + +========================================= diff --git a/tlatools/test-model/suite/test13.cfg b/tlatools/test-model/suite/test13.cfg new file mode 100644 index 0000000000000000000000000000000000000000..b0ee260b28eef2026bbc2f6cb01c89fd3d01d947 --- /dev/null +++ b/tlatools/test-model/suite/test13.cfg @@ -0,0 +1,8 @@ + INIT + Init + + NEXT + Next + + INVARIANT Inv + diff --git a/tlatools/test-model/suite/test13.tla b/tlatools/test-model/suite/test13.tla new file mode 100644 index 0000000000000000000000000000000000000000..a72f9b93132ade5d59c03cbade567a57c246ac8b --- /dev/null +++ b/tlatools/test-model/suite/test13.tla @@ -0,0 +1,70 @@ +--------------- MODULE test13 ------------- + +(* Test of Tuples and Cartesian products. *) + +EXTENDS Integers, TLC, FiniteSets, Sequences + + +VARIABLE x +Type == x \in BOOLEAN +Init == x = TRUE +Next == UNCHANGED x + +Inv == +LET S1 == {1,2,3} \X {"a", "b", "c", "d"} + S2 == Int \X [{1,2,3} -> {4, 5, 6}] + S3 == {1,2,3} \X {"a", "b", "c", "d"} \X {4,5} + S4 == Int \X [{1,2,3} -> {4, 5, 6}] \X Seq(Int) + +IN + /\ IF Cardinality(S1) # 12 \/ Cardinality(S3) # 24 + THEN Assert(FALSE, "Test 1 Failed") + ELSE Print("Test 1 OK", TRUE) + + /\ IF \/ <<-5, <<4,4,4>> >> \notin S2 + \/ <<-5, <<4,4,4>>, <<-1,-2,-3>> >> \notin S4 + THEN Assert(FALSE, "Test 2 Failed") + ELSE Print("Test 2 OK", TRUE) + + /\ IF <<-5, -4>> \notin Int \X Int + THEN Assert(FALSE, "Test 3 Failed") + ELSE Print("Test 3 OK", TRUE) + + /\ IF <<-5, <<4,4,3>>>> \in S2 + THEN Assert(FALSE, "Test 4 Failed") + ELSE Print("Test 4 OK", TRUE) + + /\ IF {<<"a", "b", "c">>[i] : i \in 1..3} + # DOMAIN [a |-> 1, b |-> 2, c |-> {}] + THEN Assert(FALSE, "Test 5 Failed") + ELSE Print("Test 5 OK", TRUE) + + /\ IF [i \in {1, 2} |-> IF i = 1 THEN "a" ELSE 22] # <<"a", 22>> + THEN Assert(FALSE, "Test 6 Failed") + ELSE Print("Test 6 OK", TRUE) + + /\ IF [i \in {1, 2} |-> IF i = 1 THEN "a" ELSE 22] = <<"a", 23>> + THEN Assert(FALSE, "Test 7 Failed") + ELSE Print("Test 7 OK", TRUE) + + /\ IF <<1,2>> \notin Seq(Int) + THEN Assert(FALSE, "Test 8 Failed") + ELSE Print("Test 8 OK", TRUE) + + /\ IF <<1,2>> \notin Seq({n \in Int : n < 22}) + THEN Assert(FALSE, "Test 9 Failed") + ELSE Print("Test 9 OK", TRUE) + + /\ IF <<1,2>> \in Seq({n \in Int : n > 22}) + THEN Assert(FALSE, "Test 9 Failed") + ELSE Print("Test 9 OK", TRUE) + + /\ IF <<-5, -4, -3>> \notin Int \X Int \X Int + THEN Assert(FALSE, "Test 10 Failed") + ELSE Print("Test 10 OK", TRUE) + + /\ IF <<-5, <<4,4,3>>, <<1,-1>> >> \in S4 + THEN Assert(FALSE, "Test 11 Failed") + ELSE Print("Test 11 OK", TRUE) + + ========================================= diff --git a/tlatools/test-model/suite/test14.cfg b/tlatools/test-model/suite/test14.cfg new file mode 100644 index 0000000000000000000000000000000000000000..b0ee260b28eef2026bbc2f6cb01c89fd3d01d947 --- /dev/null +++ b/tlatools/test-model/suite/test14.cfg @@ -0,0 +1,8 @@ + INIT + Init + + NEXT + Next + + INVARIANT Inv + diff --git a/tlatools/test-model/suite/test14.tla b/tlatools/test-model/suite/test14.tla new file mode 100644 index 0000000000000000000000000000000000000000..4cd3b1783d7ffbf7ea9c5f6cade784c6b79d925f --- /dev/null +++ b/tlatools/test-model/suite/test14.tla @@ -0,0 +1,54 @@ +--------------- MODULE test14 ------------- + +(* Test of IF and CASE and nested junctions *) + +EXTENDS Integers, TLC, FiniteSets, Sequences + + +VARIABLE x +Type == x \in BOOLEAN +Init == x = TRUE +Next == UNCHANGED x + +Inv == + + /\ IF (IF TRUE THEN IF FALSE THEN 3 ELSE 2 ELSE 1) # 2 + THEN Assert(FALSE, "Test 1 Failed") + ELSE Print("Test 1 OK", TRUE) + + /\ IF (CASE FALSE -> 1 [] TRUE -> CASE FALSE -> 2 [] TRUE -> 3 [] OTHER -> 4) + # 3 + THEN Assert(FALSE, "Test 2 Failed") + ELSE Print("Test 2 OK", TRUE) + + /\ IF (CASE FALSE -> 1 [] FALSE -> CASE FALSE -> 2 [] TRUE -> 3 + [] OTHER -> 4 [] TRUE -> 5) # 5 + THEN Assert(FALSE, "Test 3 Failed") + ELSE Print("Test 3 OK", TRUE) + + /\ IF (CASE FALSE -> 1 [] TRUE -> CASE FALSE -> 2 [] FALSE -> 3 + [] OTHER -> 4 [] TRUE -> 5) # 4 + THEN Assert(FALSE, "Test 4 Failed") + ELSE Print("Test 4 OK", TRUE) + + /\ IF (CASE FALSE -> 1 [] FALSE -> CASE TRUE -> 2 [] TRUE -> 3 + [] OTHER -> 4 [] OTHER -> 5) # 5 + THEN Assert(FALSE, "Test 5 Failed") + ELSE Print("Test 5 OK", TRUE) + + /\ IF (CASE TRUE -> 1 [] TRUE -> CASE TRUE -> 2 [] TRUE -> 3 + [] OTHER -> 4 [] OTHER -> 5) \notin {1, 2, 3} + THEN Assert(FALSE, "Test 6 Failed") + ELSE Print("Test 6 OK", TRUE) + + /\ IF ~ \/ /\ 1=3 + /\ "a" = 1 + \/ /\ 1 = 2 + /\ "b" = 1 + \/ /\ 1 = 1 + /\ 2 = 2 + \/ "c" = 1 + THEN Assert(FALSE, "Test 7 Failed") + ELSE Print("Test 7 OK", TRUE) + +========================================= diff --git a/tlatools/test-model/suite/test15.cfg b/tlatools/test-model/suite/test15.cfg new file mode 100644 index 0000000000000000000000000000000000000000..b0ee260b28eef2026bbc2f6cb01c89fd3d01d947 --- /dev/null +++ b/tlatools/test-model/suite/test15.cfg @@ -0,0 +1,8 @@ + INIT + Init + + NEXT + Next + + INVARIANT Inv + diff --git a/tlatools/test-model/suite/test15.tla b/tlatools/test-model/suite/test15.tla new file mode 100644 index 0000000000000000000000000000000000000000..e4d977e74e918a8f2956f3673701651262911d45 --- /dev/null +++ b/tlatools/test-model/suite/test15.tla @@ -0,0 +1,78 @@ +--------------- MODULE test15 ------------- +(* Test of handling the empty set *) + +EXTENDS TLC, Naturals, Sequences + +VARIABLE x +Type == x \in BOOLEAN +Init == x = TRUE +Next == UNCHANGED x + + + +Inv == + /\ IF SUBSET {} # {{}} + THEN Assert(FALSE, "Test 1 Failed") + ELSE Print("Test 1 OK", TRUE) + + /\ IF UNION {} # {} + THEN Assert(FALSE, "Test 2 Failed") + ELSE Print("Test 2 OK", TRUE) + + /\ IF {y \in {} : y = y} # {} + THEN Assert(FALSE, "Test 3 Failed") + ELSE Print("Test 3 OK", TRUE) + + /\ IF [y \in {} |-> y] # << >> + THEN Assert(FALSE, "Test 4 Failed") + ELSE Print("Test 4 OK", TRUE) + + /\ IF (\E S \in {} : TRUE) # FALSE + THEN Assert(FALSE, "Test 5 Failed") + ELSE Print("Test 5 OK", TRUE) + + /\ IF (\A S \in {} : FALSE) # TRUE + THEN Assert(FALSE, "Test 6 Failed") + ELSE Print("Test 6 OK", TRUE) + + /\ IF DOMAIN <<>> # {} + THEN Assert(FALSE, "Test 7 Failed") + ELSE Print("Test 7 OK", TRUE) + + /\ IF 1..0 # {} + THEN Assert(FALSE, "Test 8 Failed") + ELSE Print("Test 8 OK", TRUE) + + /\ IF <<>> \notin Seq({}) + THEN Assert(FALSE, "Test 9 Failed") + ELSE Print("Test 9 OK", TRUE) + + /\ IF <<{}>> \in Seq ({}) + THEN Assert(FALSE, "Test 10 Failed") + ELSE Print("Test 10 OK", TRUE) + + /\ IF \E S \in SUBSET {} : S # {} + THEN Assert(FALSE, "Test 11 Failed") + ELSE Print("Test 11 OK", TRUE) + /\ IF [{} -> {1, 2, 3}] # {<<>>} + THEN Assert(FALSE, "Test 12 Failed") + ELSE Print("Test 12 OK", TRUE) + + /\ IF [{1, 2, 3} -> {}] # {} + THEN Assert(FALSE, "Test 13 Failed") + ELSE Print("Test 13 OK", TRUE) + + + /\ IF (UNION {{1,2,3}, {}}) # (UNION {{1,2,3}}) + THEN Assert(FALSE, "Test 14 Failed") + ELSE Print("Test 14 OK", TRUE) + + /\ IF 1..0 # {} + THEN Assert(FALSE, "Test 15 Failed") + ELSE Print("Test 15 OK", TRUE) + + /\ IF 1..0 # 5..2 + THEN Assert(FALSE, "Test 16 Failed") + ELSE Print("Test 16 OK", TRUE) + +========================================= diff --git a/tlatools/test-model/suite/test16.cfg b/tlatools/test-model/suite/test16.cfg new file mode 100644 index 0000000000000000000000000000000000000000..d191341c2404a4660de5819024419df4563278cd --- /dev/null +++ b/tlatools/test-model/suite/test16.cfg @@ -0,0 +1,9 @@ + INIT + Init + + NEXT + Next + + + INVARIANT Inv + diff --git a/tlatools/test-model/suite/test16.tla b/tlatools/test-model/suite/test16.tla new file mode 100644 index 0000000000000000000000000000000000000000..4a5dcc10ca9724d64afc857f3e6f3d4ee083cf7b --- /dev/null +++ b/tlatools/test-model/suite/test16.tla @@ -0,0 +1,40 @@ +--------------- MODULE test16 ------------- +(* Test of handling of one-tuples *) + +EXTENDS TLC, Naturals, Sequences + +VARIABLE x +Type == x \in BOOLEAN +Init == x = TRUE +Next == UNCHANGED x + +S == {<<y>> : y \in {1, 2, 3, 4}} + +Inv == + + /\ IF <<1>> \notin S + THEN Assert(FALSE, "Test 1 Failed") + ELSE Print("Test 1 OK", TRUE) + + /\ IF ~(\E <<y>> \in S : y = 2) + THEN Assert(FALSE, "Test 2 Failed") + ELSE Print("Test 2 OK", TRUE) + + /\ IF ~(\E y \in S : y[1] = 2) + THEN Assert(FALSE, "Test 3 Failed") + ELSE Print("Test 3 OK", TRUE) + + /\ IF {<<y>> \in S : y > 3} # {<<4>>} + THEN Assert(FALSE, "Test 4 Failed") + ELSE Print("Test 4 OK", TRUE) + + /\ IF <<1>> \o <<2>> # <<1, 2>> + THEN Assert(FALSE, "Test 5 Failed") + ELSE Print("Test 5 OK", TRUE) + + /\ IF [<<y>> \in S |-> y+1][<<2>>] # 3 + THEN Assert(FALSE, "Test 6 Failed") + ELSE Print("Test 6 OK", TRUE) + + +========================================= diff --git a/tlatools/test-model/suite/test17.cfg b/tlatools/test-model/suite/test17.cfg new file mode 100644 index 0000000000000000000000000000000000000000..d191341c2404a4660de5819024419df4563278cd --- /dev/null +++ b/tlatools/test-model/suite/test17.cfg @@ -0,0 +1,9 @@ + INIT + Init + + NEXT + Next + + + INVARIANT Inv + diff --git a/tlatools/test-model/suite/test17.tla b/tlatools/test-model/suite/test17.tla new file mode 100644 index 0000000000000000000000000000000000000000..d6975f3f805075b4678a5a13c09d48feb550fc1e --- /dev/null +++ b/tlatools/test-model/suite/test17.tla @@ -0,0 +1,131 @@ +--------------- MODULE test17 ------------- + +(* Test of handling of handling expressions with primes. *) + +EXTENDS TLC, Naturals, Sequences + +VARIABLE x, y + +Init == /\ Print("There should be two distinct states", TRUE) + /\ x = 1 + /\ y = 1 + +f[i \in Nat ] == x'+ x + 2*i +g == [i \in {x'} |-> x+i] + + +Foo(a) == a' = x+1 + +Inv == TRUE + +TNext == + /\ x' = x+1 + /\ x' = 2 + /\ \/ UNCHANGED y + + \/ /\ Print("Starting Test 1", TRUE) + /\ y' = IF x' = 2 + THEN 1 + ELSE Assert(FALSE, "Failed Test 1") + + \/ /\ Print("Starting Test 2", TRUE) + /\ IF x' = 2 + THEN y' = 1 + ELSE Assert(FALSE, "Failed Test 2") + + \/ /\ Print("Starting Test 3", TRUE) + /\ x' # 2 + /\ Assert(FALSE, "Failed Test 3") + + \/ /\ Print("Starting Test 4", TRUE) + /\ ~(\E i \in {x'} : i = 2) + /\ Assert(FALSE, "Failed Test 4") + + \/ /\ Print("Starting Test 5", TRUE) + /\ ~ (\A i \in {x'} : i = 2) + /\ Assert(FALSE, "Failed Test 5") + + \/ /\ Print("Starting Test 6", TRUE) + /\ (CHOOSE i \in {x'} : TRUE) # 2 + /\ Assert(FALSE, "Failed Test 6") + + \/ /\ Print("Starting Test 7", TRUE) + /\ 2 \notin {x'} + /\ Assert(FALSE, "Failed Test 7") + + \/ /\ Print("Starting Test 8", TRUE) + /\ {i \in {x'} : TRUE} # {2} + /\ Assert(FALSE, "Failed Test 8") + + \/ /\ Print("Starting Test 9", TRUE) + /\ {i' : i \in {x'}} # {2} + /\ Assert(FALSE, "Failed Test 9") + + \/ /\ Print("Starting Test 10", TRUE) + /\ {2} \notin SUBSET {x'} + /\ Assert(FALSE, "Failed Test 10") + + \/ /\ Print("Starting Test 11", TRUE) + /\ {2} # UNION {{x'}, {}} + /\ Assert(FALSE, "Failed Test 11") + + \/ /\ Print("Starting Test 12", TRUE) + /\ f[1] # 5 + /\ Assert(FALSE, "Failed Test 12") + + \/ /\ Print("Starting Test 13", TRUE) + /\ f[x'] # 7 + /\ Assert(FALSE, "Failed Test 13") + + \/ /\ Print("Starting Test 14", TRUE) + /\ [f EXCEPT ![x'] = x'+x+3][2] # 6 + /\ Assert(FALSE, "Failed Test 14") + + \/ /\ Print("Starting Test 15", TRUE) + /\ [f EXCEPT ![2] = x'+x+3][x'] # 6 + /\ Assert(FALSE, "Failed Test 15") + + \/ /\ Print("Starting Test 16", TRUE) + /\ [{x'-1} -> {x'}] # {<<2>>} + /\ Assert(FALSE, "Failed Test 16") + + \/ /\ Print("Starting Test 17", TRUE) + /\ [{x',x} -> {x'}] # {<<2, 2>>} + /\ Assert(FALSE, "Failed Test 17") + + \/ /\ Print("Starting Test 18", TRUE) + /\ [{x',x} -> {x}] # {<<1, 1>>} + /\ Assert(FALSE, "Failed Test 18") + + \/ /\ Print("Starting Test 19", TRUE) + /\ x'=2 => x'=3 + /\ Assert(FALSE, "Failed Test 19") + + \/ /\ Print("Starting Test 20", TRUE) + /\ CASE x'= x+1 -> FALSE + [] x'# x+1 -> TRUE + /\ Assert(FALSE, "Failed Test 20") + + \/ /\ Print("Starting Test 21", TRUE) + /\ DOMAIN g # {2} + /\ Assert(FALSE, "Failed Test 21") + + \/ /\ Print("Starting Test 22", TRUE) + /\ g[2] # 3 + /\ Assert(FALSE, "Failed Test 22") + + \/ /\ Print("Starting Test 23", TRUE) + /\ CASE x' # 2 -> Assert(FALSE, "Failed Test 23") + [] x' = 2 -> y' = 1 + + \/ /\ Print("Starting Test 24", TRUE) + /\ ~Foo(x) + /\ Assert(FALSE, "Failed Test 24") + + \/ /\ Print("Starting Test 25", TRUE) + /\ ~Foo(x) + /\ Assert(FALSE, "Failed Test 25") + + +Next == TNext \/ UNCHANGED <<x, y>> +========================================= diff --git a/tlatools/test-model/suite/test18.cfg b/tlatools/test-model/suite/test18.cfg new file mode 100644 index 0000000000000000000000000000000000000000..d191341c2404a4660de5819024419df4563278cd --- /dev/null +++ b/tlatools/test-model/suite/test18.cfg @@ -0,0 +1,9 @@ + INIT + Init + + NEXT + Next + + + INVARIANT Inv + diff --git a/tlatools/test-model/suite/test18.tla b/tlatools/test-model/suite/test18.tla new file mode 100644 index 0000000000000000000000000000000000000000..de889dc7a9ccc0fb5f583aeabd4e8ce506de8412 --- /dev/null +++ b/tlatools/test-model/suite/test18.tla @@ -0,0 +1,64 @@ +--------------- MODULE test18 ------------- + +(* Test of fingerprinting. *) + +EXTENDS TLC, Naturals, Sequences + +VARIABLE x, y, z, w, u + +f == [i \in {1,2}, j \in {3,4} |-> i+j] +g == [p \in {1,2} \X {3,4} |-> p[1] + p[2]] +m == [i \in {3,4} |-> <<i, 7>>] + +Init == /\ Print("There should be only one distinct state", TRUE) + /\ x = [a |-> 1, b |-> 2] + /\ y = <<3, 4>> + /\ z = {1, 2, 3} + /\ w = f + /\ u = [m EXCEPT ![4] = <<4, 8>>] + /\ IF y # [i \in {1, 2} |-> IF i = 1 THEN 3 ELSE 4] + THEN Print("Error in comparing seq and fcn", TRUE) + ELSE TRUE + + +Inv == /\ TRUE + +Next == + \/ UNCHANGED <<x, y, z, w, u>> + + \/ /\ x' = [i \in {"a", "b"} |-> IF i = "a" THEN 1 ELSE 2] + /\ UNCHANGED <<y, z, w, u>> + + \/ /\ y' = [i \in {1, 2} |-> IF i = 1 THEN 3 ELSE 4] + /\ UNCHANGED <<x, z, w, u>> + + \/ /\ y' = <<3>> \o <<4>> + /\ UNCHANGED <<x, z, w, u>> + + \/ /\ y' = [y EXCEPT ![2]=4] + /\ UNCHANGED <<x, z, w, u>> + + \/ /\ y' = [<<3, 7>> EXCEPT ![2]=4] + /\ UNCHANGED <<x, z, w, u>> + + \/ /\ y' = <<3>> \o <<4>> + /\ UNCHANGED <<x, z, w, u>> + + \/ /\ z' = {3, 3, 2, 2, 1} + /\ UNCHANGED <<x, y, w, u>> + + \/ /\ z' = 1..3 + /\ UNCHANGED <<x, y, w, u>> + + \/ /\ z' = {i - 3 : i \in {4, 5, 6, 4, 5}} + /\ UNCHANGED <<x, y, w, u>> + + \/ /\ z' = DOMAIN <<"a", "b", 4>> + /\ UNCHANGED <<x, y, w, u>> + + \/ /\ w' = g + /\ UNCHANGED <<x, y, z, u>> + + \/ /\ u' = [m EXCEPT ![4][2] = @+1] + /\ UNCHANGED <<x, y, z, w>> +========================================= diff --git a/tlatools/test-model/suite/test19.cfg b/tlatools/test-model/suite/test19.cfg new file mode 100644 index 0000000000000000000000000000000000000000..d191341c2404a4660de5819024419df4563278cd --- /dev/null +++ b/tlatools/test-model/suite/test19.cfg @@ -0,0 +1,9 @@ + INIT + Init + + NEXT + Next + + + INVARIANT Inv + diff --git a/tlatools/test-model/suite/test19.tla b/tlatools/test-model/suite/test19.tla new file mode 100644 index 0000000000000000000000000000000000000000..9d4ac22b6d9d3e8b317a150ba1abb11811944f4c --- /dev/null +++ b/tlatools/test-model/suite/test19.tla @@ -0,0 +1,15 @@ +--------------- MODULE test19 ------------- + +(* Test of large initial state space *) + +EXTENDS Naturals, Sequences + +VARIABLE x, y, z +Init == /\ x \in 1..100 + /\ y \in 1..200 + /\ z \in 1..20 + +Next == UNCHANGED <<x, y, z>> + +Inv == TRUE +========================================= diff --git a/tlatools/test-model/suite/test2.cfg b/tlatools/test-model/suite/test2.cfg new file mode 100644 index 0000000000000000000000000000000000000000..d32cfa879ee7389879ef2ad6bdf490a5e474c707 --- /dev/null +++ b/tlatools/test-model/suite/test2.cfg @@ -0,0 +1,12 @@ + INIT + Init + + NEXT + Next + + INVARIANT Inv + + CONSTANT + a = a + b = b + c = c diff --git a/tlatools/test-model/suite/test2.tla b/tlatools/test-model/suite/test2.tla new file mode 100644 index 0000000000000000000000000000000000000000..45ff6602c2d7208ae5df4b1914f195fd666178ef --- /dev/null +++ b/tlatools/test-model/suite/test2.tla @@ -0,0 +1,78 @@ +--------------- MODULE test2 ------------- + +(* test equality of various representations of functions. *) + +EXTENDS Naturals, TLC + +VARIABLE x +CONSTANT a, b, c +f == [i \in {1,2}, j \in {3,4} |-> i+j] +g == [p \in {1,2} \X {3,4} |-> p[1] + p[2]] +h == [i \in {1,2} |-> [j \in {3,4} |-> i+j]] + +m == [i \in {3,4} |-> <<i, 7>>] + + +Type == x \in {1} +Inv == + /\ TRUE + + /\ IF [i \in {3, 2, 1} |-> i+2] # <<3, 4, 5>> + THEN Assert(FALSE, "Failed Test 1") + ELSE Print("Test 1 OK", TRUE) + + /\ IF [i \in {"a", "b", "c"} |-> + CASE i = "a" -> 1 [] i = "b" -> 2 [] i = "c" -> 3] + # [c |-> 3, a |-> 1, b |-> 2] + THEN Assert(FALSE, "Failed Test 2") + ELSE Print("Test 2 OK", TRUE) + + /\ IF [c |-> 3, a |-> 1, b |-> 2] # + [[a |-> 1, b |-> 3, c |-> 4] EXCEPT !.b =2 , !.c=3] + THEN Assert(FALSE, "Failed Test 3") + ELSE Print("Test 3 OK", TRUE) + + /\ IF <<3, 4, 5>> # [<<3, 42, 75>> EXCEPT ![3]=5, ![2]=4] + THEN Assert(FALSE, "Failed Test 4") + ELSE Print("Test 4 OK", TRUE) + + /\ IF [i \in {3, 2, 1} |-> i+2] # [ j \in 1..3 |-> j+2] + THEN Assert(FALSE, "Failed Test 5") + ELSE Print("Test 5 OK", TRUE) + + /\ IF [i \in {} |-> i] # <<>> + THEN Assert(FALSE, "Failed Test 6") + ELSE Print("Test 6 OK", TRUE) + + /\ IF {i \in [{1,2} -> {a, b, c}] : i[1] = a} # {<<a, a>>, <<a, b>>, <<a, c>>} + THEN Assert(FALSE, "Failed Test 7") + ELSE Print("Test 7 OK", TRUE) + + /\ IF [{"a", "bc"} -> {1, 2, 3}] # [bc : {1,2,3}, a : {1,2,3}] + THEN Assert(FALSE, "Failed Test 8") + ELSE Print("Test 8 OK", TRUE) + + /\ IF f # g + THEN Assert(FALSE, "Test 9 Failed") + ELSE Print("Test 9 OK", TRUE) + + /\ IF f = h + THEN Assert(FALSE, "Test 10 Failed") + ELSE Print("Test 10 OK", TRUE) + + /\ IF [m EXCEPT ![4] = <<4, 8>>] # [m EXCEPT ![4][2] = @+1] + THEN Assert(FALSE, "Test 11 Failed") + ELSE Print("Test 11 OK", TRUE) + + /\ IF m#[m EXCEPT ![4][2] = 7] + THEN Assert(FALSE, "Test 12 Failed") + ELSE Print("Test 12 OK", TRUE) + + /\ IF f # [f EXCEPT ![1,3] = 4] + THEN Assert(FALSE, "Test 13 Failed") + ELSE Print("Test 13 OK", TRUE) + +Init == x = 1 + +Next == UNCHANGED x +========================================= diff --git a/tlatools/test-model/suite/test20.cfg b/tlatools/test-model/suite/test20.cfg new file mode 100644 index 0000000000000000000000000000000000000000..d191341c2404a4660de5819024419df4563278cd --- /dev/null +++ b/tlatools/test-model/suite/test20.cfg @@ -0,0 +1,9 @@ + INIT + Init + + NEXT + Next + + + INVARIANT Inv + diff --git a/tlatools/test-model/suite/test20.tla b/tlatools/test-model/suite/test20.tla new file mode 100644 index 0000000000000000000000000000000000000000..984160fbd869ab16f92daa1fed3e5d210f6ccd78 --- /dev/null +++ b/tlatools/test-model/suite/test20.tla @@ -0,0 +1,85 @@ +--------------- MODULE test20 ------------- + +(* Test of ENABLED *) + +EXTENDS Naturals, Sequences, TLC + +VARIABLE x, y + +Init == x = 1 /\ y = 1 + +Step(p) == x' = p + +Foo(A) == ENABLED((x' = x) /\ (A'=x+1)) + +Next == + \/ /\ x'=x + /\ y'=y + + \/ /\ Print("Test N1 begun", TRUE) + /\ ~ENABLED (x'=2) + /\ Assert(FALSE, "Test N1 Failed") + + \/ IF ENABLED (x'=2) + THEN /\ Print("Test N2 Passed", TRUE) + /\ UNCHANGED <<x, y>> + ELSE Assert(FALSE, "Test N2 Failed") + \/ IF ENABLED Step(17) + THEN /\ Print("Test N3 Passed", TRUE) + /\ UNCHANGED <<x, y>> + ELSE Assert(FALSE, "Test N3 Failed") + + \/ IF \A p \in 1..3 : ENABLED Step(p) + THEN /\ Print("Test N4 Passed", TRUE) + /\ UNCHANGED <<x, y>> + ELSE Assert(FALSE, "Test N4 Failed") + +Inv == + /\ IF ENABLED <<x'=1 /\ y'=y>>_<<x,y>> + THEN Assert(FALSE, "Test 1 Failed") + ELSE Print("Test 1 OK", TRUE) + + /\ IF ~ ENABLED (x'="a" /\ y'=y) + THEN Assert(FALSE, "Test 2 Failed") + ELSE Print("Test 2 OK", TRUE) + + /\ IF ENABLED (x > 2) + THEN Assert(FALSE, "Test 3 Failed") + ELSE Print("Test 3 OK", TRUE) + + /\ IF ~ENABLED (x=1) + THEN Assert(FALSE, "Test 4 Failed") + ELSE Print("Test 4 OK", TRUE) + + /\ IF ENABLED ((x > 2) /\ Next) + THEN Assert(FALSE, "Test 5 Failed") + ELSE Print("Test 5 OK", TRUE) + + /\ IF ~ENABLED ((x=1) /\ Next) + THEN Assert(FALSE, "Test 6 Failed") + ELSE Print("Test 6 OK", TRUE) + + /\ IF ~ ENABLED (x'=1) + THEN Assert(FALSE, "Test 7 Failed") + ELSE Print("Test 7 OK", TRUE) + + /\ IF Foo(x) + THEN Assert(FALSE, "Test 8 Failed") + ELSE Print("Test 8 OK", TRUE) + + /\ LET A == x + IN IF Foo(A) + THEN Assert(FALSE, "Test 9 Failed") + ELSE Print("Test 9 OK", TRUE) + + /\ IF ~Foo(y) + THEN Assert(FALSE, "Test 10 Failed") + ELSE Print("Test 10 OK", TRUE) + + /\ LET A == x + IN IF ~Foo(y) + THEN Assert(FALSE, "Test 11 Failed") + ELSE Print("Test 11 OK", TRUE) + + +========================================= diff --git a/tlatools/test-model/suite/test201.cfg b/tlatools/test-model/suite/test201.cfg new file mode 100644 index 0000000000000000000000000000000000000000..139597f9cb07c5d48bed18984ec4747f4b4f3438 --- /dev/null +++ b/tlatools/test-model/suite/test201.cfg @@ -0,0 +1,2 @@ + + diff --git a/tlatools/test-model/suite/test201.tla b/tlatools/test-model/suite/test201.tla new file mode 100644 index 0000000000000000000000000000000000000000..b2d3d5752a44c8ecb35d5802c3b0a7f1834cfc3f --- /dev/null +++ b/tlatools/test-model/suite/test201.tla @@ -0,0 +1,20 @@ +-------------------- MODULE test201 ---------------- +\* Test of lambda expressions + +EXTENDS Naturals, TLC +Op(A(_)) == A(4) + +bar == Op(LAMBDA x : x^2) + +ASSUME /\ Op(LAMBDA x : {x,2} ) = {4,2} + /\ PrintT("Test1 OK") + +ASSUME /\ bar = 16 + /\ PrintT("Test2 OK") + +foo == INSTANCE test201a WITH A <- LAMBDA y : y^3, b <- 4 + +ASSUME /\ foo!def = 64 + /\ PrintT("Test201 OK") +================================================= + diff --git a/tlatools/test-model/suite/test201a.cfg b/tlatools/test-model/suite/test201a.cfg new file mode 100644 index 0000000000000000000000000000000000000000..139597f9cb07c5d48bed18984ec4747f4b4f3438 --- /dev/null +++ b/tlatools/test-model/suite/test201a.cfg @@ -0,0 +1,2 @@ + + diff --git a/tlatools/test-model/suite/test201a.tla b/tlatools/test-model/suite/test201a.tla new file mode 100644 index 0000000000000000000000000000000000000000..1f5c21d50552d1fa1a74e50dab58a381f7acb211 --- /dev/null +++ b/tlatools/test-model/suite/test201a.tla @@ -0,0 +1,7 @@ +-------------------- MODULE test201a ---------------- + +CONSTANT A(_), b + +def == A(b) + +============================================================================= \ No newline at end of file diff --git a/tlatools/test-model/suite/test202.cfg b/tlatools/test-model/suite/test202.cfg new file mode 100644 index 0000000000000000000000000000000000000000..139597f9cb07c5d48bed18984ec4747f4b4f3438 --- /dev/null +++ b/tlatools/test-model/suite/test202.cfg @@ -0,0 +1,2 @@ + + diff --git a/tlatools/test-model/suite/test202.tla b/tlatools/test-model/suite/test202.tla new file mode 100644 index 0000000000000000000000000000000000000000..bf779d2986e617f83e4070bf003f0cf88ea007ef --- /dev/null +++ b/tlatools/test-model/suite/test202.tla @@ -0,0 +1,32 @@ +-------------------- MODULE test202 ---------------- +\* Test of recursion + +EXTENDS Naturals, TLC + +RECURSIVE g(_) +f(a) == IF a = 0 THEN 1 ELSE a * g(a-1) +g(a) == f(a) + + +ASSUME /\ f(0) = 1 + /\ f(4) = 24 + /\ \A i \in 1..5 : f(i) = i * f(i-1) + /\ PrintT("Test 1 OK") + +ASSUME /\ g(0) = 1 + /\ g(4) = 24 + /\ \A i \in 1..5 : g(i) = i * g(i-1) + /\ PrintT("Test 2 OK") + +RECURSIVE fact +fact1[n \in Nat] == fact[n] +fact[n \in Nat] == IF n = 0 THEN 1 ELSE n * fact1[n-1] + +ASSUME /\ fact1[3] = 6 + /\ fact[4] = 24 + /\ \A i \in 0..5 : fact[i] = g(i) + /\ PrintT("Test 3 OK") + + +================================================= + diff --git a/tlatools/test-model/suite/test203.cfg b/tlatools/test-model/suite/test203.cfg new file mode 100644 index 0000000000000000000000000000000000000000..139597f9cb07c5d48bed18984ec4747f4b4f3438 --- /dev/null +++ b/tlatools/test-model/suite/test203.cfg @@ -0,0 +1,2 @@ + + diff --git a/tlatools/test-model/suite/test203.tla b/tlatools/test-model/suite/test203.tla new file mode 100644 index 0000000000000000000000000000000000000000..47ba23369b9232e869c454ec290ea756d2ee3c43 --- /dev/null +++ b/tlatools/test-model/suite/test203.tla @@ -0,0 +1,17 @@ +-------------------- MODULE test203 ---------------- +\* Test of recursion + +EXTENDS Naturals, TLC + +RECURSIVE g(_) + +foo(n) == INSTANCE test203a WITH A <- g, b <- n + +g(n) == IF n = 0 THEN 1 ELSE n * foo(n-1)!h + +ASSUME /\ g(0) = 1 + /\ g(4) = 24 + /\ \A i \in 1..5 : g(i) = i * g(i-1) + /\ PrintT("Test1 OK") +================================================= + diff --git a/tlatools/test-model/suite/test203a.cfg b/tlatools/test-model/suite/test203a.cfg new file mode 100644 index 0000000000000000000000000000000000000000..139597f9cb07c5d48bed18984ec4747f4b4f3438 --- /dev/null +++ b/tlatools/test-model/suite/test203a.cfg @@ -0,0 +1,2 @@ + + diff --git a/tlatools/test-model/suite/test203a.tla b/tlatools/test-model/suite/test203a.tla new file mode 100644 index 0000000000000000000000000000000000000000..4e3cc2422ced3efb13590461bf82d745063cb183 --- /dev/null +++ b/tlatools/test-model/suite/test203a.tla @@ -0,0 +1,8 @@ +-------------------- MODULE test203a ---------------- + +CONSTANT A(_), b + +h == A(b) + + +============================================================================= \ No newline at end of file diff --git a/tlatools/test-model/suite/test204.cfg b/tlatools/test-model/suite/test204.cfg new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/tlatools/test-model/suite/test204.cfg @@ -0,0 +1 @@ + diff --git a/tlatools/test-model/suite/test204.tla b/tlatools/test-model/suite/test204.tla new file mode 100644 index 0000000000000000000000000000000000000000..b65c21c4c88718c8b9eaa17d6419854a753c2200 --- /dev/null +++ b/tlatools/test-model/suite/test204.tla @@ -0,0 +1,178 @@ +--------------- MODULE test204 ------------- + +(***************************************************************************) +(* Test definability and use of all user-defined operators as prefix *) +(* operators, as in +(a, b). It does not work with binary minus. *) +(***************************************************************************) + +z - y == <<1, z, y>> +\* ASSUME -(4, 6) = <<1, 4, 6>> +z + y == <<2, z, y>> +ASSUME +(4, 6) = <<2, 4, 6>> +z * y == <<3, z, y>> +ASSUME *(4, 6) = <<3, 4, 6>> +z / y == <<4, z, y>> +ASSUME /(4, 6) = <<4, 4, 6>> +z ^ y == <<5, z, y>> +ASSUME ^(4, 6) = <<5, 4, 6>> +z < y == <<6, z, y>> +ASSUME <(4, 6) = <<6, 4, 6>> +z > y == <<7, z, y>> +ASSUME >(4, 6) = <<7, 4, 6>> +z .. y == <<8, z, y>> +ASSUME ..(4, 6) = <<8, 4, 6>> +z ... y == <<9, z, y>> +ASSUME ...(4, 6) = <<9, 4, 6>> +z | y == <<10, z, y>> +ASSUME |(4, 6) = <<10, 4, 6>> +z || y == <<11, z, y>> +ASSUME ||(4, 6) = <<11, 4, 6>> +z & y == <<12, z, y>> +ASSUME &(4, 6) = <<12, 4, 6>> +z && y == <<13, z, y>> +ASSUME &&(4, 6) = <<13, 4, 6>> +z $ y == <<14, z, y>> +ASSUME $(4, 6) = <<14, 4, 6>> +z $$ y == <<15, z, y>> +ASSUME $$(4, 6) = <<15, 4, 6>> +z ?? y == <<16, z, y>> +ASSUME ??(4, 6) = <<16, 4, 6>> +z %% y == <<17, z, y>> +ASSUME %%(4, 6) = <<17, 4, 6>> +z ## y == <<18, z, y>> +ASSUME ##(4, 6) = <<18, 4, 6>> +z ++ y == <<19, z, y>> +ASSUME ++(4, 6) = <<19, 4, 6>> +z -- y == <<20, z, y>> +ASSUME --(4, 6) = <<20, 4, 6>> +z ** y == <<21, z, y>> +ASSUME **(4, 6) = <<21, 4, 6>> +z // y == <<22, z, y>> +ASSUME //(4, 6) = <<22, 4, 6>> +z ^^ y == <<23, z, y>> +ASSUME ^^(4, 6) = <<23, 4, 6>> +z @@ y == <<23, z, y>> +ASSUME @@(4, 6) = <<23, 4, 6>> +z !! y == <<24, z, y>> +ASSUME !!(4, 6) = <<24, 4, 6>> +z |- y == <<25, z, y>> +ASSUME |-(4, 6) = <<25, 4, 6>> +z |= y == <<26, z, y>> +ASSUME |=(4, 6) = <<26, 4, 6>> +z -| y == <<27, z, y>> +ASSUME -|(4, 6) = <<27, 4, 6>> +z =| y == <<28, z, y>> +ASSUME =|(4, 6) = <<28, 4, 6>> +z <: y == <<29, z, y>> +ASSUME <:(4, 6) = <<29, 4, 6>> +z :> y == <<29, z, y>> +ASSUME :>(4, 6) = <<29, 4, 6>> +z := y == <<30, z, y>> +ASSUME :=(4, 6) = <<30, 4, 6>> +z ::= y == <<31, z, y>> +ASSUME ::=(4, 6) = <<31, 4, 6>> +z \uplus y == <<32, z, y>> +ASSUME \uplus(4, 6) = <<32, 4, 6>> +z \sqcap y == <<33, z, y>> +ASSUME \sqcap(4, 6) = <<33, 4, 6>> +z \sqcup y == <<34, z, y>> +ASSUME \sqcup(4, 6) = <<34, 4, 6>> +z \div y == <<35, z, y>> +ASSUME \div(4, 6) = <<35, 4, 6>> +z \wr y == <<36, z, y>> +ASSUME \wr(4, 6) = <<36, 4, 6>> +z \star y == <<37, z, y>> +ASSUME \star(4, 6) = <<37, 4, 6>> +z \bigcirc y == <<38, z, y>> +ASSUME \bigcirc(4, 6) = <<38, 4, 6>> +z \bullet y == <<39, z, y>> +ASSUME \bullet(4, 6) = <<39, 4, 6>> +z \prec y == <<40, z, y>> +ASSUME \prec(4, 6) = <<40, 4, 6>> +z \succ y == <<41, z, y>> +ASSUME \succ(4, 6) = <<41, 4, 6>> +z \preceq y == <<42, z, y>> +ASSUME \preceq(4, 6) = <<42, 4, 6>> +z \succeq y == <<43, z, y>> +ASSUME \succeq(4, 6) = <<43, 4, 6>> +z \sim y == <<44, z, y>> +ASSUME \sim(4, 6) = <<44, 4, 6>> +z \simeq y == <<45, z, y>> +ASSUME \simeq(4, 6) = <<45, 4, 6>> +z \ll y == <<46, z, y>> +ASSUME \ll(4, 6) = <<46, 4, 6>> +z \gg y == <<47, z, y>> +ASSUME \gg(4, 6) = <<47, 4, 6>> +z \asymp y == <<48, z, y>> +ASSUME \asymp(4, 6) = <<48, 4, 6>> +z \subset y == <<49, z, y>> +ASSUME \subset(4, 6) = <<49, 4, 6>> +z \supset y == <<50, z, y>> +ASSUME \supset(4, 6) = <<50, 4, 6>> +z \supseteq y == <<51, z, y>> +ASSUME \supseteq(4, 6) = <<51, 4, 6>> +z \approx y == <<52, z, y>> +ASSUME \approx(4, 6) = <<52, 4, 6>> +z \cong y == <<53, z, y>> +ASSUME \cong(4, 6) = <<53, 4, 6>> +z \sqsubset y == <<54, z, y>> +ASSUME \sqsubset(4, 6) = <<54, 4, 6>> +z \sqsupset y == <<55, z, y>> +ASSUME \sqsupset(4, 6) = <<55, 4, 6>> +z \sqsubseteq y == <<56, z, y>> +ASSUME \sqsubseteq(4, 6) = <<56, 4, 6>> +z \sqsupseteq y == <<57, z, y>> +ASSUME \sqsupseteq(4, 6) = <<57, 4, 6>> +z \doteq y == <<58, z, y>> +ASSUME \doteq(4, 6) = <<58, 4, 6>> +z \propto y == <<59, z, y>> +ASSUME \propto(4, 6) = <<59, 4, 6>> + +z ^+ == <<60, z>> +ASSUME ^+(4) = <<60, 4>> +z ^* == <<61, z>> +ASSUME ^*(4) = <<61, 4>> +z ^# == <<62, z>> +ASSUME ^#(4) = <<62, 4>> +-. z == <<63, z>> +ASSUME -.(4) = <<63, 4>> + +z \leq y == <<64, z, y>> +ASSUME \leq(4, 6) = <<64, 4, 6>> +ASSUME <=(4, 6) = <<64, 4, 6>> +ASSUME =<(4, 6) = <<64, 4, 6>> + +z \geq y == <<65, z, y>> +ASSUME \geq(4, 6) = <<65, 4, 6>> +ASSUME >=(4, 6) = <<65, 4, 6>> + +z \oplus y == <<66, z, y>> +ASSUME \oplus(4, 6) = <<66, 4, 6>> +ASSUME (+) (4, 6) = <<66, 4, 6>> + +z \ominus y == <<67, z, y>> +ASSUME \ominus(4, 6) = <<67, 4, 6>> +ASSUME (-) (4, 6) = <<67, 4, 6>> + + +z \odot y == <<68, z, y>> +ASSUME \odot(4, 6) = <<68, 4, 6>> +ASSUME (.) (4, 6) = <<68, 4, 6>> + +z \otimes y == <<69, z, y>> +ASSUME \otimes(4, 6) = <<69, 4, 6>> +ASSUME (\X) (4, 6) = <<69, 4, 6>> + +z \oslash y == <<70, z, y>> +ASSUME \oslash(4, 6) = <<70, 4, 6>> +ASSUME (/) (4, 6) = <<70, 4, 6>> + +z \circ y == <<71, z, y>> +ASSUME \circ(4, 6) = <<71, 4, 6>> +ASSUME \o (4, 6) = <<71, 4, 6>> + +ASSUME \in(1, {1}) + +ASSUME \cup({1}, {2}) = {1,2} + +============================================================================= diff --git a/tlatools/test-model/suite/test205.cfg b/tlatools/test-model/suite/test205.cfg new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/tlatools/test-model/suite/test205.cfg @@ -0,0 +1 @@ + diff --git a/tlatools/test-model/suite/test205.tla b/tlatools/test-model/suite/test205.tla new file mode 100644 index 0000000000000000000000000000000000000000..c057b6abba236aa393e4df701a08f4fee26edac3 --- /dev/null +++ b/tlatools/test-model/suite/test205.tla @@ -0,0 +1,175 @@ +--------------- MODULE test205 ------------- + +(***************************************************************************) +(* Test use of notation R!+(a, b) for instantiated *-fix operators. *) +(***************************************************************************) + +F == INSTANCE test204 + +\*z - y == <<1, z, y>> +ASSUME F!-(4, 6) = <<1, 4, 6>> +\*z + y == <<2, z, y>> +ASSUME F!+(4, 6) = <<2, 4, 6>> +\*z * y == <<3, z, y>> +ASSUME F!*(4, 6) = <<3, 4, 6>> +\*z / y == <<4, z, y>> +ASSUME F!/(4, 6) = <<4, 4, 6>> +\*z ^ y == <<5, z, y>> +ASSUME F!^(4, 6) = <<5, 4, 6>> +\*z < y == <<6, z, y>> +ASSUME F!<(4, 6) = <<6, 4, 6>> +\*z > y == <<7, z, y>> +ASSUME F!>(4, 6) = <<7, 4, 6>> +\*z .. y == <<8, z, y>> +ASSUME F!..(4, 6) = <<8, 4, 6>> +\*z ... y == <<9, z, y>> +ASSUME F!...(4, 6) = <<9, 4, 6>> +\*z | y == <<10, z, y>> +ASSUME F!|(4, 6) = <<10, 4, 6>> +\*z || y == <<11, z, y>> +ASSUME F!||(4, 6) = <<11, 4, 6>> +\*z & y == <<12, z, y>> +ASSUME F!&(4, 6) = <<12, 4, 6>> +\*z && y == <<13, z, y>> +ASSUME F!&&(4, 6) = <<13, 4, 6>> +\*z $ y == <<14, z, y>> +ASSUME F!$(4, 6) = <<14, 4, 6>> +\*z $$ y == <<15, z, y>> +ASSUME F!$$(4, 6) = <<15, 4, 6>> +\*z ?? y == <<16, z, y>> +ASSUME F!??(4, 6) = <<16, 4, 6>> +\*z %% y == <<17, z, y>> +ASSUME F!%%(4, 6) = <<17, 4, 6>> +\*z ## y == <<18, z, y>> +ASSUME F!##(4, 6) = <<18, 4, 6>> +\*z ++ y == <<19, z, y>> +ASSUME F!++(4, 6) = <<19, 4, 6>> +\*z -- y == <<20, z, y>> +ASSUME F!--(4, 6) = <<20, 4, 6>> +\*z ** y == <<21, z, y>> +ASSUME F!**(4, 6) = <<21, 4, 6>> +\*z // y == <<22, z, y>> +ASSUME F!//(4, 6) = <<22, 4, 6>> +\*z ^^ y == <<23, z, y>> +ASSUME F!^^(4, 6) = <<23, 4, 6>> +\*z @@ y == <<23, z, y>> +ASSUME F!@@(4, 6) = <<23, 4, 6>> +\*z !! y == <<24, z, y>> +ASSUME F ! !!(4, 6) = <<24, 4, 6>> +\*z |- y == <<25, z, y>> +ASSUME F!|-(4, 6) = <<25, 4, 6>> +\*z |= y == <<26, z, y>> +ASSUME F!|=(4, 6) = <<26, 4, 6>> +\*z -| y == <<27, z, y>> +ASSUME F!-|(4, 6) = <<27, 4, 6>> +\*z =| y == <<28, z, y>> +ASSUME F!=|(4, 6) = <<28, 4, 6>> +\*z <: y == <<29, z, y>> +ASSUME F!<:(4, 6) = <<29, 4, 6>> +\*z :> y == <<29, z, y>> +ASSUME F!:>(4, 6) = <<29, 4, 6>> +\*z := y == <<30, z, y>> +ASSUME F!:=(4, 6) = <<30, 4, 6>> +\*z ::= y == <<31, z, y>> +ASSUME F!::=(4, 6) = <<31, 4, 6>> +\*z \uplus y == <<32, z, y>> +ASSUME F!\uplus(4, 6) = <<32, 4, 6>> +\*z \sqcap y == <<33, z, y>> +ASSUME F!\sqcap(4, 6) = <<33, 4, 6>> +\*z \sqcup y == <<34, z, y>> +ASSUME F!\sqcup(4, 6) = <<34, 4, 6>> +\*z \div y == <<35, z, y>> +ASSUME F!\div(4, 6) = <<35, 4, 6>> +\*z \wr y == <<36, z, y>> +ASSUME F!\wr(4, 6) = <<36, 4, 6>> +\*z \star y == <<37, z, y>> +ASSUME F!\star(4, 6) = <<37, 4, 6>> +\*z \bigcirc y == <<38, z, y>> +ASSUME F!\bigcirc(4, 6) = <<38, 4, 6>> +\*z \bullet y == <<39, z, y>> +ASSUME F!\bullet(4, 6) = <<39, 4, 6>> +\*z \prec y == <<40, z, y>> +ASSUME F!\prec(4, 6) = <<40, 4, 6>> +\*z \succ y == <<41, z, y>> +ASSUME F!\succ(4, 6) = <<41, 4, 6>> +\*z \preceq y == <<42, z, y>> +ASSUME F!\preceq(4, 6) = <<42, 4, 6>> +\*z \succeq y == <<43, z, y>> +ASSUME F!\succeq(4, 6) = <<43, 4, 6>> +\*z \sim y == <<44, z, y>> +ASSUME F!\sim(4, 6) = <<44, 4, 6>> +\*z \simeq y == <<45, z, y>> +ASSUME F!\simeq(4, 6) = <<45, 4, 6>> +\*z \ll y == <<46, z, y>> +ASSUME F!\ll(4, 6) = <<46, 4, 6>> +\*z \gg y == <<47, z, y>> +ASSUME F!\gg(4, 6) = <<47, 4, 6>> +\*z \asymp y == <<48, z, y>> +ASSUME F!\asymp(4, 6) = <<48, 4, 6>> +\*z \subset y == <<49, z, y>> +ASSUME F!\subset(4, 6) = <<49, 4, 6>> +\*z \supset y == <<50, z, y>> +ASSUME F!\supset(4, 6) = <<50, 4, 6>> +\*z \supseteq y == <<51, z, y>> +ASSUME F!\supseteq(4, 6) = <<51, 4, 6>> +\*z \approx y == <<52, z, y>> +ASSUME F!\approx(4, 6) = <<52, 4, 6>> +\*z \cong y == <<53, z, y>> +ASSUME F!\cong(4, 6) = <<53, 4, 6>> +\*z \sqsubset y == <<54, z, y>> +ASSUME F!\sqsubset(4, 6) = <<54, 4, 6>> +\*z \sqsupset y == <<55, z, y>> +ASSUME F!\sqsupset(4, 6) = <<55, 4, 6>> +\*z \sqsubseteq y == <<56, z, y>> +ASSUME F!\sqsubseteq(4, 6) = <<56, 4, 6>> +\*z \sqsupseteq y == <<57, z, y>> +ASSUME F!\sqsupseteq(4, 6) = <<57, 4, 6>> +\*z \doteq y == <<58, z, y>> +ASSUME F!\doteq(4, 6) = <<58, 4, 6>> +\*z \propto y == <<59, z, y>> +ASSUME F!\propto(4, 6) = <<59, 4, 6>> + +\*z ^+ == <<60, z>> +ASSUME F!^+(4) = <<60, 4>> +\*z ^* == <<61, z>> +ASSUME F!^*(4) = <<61, 4>> +\*z ^# == <<62, z>> +ASSUME F!^#(4) = <<62, 4>> +\*-. z == <<63, z>> +ASSUME F!-.(4) = <<63, 4>> + +\*z \leq y == <<64, z, y>> +ASSUME F!\leq(4, 6) = <<64, 4, 6>> +ASSUME F!<=(4, 6) = <<64, 4, 6>> +ASSUME F!=<(4, 6) = <<64, 4, 6>> + +\*z \geq y == <<65, z, y>> +ASSUME F!\geq(4, 6) = <<65, 4, 6>> +ASSUME F!>=(4, 6) = <<65, 4, 6>> + +\*z \oplus y == <<66, z, y>> +ASSUME F!\oplus(4, 6) = <<66, 4, 6>> +ASSUME F!(+) (4, 6) = <<66, 4, 6>> + +\*z \ominus y == <<67, z, y>> +ASSUME F!\ominus(4, 6) = <<67, 4, 6>> +ASSUME F!(-) (4, 6) = <<67, 4, 6>> + + +\*z \odot y == <<68, z, y>> +ASSUME F!\odot(4, 6) = <<68, 4, 6>> +ASSUME F!(.) (4, 6) = <<68, 4, 6>> + +\*z \otimes y == <<69, z, y>> +ASSUME F!\otimes(4, 6) = <<69, 4, 6>> +ASSUME F!(\X) (4, 6) = <<69, 4, 6>> + +\*z \oslash y == <<70, z, y>> +ASSUME F!\oslash(4, 6) = <<70, 4, 6>> +ASSUME F!(/) (4, 6) = <<70, 4, 6>> + +\*z \circ y == <<71, z, y>> +ASSUME F!\circ(4, 6) = <<71, 4, 6>> +ASSUME F!\o (4, 6) = <<71, 4, 6>> + +============================================================================= diff --git a/tlatools/test-model/suite/test206.cfg b/tlatools/test-model/suite/test206.cfg new file mode 100644 index 0000000000000000000000000000000000000000..8f848aa991a48bf066529f284b3990008f6100d4 --- /dev/null +++ b/tlatools/test-model/suite/test206.cfg @@ -0,0 +1 @@ +CONSTANT C = 4 \ No newline at end of file diff --git a/tlatools/test-model/suite/test206.tla b/tlatools/test-model/suite/test206.tla new file mode 100644 index 0000000000000000000000000000000000000000..16f9e534db5b450cdc3242ad42bf6f9baa6848fb --- /dev/null +++ b/tlatools/test-model/suite/test206.tla @@ -0,0 +1,238 @@ +------------------------------- MODULE test206 ------------------------------ +\* Test of labels and their use in subexpression selectors. + +EXTENDS TLC, Integers + +CONSTANT C \* Test with C = 4 + +Op(Arg(_), p) == + \A x \in {1,2}, <<y, z>> \in {<<3, 4>>} : + lab(x,y,z) :: LET a ++ b == + Arg(a) + b + x + 2*y + 3*z + IN (Arg(1) + Arg(p) + C + + 2*x + 4*y + 6*z) = + 1 ++ ( label2 :: p ++ C) +\* IN /\label2:: PrintT("FOO") +\* /\ PrintT(Arg(1)) +\* /\ PrintT("FOO2") +\* /\ PrintT(<<p, Arg(p)>>) +\* /\ PrintT("FOO3") +\* /\ PrintT(<<p, C>>) +\* /\ PrintT("FOO3a") +\* /\ PrintT(1++2) +\* /\ PrintT(p++C) +\* /\ PrintT("FOO4") +\* /\ PrintT(<<x, y, z>>) + +Foo(u) == 2*u + +ASSUME Foo(5) = Foo(5)!: + +THEOREM Thm == ASSUME FALSE + PROVE lab:: C > 0 + +THEOREM Thm2 == lab3 :: TRUE + + +ASSUME Foo(25) = 50 + +Bar == Op(Foo, 9)!lab(1,2,3) + +ASSUME Op(Foo, 9) + +ASSUME Thm2!lab3 + +ASSUME Bar + +ASSUME Op(Foo, 9)!lab(1,2,3)!label2 = Op(Foo, 9)!lab(1,2,3)!:!++(9, 4) + +Op3(A(_,_,_), a1, a2, a3) == A(a1, a2, a3) + +Op1(a) == \A x \in {} : lab(x) :: IF TRUE + THEN LET Op1a(c) == 4 * c + a*x + IN a + Op1a(x) + ELSE 1 +SOp1(a) == a +OOp1(A(_), a) == A(a) +ASSUME OOp1(SOp1, 2) = 2 + +SOp1a(a) == {x \in {1,2} : a = x} +OOp2(A(_,_), a, b) == A(a, b) +ASSUME OOp2(SOp1a!@, 2, 2) = TRUE + +SOp1b(a) == {x + a : x \in {1,2}} +ASSUME OOp2(SOp1b!@, 2, 3) = 5 + +SOp1c(a) == {(LET c == a * x IN x + a) : x \in {1,2}} +ASSUME OOp2(SOp1c!@!c, 2, 3) = 2 * 3 + +SOp1d(a) == {(LET c(y) == (a * x) + y IN x + a) : x \in {1,2}} +OOp3(A(_,_,_), a, b, c) == A(a, b, c) +ASSUME OOp3(SOp1d!@!c, 2, 3, 4) = 2 * 3 + 4 + +OOp4(A(_,_,_,_), a, b, c, d) == A(a, b, c, d) + + +\* Foo1 == Op1(2)!(3)!2!1 +ASSUME Op1(2)!(3)!2!1 = 2 + 4*3 + 6 +ASSUME Op1(2)!(3)!2!Op1a(4) = 16 + 6 +ASSUME Op3(Op1!@!2!Op1a, 2, 3, 4) = 22 + +COp(q, p) == + CHOOSE x : lab(x) :: LET a ++ b == {a, b, q, p, x} + IN <<q, 99 ++ p >> + + +ASSUME COp(1, 2)!(3)!++(4, 5) = {2,1, 3, 4, 5} + +ASSUME COp(1, 2)!(3) = <<1, {99, 1, 2, 3} >> +ASSUME COp(1, 2)!(3)!1 = <<1, {99, 1, 2, 3} >> +ASSUME COp(1, 2)!(3)!1!>> = {99, 1, 2, 3} + +OOp5(A(_, _, _, _, _), a1, a2, a3, a4, a5) == A(a1, a2, a3, a4, a5) + +ASSUME OOp5(COp!@!++, 1, 2, 3, 4, 5) = {2,1, 3, 4, 5} +ASSUME COp(1, 2)!lab(3)!:!++(4, 5) = {2,1, 3, 4, 5} + +Inst == INSTANCE test206a WITH A5 <- COp!@!++ +ASSUME Inst!Foo(1, 2, 3, 4, 5) = {2,1, 3, 4, 5} +ASSUME Inst!Bar(3)!>> = 3 +ASSUME Inst!Bar2(3)!>> = 3 +ASSUME Inst!Bar3!>> = 2 +ASSUME Inst!Bar4(1)!(2)!<< = 2 +ASSUME Inst!Bar4(1)!(2)!>> = 1 +ASSUME Inst!Bar4(1)!lab(2)!<< = 2 +ASSUME Inst!Bar4(1)!lab(2)!>> = 1 +ASSUME OOp2(Inst!Bar4!@!<<, 1, 2) = 2 +ASSUME OOp2(Inst!Bar4!@!>>, 1, 2) = 1 +ASSUME OOp2(Inst!Bar4!lab!<<, 1, 2) = 2 +ASSUME OOp2(Inst!Bar4!lab!>>, 1, 2) = 1 +ASSUME Inst!Bar5(1)!lab(2)!:!++(3, 4) = <<1, 3, 4, 2>> +ASSUME Inst!Bar5(1)!(2)!++(3, 4) = <<1, 3, 4, 2>> +ASSUME OOp4(Inst!Bar5!lab!:!++, 1, 2, 3, 4) = <<1, 3, 4, 2>> + +Inst2(A5(_, _, _, _, _)) == INSTANCE test206a +ASSUME Inst2(COp!@!++)!Foo(1, 2, 3, 4, 5) = {2,1, 3, 4, 5} + + +COp2(q, p) == + CHOOSE x : lab(x) :: LET a ++ b == {lab(y):: y + a + b : y \in {1}} + IN <<q, 99 ++ p >> + +ASSUME COp2(1,2)!lab(3)!:!++(9,10)!lab(11) = 30 +ASSUME COp2(1,2)!(3)!++(9,10)!lab(11) = 30 +ASSUME COp2(1,2)!lab(3)!:!++(9,10)!(11) = 30 +OOp6(A(_, _, _, _, _, _), a1, a2, a3, a4, a5, a6) == A(a1, a2, a3, a4, a5, a6) +ASSUME OOp6(COp2!lab!:!++!lab, 1, 2, 3, 9, 10, 11) = 30 +ASSUME OOp6(COp2!@!++!lab, 1, 2, 3, 9, 10, 11) = 30 +ASSUME OOp6(COp2!lab!:!++!@, 1, 2, 3, 9, 10, 11) = 30 + +\* Let's test all the different kinds of operators + +Def1 == {1, 2, 3, 4} +ASSUME Def1!2 = 2 + +Def2 == {x \in {1, 2, 3} : x > 17} +ASSUME Def2!1 = {1, 2, 3} +ASSUME Def2!(14)!<< = 14 + +Def3 == {<<x, y>> : x \in {1}, y \in {2}} +ASSUME Def3!2!1 = 2 +ASSUME Def3!(1, 2)!<< = 1 + +Def4 == SUBSET UNION {{1}, {2}} +ASSUME Def4!<<!<<!2 = {2} + +Def5 == [i \in 1..10 |-> i+1][3] +ASSUME Def5!>> = 3 +ASSUME Def5!<<[4] = 5 +ASSUME Def5!<<!1!>> = 10 + +Def6 == [[i \in 1..10 |-> i+1] EXCEPT ![2] = 1, ![3] = 2] +ASSUME Def6!1[3] = 4 +\* The following two subexpression expressions are now +\* considered to be illegal because they could contain +\* a "@". +\* ASSUME Def6!2=1 +\* ASSUME Def6!3=2 + +Def7 == 3.a +ASSUME Def7!1 = 3 +ASSUME Def7!2 = "a" + +Def8 == [a |-> 1, b |-> 2] +ASSUME Def8!2 = 2 + +Def9 == [a : 1, b : 2] +ASSUME Def9!2 = 2 + +Def10 == <<1, 2, 3>> +ASSUME Def10!2 = 2 + +Def11 == {1} \X {2} \X {3} +ASSUME Def11!3!1 = 3 + +Def12 == IF 2=3 THEN 2 ELSE 3 +ASSUME /\ Def12!1!<< = 2 + /\ Def12!2 = 2 + /\ Def12!3 = 3 + +Def13 == \A x : CASE x=1 -> 1 + [] x=2 -> 2 + [] OTHER -> 3 +ASSUME /\ Def13!(42)!1!<<!<< = 42 + /\ Def13!(42)!1!>> = 1 + /\ Def13!(42)!2!>> = 2 + /\ Def13!(42)!3!>> = 3 + +Def14 == \A x \in {1} : x = 1 +ASSUME /\ Def14!(2)!<< = 2 + /\ Def14!1!1 = 1 + +Def15 == \A x \in {1}, y \in {2} : (x = 1) /\ (y = 2) +ASSUME /\ Def15!(2,3)!<<!<< = 2 + /\ Def15!(2,3)!>>!<< = 3 + /\ Def15!2!<< = 2 + +Def16 == \E x \in {1}, <<y, z>> \in {<<2,3>>} : /\ ((x = 1)) + /\ y = 2 + /\ (z=3) +ASSUME /\ Def16!(2,3,4)!1!1 = 2 + /\ Def16!(2,3,4)!2!1 = 3 + /\ Def16!2!1!<< = 2 + +Def17 == \E x , y, z : /\ ((x = 1)) + /\ y = 2 + /\ (z=3) +ASSUME /\ Def17!(2,3,4)!1!1 = 2 + /\ Def17!(2,3,4)!2!1 = 3 + +Def18 == CHOOSE x : (x=1) +ASSUME Def18!(2)!<< = 2 + +Def19 == CHOOSE x \in {42}: (x=1) +ASSUME /\ Def19!(2)!<< = 2 + /\ Def19!1!1 = 42 + +Def20 == [12]_(23) +ASSUME /\ Def20!1 = 12 + /\ Def20!>> = 23 + +Def21 == <<12>>_(23) +ASSUME /\ Def21!1 = 12 + /\ Def21!>> = 23 + +Def22 == WF_(12)(23) +ASSUME /\ Def22!1 = 12 + /\ Def22!>> = 23 + +Def23 == \EE x , y, z : /\ ((x = 1)) + /\ y = 2 + /\ (z=3) +ASSUME /\ Def23!(2,3,4)!1!1 = 2 + /\ Def23!(2,3,4)!2!1 = 3 + + +============================================================================= + +last modified on Tue 9 March 2010 at 11:49:54 PST by lamport \ No newline at end of file diff --git a/tlatools/test-model/suite/test206a.cfg b/tlatools/test-model/suite/test206a.cfg new file mode 100644 index 0000000000000000000000000000000000000000..8f848aa991a48bf066529f284b3990008f6100d4 --- /dev/null +++ b/tlatools/test-model/suite/test206a.cfg @@ -0,0 +1 @@ +CONSTANT C = 4 \ No newline at end of file diff --git a/tlatools/test-model/suite/test206a.tla b/tlatools/test-model/suite/test206a.tla new file mode 100644 index 0000000000000000000000000000000000000000..add74af1a0e13af6987c95283dacbef9eab2d75c --- /dev/null +++ b/tlatools/test-model/suite/test206a.tla @@ -0,0 +1,12 @@ +----------------------------- MODULE test206a ----------------------------- + +CONSTANT A5(_, _, _, _, _) + +Foo(a1, a2, a3, a4, a5) == A5(a1, a2, a3, a4, a5) +Bar(a) == <<1, a>> +Bar2(a) == <<1, lab::a>> +Bar3 == <<1, 2>> +Bar4(a) == {x \in {1} : lab(x) :: <<x, a>>} +Bar5(a) == {x \in {1} : lab(x) :: LET b ++ c == <<a, b, c, x>> + IN 42 } +============================================================================= \ No newline at end of file diff --git a/tlatools/test-model/suite/test207.cfg b/tlatools/test-model/suite/test207.cfg new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/tlatools/test-model/suite/test207.cfg @@ -0,0 +1 @@ + diff --git a/tlatools/test-model/suite/test207.tla b/tlatools/test-model/suite/test207.tla new file mode 100644 index 0000000000000000000000000000000000000000..c8b6cd6974c96fd1d3289c707bca5039404a12f5 --- /dev/null +++ b/tlatools/test-model/suite/test207.tla @@ -0,0 +1,38 @@ +\* SANY2 test. +\* Test of scope of NEW declarations. They should be restricted +\* to the statement's proof, except for SUFFICES which should be +\* visible to the rest of the current proof. + +-------------------- MODULE test207 ---------------- +EXTENDS TLC +a + b == <<a, b>> +THEOREM Thm == ASSUME NEW J PROVE lab :: TRUE +<1>1 ASSUME NEW I + PROVE Thm!lab + <2>1 I = J + <2>2 QED +<1> DEFINE I == 1 +<1>2 ASSUME NEW K + PROVE TRUE +<1>3 SUFFICES ASSUME NEW K + PROVE TRUE + <2>1. \A K : TRUE + <2>2. SUFFICES ASSUME NEW L + PROVE TRUE + <2>3. QED +<1>3a. DEFINE L == 0 +<1>4. K = 0 \* 12 June 14 Changed from <*>4 which is now illegal. + <2>1. DEFINE F == 7 + <2>4. TRUE \* 12 June 14 Changed from <*>4 which is now illegal. + <2>2. QED + BY <2>4 +<1>4a. DEFINE F == 42 +<1>5 QED + BY <1>4 +J == 0 +I == 0 +F == 1 + +ASSUME PrintT("SANY2 Test") + +============================= diff --git a/tlatools/test-model/suite/test208.cfg b/tlatools/test-model/suite/test208.cfg new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/tlatools/test-model/suite/test208.cfg @@ -0,0 +1 @@ + diff --git a/tlatools/test-model/suite/test208.tla b/tlatools/test-model/suite/test208.tla new file mode 100644 index 0000000000000000000000000000000000000000..8f938c37b8b8d648da0e14862f7362d0fc60bf0a --- /dev/null +++ b/tlatools/test-model/suite/test208.tla @@ -0,0 +1,203 @@ +\* SANY2 test. +\* Test to make sure that all keywords are useable as record fields. + +-------------------- MODULE test208 ---------------- +bar == 17 + +LEMMA TRUE +PROPOSITION TRUE +ASSUMPTION TRUE + +Foo == +/\ bar.NEW \cup [bar EXCEPT !.NEW = 0] +/\ bar.SF_ \cup [bar EXCEPT !.SF_ = 0] +/\ bar.WF_ \cup [bar EXCEPT !.WF_ = 0] +/\ bar.THEN \cup [bar EXCEPT !.THEN = 0] +/\ bar.BY \cup [bar EXCEPT !.BY = 0] \* 68 XXXXXX +/\ bar.HAVE \cup [bar EXCEPT !.HAVE = 0] +/\ bar.QED \cup [bar EXCEPT !.QED = 0] +/\ bar.TAKE \cup [bar EXCEPT !.TAKE = 0] + +/\ bar.DEF \cup [bar EXCEPT !.DEF = 0] +/\ bar.HIDE \cup [bar EXCEPT !.HIDE = 0] +/\ bar.RECURSIVE \cup [bar EXCEPT !.RECURSIVE = 0] \* XXXXX 82 +/\ bar.USE \cup [bar EXCEPT !.USE = 0] + +/\ bar.DEFINE \cup [bar EXCEPT !.DEFINE = 0] +/\ bar.PROOF \cup [bar EXCEPT !.PROOF = 0] +/\ bar.WITNESS \cup [bar EXCEPT !.WITNESS = 0] +/\ bar.PICK \cup [bar EXCEPT !.PICK = 0] + +/\ bar.DEFS \cup [bar EXCEPT !.DEFS = 0] +/\ bar.PROVE \cup [bar EXCEPT !.PROVE = 0] +/\ bar.SUFFICES \cup [bar EXCEPT !.SUFFICES = 0] +/\ bar.NEW \cup [bar EXCEPT !.NEW = 0] + +/\ bar.LAMBDA \cup [bar EXCEPT !.LAMBDA = 0] +/\ bar.LEMMA \cup [bar EXCEPT !.LEMMA = 0] +/\ bar.PROPOSITION \cup [bar EXCEPT !.PROPOSITION = 0] +/\ bar.STATE \cup [bar EXCEPT !.STATE = 0] +/\ bar.ACTION \cup [bar EXCEPT !.ACTION = 0] +/\ bar.TEMPORAL \cup [bar EXCEPT !.TEMPORAL = 0] +/\ bar.VARIABLE \cup [bar EXCEPT !.VARIABLE = 0] + +/\ bar.OBVIOUS \cup [bar EXCEPT !.OBVIOUS = 0] +/\ bar.OMITTED \cup [bar EXCEPT !.OMITTED = 0] + +/\ bar.ASSUME \cup [bar EXCEPT !.ASSUME = 0] +/\ bar.ELSE \cup [bar EXCEPT !.ELSE = 0] +/\ bar.LOCAL \cup [bar EXCEPT !.LOCAL = 0] +/\ bar.UNION \cup [bar EXCEPT !.UNION = 0] + +/\ bar.ASSUMPTION \cup [bar EXCEPT !.ASSUMPTION = 0] +/\ bar.ENABLED \cup [bar EXCEPT !.ENABLED = 0] +/\ bar.MODULE \cup [bar EXCEPT !.MODULE = 0] +/\ bar.VARIABLE \cup [bar EXCEPT !.VARIABLE = 0] + +/\ bar.AXIOM \cup [bar EXCEPT !.AXIOM = 0] +/\ bar.EXCEPT \cup [bar EXCEPT !.EXCEPT = 0] +/\ bar.OTHER \cup [bar EXCEPT !.OTHER = 0] +/\ bar.VARIABLES \cup [bar EXCEPT !.VARIABLES = 0] + +/\ bar.CASE \cup [bar EXCEPT !.CASE = 0] +/\ bar.EXTENDS \cup [bar EXCEPT !.EXTENDS = 0] +/\ bar.SF_ \cup [bar EXCEPT !.SF_ = 0] +/\ bar.WF_ \cup [bar EXCEPT !.WF_ = 0] + +/\ bar.CHOOSE \cup [bar EXCEPT !.CHOOSE = 0] +/\ bar.IF \cup [bar EXCEPT !.IF = 0] \* XXXXX +/\ bar.SUBSET \cup [bar EXCEPT !.SUBSET = 0] +/\ bar.WITH \cup [bar EXCEPT !.WITH = 0] + +/\ bar.CONSTANT \cup [bar EXCEPT !.CONSTANT = 0] +/\ bar.IN \cup [bar EXCEPT !.IN = 0] +/\ bar.THEN \cup [bar EXCEPT !.THEN = 0] \* XXXXX + +/\ bar.CONSTANTS \cup [bar EXCEPT !.CONSTANTS = 0] +/\ bar.INSTANCE \cup [bar EXCEPT !.INSTANCE = 0] +/\ bar.THEOREM \cup [bar EXCEPT !.THEOREM = 0] \* XXXXX + +/\ bar.DOMAIN \cup [bar EXCEPT !.DOMAIN = 0] +/\ bar.LET \cup [bar EXCEPT !.LET = 0] +/\ bar.UNCHANGED \cup [bar EXCEPT !.UNCHANGED = 0] +================================ + + + +THEOREM Bar == ASSUME NEW y, ASSUME TRUE PROVE FALSE + PROVE TRUE + + +THEOREM TRUE + <1>1. TAKE x \in << a:: {}, {}>> + <1>2. <1>1!a + <1>3. <1>1!1 + <1> QED + + +================================================== +a+b == <<a, b>> +THEOREM TRUE +<1>2 TRUE + <33>1 HAVE ~ TRUE + <33>2 TAKE Id, Id1 + <33>3 TAKE Id4 \in {1}, <<Id2, Id3>> \in {2} + <33>123 FooBar == <<Id, Id1, Id4, Id2, Id3>> + <33>4 WITNESS {1} + <33>5 WITNESS {1} \in {2}, {3} + <33>-... PICK x : ~ FALSE + <+>*. PICK y \in {1} : ~ FALSE + <*>* QED + <33>7 PICK y \in {1} : <33>5!1!1 + <33>3!1 + PROOF <55>1... TRUE + <*>2.... USE <33>5, <33>7!1!1, <33>7!(42)!<< DEF + + <55>2 QED + <33>7a Foo == << <33>7!(1)!<< , <33>7!>> >> + <33>8 PICK z \in {1}, <<y1, z1>> \in {2} : ~ FALSE + <33>9 PICK w, u : <<1, 2, 3>> + <34>* DEFINE A == <<x, y, z, y1, z1, w, u>> + <34>*. B == A!1 + <34> QED + <33>44 DEFINE A == <<x, y, z, y1, z1, w, u>> + <33>*. B == <33>9!(1,2)!3 + <33>. BBC(Op(_,_)) == Op(1,2) + DD == BBC(<33>9!@) + <33>22 ASSUME TRUE PROVE FALSE + <33> BBB == \A xxxx \in {} : TRUE + <33>14. INSTANCE Test1 + <*> AA == INSTANCE Test1 + <33> B3 == AA!Foo3!Def2 + <*>a ASSUME FALSE PROVE TRUE + <33>b CASE TRUE + <33>c SUFFICES TRUE + <33>. SUFFICES ASSUME FALSE PROVE TRUE + <33>2a. QED +<1>14 HAVE <<1, 2, 3>> +<1>14a havedef == <1>14!1!3 +<*> TAKE Id7 \in {1} +<*> WITNESS {1} \in {2} +<*> PICK x2 \in {1}, y2 \in {2} : ~ FALSE + <+> PICK x3, y3 : ~ FALSE + <*>*. DEFINE A2 == 1 + <*>2 B2 == 17 + <*> ASSUME FALSE PROVE TRUE + <*> CASE TRUE + <*> TRUE + <*> SUFFICES ASSUME FALSE PROVE TRUE + <2> QED +<1>... DEFINE A == 1 +<1> B == 17 +<1> INSTANCE Test1 +<1> AA == INSTANCE Test2 WITH B <- 1 +<1>3. QED + +=============================== + +\* Foo == \A x , y, z : TRUE +\* Bar == \A x \in {}, y, z \in {}, <<u, v>> \in {} : TRUE +\* Inst(a) == INSTANCE Test1 +\* Foo == Inst(7)!Bar1 +THEOREM Thm2 == TRUE + <1>2 QED + PROOF USE <1>2 + QED +==================== + PROOF +\* DEFINE Def1 == TRUE + USE Thm2!: \* DEF Thm2 , Inst!Bar2 +\* Def2 == TRUE + QED +\* PROOF USE Def1 DEFS Def2 +\* QED +================================= +THEOREM Thm3 == FALSE + PROOF DEFINE A == B + PROVE /\ TRUE + /\ 1=1 + BY Thm2 + QED +========================================================== +THEOREM 1 + PROOF QED + PROOF OBVIOUS +================================================= +============================================================================= + +BY [.] +USE DEF FALSE +HIDE +<1>abc FALSE + BY MODULE M DEF MODULE M, 2 [.] +A == B +DEFINE F == INSTANCE M +A[x \in S] == B +INSTANCE M +<1>2. QED +(******** + PROOF ASSUME TRUE PROVE FALSE + <1>1. TRUE + <1>2. QED + HAVE TRUE + QED +********) +============================================================================= diff --git a/tlatools/test-model/suite/test209.cfg b/tlatools/test-model/suite/test209.cfg new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/tlatools/test-model/suite/test209.cfg @@ -0,0 +1 @@ + diff --git a/tlatools/test-model/suite/test209.tla b/tlatools/test-model/suite/test209.tla new file mode 100644 index 0000000000000000000000000000000000000000..70d8ad7998d6035eaca0e392f88c6af640c93c9e --- /dev/null +++ b/tlatools/test-model/suite/test209.tla @@ -0,0 +1,293 @@ +\* Test of positional subexpression naming. +-------------------- MODULE test209 ---------------- +EXTENDS Naturals, Sequences + +xx == <<11, <<22,33>>, 33>> +F(a) == /\ TRUE + /\ TRUE + /\ Len(xx[a]) > 0 + /\ TRUE + +ASSUME F(2)!3 = (Len(xx[2]) > 0) +ASSUME F(2)!3!1 = Len(xx[2]) +ASSUME F(2)!3!1!1 = xx[2] +ASSUME F(2)!3!1!1!2 = 2 +ASSUME F(2)!3!1!1!1 = xx +ASSUME F(2)!3!<< = Len(xx[2]) +ASSUME F(2)!3!<<!<< = xx[2] +ASSUME F(2)!3!<<!<<!>> = 2 +ASSUME F(2)!3!<<!<<!<< = xx + +G == [xx EXCEPT ![1] = 42, ![2] = 55] +H == xx.foo +I == [fld1 |-> 1, fld2 |->2, fld3 |-> 3] +J == [fld1 : 1, fld2 :2, fld3 : 3] + +ASSUME G!1 = xx +\* Selection of EXCEPT fields disabled because of "@" problem. +\* ASSUME G!2 = 42 +\* ASSUME G!3 = 55 +ASSUME H!1 = xx +ASSUME H!2 = "foo" +\* ASSUME I!3 = "fld3" +ASSUME I!3 = 3 + +\* ASSUME J!3 = "fld3" +ASSUME J!3 = 3 + +K == IF 41 THEN 42 ELSE 43 +ASSUME K!1 = 41 +ASSUME K!2 = 42 +ASSUME K!3 = 43 + +L == CASE 1 -> 11 [] + 2 -> 22 [] + OTHER -> 33 + +ASSUME L!1!1 = 1 +ASSUME L!1!2 = 11 +ASSUME L!2!1 = 2 +ASSUME L!2!2 = 22 +ASSUME L!3!2 = 33 + +M == LET Z == 23 IN Z +Z == 44 +ASSUME M!1 = 23 + +NN == 55 +N == [44]_NN +O == <<44>>_NN +P == WF_NN(44) +Q == SF_NN(44) + +ASSUME N!1 = 44 +ASSUME N!2 = 55 +ASSUME O!1 = 44 +ASSUME O!2 = 55 +ASSUME P!1 = 55 +ASSUME P!2 = 44 +ASSUME Q!1 = 55 +ASSUME Q!2 = 44 + +R == \E x \in {1}, y \in {2} : x + y > 2 +ASSUME R!1 = {1} +ASSUME R!2 = {2} +ASSUME R!(1,2)!<< = 3 + +S == [x, y \in {1, 2}, z \in {3} |-> x+y+z] +ASSUME S!1 = {1, 2} +ASSUME S!2 = {3} +ASSUME S!(2, 3, 4) = 2+3+4 + +T(Op(_)) == Op(42) +U == /\ LET Op(x) == x+1 + IN 44 + /\ TRUE +ASSUME T(U!1!Op) = 43 +ASSUME U!1!Op(42) = 43 +ASSUME U!1!Op(42)!: = 43 +V == \A x \in {} : 2*x +ASSUME T(V!@) = 84 +ASSUME T(LAMBDA x : 2*x) = 84 + +============================================================================= + +CONSTANT bar + +LEMMA TRUE +PROPOSITION TRUE +ASSUMPTION TRUE + +Foo == +/\ bar.NEW \cup [bar EXCEPT !.NEW = 0] +/\ bar.SF_ \cup [bar EXCEPT !.SF_ = 0] +/\ bar.WF_ \cup [bar EXCEPT !.WF_ = 0] +/\ bar.THEN \cup [bar EXCEPT !.THEN = 0] +/\ bar.BY \cup [bar EXCEPT !.BY = 0] \* 68 XXXXXX +/\ bar.HAVE \cup [bar EXCEPT !.HAVE = 0] +/\ bar.QED \cup [bar EXCEPT !.QED = 0] +/\ bar.TAKE \cup [bar EXCEPT !.TAKE = 0] + +/\ bar.DEF \cup [bar EXCEPT !.DEF = 0] +/\ bar.HIDE \cup [bar EXCEPT !.HIDE = 0] +/\ bar.RECURSIVE \cup [bar EXCEPT !.RECURSIVE = 0] \* XXXXX 82 +/\ bar.USE \cup [bar EXCEPT !.USE = 0] + +/\ bar.DEFINE \cup [bar EXCEPT !.DEFINE = 0] +/\ bar.PROOF \cup [bar EXCEPT !.PROOF = 0] +/\ bar.WITNESS \cup [bar EXCEPT !.WITNESS = 0] +/\ bar.PICK \cup [bar EXCEPT !.PICK = 0] + +/\ bar.DEFS \cup [bar EXCEPT !.DEFS = 0] +/\ bar.PROVE \cup [bar EXCEPT !.PROVE = 0] +/\ bar.SUFFICES \cup [bar EXCEPT !.SUFFICES = 0] +/\ bar.NEW \cup [bar EXCEPT !.NEW = 0] + +/\ bar.LAMBDA \cup [bar EXCEPT !.LAMBDA = 0] +/\ bar.LEMMA \cup [bar EXCEPT !.LEMMA = 0] +/\ bar.PROPOSITION \cup [bar EXCEPT !.PROPOSITION = 0] +/\ bar.STATE \cup [bar EXCEPT !.STATE = 0] +/\ bar.ACTION \cup [bar EXCEPT !.ACTION = 0] +/\ bar.TEMPORAL \cup [bar EXCEPT !.TEMPORAL = 0] +/\ bar.VARIABLE \cup [bar EXCEPT !.VARIABLE = 0] + +/\ bar.OBVIOUS \cup [bar EXCEPT !.OBVIOUS = 0] +/\ bar.OMITTED \cup [bar EXCEPT !.OMITTED = 0] + +/\ bar.ASSUME \cup [bar EXCEPT !.ASSUME = 0] +/\ bar.ELSE \cup [bar EXCEPT !.ELSE = 0] +/\ bar.LOCAL \cup [bar EXCEPT !.LOCAL = 0] +/\ bar.UNION \cup [bar EXCEPT !.UNION = 0] + +/\ bar.ASSUMPTION \cup [bar EXCEPT !.ASSUMPTION = 0] +/\ bar.ENABLED \cup [bar EXCEPT !.ENABLED = 0] +/\ bar.MODULE \cup [bar EXCEPT !.MODULE = 0] +/\ bar.VARIABLE \cup [bar EXCEPT !.VARIABLE = 0] + +/\ bar.AXIOM \cup [bar EXCEPT !.AXIOM = 0] +/\ bar.EXCEPT \cup [bar EXCEPT !.EXCEPT = 0] +/\ bar.OTHER \cup [bar EXCEPT !.OTHER = 0] +/\ bar.VARIABLES \cup [bar EXCEPT !.VARIABLES = 0] + +/\ bar.CASE \cup [bar EXCEPT !.CASE = 0] +/\ bar.EXTENDS \cup [bar EXCEPT !.EXTENDS = 0] +/\ bar.SF_ \cup [bar EXCEPT !.SF_ = 0] +/\ bar.WF_ \cup [bar EXCEPT !.WF_ = 0] + +/\ bar.CHOOSE \cup [bar EXCEPT !.CHOOSE = 0] +/\ bar.IF \cup [bar EXCEPT !.IF = 0] \* XXXXX +/\ bar.SUBSET \cup [bar EXCEPT !.SUBSET = 0] +/\ bar.WITH \cup [bar EXCEPT !.WITH = 0] + +/\ bar.CONSTANT \cup [bar EXCEPT !.CONSTANT = 0] +/\ bar.IN \cup [bar EXCEPT !.IN = 0] +/\ bar.THEN \cup [bar EXCEPT !.THEN = 0] \* XXXXX + +/\ bar.CONSTANTS \cup [bar EXCEPT !.CONSTANTS = 0] +/\ bar.INSTANCE \cup [bar EXCEPT !.INSTANCE = 0] +/\ bar.THEOREM \cup [bar EXCEPT !.THEOREM = 0] \* XXXXX + +/\ bar.DOMAIN \cup [bar EXCEPT !.DOMAIN = 0] +/\ bar.LET \cup [bar EXCEPT !.LET = 0] +/\ bar.UNCHANGED \cup [bar EXCEPT !.UNCHANGED = 0] +================================ + + + +THEOREM Bar == ASSUME NEW y, ASSUME TRUE PROVE FALSE + PROVE TRUE + + +THEOREM TRUE + <1>1. TAKE x \in << a:: {}, {}>> + <1>2. <1>1!a + <1>3. <1>1!1 + <1> QED + + +================================================== +a+b == <<a, b>> +THEOREM TRUE +<1>2 TRUE + <33>1 HAVE ~ TRUE + <33>2 TAKE Id, Id1 + <33>3 TAKE Id4 \in {1}, <<Id2, Id3>> \in {2} + <33>123 FooBar == <<Id, Id1, Id4, Id2, Id3>> + <33>4 WITNESS {1} + <33>5 WITNESS {1} \in {2}, {3} + <33>-... PICK x : ~ FALSE + <+>*. PICK y \in {1} : ~ FALSE + <*>* QED + <33>7 PICK y \in {1} : <33>5!1!1 + <33>3!1 + PROOF <55>1... TRUE + <*>2.... USE <33>5, <33>7!1!1, <33>7!(42)!<< DEF + + <55>2 QED + <33>7a Foo == << <33>7!(1)!<< , <33>7!>> >> + <33>8 PICK z \in {1}, <<y1, z1>> \in {2} : ~ FALSE + <33>9 PICK w, u : <<1, 2, 3>> + <34>* DEFINE A == <<x, y, z, y1, z1, w, u>> + <34>*. B == A!1 + <34> QED + <33>44 DEFINE A == <<x, y, z, y1, z1, w, u>> + <33>*. B == <33>9!(1,2)!3 + <33>. BBC(Op(_,_)) == Op(1,2) + DD == BBC(<33>9!@) + <33>22 ASSUME TRUE PROVE FALSE + <33> BBB == \A xxxx \in {} : TRUE + <33>14. INSTANCE Test1 + <*> AA == INSTANCE Test1 + <33> B3 == AA!Foo3!Def2 + <*>a ASSUME FALSE PROVE TRUE + <33>b CASE TRUE + <33>c SUFFICES TRUE + <33>. SUFFICES ASSUME FALSE PROVE TRUE + <33>2a. QED +<1>14 HAVE <<1, 2, 3>> +<1>14a havedef == <1>14!1!3 +<*> TAKE Id7 \in {1} +<*> WITNESS {1} \in {2} +<*> PICK x2 \in {1}, y2 \in {2} : ~ FALSE + <+> PICK x3, y3 : ~ FALSE + <*>*. DEFINE A2 == 1 + <*>2 B2 == 17 + <*> ASSUME FALSE PROVE TRUE + <*> CASE TRUE + <*> TRUE + <*> SUFFICES ASSUME FALSE PROVE TRUE + <2> QED +<1>... DEFINE A == 1 +<1> B == 17 +<1> INSTANCE Test1 +<1> AA == INSTANCE Test2 WITH B <- 1 +<1>3. QED + +=============================== + +\* Foo == \A x , y, z : TRUE +\* Bar == \A x \in {}, y, z \in {}, <<u, v>> \in {} : TRUE +\* Inst(a) == INSTANCE Test1 +\* Foo == Inst(7)!Bar1 +THEOREM Thm2 == TRUE + <1>2 QED + PROOF USE <1>2 + QED +==================== + PROOF +\* DEFINE Def1 == TRUE + USE Thm2!: \* DEF Thm2 , Inst!Bar2 +\* Def2 == TRUE + QED +\* PROOF USE Def1 DEFS Def2 +\* QED +================================= +THEOREM Thm3 == FALSE + PROOF DEFINE A == B + PROVE /\ TRUE + /\ 1=1 + BY Thm2 + QED +========================================================== +THEOREM 1 + PROOF QED + PROOF OBVIOUS +================================================= +============================================================================= + +BY [.] +USE DEF FALSE +HIDE +<1>abc FALSE + BY MODULE M DEF MODULE M, 2 [.] +A == B +DEFINE F == INSTANCE M +A[x \in S] == B +INSTANCE M +<1>2. QED +(******** + PROOF ASSUME TRUE PROVE FALSE + <1>1. TRUE + <1>2. QED + HAVE TRUE + QED +********) +============================================================================= diff --git a/tlatools/test-model/suite/test21.cfg b/tlatools/test-model/suite/test21.cfg new file mode 100644 index 0000000000000000000000000000000000000000..d191341c2404a4660de5819024419df4563278cd --- /dev/null +++ b/tlatools/test-model/suite/test21.cfg @@ -0,0 +1,9 @@ + INIT + Init + + NEXT + Next + + + INVARIANT Inv + diff --git a/tlatools/test-model/suite/test21.tla b/tlatools/test-model/suite/test21.tla new file mode 100644 index 0000000000000000000000000000000000000000..fbf1187b3b272a306d395f7aa65133829d47488d --- /dev/null +++ b/tlatools/test-model/suite/test21.tla @@ -0,0 +1,83 @@ +--------------- MODULE test21 ------------- + +(* Test of priming and variable confusion *) + +EXTENDS Naturals, Sequences, TLC + +Bar(z) == z' = 2 +Foo(y) == y = 2 +FooBar(y) == y = 1 + +VARIABLE x, y + +LetTest == LET s == x=1 + IN s + + +Op(a) == x+a + +Init == /\ x = 1 + /\ y = 3 +Next == + + \/ /\ x = 1 + /\ x'=x+1 + /\ y' = y + /\ IF Bar(x) + THEN Print("Test 1 OK", TRUE) + ELSE Assert(FALSE, "Test 1 Failed") + + \/ /\ x = 1 + /\ x'=x+1 + /\ y' = y + /\ IF Foo(x)' + THEN Print("Test 2 OK", TRUE) + ELSE Assert(FALSE, "Test 2 Failed") + + \/ /\ x = 1 + /\ x'=x+1 + /\ y' = y + /\ IF FooBar(x) + THEN Print("Test 3 OK", TRUE) + ELSE Assert(FALSE, "Test 3 Failed") + /\ IF LetTest' THEN Assert(FALSE, "Test 4 Failed") + ELSE Print("Test 4 OK", TRUE) + /\ LET a == x + IN IF a' = 2 THEN Print("Test 5 OK", TRUE) + ELSE Assert(FALSE, "Test 5 Failed") + + /\ IF FooBar(x)' THEN Assert(FALSE, "Test 6 Failed") + ELSE Print("Test 6 OK", TRUE) + + /\ IF FooBar(x') THEN Assert(FALSE, "Test 7 Failed") + ELSE Print("Test 7 OK", TRUE) + + \/ IF (x = 1) + THEN /\ x'=x+1 + /\ y'=y + /\ Print("Test 8 OK", TRUE) + ELSE /\ x'=x+1 + /\ y'=y + /\ Print("Test 12 OK", FALSE) + + \/ /\ x = 1 + /\ x'=x+1 + /\ y'=y + /\ IF Op(1)' = 3 + THEN Print("Test 9 OK", TRUE) + ELSE Assert(FALSE, "Test 9 Failed") + /\ IF LET LetOp(a) == x+a + IN LetOp(1)' = 3 + THEN Print("Test 10 OK", TRUE) + ELSE Assert(FALSE, "Test 10 Failed") + + /\ IF (LET LetOp(a) == x+a + IN LetOp(1) = 3 )' + THEN Print("Test 11 OK", TRUE) + ELSE Assert(FALSE, "Test 11 Failed") + + \/ UNCHANGED <<x,y>> + +Inv == TRUE + +========================================= diff --git a/tlatools/test-model/suite/test210.cfg b/tlatools/test-model/suite/test210.cfg new file mode 100644 index 0000000000000000000000000000000000000000..d191341c2404a4660de5819024419df4563278cd --- /dev/null +++ b/tlatools/test-model/suite/test210.cfg @@ -0,0 +1,9 @@ + INIT + Init + + NEXT + Next + + + INVARIANT Inv + diff --git a/tlatools/test-model/suite/test210.tla b/tlatools/test-model/suite/test210.tla new file mode 100644 index 0000000000000000000000000000000000000000..8bde6fb282f8fc24860c529ff00f3fb58f3ed1be --- /dev/null +++ b/tlatools/test-model/suite/test210.tla @@ -0,0 +1,293 @@ +\* This module is meant to produce parsing errors when evaluating any +\* subexpression reference ref in an expression of the form ref <=> +\* ILLEGAL and any label labeling "ILLEGAL" + + +-------------------- MODULE test210 -------------- +ILLEGAL == FALSE +LEGAL == TRUE + +ASSUME assump == /\ lab :: LEGAL + /\ TRUE + +AssDef == /\ assump!lab <=> LEGAL + /\ assump!1 <=> LEGAL + +THEOREM foo == ASSUME NEW x \in labx :: LEGAL, + NEW y \in laby :: LEGAL, + labz :: LEGAL, + ASSUME TRUE PROVE labi :: LEGAL + PROVE labu :: LEGAL +<1>1. ASSUME LEGAL, + NEW q \in labx :: LEGAL, + NEW r \in laby :: LEGAL + PROVE /\ foo!3 <=> LEGAL + /\ foo!labz <=> LEGAL + /\ foo!5 <=> LEGAL + /\ foo!labu <=> LEGAL + /\ foo!labi <=> LEGAL + BY <1>1!labx <=> LEGAL, <1>1!laby <=> LEGAL, <1>1!4 <=> LEGAL, + foo!4!2 <=> LEGAL, foo!4!1 <=> LEGAL + +<1>1a. /\ <1>1!labx <=> LEGAL + /\ <1>1!laby <=> ILLEGAL + /\ <1>1!1 <=> LEGAL + /\ <1>1!4 <=> ILLEGAL + +<1>2. SUFFICES ASSUME NEW q \in labx :: LEGAL, + TRUE, + NEW r \in laby :: LEGAL, + ASSUME lab1 :: LEGAL, + NEW a \in lab2 :: LEGAL, + NEW b \in lab3 :: ILLEGAL + PROVE lab4 :: ILLEGAL + PROVE /\ foo!3 <=> LEGAL + /\ foo!labz <=> LEGAL + /\ foo!5 <=> LEGAL + /\ foo!labu <=> LEGAL + +<1>2a. /\ <1>2!laby <=> LEGAL + /\ <1>2!labx <=> LEGAL + /\ <1>2!lab1 <=> LEGAL + /\ <1>2!lab2 <=> LEGAL + /\ <1>2!1!5 <=> LEGAL + /\ <1>2!1!5!2 <=> LEGAL + /\ <1>2!2 <=> ILLEGAL + +<1>3. SUFFICES 1 = 2 + +<1>4. QED + +Bar == /\ foo!labx <=> LEGAL + /\ foo!laby <=> ILLEGAL + /\ foo!labi <=> ILLEGAL + /\ foo!labu <=> ILLEGAL + /\ foo!3 <=> ILLEGAL +================== + +CONSTANT x +THEOREM TRUE +<1>1. x = 1 +<1>4. QED +================ +<2>21 bar == 1 foobar == 2 +<2>31 foo + BY DEF <2>21 +(**** +<2>22 WITNESS TRUE +<2>32 <2>22 +<2>23 TAKE x \in {} +<2>33 <2>23 +<2>24 PICK y \in {} : TRUE +<2>34 <2>24 +<2>25 CASE TRUE +<2>35 <2>25 +*****) +<2>3 QED + <3>1 TRUE\* <2>3 + BY <2>31 + <3>2 QED +================= +VARIABLE y +Foo(x) == INSTANCE Test1 +LOCAL fcn[x \in {}] == x +USE MODULE Test, MODULE Test1 DEFS (* MODULE Test, *) MODULE Test1, Foo +Bar == Foo(1)!-(1, 2) +================================ + +THEOREM ASSUME ASSUME P PROVE + PROVE P +BY Foo \* DEFS Foo + +THEOREM foo7 == TRUE + <0>1. TRUE + <*>.... <0>1 + <007>1. TRUE + <07>2. QED + <000>3 QED + +================= +\* Foo == <1>1 +THEOREM foobar == TRUE +<*>1. TRUE +PROOF + <+>1. TRUE + <+> TRUE + <*>11. TRUE + <*>12 TRUE + <2>13. TRUE + <2>14 TRUE + <2>2a TRUE + <*>2 <2>2a + <*>3. TRUE + <*> QED + <*>2. QED +<0>4. TRUE +<0>1a <0>4 +<0>2. QED + +THEOREM foo == ASSUME CONSTANT a, + VARIABLE c, + STATE b + PROVE TRUE +PROOF +<+>1. TRUE + PROOF <+>a. TRUE + <1>3. TRUE + <*> Test == <1>a + + <1>4. QED + <+> <1>3 + <2> QED +<0>... QED + +AXIOM TRUE + +THEOREM ASSUME CONSTANT a, + CONSTANT b \in {} , + VARIABLE c, + STATE d, + ACTION e, + TEMPORAL f + PROVE e = e + +THEOREM ASSUME NEW a, + NEW CONSTANT b \in {} , + NEW VARIABLE c, + NEW STATE d, + NEW ACTION e, + NEW TEMPORAL f + PROVE TRUE + +OBVIOUS + +THEOREM TRUE +OMITTED + +THEOREM TRUE +PROOF OBVIOUS + +THEOREM TRUE +PROOF OMITTED + +a+b == <<a, b>> + +THEOREM TRUE +BY TRUE DEF + + +THEOREM TRUE +BY TRUE DEFS + + +THEOREM TRUE +PROOF BY TRUE DEFS + + + + +THEOREM TRUE +<1>ab.... TRUE + PROOF + <33>0 TRUE + BY <1>ab + <33>1 HAVE ~ (TRUE /\ <33>0) + <33>2 TAKE Id, Id1 + <33>3 TAKE Id4 \in {1}, <<Id2, Id3>> \in {2} + <33>123 FooBar == <<Id, Id1, Id4, Id2, Id3>> + <33>4 WITNESS {1} \cup <33>1!1!1!2 + <33>5 WITNESS {1} \in {2}, {3} + <33>-... PICK x : ~ FALSE + <+>*. PICK y \in {1} : ~ FALSE + <*>* QED + <33>7 PICK y \in {1} : <33>5!1!1 + <33>3!1 + PROOF <*>1... TRUE + <*>2.... USE <*>1, <33>5, <33>7!1!1, <33>7!(42)!<< DEF + + + <*>3 QED + <33>7a Foo == << <33>7!(1)!<< , <33>7!>> >> + <33>8 PICK z \in {1}, <<y1, z1>> \in {2} : ~ FALSE + <33>9 PICK w, u : <<1, 2, 3>> + <34>* DEFINE A == <<x, y, z, y1, z1>> + <34>*. B == A!1 + C == A + <34> QED + <33>44 DEFINE A == <<x, y, z, y1, z1, w, u>> + <33>*. B == <33>9!(1,2)!3 + <33>. BBC(Op(_,_)) == Op(1,2) + DD == BBC(<33>9!@) + <33>22 ASSUME TRUE PROVE FALSE + <33> BBB == \A xxxx \in {} : TRUE + <33>14. INSTANCE Test1 + <*> AA == INSTANCE Test1 + <*>a ASSUME FALSE PROVE TRUE + <33>b CASE TRUE /\ <33>22!1 + <33>c SUFFICES TRUE + <33>. SUFFICES ASSUME FALSE PROVE TRUE + <33>2a. QED +<1>14 HAVE <<1, 2, 3>> +<1>14a havedef == <1>14!1!3 +<*> TAKE Id7 \in {1} +<*> WITNESS {1} \in {2} +<*> PICK x2 \in {1}, y2 \in {2} : ~ FALSE + <+> PICK x3, y3 : ~ FALSE + <*>*. DEFINE A2 == 1 + <*>2 B2 == 17 + <*> ASSUME FALSE PROVE TRUE + <*> CASE TRUE + <*> TRUE + <*> SUFFICES ASSUME FALSE PROVE TRUE + <2> QED +<1>... DEFINE A == 1 +<1> B == 17 +<1> INSTANCE Test1 WITH x <- 27 +<1> AA == INSTANCE Test2 WITH B <- 1 +<1>3. QED + +=============================== + +\* Foo == \A x , y, z : TRUE +\* Bar == \A x \in {}, y, z \in {}, <<u, v>> \in {} : TRUE +\* Inst(a) == INSTANCE Test1 +\* Foo == Inst(7)!Bar1 +THEOREM Thm2 == TRUE + <1>2 QED + PROOF USE <1>2 + QED +==================== + PROOF +\* DEFINE Def1 == TRUE + USE Thm2!: \* DEF Thm2 , Inst!Bar2 +\* Def2 == TRUE + QED +\* PROOF USE Def1 DEFS Def2 +\* QED +================================= +THEOREM Thm3 == FALSE + PROOF DEFINE A == B + PROVE /\ TRUE + /\ 1=1 + BY Thm2 + QED +========================================================== +THEOREM 1 + PROOF QED + PROOF OBVIOUS +================================================= +============================================================================= + +BY [.] +USE DEF FALSE +HIDE +<1>abc FALSE + BY MODULE M DEF MODULE M, 2 [.] +A == B +DEFINE F == INSTANCE M +A[x \in S] == B +INSTANCE M +<1>2. QED +(******** + PROOF ASSUME TRUE PROVE FALSE + <1>1. TRUE + <1>2. QED + HAVE TRUE + QED +********) +============================================================================= diff --git a/tlatools/test-model/suite/test211.tla b/tlatools/test-model/suite/test211.tla new file mode 100644 index 0000000000000000000000000000000000000000..2b7f396c514fdb48a5364b0ae090e590c325b0c9 --- /dev/null +++ b/tlatools/test-model/suite/test211.tla @@ -0,0 +1,1114 @@ +\* Test of moderately large proof. + +`. last modified on Wed 4 March 2009 at 17:55:12 PST by lamport + Major changes made 21 Sep 2007: + variable chosen renamed learned + action Choose renamed Learn + Major changes made 22 Sep 2007: + removed variable noccmd +.' + + +--------------------------------- MODULE test211 -------------------------------- +EXTENDS Integers, FiniteSets +----------------------------------------------------------------------------- +(***************************************************************************) +(* CONSTANTS *) +(* *) +(* For convenience, we take configuration numbers, instance numbers, and *) +(* ballot numbers all to be the set of naturals. However, to make the *) +(* meanings of our formulas easier to understand, we use different names *) +(* for these three equal sets. *) +(***************************************************************************) +CNum == Nat \* The set of configuration numbers. +INum == Nat \* The set of instance numbers. +BNum == Nat \* The set of ballot numbers. + +CONSTANT Acc \* The set of acceptors + +(***************************************************************************) +(* We define Config to be the set of configurations, where a configuration *) +(* is a record with a cnum field that is the configuration number field *) +(* and a quorums field that is a set of quorums, which is a set of *) +(* pairwise non-disjoint sets of acceptors. *) +(***************************************************************************) +Config == [cnum : CNum, + quorums : {C \in SUBSET SUBSET Acc : + \A P1, P2 \in C : P1 \cap P2 # {}}] + +CONSTANTS Cmd, \* The set of commands (choosable values). + + ConfigCmd(_), \* ConfigCmd(C) is the command that sets the + \* current configuration to C. + + InitConfig \* The initial configuration + +(***************************************************************************) +(* We assume that ConfigCmd(C) is a distinct command for every *) +(* configuration C, and that InitConfig is a configuration with cnum 0. *) +(***************************************************************************) +ASSUME ConstantAssumption == + /\ \A C \in Config : ConfigCmd(C) \in Cmd + /\ \A C, D \in Config : (C # D) => (ConfigCmd(C) # ConfigCmd(D)) + /\ InitConfig \in Config + /\ InitConfig.cnum = 0 + +(***************************************************************************) +(* The configuration of a configuration command. *) +(***************************************************************************) +ConfigOfCmd(c) == CHOOSE C \in Config : c = ConfigCmd(C) + + +(***************************************************************************) +(* CCmd is the set of reconfiguration commands. *) +(***************************************************************************) +CCmd == {ConfigCmd(C) : C \in Config} + +(***************************************************************************) +(* Vote is the set of votes, where a vote is a record with cnum *) +(* (configuration number) field and cmd (command) field. We define init *) +(* and `none' to be two different values a that are not votes. *) +(***************************************************************************) +Vote == [cnum : CNum, cmd : Cmd] +init == CHOOSE v : v \notin Vote +none == CHOOSE v : v \notin Vote \cup {init} +----------------------------------------------------------------------------- +(***************************************************************************) +(* VARIABLES *) +(* *) +(* The algorithm has two variables avote and `learned', where: *) +(* *) +(* - avote[a][i][b] is either the vote cast by acceptor a in instance i *) +(* for ballot b. It initially equals init from which it can be set to *) +(* a vote, to the value `none' indicating that `a' abstains in that *) +(* ballot. *) +(* *) +(* - `learned' is the set of pairs <<i, cmd>> indicating that command cmd *) +(* has been learned in instance i. *) +(***************************************************************************) +VARIABLE avote, learned + +(***************************************************************************) +(* BOUND IDENTIFIER CONVENTIONS *) +(* *) +(* We use the following conventions for the names used in quantifiers and *) +(* operator parameters: *) +(* *) +(* `a' : An acceptor *) +(* *) +(* i : An instance number. *) +(* *) +(* b : A ballot number *) +(* *) +(* C : A configuration *) +(* *) +(* c, cmd : A command *) +(* *) +(* n, cnum : A configuration number. *) +(* *) +(* Multiple identifiers of the same type are named in an obvious way--for *) +(* example b and bb, or c1 and c2. *) +(***************************************************************************) +----------------------------------------------------------------------------- +(***************************************************************************) +(* STATE PREDICATES *) +(* *) +(* The initial predicate asserts that avote[a][i][b] = init for all `a', *) +(* i, and b, and that `learned' contains the single element <<-1, c>> *) +(* where c is the command that sets the current configuration to *) +(* InitConfig. *) +(***************************************************************************) +Init == /\ avote = [a \in Acc |-> [i \in INum |-> [b \in BNum |-> init]]] + /\ learned = {<<-1, ConfigCmd(InitConfig)>>} + +(***************************************************************************) +(* The following state predicate is true iff `a' voted in instance i at *) +(* ballot b for command c with configuration number n. *) +(***************************************************************************) +Voted(a, i, b, n, c) == /\ avote[a][i][b] \in Vote + /\ avote[a][i][b] = [cnum |-> n, cmd |-> c] + +(***************************************************************************) +(* A type correctness assertion, which is an invariant of the algorithm. *) +(* It is trivial to check that this is true in the initial state and *) +(* preserved by every step of the algorithm. *) +(***************************************************************************) +TypeOK == + /\ (**********************************************************************) + (* avote is a function with the correct domain and range. *) + (**********************************************************************) + avote \in [Acc -> [INum -> [BNum -> Vote \cup {init, none}] ] ] + + /\ (**********************************************************************) + (* An acceptor votes only for a new configuration with a *) + (* configuration number 1 greater than that of the current *) + (* configuration. *) + (**********************************************************************) + \A a \in Acc, i \in INum, b \in BNum, n \in CNum, C \in Config : + Voted(a, i, b, n, ConfigCmd(C)) => (C.cnum = n+1) + + /\ (**********************************************************************) + (* `learned' is a set of elements of the proper type. *) + (**********************************************************************) + learned \subseteq (INum \cup {-1}) \X Cmd + + /\ (**********************************************************************) + (* There is exactly one element of the form <<-1, c>> \in learned, *) + (* where c is the command that sets the configuration to InitConfig. *) + (**********************************************************************) + \A c \in Cmd : (<<-1, c>> \in learned) <=> (c = ConfigCmd(InitConfig)) + + +(***************************************************************************) +(* The following state predicates are the usual ones for Paxos for a *) +(* particular instance i and a fixed configuration C. *) +(***************************************************************************) +ChosenInBal(i, b, C, cmd) == + (*************************************************************************) + (* True iff command cmd is chosen in ballot b. *) + (*************************************************************************) + \E Q \in C.quorums : + \A a \in Q : Voted(a, i, b, C.cnum, cmd) + +ChosenIn(i, C, cmd) == \E b \in BNum : ChosenInBal(i, b, C, cmd) + (*************************************************************************) + (* True iff command cmd is chosen. *) + (*************************************************************************) + +ChoosableAt(i, b, C, cmd) == + (*************************************************************************) + (* True iff command c has been chosen or could still be chosen in ballot *) + (* b. *) + (*************************************************************************) + \E Q \in C.quorums : + \A a \in Q : \/ Voted(a, i, b, C.cnum, cmd) + \/ avote[a][i][b] = init + +UnchoosableBefore(i, b, C, cmd) == + (*************************************************************************) + (* True iff it is not possible for command c ever to be chosen in any *) + (* ballot numbered less than b. *) + (*************************************************************************) + \A bb \in 0 .. (b-1): ~ ChoosableAt(i, bb, C, cmd) + +SafeAt(i, b, C, cmd) == + (*************************************************************************) + (* True iff it is safe for an acceptor to vote for command cmd in ballot *) + (* b (because no other command can be chosen with a smaller ballot *) + (* number). *) + (*************************************************************************) + \A c \in Cmd \ {cmd} : UnchoosableBefore(i, b, C, c) + + +(***************************************************************************) +(* The following state predicate asserts that it is safe to try to choose *) +(* a command in ballot b of instance i using configuration C. It assserts *) +(* that C was learned in some instance ii < i and that in every instance j *) +(* with ii < j < i, no reconfiguration command can possibly be chosen in a *) +(* ballot < b and no acceptor can vote for a reconfiguration command in *) +(* ballot b. *) +(***************************************************************************) +ConfigOKAt(i, b, C) == + \E ii \in -1 .. (i-1) : + /\ <<ii, ConfigCmd(C)>> \in learned + /\ \A j \in (ii+1)..(i-1), c \in CCmd : + /\ UnchoosableBefore(j, b, C, c) + /\ \A a \in Acc : ~Voted(a, j, b, C.cnum, c) + (***************************************************************) + (* Note: a ConfigOK formula is used as an enabling condition *) + (* in the action by which an acceptor votes. In practice, the *) + (* real enabling condition will be the stronger formula *) + (* *) + (* \A a \in Acc, n \in BNum : ~ Voted(a, j, b, n, c) *) + (* *) + (* However, my proof seems to requires the weaker formula. I *) + (* think the algorithm remains correct with the weaker *) + (* precondition. *) + (***************************************************************) +----------------------------------------------------------------------------- +(***************************************************************************) +(* ACTIONS *) +(* *) +(* The following is the action in which acceptor `a' votes in instance i *) +(* at ballot b for command cmd using configuration numbere cnum. The *) +(* first three conjuncts are enabling conditions. In an implementation, *) +(* acceptor `a' will perform this action only if it receives of a phase2a *) +(* message containing i, b, cnum, and cmd from the leader of ballot b. *) +(* It is up to the leader to ensure that the second and third conjuncts *) +(* will be satisfied when that message is received. After preforming the *) +(* action (meaning after the new value of avote[a][i][b] is written in *) +(* stable storage), acceptor `a' will send a phase2b message. The *) +(* purpose of those phase2b messages is to enable some process to learn *) +(* if command cmd has been chosen. *) +(***************************************************************************) +VoteFor(a, i, b, cnum, cmd) == + /\ (**********************************************************************) + (* `a' has not already voted or abstained in this ballot. *) + (**********************************************************************) + avote[a][i][b] = init + + /\ (**********************************************************************) + (* Any other acceptor that has voted in ballot b of instance i has *) + (* voted for cmd with configuration number cnum. *) + (**********************************************************************) + \A aa \in Acc \ {a} : + (avote[aa][i][b] \in Vote) => + (avote[aa][i][b] = [cnum |-> cnum, cmd |-> cmd]) + + /\ (**********************************************************************) + (* It is safe to try to choose a command with ballot b in instance i *) + (* for some configuration C with number cnum. Moreover, if cmd is a *) + (* reconfiguration command, then no configuration command can ever be *) + (* chosen with configuration C in any instance after i with a ballot *) + (* number < b. *) + (**********************************************************************) + \E C \in Config : + /\ C.cnum = cnum + /\ ConfigOKAt(i, b, C) + /\ SafeAt(i, b, C, cmd) + /\ \A newC \in Config : + (cmd = ConfigCmd(newC)) + => /\ newC.cnum = cnum + 1 + /\ \A ii \in INum, c \in Cmd : + (ii > i) => + /\ UnchoosableBefore(ii, b, C, c) + /\ \/ ChosenInBal(i, b, C, cmd) + \/ \A aa \in Acc : + avote[aa][ii][b] \notin Vote + (***************************************************************) + (* When the leader sends the Phase2a message for this vote, *) + (* the last disjunct will be true. However, it can send *) + (* Phase2a messages in instance ii once it knows that cmd is *) + (* chosen in ballot b. In that case, the preceding disjunct *) + (* will be true when the first Phase2a message arrives at `a'. *) + (***************************************************************) + + /\ (**********************************************************************) + (* Set avote[a][i][b] to a vote with configuration number cnum and *) + (* command cmd. *) + (**********************************************************************) + avote' = [avote EXCEPT ![a][i][b] = [cnum |-> cnum, cmd |-> cmd]] + + /\ (**********************************************************************) + (* Leave learned unchanged. *) + (**********************************************************************) + UNCHANGED learned + +(***************************************************************************) +(* The following action is one in which acceptor `a' abstains (votes *) +(* `none') for every pair <<i, b>> in the set S of instance number, ballot *) +(* number pairs in which it has not yet voted. This action is always *) +(* enabled. In an actual implementation, acceptor `a' performs this *) +(* action for the set S equal to the set of <<i, b>> with b < b0 and i any *) +(* instance number when it receives a phase1b message with ballot *) +(* number b0. It also performs the action for S the set of all <<i, b>> *) +(* with b < b0 when it receives a phase2a message for instance i with *) +(* ballot number b0 before it performs the Vote action. This action will *) +(* be a no-op (will perform a stuttering step) if `a' discards the phase1a *) +(* or phase2a message because it has already seen one with a ballow number *) +(* \geq b0. *) +(***************************************************************************) +Abstain(a, S) == + /\ avote' = [avote EXCEPT ![a] = + [i \in INum |-> [b \in BNum |-> IF /\ <<i, b>> \in S + /\ avote[a][i][b] = init + THEN none + ELSE avote[a][i][b] ] ] ] + /\ UNCHANGED learned + +(***************************************************************************) +(* The action by which a command cmd is learned in instance i at ballot b. *) +(* It occurs if cmd is chosen for that instance and ballot by a *) +(* configuration C for which it is safe to choose commands in that *) +(* instance and ballot. *) +(***************************************************************************) +Learn(i, b, cmd) == + /\ \E C \in Config : /\ ConfigOKAt(i, b, C) + /\ ChosenInBal(i, b, C, cmd) + /\ learned' = learned \cup {<<i, cmd>>} + /\ UNCHANGED avote + + +(***************************************************************************) +(* The next-state action is one that performs any of the Vote, Abstain, or *) +(* Learn steps described above. *) +(***************************************************************************) +Next == \/ \E a \in Acc: + \/ \E S \in SUBSET (INum \X BNum) : Abstain(a, S) + \/ \E i \in INum, b \in BNum, cnum \in CNum, cmd \in Cmd : + VoteFor(a, i, b, cnum, cmd) + \/ \E i \in INum, b \in BNum, cmd \in Cmd : Learn(i, b, cmd) +----------------------------------------------------------------------------- +(***************************************************************************) +(* The standard TLA+ formula that represents the complete algorithm, with *) +(* no liveness assumptions on when actions must be performed. *) +(***************************************************************************) +Spec == Init /\ [][Next]_<<avote, learned>> + +(***************************************************************************) +(* The fact that TypeOK is an invariant of the algorithm is expressed *) +(* formally by: *) +(***************************************************************************) +THEOREM Spec => []TypeOK + +(***************************************************************************) +(* The following is the invariant that expresses the desired safety *) +(* property, and the theorem asserting that it is an invariant. *) +(***************************************************************************) +SafetyInv == \A ch1, ch2 \in learned : (ch1[1] = ch2[1]) => (ch1 = ch2) +THEOREM Spec => []SafetyInv +----------------------------------------------------------------------------- + +(***************************************************************************) +(* We first prove some stability results. A predicate P is generally *) +(* called stable if it can't be made false by executing a step. When *) +(* formalized in an untyped system, this generally assumes that the *) +(* starting state is type-correct. *) +(***************************************************************************) +Stable(P) == TypeOK /\ P /\ Next => P' + +THEOREM Stability == + /\(*1*) \A i \in INum, c \in Cmd : Stable(<<i, c>> \in learned) + /\(*2*) \A a \in Acc, i \in INum, b \in BNum, n \in CNum, c \in Cmd: + /\(*2.1*) Stable( Voted(a, i, b, n, c) ) + /\(*2.2*) Stable( /\ ~Voted(a, i, b, n, c) + /\ avote[a][i][b] # init ) + /\(*3*) \A i \in INum, b \in BNum, c \in Cmd, C \in Config: + /\(*3.1*) Stable( ChosenInBal(i, b, C, c) ) + /\(*3.2*) Stable( ~ChoosableAt(i, b, C, c) ) + /\(*3.3*) Stable( UnchoosableBefore(i, b, C, c) ) + /\(*3.4*) Stable( SafeAt(i, b, C, c) ) + +(***************************************************************************) +(* Proof: 1 and 2 are obvious since no action removes an element from *) +(* `learned' and once an acceptor has voted or abstained in a ballot, it *) +(* cannot retract that decision. The proof of 3 uses 1 and 2 and the fact *) +(* that the conjunction or disjunction (and hence \A and \E) of stable *) +(* predicates are stable. The only part that takes a little work is 3.2, *) +(* which requires first rewriting ~ChoosableAt(i, b, C, c) as *) +(* *) +(* \A Q \in C.quorums : *) +(* \E a \in Q : ~Voted(a, i, b, C.cnum, c) /\ avote[a][i][b] # init *) +(* *) +(* and applying 2.1. *) +(***************************************************************************) + + +(***************************************************************************) +(* We define Max(S) to be the maximum of the set S of numbers, if S is *) +(* non-empty. *) +(***************************************************************************) +Max(S) == CHOOSE k \in S : \A j \in S : k \geq j + + +(***************************************************************************) +(* Suppose that m+1 reconfiguration commands have been chosen and they *) +(* can be numbered C_0, ... , C_m such that each C_j with j>0 was chosen *) +(* in ballot b_j of instance i_j using configuration C_(j-1) with *) +(* i_1 < i_2 < ... < i_m. Let i_0 = -1 and b_0 = 0, and let c_j be the *) +(* command that chooses new configuration C_j. Then the following *) +(* definitions define *) +(* *) +(* maxcfg = m *) +(* *) +(* cfgl[j] = <<i_j, c_j>> *) +(* *) +(* cfgi[j] = i_j *) +(* *) +(* cfgc[j] = c_j *) +(* *) +(* cfgC[j] = C_j *) +(* *) +(* cfgb[j] = b_j *) +(***************************************************************************) +learnedRcfg == { ln \in learned : ln[2] \in CCmd } +maxcfg == Cardinality(learnedRcfg) - 1 +cfgl == [j \in 0..maxcfg |-> CHOOSE ln \in learnedRcfg : ln[2].cnum = j ] +cfgi == [j \in 0..maxcfg |-> cfgl[j][1]] +cfgc == [j \in 0..maxcfg |-> cfgl[j][2]] +cfgC == [j \in 0..maxcfg |-> ConfigOfCmd(cfgc[j])] + +RECURSIVE cfgb \* This statement warns that the definition of cfgb is recursive. +cfgb == [j \in 0..maxcfg |-> + IF j = 0 THEN 0 + ELSE CHOOSE b \in BNum : + /\ ChosenInBal(cfgi[j], b, cfgC[j-1], cfgc[j]) + /\ ConfigOKAt(cfgi[j], b, cfgC[j-1]) ] + (***************************************************) + (* The choice isn't necessarily unique, but it *) + (* doesn't matter which b is chosen. *) + (***************************************************) + + +(***************************************************************************) +(* The definitions of cfgl, etc. define what they are supposed to only if *) +(* the set of chosen reconfiguration commands satisfies a certain *) +(* property. The following predicate asserts that they really do satisfy *) +(* those properties. More precisely, it asserts enough so that those *) +(* properties follow from it and the definitions. *) +(***************************************************************************) +CfgOK == + /\ cfgl \in [0..maxcfg -> learnedRcfg] + /\ cfgb \in [0..maxcfg -> BNum] + /\ cfgi[0] = -1 + /\ cfgC[0] = InitConfig + /\ cfgb[0] = 0 + /\ \A j \in 0..maxcfg : /\ <<cfgi[j], cfgc[j]>> \in learnedRcfg + /\ cfgC[j].cnum = j + /\ \A j \in 1..maxcfg : /\ ChosenInBal(cfgi[j], cfgb[j], cfgC[j-1], cfgc[j]) + /\ ConfigOKAt(cfgi[j], cfgb[j], cfgC[j-1]) + /\ \A j \in 1..maxcfg, i \in INum : + (i > cfgi[j]) => \A c \in Cmd : UnchoosableBefore(i, cfgb[j], cfgC[j-1], c) + +(***************************************************************************) +(* The following theorem asserts that the desired properties of cfgi, etc. *) +(* not explicitly stated in CfgOK follow from it and the definitions. *) +(* (This theorem isn't used explicitly, but it's probably used *) +(* implicitly.) *) +(***************************************************************************) +THEOREM TypeOK /\ CfgOK => + /\ cfgi \in [0..maxcfg -> INum \cup {-1}] + /\ cfgC \in [0..maxcfg -> Config] + /\ cfgb \in [0..maxcfg -> BNum ] + /\ \A j\in 0..maxcfg : cfgc[j] = ConfigCmd(cfgC[j]) + /\ learnedRcfg = {cfgl[j] : j \in 0..maxcfg} + PROOF OBVIOUS + +(***************************************************************************) +(* The following stability property is used in the invariance proof. *) +(***************************************************************************) +THEOREM CfgStability == + CfgOK => + \A i \in INum, b \in BNum, C \in Config : + /\ \A a \in Acc, c \in Cmd : Stable( /\ Voted(a, i, b, C.cnum, c) + /\ ConfigOKAt(i, b, C) ) + /\ \A c \in Cmd : Stable( /\ ChosenInBal(i, b, C, c) + /\ ConfigOKAt(i, b, C) ) + +<1>1 HAVE CfgOK + (*************************************************************************) + (* This statement means that we're assuming CfgOK and proving the *) + (* formula that it's supposed to imply. *) + (*************************************************************************) + +<1>2. TAKE i \in INum, b \in BNum, C \in Config + (*************************************************************************) + (* This statement means that our current goal is a formula of the form *) + (* *) + (* \A i \in INum, b \in BNum, C \in Config : P *) + (* *) + (* and we're going to prove it by introducing these new constants i, b, *) + (* and C and proving P. *) + (*************************************************************************) + + +<1>3. ASSUME CONSTANT a \in Acc, + CONSTANT c \in Cmd + PROVE Stable( /\ Voted(a, i, b, C.cnum, c) + /\ ConfigOKAt(i, b, C) ) + + <2>1. SUFFICES ASSUME Next, Voted(a, i, b, C.cnum, c), + ConfigOKAt(i, b, C) + PROVE ConfigOKAt(i, b, C)' + + (***********************************************************************) + (* Note: This statement is read "it suffices to assume ... and *) + (* prove ..." *) + (* *) + (* Proof: By definition of stability and the fact that Voted(...) is *) + (* stable. *) + (***********************************************************************) + <2>2. PICK ii \in -1 .. (i-1) : + /\ <<ii, ConfigCmd(C)>> \in learned + /\ \A j \in (ii+1)..(i-1), c4 \in CCmd : + /\ UnchoosableBefore(j, b, C, c4) + /\ \A aa \in Acc : ~ Voted(aa, j, b, C.cnum, c4) + (***********************************************************************) + (* By the assumption ConfigOKAt(...) of <2>1 and the definition of *) + (* ConfigOKAt. *) + (***********************************************************************) + + <2>3. SUFFICES ASSUME CONSTANT j \in (ii+1)..(i-1), + CONSTANT c4 \in CCmd, + CONSTANT aa \in Acc, + ~ Voted(aa, j, b, C.cnum, c4), + Voted(aa, j, b, C.cnum, c4)' + PROVE FALSE + (***********************************************************************) + (* Proof: By <2>1 we're assuming ConfigOKAt(i, b, C) and have to prove *) + (* ConfigOKAt(i, b, C)'. ConfiguOKAt(...) is the disjunction and *) + (* conjunction of formulas all of which are stable except for *) + (* *) + (* \A a \in Acc: ~ Voted(a, j, b, C.cnum, c), *) + (* *) + (* So we just have to show that ~ Voted(aa, j, b, n, c4) is stable for *) + (* every aa, j, c4, and n. The proof is by contradiction. *) + (***********************************************************************) + + <2>4. VoteFor(aa, j, b, C.cnum, c4) + (**********************************************************************) + (* Proof: By the assumptions of <2>3, because this VoteFor action is *) + (* the only subaction of Next that can make *) + (* Voted(aa, j, b, C.cnum, c) become true. *) + (**********************************************************************) + + <2>5. PICK CC \in Config : + /\ CC.cnum = C.cnum + /\ ConfigOKAt(j, b, CC) + /\ ChosenInBal(j, b, CC, c4) + (**********************************************************************) + (* Proof: The required CC exists by <2>4 and the third conjunct of *) + (* VoteFor(aa, j, b, C.cnum, c4), using the assumption c4 \in CCmd of *) + (* <2>3 and the assumption Voted(a, i, b, C.cnum, c) of <2>1, which *) + (* implies avote[a][i][b] \in Vote, so the final disjunct at the end *) + (* of that conjunct is false. *) + (**********************************************************************) + + <2>6. CC = C + (**********************************************************************) + (* This follows from ConfigOKAt(j, b, CC) (from <2>5) and *) + (* ConfigOKAt(i, b, C) (from <2>1, which imply that ConfigCmd(CC) and *) + (* ConfigCmd(C) are both learned commands. It then follows from *) + (* CC.cnum = C.cnum (from <2>5) and CfigOK (<1>1), which implies that *) + (* chosen reconfiguration commands have unique numbers, that CC = C. *) + (**********************************************************************) + + <2>7. \A a3 \in Acc : ~Voted(a3, j, b, C.cnum, c4) + (***********************************************************************) + (* Proof: By the last conjunct of <2>2, since <2>3 implies *) + (* j \in (ii+1)..(i-1) and c4 \in CCmd *) + (***********************************************************************) + + <2>8. QED + (***********************************************************************) + (* Proof: ChosenInBal(j, b, CC, c4) (from <2>5) implies Voted(aa, b, *) + (* CC.cnum, c4) aa. By <2>6 (CC = C), this contradicts <2>7. *) + (***********************************************************************) + +<1>4. ASSUME CONSTANT c1 \in Cmd \* Bug in parser prevents reuse of symbol c + PROVE Stable( /\ ChosenInBal(i, b, C, c1) + /\ ConfigOKAt(i, b, C) ) + (*************************************************************************) + (* Proof: Since ChosenInBal(i, b, C, c1) implies *) + (* Voted(a, i, b, C.cnum, c1), for some `a', this follows easily from *) + (* <1>3 and the stability of ChosenInBal(i, b, C, c1). *) + (*************************************************************************) + BY <1>4!2 + 1 + +<1>5. QED + BY <1>1, <1>2, <1>3, <1>4 +----------------------------------------------------------------------------- + +(***************************************************************************) +(* We now define the inductive invariant that is the heart of the safety *) +(* proof. *) +(***************************************************************************) +Inv == + /\(*1*) TypeOK + + /\(*2*) \A a \in Acc, i \in INum, b \in BNum, n \in CNum, c \in Cmd : + Voted(a, i, b, n, c) => + \A aa \in Acc, nn \in CNum, cc \in Cmd : + Voted(aa, i, b, nn, cc) => (<<nn, cc>> = <<n, c>>) + + /\(*3*) \A i \in INum, c \in Cmd : + (<<i, c>> \in learned) => + \E b \in BNum, C \in Config : /\ ConfigOKAt(i, b, C) + /\ ChosenInBal(i, b, C, c) + + /\(*4*) CfgOK + + /\(*5*) \A a \in Acc, i \in INum, b \in BNum, n \in CNum, c \in Cmd : + Voted(a, i, b, n, c) => + \E C \in Config : + /\ C.cnum = n + /\ ConfigOKAt(i, b, C) + /\ SafeAt(i, b, C, c) + /\ c \in CCmd => /\ ConfigOfCmd(c).cnum = n+1 + /\ \A j \in INum, cc \in Cmd : + (j > i) => UnchoosableBefore(j, b, C, cc) + /\ ChoosableAt(i, b, C, c) => + C = cfgC[ Max({j \in 0..maxcfg : cfgi[j] < i}) ] + + +(***************************************************************************) +(* We first prove that this Inv is strong enough--that is, it implies the *) +(* predicate that we want to show is invariant. *) +(***************************************************************************) +THEOREM Inv => SafetyInv + +<1>1. SUFFICES ASSUME Inv, + CONSTANT i \in INum, + CONSTANT c1 \in Cmd, + CONSTANT c2 \in Cmd, + <<i, c1>> \in learned /\ <<i, c2>> \in learned + PROVE c1 = c2 + PROOF OBVIOUS + +<1>2. PICK C1, C2 \in Config, b1, b2 \in BNum : + /\ ConfigOKAt(i, b1, C1) + /\ ChosenInBal(i, b1, C1, c1) + /\ ConfigOKAt(i, b2, C2) + /\ ChosenInBal(i, b2, C2, c2) + (*************************************************************************) + (* Proof: By <<i, c1>> and <<i, c2>> in `learned' (from <1>1) and Inv!3 *) + (* (which is assumed by <1>1). *) + (*************************************************************************) + +<1>3. PICK a1, a2 \in Acc : /\ Voted(a1, i, b1, C1.cnum, c1) + /\ Voted(a2, i, b2, C2.cnum, c2) + (*************************************************************************) + (* Proof: By <1>2, since ChosenInBal implies at least one vote. *) + (*************************************************************************) + +<1>4. PICK CC1, CC2 \in Config : + /\ CC1.cnum = C1.cnum + /\ ConfigOKAt(i, b1, CC1) + /\ ChoosableAt(i, b1, CC1, c1) => + CC1 = cfgC[Max ({j \in 0 .. maxcfg : cfgi[j] < i})] + /\ CC2.cnum = C2.cnum + /\ ConfigOKAt(i, b2, CC2) + /\ ChoosableAt(i, b2, CC2, c2) => + CC2 = cfgC[Max ({j \in 0 .. maxcfg : cfgi[j] < i})] + (*************************************************************************) + (* Proof: By <1>3 and conjunction 5 of Inv. *) + (*************************************************************************) + +<1>5. (CC1 = C1) /\ (CC2 = C2) + (*************************************************************************) + (* Proof: ConfigOKAt(i, b1, C1) (1st conjunct of <1>2) and *) + (* ConfigOKAt(i, b1, CC1) (2nd conjunct of <1>4) imply that <<i1, C1>> *) + (* and <<ii1, CC1>> are in `learned' for some i1 and ii1, and C1.cnum = *) + (* CC1.cnum (<1>4!1). By CfgOK, this implies that C1 = CC1. Similarly, *) + (* C2 = CC2. *) + (*************************************************************************) + +<1>6. C1 = C2 + (*************************************************************************) + (* Proof: We have ChosenInBal(i, b1, C1, c1) (second conjunct of <1>2), *) + (* which by definition of ChosenInBal and ChoosableAt implies *) + (* ChoosableAt(i, b1, C1, c1). By <1>5 and the 3rd conjunct of <1>4, *) + (* this shows that *) + (* *) + (* C1 = cfgC[Max ({j \in 0 .. maxcfg : cfgi[j] < i})] *) + (* *) + (* A similar argument shows that C2 equals the same expression. *) + (*************************************************************************) + +<1>7. QED + (*************************************************************************) + (* Proof: <1>6 and <1>2 imply ChosenInBal(i, b1, C, c1) and *) + (* ChosenInBal(i, b2, C, c2) for C = C1 = C2. The same simple reasoning *) + (* used in the proof of ordinary Paxos (with a fixed configuration C) *) + (* shows that this implies c1 = c2. *) + (*************************************************************************) +----------------------------------------------------------------------------- + +(***************************************************************************) +(* Here is the heart of the proof--the proof that Inv is an inductive *) +(* invariant. *) +(***************************************************************************) +THEOREM InductiveInvariance == Inv /\ Next => Inv' +<1>1. HAVE Inv /\ Next + +<1>2. Inv!1' + (*************************************************************************) + (* It is straighforward to show that TypeOK /\ Next => TypeOK'. *) + (*************************************************************************) + +\* <1> USE TypeOK +\* On 4 Mar 2009 it became illegal to use arbitrary expressions as +\* facts in a USE or HIDE--making this USE illegal + +<1>2a. TypeOK + PROOF OBVIOUS +<1> USE <1>2a + (*************************************************************************) + (* This statement means that from now on, we are free to use TypeOK *) + (* without explicitly mentioning that we're using it. In an informal *) + (* proof like this one, there are undoubtedly lots of places where we *) + (* assume other things without mentioning them. If we've been careful, *) + (* then we haven't assumed anything that we're not allowed to. *) + (*************************************************************************) + +<1>3. Inv!2' + (*************************************************************************) + (* Proof: Next and Inv!2 true and Inv!2' false implies that *) + (* VoteFor(a1, i, b, n, c) /\ Voted(a2, i, b, nn, cc) is true for some *) + (* a2 # a1 and cc # c. The second conjunct of VoteFor(a1, i, b, n, c) *) + (* implies that this is impossible. *) + (*************************************************************************) + +<1>4. Inv!3' + (*************************************************************************) + (* Proof: Next and Inv!3 true and Inv!3' false implies that there is *) + (* some i, bb, and c such that Learn(i, bb, c) is true but *) + (* *) + (* \E b \in BNum, C \in Config : /\ ConfigOKAt(i, b, C) *) + (* /\ ChosenInBal(i, b, C, c) *) + (* *) + (* is false. The definition of Learn(i, bb, c) implies that this is *) + (* impossible. *) + (*************************************************************************) + +<1>5. Inv!4' + <2>1. CASE learnedRcfg' = learnedRcfg + <3>1. maxcfg' = maxcfg /\ cfgl' = cfgl + (*********************************************************************) + (* Proof: By case assumption <2>1 becuase maxcfg is defined in terms *) + (* of learnedRcfg and cfgl is defined in terms of maxcfg and *) + (* learnedRcfg. *) + (*********************************************************************) + <3>2. cfgi' = cfgi /\ cfgc' = cfgc /\ cfgC' = cfgc /\ cfgb' = cfgb + (*********************************************************************) + (* Proof: By <3>1, since All these values are defined in terms of *) + (* cfgl and maxcfg. *) + (*********************************************************************) + <3>3. QED + (*********************************************************************) + (* Proof: CfgOK' follows by assumption <2>1, <3>1, <3>2, the *) + (* stability of *) + (* *) + (* /\ ChosenInBal(cfgi[j], cfgb[j], cfgC[j-1], cfgc[j]) *) + (* /\ ConfigOKAt(cfgi[j], cfgb[j], cfgC[j-1]) *) + (* *) + (* for all j (by <1>1 and Theorem CfgStability) and the stability of *) + (* *) + (* UnchoosableBefore(i, cfgb[j], cfgC[j-1], c) *) + (* *) + (* for all j, c (by Theorem Stability). *) + (*********************************************************************) + + <2>2. CASE learnedRcfg' # learnedRcfg + + <3>1. PICK i \in INum, b \in BNum, C \in Config : Learn(i, b, ConfigCmd(C)) + (*********************************************************************) + (* Proof: Case assumption <2>2 and the definition of Next. *) + (*********************************************************************) + <3> DEFINE c == ConfigCmd(C) + + <3>2. PICK k \in 0..maxcfg : + /\ ConfigOKAt(i, b, cfgC[k]) + /\ ChosenInBal(i, b, cfgC[k], c) + /\ learned' = learned \cup {<<i, c>>} + (*********************************************************************) + (* Proof: By <3>1, the definition of Learn, and CfgOK (Inv!4), which *) + (* implies that the configuration whose existence is implied by the *) + (* definition Learn(i, b, c) must be one of the cfgC[j]. *) + (*********************************************************************) + <3>3. i > cfgi[k] + (**********************************************************************) + (* Proof: By ChosenInBal(i, b, cfgC[k], c) (<3>2), definition of *) + (* ConfigOKAt, and CfgOK *) + (**********************************************************************) + + <3>4. k = maxcfg + <4>1. SUFFICES ASSUME k < maxcfg + PROVE FALSE + (*******************************************************************) + (* Proof by contradiction. *) + (*******************************************************************) + + <4> HAVE k < maxcfg + + <4>2. CASE i > cfgi[k+1] + <5>1. b < cfgb[k+1] + <6>1. /\ UnchoosableBefore(cfgi[k+1], b, cfgC[k], cfgc[k+1]) + /\ \A a \in Acc: ~Voted(a, cfgi[k+1], b, cfgC[k].cnum, cfgc[k+1]) + (***************************************************************) + (* Proof: By ConfigOKAt(i, b, cfgC[k]) (<3>2), case assumption *) + (* <4>2, and cfgi[k] < cfgi[k+1] (by CfgOK). *) + (***************************************************************) + <6>2. QED + (***************************************************************) + (* Proof: Since CfgOK implies *) + (* *) + (* ChosenInBal(cfgi[k+1], cfgb[k+1], cfgC[k], cfgc[k+1]) *) + (* *) + (* <6>1 implies that cfgb[k+1] cannot be \leq b. *) + (***************************************************************) + + <5>2. UnchoosableBefore(i, cfgb[k+1], cfgC[k], c) + (*****************************************************************) + (* Proof: By the last conjunct of CfgOK, substituting k+1 for j, *) + (* using case assumption <4>2. *) + (*****************************************************************) + + <5>3. QED + (*****************************************************************) + (* Proof: <5>1 and <5>2 contradict <3>2, which implies *) + (* ChosenInBal(i, b, cfgC[k], c). *) + (*****************************************************************) + + <4>3. CASE i < cfgi[k+1] + <5>1. b > cfgb[k+1] + <6>1. /\ UnchoosableBefore(i, cfgb[k+1], cfgC[k], c) + /\ \A a \in Acc : ~Voted(a, i, cfgb[k+1], cfgC[k].cnum, c) + (***************************************************************) + (* Proof: By CfgOK, which implies *) + (* ConfigOKAt(cfgi[k+1], cfgb[k+1], cfgC[k]) and the *) + (* definition of ConfigOKAt, since case assumption <4>3 and *) + (* <3>3 allows us to substitute i for j in the definition. *) + (***************************************************************) + <6>2. QED + (***************************************************************) + (* Proof: <3>2 implies ChosenInBal(i, b, cfgC[k], c), which *) + (* would contradict <6>1 if b \leq cfgb[k+1]. *) + (***************************************************************) + + <5>2. PICK a \in Acc : Voted(a, i, b, cfgC[k].cnum, c) + (*****************************************************************) + (* Proof: By ChosenInBal(i, b, cfgC[k], c) (from <3>2). *) + (*****************************************************************) + + <5>3. PICK CC \in Config : + /\ CC.cnum = cfgC[k].cnum + /\ ConfigOKAt(i, b, CC) + /\ UnchoosableBefore(cfgi[k+1], b, CC, cfgc[k+1]) + (*****************************************************************) + (* Proof: By <5>2 and the Inv!5, using case assumption <4>3 to *) + (* justify substituting cfgc[k+1] for j in the 4th conjunct of *) + (* the body of the \E C \in Config expression. *) + (*****************************************************************) + + <5>4. CC = cfgC[k] + (*****************************************************************) + (* Proof: ConfigOKAt(i, b, CC) (from <5>3) and CfgOK implies CC *) + (* equals cfgC[j] for some j, and CC.cnum = cfgC[k].cnum (from *) + (* <5>3) implies j = k. *) + (*****************************************************************) + + <5>5. QED + (*****************************************************************) + (* Proof: <5>3 and <5>4 imply *) + (* *) + (* UnchoosableBefore(cfgi[k+1], b, cfg[k], cfgc[k+1]) *) + (* *) + (* and by b > cfgb[k+1] (from <5>1) this contradicts CfgOK, *) + (* which implies *) + (* *) + (* ChosenInBal(cfgi[k+1], cfgb[k+1], cfgC[k], cfgc[k+1]) *) + (*****************************************************************) + + <4>4. CASE i = cfgi[k+1] + <5>1. ChosenInBal(i, b, cfgC[k], c) + BY <3>2 + + <5>2. ChosenInBal(cfgi[k+1], cfgb[k+1], cfgC[k], cfgc[k+1]) + BY CfgOK + + <5>3. c = cfgc[k+1] + (*****************************************************************) + (* Proof: By same simple reasoning used to prove the consistency *) + (* of ordinary Paxos (for instance i = cfgi[k+1] and the (fixed) *) + (* configuration cfgC[k]). *) + (*****************************************************************) + + <5>4. QED + (*****************************************************************) + (* Proof: CfgOK implies <<cfgi[k+1], cfgc[k+1]>> \in `learned', *) + (* so Learn(i, b, c) implies that `learned' is unchanged, *) + (* contradicting case assumption <2>2 (learnedRcfg' # *) + (* learnedRcfg). *) + (*****************************************************************) + + <4>5. QED + BY <4>2, <4>3, <4>4 + + <3>5. /\(*1*) <<i, c>> \in learned' \ {learned} + /\(*2*) C.cnum = maxcfg + 1 + /\(*3*) ChosenInBal(i, b, cfgC[maxcfg], c) + /\(*4*) ConfigOKAt(i, b, cfgC[maxcfg]) + /\(*5*) \A ii \in INum : + (ii > i) => + \A cc \in Cmd : UnchoosableBefore(ii, b, cfgC[maxcfg], cc) + <4>1. <3>5!1 + (*******************************************************************) + (* Proof: By <3>1 and case assumption <2>2. *) + (*******************************************************************) + + <4>2. <3>5!3 + BY <3>2 + + <4>3. <3>5!4 + BY <3>2 + + <4>4. /\ C.cnum = cfgC[maxcfg].cnum + 1 + /\ \A ii \in INum, cc \in Cmd : + (ii > i) => UnchoosableBefore(ii, b, cfgC[maxcfg], cc) + + <5>1. PICK a \in Acc : Voted(a, i, b, cfgC[maxcfg], c) + BY <4>2 + <5>2. PICK CC \in Config : + /\ CC.cnum = cfgC[maxcfg] + /\ ConfigOKAt(i, b, CC) + /\ C.cnum = cfgC[maxcfg].cnum + 1 + /\ \A j \in INum, cc \in Cmd : + (j > i) => UnchoosableBefore(j, b, CC, cc) + (*****************************************************************) + (* Proof: By <5>1 and Inv!5, since c \in CCmd and C = *) + (* ConfigOfCmd(c). *) + (*****************************************************************) + <5>3. CC = cfgC[maxcfg] + (*****************************************************************) + (* Proof: BY <5>2, since ConfigOKAt(i, b, CC) implies *) + (* <<j, CmdOfConfig(CC)>> in `learned', so it equals cfgC[j] for *) + (* some j, and CC.cnum = cfgC[maxcfg] implies j = maxcfg. *) + (*****************************************************************) + <5>4. QED + BY <5>2, <5>3 + + <4>5. <3>5!5 + + <4>6. QED + BY <4>1, <4>2, <4>3, <4>4, <4>5 + + <3>6. QED + (*********************************************************************) + (* Proof: <3>5!1 and <3>5!2 imply that maxcfg' = maxcfg+1 and *) + (* *) + (* cfgl' = [j \in 0..maxcfg' |-> *) + (* IF j < maxcfg THEN cfgl[j] ELSE <<i, c>>] *) + (* *) + (* from which it follows that the functions cfgi', cfgc', cfgC', *) + (* and cfgb' are the expected "extensions" of cfgi, cfgc, cfgC, and *) + (* cfgb, where <3>5!3 and <3>5!4 imply the existence of the b in *) + (* BNum satisfying the CHOOSE expression in the definition of *) + (* cfgb'[maxcfg']. This proves the first 5 conjuncts of CfgOK'. *) + (* The remaining three conjuncts are all \A j formulas. For j < *) + (* maxcfg', they follow from CfgOK. For j = maxcfg', they follow *) + (* from <3>5. *) + (*********************************************************************) + <2>3. QED + BY <2>1, <2>2 + +<1>6. Inv!5' + <2>1. TAKE a \in Acc, i \in INum, b \in BNum, n \in CNum, c \in Cmd + <2>2. HAVE Voted(a, i, b, n, c)' + <2>3. CASE Voted(a, i, b, n, c) + (***********************************************************************) + (* Proof: This case follows from the Theorems Stability and *) + (* CfgStability, which imply the stability of all the non-constant *) + (* subformuulas in the \E C \in Config expression in Inv!5. *) + (***********************************************************************) + <2>4. CASE ~Voted(a, i, b, n, c) + <3>1. VoteFor(a, i, b, n, c) + (*********************************************************************) + (* Proof: Next, <2>2 and case assumption <2>4, implies this VoteFor *) + (* action. *) + (*********************************************************************) + + <3>2. SUFFICES (* to prove *) + \E C \in Config : + /\ C.cnum = n + /\ ConfigOKAt(i, b, C) + /\ SafeAt(i, b, C, c) + /\ c \in CCmd => /\ ConfigOfCmd(c).cnum = n+1 + /\ \A j \in INum, cc \in Cmd : + (j > i) => UnchoosableBefore(j, b, C, cc) + /\ ChoosableAt(i, b, C, c) => + C = cfgC[ Max({j \in 0..maxcfg : cfgi[j] < i}) ] + (*********************************************************************) + (* Proof: For all other tuples <<aa, ii, bb, nn, cc>>, the body of *) + (* the \A of Inv!5' follows from Inv!5 and Theorems Stability and *) + (* CfgStability. *) + (*********************************************************************) + + <3>3. PICK C \in Config : + /\ C.cnum = n + /\ ConfigOKAt(i, b, C) + /\ SafeAt(i, b, C, c) + /\ c \in CCmd => /\ ConfigOfCmd(c).cnum = n+1 + /\ \A j \in INum, cc \in Cmd : + (j > i) => UnchoosableBefore(j, b, C, cc) + (*********************************************************************) + (* Proof: The existence of C follows from <3>1 and the definition of *) + (* VoteFor. *) + (*********************************************************************) + + <3>4. ASSUME ChoosableAt(i, b, C, c), + C # cfgC[ Max({j \in 0..maxcfg : cfgi[j] < i}) ] + PROVE FALSE + <4>1. PICK j \in 0..maxcfg : C = cfgC[j] + (*******************************************************************) + (* Proof: By CfgOK and ConfigOKAt(i, b, C), which imply that C is *) + (* one of the cfgC[j], and *) + (*******************************************************************) + <4>2. i > cfgi[j] + (*******************************************************************) + (* Proof: <3>3 and <4>1 imply ConfigOKAt(i, b, cfgC[j]), which *) + (* implies i > cfgi[j]. *) + (*******************************************************************) + <4>3. i > cfgi[j+1] + (*******************************************************************) + (* Proof: <4>1, <4>2, and the assumption <3>4!2. *) + (*******************************************************************) + <4>4. cfgb[j+1] > b + (*******************************************************************) + (* Proof: Since and i > cfgi[j+1] > cfgi[j] (by <4>3 and CfgOK), *) + (* ConfigOKAt(i, b, cfgC[j]) (which holds by <3>3 and <4>1) *) + (* implies UnchoosableBefore(cfgi[j+1], b, cfgC[j], cfgc[j+1]) and *) + (* ~ChosenInBal(cfgi[j+1], b, cfgC[j], cfgc[j+1]). Since CfgOK *) + (* implies ChosenInBal(cfgi[j+1], cfgb[j+1], cfgC[j], cfgc[j+1]), *) + (* we cannot have b \geq cfgb[j+1]. *) + (*******************************************************************) + <4>5. QED + (*******************************************************************) + (* Proof: The last conjunct of CfgOK implies *) + (* *) + (* UnchoosableBefore(i, cfgb[j+1], cfgC[j], c) *) + (* *) + (* By <4>4 and <4>1, this contradicts ChoosableAt(i, b, C, c), *) + (* which is assumption <3>4!1. *) + (*******************************************************************) + <3>5. QED + BY <3>2, <3>3, <3>4 + + <2>5. QED + BY <2>3, <2>4 + +<1>7. QED + BY <1>1, <1>2, <1>3, <1>4, <1>5, <1>6 + +============================================================================= + + +TLA+2 problems: + + - Can't use "!" selectors to name "c \in S" in + ASSUME CONSTANT c \in S + PARTIALLY FIXED 10 Oct 2007 to allow "CONSTANT c \in S" to + be used as a fact. + + - <i>j. PICK x ... makes x declared in the proof of <i>j, which + is wrong, since it's not legal to use it there. + FIXED 10 Oct 2007 + + - For subexpression naming, PICK is treated like a \A, even though + the variables being picked are being declared externally to the + expression. Thus, we have to write + + <3>4. PICK x : P(x) + <3>5. ... + BY <3>4!(x) \* This names P(x) + + This seems wrong, since x is declared in the scope of the use. + However, the !(x) can't be omitted if we want to refer to P inside + the proof of <3>4. Since we don't want the naming convention to + depend on where the name appears, we don't have much choice but to + let it stand as is. + + - Doesn't allow labels in numbered steps. This seems to be a bug, + since the parser allows positional naming of subexpressions of + a numbered step. FIXED 10 Oct 2007 + + - Bug: declaration of NEWs introduced in an ASSUME/PROVE step leak + out into the rest of the proof. (They should only for a + SUFFICE ASSUME/PROVE step.) FIXED 10 Oct 2007 + + - Bug: The error message for duplicate step numbers reports the position + as the entire containing proof body, not the step. This indicates + that some semantic node is being created with the entire proof body + rather than just its own part of the spec as its syntactic node. + FIXED 10 Oct 2007 +============================================================================= diff --git a/tlatools/test-model/suite/test212.cfg b/tlatools/test-model/suite/test212.cfg new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/tlatools/test-model/suite/test212.tla b/tlatools/test-model/suite/test212.tla new file mode 100644 index 0000000000000000000000000000000000000000..886fe8e0aba2014d6ae394130e5baa181e20eaba --- /dev/null +++ b/tlatools/test-model/suite/test212.tla @@ -0,0 +1,33 @@ +--------------------------- MODULE test212 -------------------------- +\* Test of Leibniz checking +\* Uses the fact that you can't substitute a non-Leibniz operator +\* for a constant operator. +VARIABLE x + +Leibniz(a2) == {a2 , x'} +NonLeibniz2(a, b) == <<a, b'>> +L == INSTANCE test212b WITH y <- x + +NonLeibniz(a1) == a1' +Leibniz2(a, b) == <<a, b, x'>> + +I(a) == INSTANCE test212b WITH y <- a +LL == INSTANCE test212b WITH y <- ENABLED (x'=x) +NonL(a) ==INSTANCE test212b WITH y <- ENABLED a + +\* Legal +L1 == INSTANCE test212a WITH Op <- Leibniz, Op2 <- Leibniz2 +L2 == INSTANCE test212a WITH Op <- LAMBDA a : I(a)!Leibniz(1), + Op2 <- Leibniz2 +L3 == INSTANCE test212a WITH Op <- Leibniz, Op2 <- I!Leibniz +L4 == INSTANCE test212a WITH Op <- Leibniz, Op2 <- LL!Leibniz2 +L5 == INSTANCE test212a WITH Op <- LL!Leibniz, Op2 <- Leibniz2 + +\* Illegal +I1 == INSTANCE test212a WITH Op <- Leibniz, Op2 <- NonLeibniz2 +I2 == INSTANCE test212a WITH Op <- NonLeibniz, Op2 <- Leibniz2 +I3 == INSTANCE test212a WITH Op <- NonL!NoArg, Op2 <- Leibniz2 +I4 == INSTANCE test212a WITH Op <- Leibniz, Op2 <- L!NonLeibniz2 +I5 == INSTANCE test212a WITH Op <- Leibniz, Op2 <- NonL!Leibniz +I6 == INSTANCE test212a WITH Op <- Leibniz, Op2 <- I!NonLeibniz +================================================================== \ No newline at end of file diff --git a/tlatools/test-model/suite/test212a.tla b/tlatools/test-model/suite/test212a.tla new file mode 100644 index 0000000000000000000000000000000000000000..175fea251f1af826e385c7502a6b02b0ed34d7fd --- /dev/null +++ b/tlatools/test-model/suite/test212a.tla @@ -0,0 +1,5 @@ +-------------------------- MODULE test212a ------------------------- + +CONSTANT Op(_) , Op2(_,_) + +============================================================================= \ No newline at end of file diff --git a/tlatools/test-model/suite/test212b.tla b/tlatools/test-model/suite/test212b.tla new file mode 100644 index 0000000000000000000000000000000000000000..c6027040a338c85e81f4fecaad02b100943cebcc --- /dev/null +++ b/tlatools/test-model/suite/test212b.tla @@ -0,0 +1,9 @@ +-------------------------- MODULE test212b ------------------------- +VARIABLE y +NoArg == y' +Leibniz(a4) == <<a4, y>> +Leibniz2(a44, a45) == <<a44, a45, y>> +NonLeibniz(a5) == ENABLED a5 +NonLeibniz2(a6, a7) == {a6', a7} + +============================================================================= diff --git a/tlatools/test-model/suite/test213.cfg b/tlatools/test-model/suite/test213.cfg new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/tlatools/test-model/suite/test213.tla b/tlatools/test-model/suite/test213.tla new file mode 100644 index 0000000000000000000000000000000000000000..b16ab4c8616d80bc47be3c071be5090b0c132668 --- /dev/null +++ b/tlatools/test-model/suite/test213.tla @@ -0,0 +1,47 @@ +------------------------------- MODULE test213 ------------------------------ +\* Test that +\* - A non-temporal theorem or proof step cannot have a +\* temporal formula in its proof. +\* - A declared constant that appears in an ASSUME or an ASSUME/PROVE +\* cannot be instantiated with a temporal formula +\* +\* Parsing only; produces errors. + +VARIABLE x + +I1 == INSTANCE test213b WITH C <- x', D <- x' +I2 == INSTANCE test213b WITH C <- [](x=0), D <- TRUE \* ERROR +I3 == INSTANCE test213b WITH C <- TRUE, D <- [](x=0) \* ERROR + +THEOREM [](x=0) +<1>1. CASE FALSE + <2>1. [](x=0) + <2>2. QED + <3>1. [](x # 0) + BY [](x # 0) + <3>2. QED +<1>2. x=0 \* ERROR + <2>1. (x=0) + <2>2. QED + <3>1. [](x=0) + <3>2. QED +<1>3. CASE TRUE + <2>1. CASE x=0 \* ERROR + <2>1a. CASE FALSE + <2>2. HAVE x=0 \* ERROR + <2>2a HAVE TRUE + <2>3. WITNESS 3 \in x \* ERROR + <2>4. WITNESS 2 \in {} + <2>5. QED +<1>4. QED + <2>1. [](x=0) + <2>2. QED + + + +THEOREM x=0 \* ERROR + <1>1. x'=0 \* ERROR + BY [](x=0) \* This BY causes an error, though it needn't + <1>2. [](x=0) \* this causes error in THEOREM + <1>3. QED +============================================================================= \ No newline at end of file diff --git a/tlatools/test-model/suite/test213b.tla b/tlatools/test-model/suite/test213b.tla new file mode 100644 index 0000000000000000000000000000000000000000..099e566da5f15f4cfa6f67e8b0246a70ccc94933 --- /dev/null +++ b/tlatools/test-model/suite/test213b.tla @@ -0,0 +1,7 @@ +------------------------------ MODULE test213b ------------------------------ +CONSTANT C, D +ASSUME C = {} + +THEOREM ASSUME D + PROVE TRUE +============================================================================= diff --git a/tlatools/test-model/suite/test214.cfg b/tlatools/test-model/suite/test214.cfg new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/tlatools/test-model/suite/test214.tla b/tlatools/test-model/suite/test214.tla new file mode 100644 index 0000000000000000000000000000000000000000..c2be50d9d42b446fec0f1fb51588a4b4237d1087 --- /dev/null +++ b/tlatools/test-model/suite/test214.tla @@ -0,0 +1,16 @@ +------------------------------ MODULE test214 ------------------------------ +\* Test that USE and HIDE cannot contain arbitrary expressions as +\* facts. + +THEOREM bar == <<1, 2>> + +USE bar, bar!1 (*ERROR*) + + +HIDE bar, TRUE (*ERROR*) + +THEOREM xx == ASSUME TRUE PROVE FALSE + <1>2. USE TRUE (*ERROR*), bar , xx , xx!1 (*ERROR*) + <1>3. QED + +============================================================================= diff --git a/tlatools/test-model/suite/test215.cfg b/tlatools/test-model/suite/test215.cfg new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/tlatools/test-model/suite/test215.tla b/tlatools/test-model/suite/test215.tla new file mode 100644 index 0000000000000000000000000000000000000000..b4d5f7845d1db4f8c0f29aea9931bb0e3ba91ca5 --- /dev/null +++ b/tlatools/test-model/suite/test215.tla @@ -0,0 +1,29 @@ +------------------------- MODULE test215 ------------------------- +\* Test of constraints on levels of temporal operators that +\* outlaw any form of raw TLA. + +VARIABLE x + +a == x ~> []x +b == x' ~> []x \* ERROR +c == []x ~> x' \* ERROR + +d == x -+-> []x +e == x' -+-> []x \* ERROR +f == []x -+-> x' \* ERROR + +g == <><<x'>>_x +h == <>(x') \* ERROR + +foo == [][x']_x + +bar == [](x') \* ERROR + +m == \A i \in {}, j \in x : []x +n == \A i \in {}, j \in x' : []x \* ERROR + +mm == \E i \in {}, j \in x : []x +nn == \E i \in {}, j \in x' : []x \* ERROR + +m2 == \A i \in x' : x' +======================================================================= \ No newline at end of file diff --git a/tlatools/test-model/suite/test216.cfg b/tlatools/test-model/suite/test216.cfg new file mode 100644 index 0000000000000000000000000000000000000000..87210cf3d20b205f9d171b521f757ef61f2f98ff --- /dev/null +++ b/tlatools/test-model/suite/test216.cfg @@ -0,0 +1,4 @@ +SPECIFICATION Spec1 \* Spec +CONSTANT C=6 +INVARIANT Inv Inv2 +PROPERTIES Prop1 Prop2 Prop3 Prop4 Prop5 \ No newline at end of file diff --git a/tlatools/test-model/suite/test216.tla b/tlatools/test-model/suite/test216.tla new file mode 100644 index 0000000000000000000000000000000000000000..e54d2f85b7e0d3b8e7422ab9b1ba590d2e490bd1 --- /dev/null +++ b/tlatools/test-model/suite/test216.tla @@ -0,0 +1,72 @@ +---------------------------- MODULE test216 ------------------------ +EXTENDS TLC, Naturals + +INSTANCE test216a WITH D <- 42 +Foo == INSTANCE test216a WITH D <- 43 +FooG == INSTANCE test216a WITH D <- 42 +VARIABLE x +CONSTANT C + +THEOREM Thm1 == x \in 0..C + +THEOREM Thm1Prime == x' \in 0..C + +THEOREM Thm1a == 1+1=2 +Bar == x \in 0..C +Init == Bar \/ Thm1!: \/ (Thm1 /\ ENABLED Thm1Prime!:) +Next == x'=(x+1)%C /\ Thm1!: + /\ Thm1Prime!: /\ Thm1 /\ Thm1Prime +Spec == /\ Init + /\ [][Next]_x + /\ WF_x(Next) + + + + +Inv == Thm1 +Inv2 == Thm1!<< \in Thm1!>> + +THEOREM Thm1b == Spec => /\ []Inv + /\ []<>(x=0) + +Spec1 == Thm1b!1 +\* need to build two tests, since there are two specs + +THEOREM Thm1c == [][Next]_x +Spec2 == Init /\ Thm1c \* Doesn't work + +Thm1d == []<>(x=0) +Prop3 == Thm1d + +Prop1 == Thm1b!2!1 +Prop2 == Thm1b!2!2 + +ASSUME Thm2 +ASSUME ~ Foo!Thm2 +ASSUME ~Foo!Thm3 +ASSUME ~Foo!I!Thm3 +ASSUME FooG!Thm2 +ASSUME FooG!Thm3 +ASSUME FooG!I!Thm3 +ASSUME Thm2!: +ASSUME ~ Foo!Thm2!: +ASSUME Thm2!1 = 42 +ASSUME Foo!Thm2!1 = 43 + +THEOREM Thm3!: +THEOREM ~ Foo!Thm3!: +ASSUME Foo!Thm3!2!2 = 42 +ASSUME Foo!I!Thm3!2!2 = 42 +ASSUME I!Thm3!2!2 = 42 +ASSUME Thm3!2!2 = 42 + + +J == INSTANCE test216c +Prop4 == J!ThmC!1 /\ J!ThmC!2 /\ J!ThmC2!: + +Thm1aDef == Thm1a + +INSTANCE test216c +ThmC3Def == ThmC3 +Prop5 == ThmC!1 /\ ThmC!2 /\ ThmC3!: /\ Thm1aDef /\ ThmC3Def +==================================================================== diff --git a/tlatools/test-model/suite/test216a.tla b/tlatools/test-model/suite/test216a.tla new file mode 100644 index 0000000000000000000000000000000000000000..bd7e81a7f7135e6f0c521c0fe0e10ad6ab545f39 --- /dev/null +++ b/tlatools/test-model/suite/test216a.tla @@ -0,0 +1,15 @@ +---------------------------- MODULE test216a ------------------------ +EXTENDS Naturals +CONSTANT D + +ASSUME Ass2 == D + 0 = 42 +THEOREM Thm2 == D + 0 = 42 +Thm2Def == D + 0 = 42 + +INSTANCE test216b WITH E <- D, y <- 0 + \* 12 June 2014: Changed from Test216b, which is now illegal +I == INSTANCE test216b WITH E <- D, y <- 0 + \* 12 June 2014: Changed from Test216b, which is now illegal + + +==================================================================== diff --git a/tlatools/test-model/suite/test216b.tla b/tlatools/test-model/suite/test216b.tla new file mode 100644 index 0000000000000000000000000000000000000000..e576811063d60194ff88e1a84535b2c8df319426 --- /dev/null +++ b/tlatools/test-model/suite/test216b.tla @@ -0,0 +1,7 @@ +---------------------------- MODULE test216b ------------------------ +CONSTANT E, y + +THEOREM Thm3 == y \in {y} /\ E = 42 + + +==================================================================== diff --git a/tlatools/test-model/suite/test216c.tla b/tlatools/test-model/suite/test216c.tla new file mode 100644 index 0000000000000000000000000000000000000000..80175592a06892a00b77745635ca7c34780e210c --- /dev/null +++ b/tlatools/test-model/suite/test216c.tla @@ -0,0 +1,15 @@ +------------------ MODULE test216c --------------------- +EXTENDS Naturals +VARIABLE x + +InitC == x = 0 +NextC == x'=(x+1) %6 + +SpecC == [][NextC]_x + +THEOREM ThmC == SpecC => [](x \in Nat) + +THEOREM ThmC2 == [](x \in Nat) + +THEOREM ThmC3 == 1+1 = 2 +====================================================== \ No newline at end of file diff --git a/tlatools/test-model/suite/test217.cfg b/tlatools/test-model/suite/test217.cfg new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/tlatools/test-model/suite/test217.tla b/tlatools/test-model/suite/test217.tla new file mode 100644 index 0000000000000000000000000000000000000000..f9028c3320e16e4d3b138a711e42b0929187bbe4 --- /dev/null +++ b/tlatools/test-model/suite/test217.tla @@ -0,0 +1,14 @@ +Sany should produce two level errors when parsing this module. + +---- MODULE test217 ---- + \* 12 June 2014: Changed module name from Test217, which is now illegal +EXTENDS test217a + \* 12 June 2014: Changed from Test217a, which is now illegal +VARIABLE x + +I(z) == INSTANCE test217a WITH y <- z + \* 12 June 2014: Changed from Test217a, which is now illegal + +FooBar == I(x')!Foo +THEOREM I({x'})!Foo +=============================== \ No newline at end of file diff --git a/tlatools/test-model/suite/test217a.tla b/tlatools/test-model/suite/test217a.tla new file mode 100644 index 0000000000000000000000000000000000000000..8617c81c5013269b6071dd14549d5930a40c697c --- /dev/null +++ b/tlatools/test-model/suite/test217a.tla @@ -0,0 +1,5 @@ +---- MODULE test217a ---- +VARIABLE y + +THEOREM Foo == y'=0 +========================= \ No newline at end of file diff --git a/tlatools/test-model/suite/test218.tla b/tlatools/test-model/suite/test218.tla new file mode 100644 index 0000000000000000000000000000000000000000..ab3fb749971531ec4e114cdce17c8b40c92383a3 --- /dev/null +++ b/tlatools/test-model/suite/test218.tla @@ -0,0 +1,11 @@ +Check fix to handling of ThmOrAssumpDefNode in Tool.java +on 23 Oct 2012. + +---- MODULE test218 ---- +EXTENDS Test218a, TLC + +I(z) == INSTANCE Test218a WITH y <- z + +ASSUME PrintT(I(55)!Foo) + +=============================== \ No newline at end of file diff --git a/tlatools/test-model/suite/test219.cfg b/tlatools/test-model/suite/test219.cfg new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/tlatools/test-model/suite/test219.tla b/tlatools/test-model/suite/test219.tla new file mode 100644 index 0000000000000000000000000000000000000000..2d17f4b8388b4b797c8865562b804eba5a2f82cb --- /dev/null +++ b/tlatools/test-model/suite/test219.tla @@ -0,0 +1,227 @@ +------------------------------- MODULE test219 ------------------------------ +\* Test of use of labels from instantiated theorems, and repeat +\* test of some instantiations + +EXTENDS TLC , Integers + +I(x) == INSTANCE test219a WITH C <- 4 +J == INSTANCE test219a WITH C <- 4 +INSTANCE test219a WITH C <- 4 + +LOCAL K(x) == INSTANCE test219a WITH C <- 4 +LOCAL L == INSTANCE test219a WITH C <- 4 + + +ASSUME J!Thm2!lab3 +ASSUME I(55)!Thm2!lab3 +ASSUME L!Thm2!lab3 +ASSUME K(55)!Thm2!lab3 +ASSUME Thm2!lab3 + + +ASSUME Op(Foo, 9) + +ASSUME Thm2!lab3 + +ASSUME Bar + +ASSUME Op(Foo, 9)!lab(1,2,3)!label2 = Op(Foo, 9)!lab(1,2,3)!:!++(9, 4) +ASSUME OOp1(SOp1, 2) = 2 +ASSUME OOp2(SOp1b!@, 2, 3) = 5 +ASSUME OOp2(SOp1c!@!c, 2, 3) = 2 * 3 +ASSUME OOp3(SOp1d!@!c, 2, 3, 4) = 2 * 3 + 4 +ASSUME Op1(2)!(3)!2!1 = 2 + 4*3 + 6 +ASSUME Op1(2)!(3)!2!Op1a(4) = 16 + 6 +ASSUME Op3(Op1!@!2!Op1a, 2, 3, 4) = 22 +ASSUME COp(1, 2)!(3)!++(4, 5) = {2,1, 3, 4, 5} +ASSUME COp(1, 2)!(3) = <<1, {99, 1, 2, 3} >> +ASSUME COp(1, 2)!(3)!1 = <<1, {99, 1, 2, 3} >> +ASSUME COp(1, 2)!(3)!1!>> = {99, 1, 2, 3} +ASSUME OOp5(COp!@!++, 1, 2, 3, 4, 5) = {2,1, 3, 4, 5} +ASSUME COp(1, 2)!lab(3)!:!++(4, 5) = {2,1, 3, 4, 5} +ASSUME Inst!Foo(1, 2, 3, 4, 5) = {2,1, 3, 4, 5} +ASSUME Inst!Bar(3)!>> = 3 +ASSUME Inst!Bar2(3)!>> = 3 +ASSUME Inst!Bar3!>> = 2 +ASSUME Inst!Bar4(1)!(2)!<< = 2 +ASSUME Inst!Bar4(1)!(2)!>> = 1 +ASSUME Inst!Bar4(1)!lab(2)!<< = 2 +ASSUME Inst!Bar4(1)!lab(2)!>> = 1 +ASSUME OOp2(Inst!Bar4!@!<<, 1, 2) = 2 +ASSUME OOp2(Inst!Bar4!@!>>, 1, 2) = 1 +ASSUME OOp2(Inst!Bar4!lab!<<, 1, 2) = 2 +ASSUME OOp2(Inst!Bar4!lab!>>, 1, 2) = 1 +ASSUME Inst!Bar5(1)!lab(2)!:!++(3, 4) = <<1, 3, 4, 2>> +ASSUME Inst!Bar5(1)!(2)!++(3, 4) = <<1, 3, 4, 2>> +ASSUME OOp4(Inst!Bar5!lab!:!++, 1, 2, 3, 4) = <<1, 3, 4, 2>> +ASSUME Inst2(COp!@!++)!Foo(1, 2, 3, 4, 5) = {2,1, 3, 4, 5} +ASSUME COp2(1,2)!lab(3)!:!++(9,10)!lab(11) = 30 +ASSUME COp2(1,2)!(3)!++(9,10)!lab(11) = 30 +ASSUME COp2(1,2)!lab(3)!:!++(9,10)!(11) = 30 +ASSUME OOp6(COp2!lab!:!++!lab, 1, 2, 3, 9, 10, 11) = 30 +ASSUME OOp6(COp2!@!++!lab, 1, 2, 3, 9, 10, 11) = 30 +ASSUME OOp6(COp2!lab!:!++!@, 1, 2, 3, 9, 10, 11) = 30 +ASSUME Def1!2 = 2 +ASSUME Def2!(14)!<< = 14 +ASSUME Def3!2!1 = 2 +ASSUME Def3!(1, 2)!<< = 1 +ASSUME Def4!<<!<<!2 = {2} +ASSUME Def5!>> = 3 +ASSUME Def5!<<[4] = 5 +ASSUME Def5!<<!1!>> = 10 +ASSUME Def6!1[3] = 4 +ASSUME Def7!1 = 3 +ASSUME Def7!2 = "a" +ASSUME Def8!2 = 2 +ASSUME Def9!2 = 2 +ASSUME Def10!2 = 2 +ASSUME Def11!3!1 = 3 + +ASSUME L!Op(Foo, 9)!lab(1,2,3)!label2 = Op(Foo, 9)!lab(1,2,3)!:!++(9, 4) +ASSUME L!OOp1(SOp1, 2) = 2 +ASSUME L!OOp2(SOp1b!@, 2, 3) = 5 +ASSUME L!OOp2(SOp1c!@!c, 2, 3) = 2 * 3 +ASSUME L!OOp3(SOp1d!@!c, 2, 3, 4) = 2 * 3 + 4 +ASSUME L!Op1(2)!(3)!2!1 = 2 + 4*3 + 6 +ASSUME L!Op1(2)!(3)!2!Op1a(4) = 16 + 6 +ASSUME L!Op3(Op1!@!2!Op1a, 2, 3, 4) = 22 +ASSUME L!COp(1, 2)!(3)!++(4, 5) = {2,1, 3, 4, 5} +ASSUME L!COp(1, 2)!(3) = <<1, {99, 1, 2, 3} >> +ASSUME L!COp(1, 2)!(3)!1 = <<1, {99, 1, 2, 3} >> +ASSUME L!COp(1, 2)!(3)!1!>> = {99, 1, 2, 3} +ASSUME L!OOp5(COp!@!++, 1, 2, 3, 4, 5) = {2,1, 3, 4, 5} +ASSUME L!COp(1, 2)!lab(3)!:!++(4, 5) = {2,1, 3, 4, 5} +ASSUME L!Inst!Foo(1, 2, 3, 4, 5) = {2,1, 3, 4, 5} +ASSUME L!Inst!Bar(3)!>> = 3 +ASSUME L!Inst!Bar2(3)!>> = 3 +ASSUME L!Inst!Bar3!>> = 2 +ASSUME L!Inst!Bar4(1)!(2)!<< = 2 +ASSUME L!Inst!Bar4(1)!(2)!>> = 1 +ASSUME L!Inst!Bar4(1)!lab(2)!<< = 2 +ASSUME L!Inst!Bar4(1)!lab(2)!>> = 1 +ASSUME L!OOp2(Inst!Bar4!@!<<, 1, 2) = 2 +ASSUME L!OOp2(Inst!Bar4!@!>>, 1, 2) = 1 +ASSUME L!OOp2(Inst!Bar4!lab!<<, 1, 2) = 2 +ASSUME L!OOp2(Inst!Bar4!lab!>>, 1, 2) = 1 +ASSUME L!Inst!Bar5(1)!lab(2)!:!++(3, 4) = <<1, 3, 4, 2>> +ASSUME L!Inst!Bar5(1)!(2)!++(3, 4) = <<1, 3, 4, 2>> +ASSUME L!OOp4(Inst!Bar5!lab!:!++, 1, 2, 3, 4) = <<1, 3, 4, 2>> +ASSUME L!Inst2(COp!@!++)!Foo(1, 2, 3, 4, 5) = {2,1, 3, 4, 5} +ASSUME L!COp2(1,2)!lab(3)!:!++(9,10)!lab(11) = 30 +ASSUME L!COp2(1,2)!(3)!++(9,10)!lab(11) = 30 +ASSUME L!COp2(1,2)!lab(3)!:!++(9,10)!(11) = 30 +ASSUME L!OOp6(COp2!lab!:!++!lab, 1, 2, 3, 9, 10, 11) = 30 +ASSUME L!OOp6(COp2!@!++!lab, 1, 2, 3, 9, 10, 11) = 30 +ASSUME L!OOp6(COp2!lab!:!++!@, 1, 2, 3, 9, 10, 11) = 30 +ASSUME L!Def1!2 = 2 +ASSUME L!Def2!(14)!<< = 14 +ASSUME L!Def3!2!1 = 2 +ASSUME L!Def3!(1, 2)!<< = 1 +ASSUME L!Def4!<<!<<!2 = {2} +ASSUME L!Def5!>> = 3 +ASSUME L!Def5!<<[4] = 5 +ASSUME L!Def5!<<!1!>> = 10 +ASSUME L!Def6!1[3] = 4 +ASSUME L!Def7!1 = 3 +ASSUME L!Def7!2 = "a" +ASSUME L!Def8!2 = 2 +ASSUME L!Def9!2 = 2 +ASSUME L!Def10!2 = 2 +ASSUME L!Def11!3!1 = 3 + +ASSUME I(55)!Op(Foo, 9)!lab(1,2,3)!label2 = Op(Foo, 9)!lab(1,2,3)!:!++(9, 4) +ASSUME I(55)!OOp1(SOp1, 2) = 2 +ASSUME I(55)!OOp2(SOp1b!@, 2, 3) = 5 +ASSUME I(55)!OOp2(SOp1c!@!c, 2, 3) = 2 * 3 +ASSUME I(55)!OOp3(SOp1d!@!c, 2, 3, 4) = 2 * 3 + 4 +ASSUME I(55)!Op1(2)!(3)!2!1 = 2 + 4*3 + 6 +ASSUME I(55)!Op1(2)!(3)!2!Op1a(4) = 16 + 6 +ASSUME I(55)!Op3(Op1!@!2!Op1a, 2, 3, 4) = 22 +ASSUME I(55)!COp(1, 2)!(3)!++(4, 5) = {2,1, 3, 4, 5} +ASSUME I(55)!COp(1, 2)!(3) = <<1, {99, 1, 2, 3} >> +ASSUME I(55)!COp(1, 2)!(3)!1 = <<1, {99, 1, 2, 3} >> +ASSUME I(55)!COp(1, 2)!(3)!1!>> = {99, 1, 2, 3} +ASSUME I(55)!OOp5(COp!@!++, 1, 2, 3, 4, 5) = {2,1, 3, 4, 5} +ASSUME I(55)!COp(1, 2)!lab(3)!:!++(4, 5) = {2,1, 3, 4, 5} +ASSUME I(55)!Inst!Foo(1, 2, 3, 4, 5) = {2,1, 3, 4, 5} +ASSUME I(55)!Inst!Bar(3)!>> = 3 +ASSUME I(55)!Inst!Bar2(3)!>> = 3 +ASSUME I(55)!Inst!Bar3!>> = 2 +ASSUME I(55)!Inst!Bar4(1)!(2)!<< = 2 +ASSUME I(55)!Inst!Bar4(1)!(2)!>> = 1 +ASSUME I(55)!Inst!Bar4(1)!lab(2)!<< = 2 +ASSUME I(55)!Inst!Bar4(1)!lab(2)!>> = 1 +ASSUME I(55)!OOp2(Inst!Bar4!@!<<, 1, 2) = 2 +ASSUME I(55)!OOp2(Inst!Bar4!@!>>, 1, 2) = 1 +ASSUME I(55)!OOp2(Inst!Bar4!lab!<<, 1, 2) = 2 +ASSUME I(55)!OOp2(Inst!Bar4!lab!>>, 1, 2) = 1 +ASSUME I(55)!Inst!Bar5(1)!lab(2)!:!++(3, 4) = <<1, 3, 4, 2>> +ASSUME I(55)!Inst!Bar5(1)!(2)!++(3, 4) = <<1, 3, 4, 2>> +ASSUME I(55)!OOp4(Inst!Bar5!lab!:!++, 1, 2, 3, 4) = <<1, 3, 4, 2>> +ASSUME I(55)!Inst2(COp!@!++)!Foo(1, 2, 3, 4, 5) = {2,1, 3, 4, 5} +ASSUME I(55)!COp2(1,2)!lab(3)!:!++(9,10)!lab(11) = 30 +ASSUME I(55)!COp2(1,2)!(3)!++(9,10)!lab(11) = 30 +ASSUME I(55)!COp2(1,2)!lab(3)!:!++(9,10)!(11) = 30 +ASSUME I(55)!OOp6(COp2!lab!:!++!lab, 1, 2, 3, 9, 10, 11) = 30 +ASSUME I(55)!OOp6(COp2!@!++!lab, 1, 2, 3, 9, 10, 11) = 30 +ASSUME I(55)!OOp6(COp2!lab!:!++!@, 1, 2, 3, 9, 10, 11) = 30 +ASSUME I(55)!Def1!2 = 2 +ASSUME I(55)!Def2!(14)!<< = 14 +ASSUME I(55)!Def3!2!1 = 2 +ASSUME I(55)!Def3!(1, 2)!<< = 1 +ASSUME I(55)!Def4!<<!<<!2 = {2} +ASSUME I(55)!Def5!>> = 3 +ASSUME I(55)!Def5!<<[4] = 5 +ASSUME I(55)!Def5!<<!1!>> = 10 +ASSUME I(55)!Def6!1[3] = 4 +ASSUME I(55)!Def7!1 = 3 +ASSUME I(55)!Def7!2 = "a" +ASSUME I(55)!Def8!2 = 2 +ASSUME I(55)!Def9!2 = 2 +ASSUME I(55)!Def10!2 = 2 +ASSUME I(55)!Def11!3!1 = 3 + +ASSUME /\ Def12!1!<< = 2 + /\ Def12!2 = 2 + /\ Def12!3 = 3 +ASSUME /\ Def13!(42)!1!<<!<< = 42 + /\ Def13!(42)!1!>> = 1 + /\ Def13!(42)!2!>> = 2 + /\ Def13!(42)!3!>> = 3 + +ASSUME /\ Def14!(2)!<< = 2 + /\ Def14!1!1 = 1 + +ASSUME /\ Def15!(2,3)!<<!<< = 2 + /\ Def15!(2,3)!>>!<< = 3 + /\ Def15!2!<< = 2 + +ASSUME /\ Def16!(2,3,4)!1!1 = 2 + /\ Def16!(2,3,4)!2!1 = 3 + /\ Def16!2!1!<< = 2 + +ASSUME /\ Def17!(2,3,4)!1!1 = 2 + /\ Def17!(2,3,4)!2!1 = 3 + +ASSUME Def18!(2)!<< = 2 + +ASSUME /\ Def19!(2)!<< = 2 + /\ Def19!1!1 = 42 + +ASSUME /\ Def20!1 = 12 + /\ Def20!>> = 23 + +ASSUME /\ Def21!1 = 12 + /\ Def21!>> = 23 + +ASSUME /\ Def22!1 = 12 + /\ Def22!>> = 23 + +ASSUME /\ Def23!(2,3,4)!1!1 = 2 + /\ Def23!(2,3,4)!2!1 = 3 + + +============================================================================= + +last modified on Thu 1 November 2012 at 10:11:43 PST by lamport +============================================================================== \ No newline at end of file diff --git a/tlatools/test-model/suite/test219a.cfg b/tlatools/test-model/suite/test219a.cfg new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/tlatools/test-model/suite/test219a.tla b/tlatools/test-model/suite/test219a.tla new file mode 100644 index 0000000000000000000000000000000000000000..efbdb448b87ea70fac806c8b0329ad875d804370 --- /dev/null +++ b/tlatools/test-model/suite/test219a.tla @@ -0,0 +1,239 @@ +------------------------------- MODULE test219a ------------------------------ +\* Test of labels and their use in subexpression selectors. + +EXTENDS TLC, Integers + +CONSTANT C +\* C == 4 + +Op(Arg(_), p) == + \A x \in {1,2}, <<y, z>> \in {<<3, 4>>} : + lab(x,y,z) :: LET a ++ b == + Arg(a) + b + x + 2*y + 3*z + IN (Arg(1) + Arg(p) + C + + 2*x + 4*y + 6*z) = + 1 ++ ( label2 :: p ++ C) +\* IN /\label2:: PrintT("FOO") +\* /\ PrintT(Arg(1)) +\* /\ PrintT("FOO2") +\* /\ PrintT(<<p, Arg(p)>>) +\* /\ PrintT("FOO3") +\* /\ PrintT(<<p, C>>) +\* /\ PrintT("FOO3a") +\* /\ PrintT(1++2) +\* /\ PrintT(p++C) +\* /\ PrintT("FOO4") +\* /\ PrintT(<<x, y, z>>) + +Foo(u) == 2*u + +\* ASSUME Foo(5) = Foo(5)!: + +THEOREM Thm == ASSUME FALSE + PROVE lab:: C > 0 + +THEOREM Thm2 == lab3 :: TRUE + + +\* ASSUME Foo(25) = 50 + +Bar == Op(Foo, 9)!lab(1,2,3) + +\* ASSUME Op(Foo, 9) +\* +\* ASSUME Thm2!lab3 +\* +\* ASSUME Bar +\* +\* ASSUME Op(Foo, 9)!lab(1,2,3)!label2 = Op(Foo, 9)!lab(1,2,3)!:!++(9, 4) + +Op3(A(_,_,_), a1, a2, a3) == A(a1, a2, a3) + +Op1(a) == \A x \in {} : lab(x) :: IF TRUE + THEN LET Op1a(c) == 4 * c + a*x + IN a + Op1a(x) + ELSE 1 +SOp1(a) == a +OOp1(A(_), a) == A(a) +\* ASSUME OOp1(SOp1, 2) = 2 + +SOp1a(a) == {x \in {1,2} : a = x} +OOp2(A(_,_), a, b) == A(a, b) +\* ASSUME OOp2(SOp1a!@, 2, 2) = TRUE + +SOp1b(a) == {x + a : x \in {1,2}} +\* ASSUME OOp2(SOp1b!@, 2, 3) = 5 + +SOp1c(a) == {(LET c == a * x IN x + a) : x \in {1,2}} +\* ASSUME OOp2(SOp1c!@!c, 2, 3) = 2 * 3 + +SOp1d(a) == {(LET c(y) == (a * x) + y IN x + a) : x \in {1,2}} +OOp3(A(_,_,_), a, b, c) == A(a, b, c) +\* ASSUME OOp3(SOp1d!@!c, 2, 3, 4) = 2 * 3 + 4 + +OOp4(A(_,_,_,_), a, b, c, d) == A(a, b, c, d) + + +\* Foo1 == Op1(2)!(3)!2!1 +\* ASSUME Op1(2)!(3)!2!1 = 2 + 4*3 + 6 +\* ASSUME Op1(2)!(3)!2!Op1a(4) = 16 + 6 +\* ASSUME Op3(Op1!@!2!Op1a, 2, 3, 4) = 22 + +COp(q, p) == + CHOOSE x : lab(x) :: LET a ++ b == {a, b, q, p, x} + IN <<q, 99 ++ p >> + + +\* ASSUME COp(1, 2)!(3)!++(4, 5) = {2,1, 3, 4, 5} + +\* ASSUME COp(1, 2)!(3) = <<1, {99, 1, 2, 3} >> +\* ASSUME COp(1, 2)!(3)!1 = <<1, {99, 1, 2, 3} >> +\* ASSUME COp(1, 2)!(3)!1!>> = {99, 1, 2, 3} + +OOp5(A(_, _, _, _, _), a1, a2, a3, a4, a5) == A(a1, a2, a3, a4, a5) + +\* ASSUME OOp5(COp!@!++, 1, 2, 3, 4, 5) = {2,1, 3, 4, 5} +\* ASSUME COp(1, 2)!lab(3)!:!++(4, 5) = {2,1, 3, 4, 5} + +Inst == INSTANCE test206a WITH A5 <- COp!@!++ +\* ASSUME Inst!Foo(1, 2, 3, 4, 5) = {2,1, 3, 4, 5} +\* ASSUME Inst!Bar(3)!>> = 3 +\* ASSUME Inst!Bar2(3)!>> = 3 +\* ASSUME Inst!Bar3!>> = 2 +\* ASSUME Inst!Bar4(1)!(2)!<< = 2 +\* ASSUME Inst!Bar4(1)!(2)!>> = 1 +\* ASSUME Inst!Bar4(1)!lab(2)!<< = 2 +\* ASSUME Inst!Bar4(1)!lab(2)!>> = 1 +\* ASSUME OOp2(Inst!Bar4!@!<<, 1, 2) = 2 +\* ASSUME OOp2(Inst!Bar4!@!>>, 1, 2) = 1 +\* ASSUME OOp2(Inst!Bar4!lab!<<, 1, 2) = 2 +\* ASSUME OOp2(Inst!Bar4!lab!>>, 1, 2) = 1 +\* ASSUME Inst!Bar5(1)!lab(2)!:!++(3, 4) = <<1, 3, 4, 2>> +\* ASSUME Inst!Bar5(1)!(2)!++(3, 4) = <<1, 3, 4, 2>> +\* ASSUME OOp4(Inst!Bar5!lab!:!++, 1, 2, 3, 4) = <<1, 3, 4, 2>> + +Inst2(A5(_, _, _, _, _)) == INSTANCE test206a +\* ASSUME Inst2(COp!@!++)!Foo(1, 2, 3, 4, 5) = {2,1, 3, 4, 5} + + +COp2(q, p) == + CHOOSE x : lab(x) :: LET a ++ b == {lab(y):: y + a + b : y \in {1}} + IN <<q, 99 ++ p >> + +\* ASSUME COp2(1,2)!lab(3)!:!++(9,10)!lab(11) = 30 +\* ASSUME COp2(1,2)!(3)!++(9,10)!lab(11) = 30 +\* ASSUME COp2(1,2)!lab(3)!:!++(9,10)!(11) = 30 +OOp6(A(_, _, _, _, _, _), a1, a2, a3, a4, a5, a6) == A(a1, a2, a3, a4, a5, a6) +\* ASSUME OOp6(COp2!lab!:!++!lab, 1, 2, 3, 9, 10, 11) = 30 +\* ASSUME OOp6(COp2!@!++!lab, 1, 2, 3, 9, 10, 11) = 30 +\* ASSUME OOp6(COp2!lab!:!++!@, 1, 2, 3, 9, 10, 11) = 30 + +\* Let's test all the different kinds of operators + +Def1 == {1, 2, 3, 4} +\* ASSUME Def1!2 = 2 + +Def2 == {x \in {1, 2, 3} : x > 17} +\* ASSUME Def2!1 = {1, 2, 3} +\* ASSUME Def2!(14)!<< = 14 + +Def3 == {<<x, y>> : x \in {1}, y \in {2}} +\* ASSUME Def3!2!1 = 2 +\* ASSUME Def3!(1, 2)!<< = 1 + +Def4 == SUBSET UNION {{1}, {2}} +\* ASSUME Def4!<<!<<!2 = {2} + +Def5 == [i \in 1..10 |-> i+1][3] +\* ASSUME Def5!>> = 3 +\* ASSUME Def5!<<[4] = 5 +\* ASSUME Def5!<<!1!>> = 10 + +Def6 == [[i \in 1..10 |-> i+1] EXCEPT ![2] = 1, ![3] = 2] +\* ASSUME Def6!1[3] = 4 +\* The following two subexpression expressions are now +\* considered to be illegal because they could contain +\* a "@". +\* ASSUME Def6!2=1 +\* ASSUME Def6!3=2 + +Def7 == 3.a +\* ASSUME Def7!1 = 3 +\* ASSUME Def7!2 = "a" + +Def8 == [a |-> 1, b |-> 2] +\* ASSUME Def8!2 = 2 + +Def9 == [a : 1, b : 2] +\* ASSUME Def9!2 = 2 + +Def10 == <<1, 2, 3>> +\* ASSUME Def10!2 = 2 + +Def11 == {1} \X {2} \X {3} +\* ASSUME Def11!3!1 = 3 + +Def12 == IF 2=3 THEN 2 ELSE 3 +\* ASSUME /\ Def12!1!<< = 2 +\* /\ Def12!2 = 2 +\* /\ Def12!3 = 3 + +Def13 == \A x : CASE x=1 -> 1 + [] x=2 -> 2 + [] OTHER -> 3 +\* ASSUME /\ Def13!(42)!1!<<!<< = 42 +\* /\ Def13!(42)!1!>> = 1 +\* /\ Def13!(42)!2!>> = 2 +\* /\ Def13!(42)!3!>> = 3 + +Def14 == \A x \in {1} : x = 1 +\* ASSUME /\ Def14!(2)!<< = 2 +\* /\ Def14!1!1 = 1 + +Def15 == \A x \in {1}, y \in {2} : (x = 1) /\ (y = 2) +\* ASSUME /\ Def15!(2,3)!<<!<< = 2 +\* /\ Def15!(2,3)!>>!<< = 3 +\* /\ Def15!2!<< = 2 + +Def16 == \E x \in {1}, <<y, z>> \in {<<2,3>>} : /\ ((x = 1)) + /\ y = 2 + /\ (z=3) +\* ASSUME /\ Def16!(2,3,4)!1!1 = 2 +\* /\ Def16!(2,3,4)!2!1 = 3 +\* /\ Def16!2!1!<< = 2 + +Def17 == \E x , y, z : /\ ((x = 1)) + /\ y = 2 + /\ (z=3) +\* ASSUME /\ Def17!(2,3,4)!1!1 = 2 +\* /\ Def17!(2,3,4)!2!1 = 3 + +Def18 == CHOOSE x : (x=1) +\* ASSUME Def18!(2)!<< = 2 + +Def19 == CHOOSE x \in {42}: (x=1) +\* ASSUME /\ Def19!(2)!<< = 2 +\* /\ Def19!1!1 = 42 + +Def20 == [12]_(23) +\* ASSUME /\ Def20!1 = 12 +\* /\ Def20!>> = 23 + +Def21 == <<12>>_(23) +\* ASSUME /\ Def21!1 = 12 +\* /\ Def21!>> = 23 + +Def22 == WF_(12)(23) +\* ASSUME /\ Def22!1 = 12 +\* /\ Def22!>> = 23 + +Def23 == \EE x , y, z : /\ ((x = 1)) + /\ y = 2 + /\ (z=3) +\* ASSUME /\ Def23!(2,3,4)!1!1 = 2 +\* /\ Def23!(2,3,4)!2!1 = 3 + + +============================================================================= + +last modified on Wed 31 October 2012 at 14:45:24 PST by lamport \ No newline at end of file diff --git a/tlatools/test-model/suite/test22.cfg b/tlatools/test-model/suite/test22.cfg new file mode 100644 index 0000000000000000000000000000000000000000..d191341c2404a4660de5819024419df4563278cd --- /dev/null +++ b/tlatools/test-model/suite/test22.cfg @@ -0,0 +1,9 @@ + INIT + Init + + NEXT + Next + + + INVARIANT Inv + diff --git a/tlatools/test-model/suite/test22.tla b/tlatools/test-model/suite/test22.tla new file mode 100644 index 0000000000000000000000000000000000000000..7f40faf963139cdfb4f01ee341e52c36b6b00b03 --- /dev/null +++ b/tlatools/test-model/suite/test22.tla @@ -0,0 +1,40 @@ +--------------- MODULE test22 ------------- + +(* Test of fingerprinting. *) + +EXTENDS TLC, Naturals, Sequences + +VARIABLE x, y + +S == {1} +T == {2, 3, 4} + + + +Init == /\ Print("Distinct States should equal Distinct Initial States", TRUE) + /\ x \in [S -> [S -> SUBSET T]] + /\ y \in [S -> [S -> SUBSET [a : {2}, b : {3, 4, 5}]]] + +Inv == IF [S -> [S -> SUBSET [b : {4, 4, 3, 5, 4}, a : {2, 2}]]] # + [S -> [S -> SUBSET [a : {2}, b : {3, 4, 5}]]] + THEN Assert(FALSE, "Test 1 Failed") + ELSE TRUE + +Next == + \/ UNCHANGED <<x, y>> + + \/ /\ x' \in [S -> [S -> SUBSET {4, 4, 4, 3, 2, 2}]] + /\ UNCHANGED y + + \/ /\ y' \in [S -> [S -> SUBSET [b : {4, 4, 3, 5, 4}, a : {2, 2}]]] + /\ UNCHANGED x + + \/ /\ y' = [y EXCEPT ![1][1] = {[b |-> 4, a |-> 2]}] + /\ UNCHANGED x + + \/ /\ y' = [y EXCEPT ![1][1] = {[b |-> 3, a |-> 2]}] + /\ UNCHANGED x + + \/ /\ y' = [y EXCEPT ![1][1] = {[b |-> 5, a |-> 2]}] + /\ UNCHANGED x +========================================= diff --git a/tlatools/test-model/suite/test220.cfg b/tlatools/test-model/suite/test220.cfg new file mode 100644 index 0000000000000000000000000000000000000000..3f4b98ea5accf7e48d18d975a5c1822b0c00f4d3 --- /dev/null +++ b/tlatools/test-model/suite/test220.cfg @@ -0,0 +1,9 @@ + INIT + Init + + NEXT + Next + + CHECK_DEADLOCK FALSE + + INVARIANT Inv diff --git a/tlatools/test-model/suite/test220.tla b/tlatools/test-model/suite/test220.tla new file mode 100644 index 0000000000000000000000000000000000000000..c5a085d4e478b09b49d7098fd55594a2d31fad56 --- /dev/null +++ b/tlatools/test-model/suite/test220.tla @@ -0,0 +1,22 @@ +--------------- MODULE test220 ------------- + +(* Testing deadlock and number of states found. *) + +EXTENDS TLC, Naturals, Sequences, FiniteSets + +VARIABLE x, y, z + +Init == /\ Print("This test should not deadlock when configured with CHECK_DEADLOCK=FALSE", TRUE) + /\ x = {} + /\ y = {} + /\ z = 1 + +Next == /\ x = {} + /\ x' = {1} + /\ y' \in SUBSET x' + /\ z' = z + /\ y' = {} + +Inv == TRUE + +========================================= diff --git a/tlatools/test-model/suite/test23.cfg b/tlatools/test-model/suite/test23.cfg new file mode 100644 index 0000000000000000000000000000000000000000..6b7101b241f4a4d2db964521a677ea247a42e163 --- /dev/null +++ b/tlatools/test-model/suite/test23.cfg @@ -0,0 +1,5 @@ +INIT Init +NEXT Next +INVARIANT Inv +CONSTANT + SubBag <- MCSubBag diff --git a/tlatools/test-model/suite/test23.tla b/tlatools/test-model/suite/test23.tla new file mode 100644 index 0000000000000000000000000000000000000000..301039f8e8fe1f484e3c7edf5f0fdaa9556903fe --- /dev/null +++ b/tlatools/test-model/suite/test23.tla @@ -0,0 +1,284 @@ +--------------- MODULE test23 ------------- + +(* Test of definitions from the Bags module *) + +EXTENDS Naturals, FiniteSets, TLC + +IsABag(B) == + (************************************************************************) + (* True iff B is a bag. *) + (************************************************************************) + B \in [DOMAIN B -> {n \in Nat : n > 0}] + +BagToSet(B) == DOMAIN B + (************************************************************************) + (* The set of elements at least one copy of which is in B. *) + (************************************************************************) + +SetToBag(S) == [e \in S |-> 1] + (************************************************************************) + (* The bag that contains one copy of every element of the set S. *) + (************************************************************************) + +BagIn(e,B) == e \in BagToSet(B) + (************************************************************************) + (* The \in operator for bags. *) + (************************************************************************) + +EmptyBag == SetToBag({}) + +BagCup(B1, B2) == + (************************************************************************) + (* The union of bags B1 and B2. *) + (************************************************************************) + [e \in (DOMAIN B1) \cup (DOMAIN B2) |-> + (IF e \in DOMAIN B1 THEN B1[e] ELSE 0) + + (IF e \in DOMAIN B2 THEN B2[e] ELSE 0) ] + +B1 ++ B2 == BagCup(B1, B2) + +BagDiff(B1, B2) == + (************************************************************************) + (* The bag B1 with the elements of B2 removed--that is, with one copy *) + (* of an element removed from B1 for each copy of the same element in *) + (* B2. If B2 has at least as many copies of e as B1, then B1 -- B2 *) + (* has no copies of e. *) + (************************************************************************) + LET B == [e \in DOMAIN B1 |-> IF e \in DOMAIN B2 THEN B1[e] - B2[e] + ELSE B1[e]] + IN [e \in {d \in DOMAIN B : B[d] > 0} |-> B[e]] + +B1 -- B2 == BagDiff(B1, B2) + +LOCAL Sum(f) == + (******************************************************************) + (* The sum of f[x] for all x in DOMAIN f. The definition assumes *) + (* that f is a Nat-valued function and that f[x] equals 0 for all *) + (* but a finite number of elements x in DOMAIN f. *) + (******************************************************************) + LET D == {e \in DOMAIN f : f[e] > 0} + DSum[S \in SUBSET DOMAIN f] == + LET elt == CHOOSE e \in S : TRUE + IN IF S = {} THEN 0 + ELSE f[elt] + DSum[S \ {elt}] + IN DSum[DOMAIN f] + +BagUnion(S) == + (************************************************************************) + (* The bag union of all elements of the set S of bags. *) + (************************************************************************) + [e \in UNION {BagToSet(B) : B \in S} |-> + Sum( [B \in S |-> IF BagIn(e, B) THEN B[e] ELSE 0] ) ] + +B1 \sqsubseteq B2 == + (************************************************************************) + (* The subset operator for bags. B1 \sqsubseteq B2 iff, for all e, bag *) + (* B2 has at least as many copies of e as bag B1 does. *) + (************************************************************************) + /\ (DOMAIN B1) \subseteq (DOMAIN B2) + /\ \A e \in DOMAIN B1 : B1[e] \leq B2[e] + +SubBag(B) == + (************************************************************************) + (* The set of all subbags of bag B. *) + (************************************************************************) + LET AllBagsOfSubset == + (******************************************************************) + (* The set of all bags SB such that BagToSet(SB) \subseteq *) + (* BagToSet(B). *) + (******************************************************************) + UNION {[SB -> {n \in Nat : n > 0}] : SB \in SUBSET BagToSet(B)} + IN {SB \in AllBagsOfSubset : \A e \in DOMAIN SB : SB[e] \leq B[e]} + +BagOfAll(F(_), B) == + (************************************************************************) + (* The bag analog of the set {F(x) : x \in B} for a set B. It's the bag *) + (* that contains, for each element e of B, one copy of F(e) for every *) + (* copy of e in B. This defines a bag iff, for any value v, the set of *) + (* e in B such that F(e) = v is finite. *) + (************************************************************************) + [e \in {F(d) : d \in BagToSet(B)} |-> + Sum( [d \in BagToSet(B) |-> IF F(d) = e THEN B[d] ELSE 0] ) ] + +BagCardinality(B) == + (************************************************************************) + (* If B is a finite bag (one such that BagToSet(B) is a finite set), *) + (* then this is its cardinality (the total number of copies of elements *) + (* in B). Its value is unspecified if B is infinite. *) + (************************************************************************) + Sum(B) + +CopiesIn(e, B) == + (************************************************************************) + (* If B is a bag, then CopiesIn(e, B) is the number of copies of e in *) + (* B. If ~BagIn(e, B), then CopiesIn(e, B) = 0. *) + (************************************************************************) + IF BagIn(e, B) THEN B[e] ELSE 0 + + + +Max(a, b) == IF a < b THEN b ELSE a +MaxCopies(B) == + (*************************************************************************) + (* Maximum of B[e] s.t. e \in DOMAIN B *) + (*************************************************************************) + LET MC[S \in SUBSET(DOMAIN B)] == + LET elt == CHOOSE elt \in S : TRUE + IN IF S = {} THEN 0 + ELSE Max(B[elt], MC[S \ {elt}]) + IN MC[DOMAIN B] + +MCSubBag(B) == + (*************************************************************************) + (* Definition of SubBag that TLC can handle. *) + (*************************************************************************) + LET AllBagsOfSubset == + (******************************************************************) + (* The set of all bags SB such that BagToSet(SB) \subseteq *) + (* BagToSet(B). *) + (******************************************************************) + UNION {[SB -> {n \in 1..MaxCopies(B) : n > 0}] : SB \in SUBSET BagToSet(B)} + IN {SB \in AllBagsOfSubset : \A e \in DOMAIN SB : SB[e] \leq B[e]} + +VARIABLES x + +(* S == {"a"} *) +S == {"a", "b", "c"} + +B1 == SetToBag(S) +B2 == BagCup(SetToBag(S), SetToBag({"a"})) +B3 == [i \in S |-> 3] +B4 == BagUnion({B1, B2, B3}) +B9 == BagCup(SetToBag(S), SetToBag({"a"})) + +F1(e) == CASE e = "a" -> "fa" + [] e = "b" -> "fa" + [] e = "c" -> "fc" + [] e = "d" -> "fc" + +Init == /\ Print("Computing Init", TRUE) + /\ x = 1 + +Next == UNCHANGED x + +Inv == + /\ Print("Computing Inv", TRUE) + /\ Print(<<"B1", B1>>, TRUE) + /\ Print(SetToBag({"a"}), TRUE) + + /\ LET B7 == (DOMAIN B1) \cup (DOMAIN SetToBag({"a"})) + e1(e, BB) == (IF e \in DOMAIN BB THEN BB[e] ELSE 0) + IN /\ IF B7 = {} + THEN Assert(FALSE, "Failed Test 0a") + ELSE Print("Test 0a OK", TRUE) + + /\ IF [e \in B7 |-> 2]["a"] # 2 + THEN Assert(FALSE, "Failed Test 0b") + ELSE Print("Test 0b OK", TRUE) + + /\ IF [e \in B7 |-> e1(e, B1)]["a"] # 1 + THEN Assert(FALSE, "Failed Test 0c") + ELSE Print("Test 0c OK", TRUE) + + /\ IF [e \in B7 |-> e1(e, SetToBag({"a"}))]["a"] # 1 + THEN Assert(FALSE, "Failed Test 0d") + ELSE Print("Test 0d OK", TRUE) + + /\ IF [e \in B7 |-> e1(e, B1) + e1(e,SetToBag({"a"}))]["a"] # 2 + THEN Assert(FALSE, "Failed Test 0e") + ELSE Print("Test 0e OK", TRUE) + + /\ IF B9["a"] # 2 + THEN Assert(FALSE, "Failed Test 1aa") + ELSE Print("Test 1aa OK", TRUE) + /\ Print("Finished test 1aa", TRUE) + + /\ IF B2["a"] # 2 + THEN Assert(FALSE, "Failed Test 1a") + ELSE Print("Test 1a OK", TRUE) + /\ Print("Finished test 1a", TRUE) + + /\ IF BagToSet(B9) # S + THEN Assert(FALSE, "Failed Test 1b") + ELSE Print("Test 1b OK", TRUE) + + /\ IF BagToSet(B2) # S + THEN Assert(FALSE, "Failed Test 1c") + ELSE Print("Test 1c OK", TRUE) + + /\ IF BagToSet(B1 -- SetToBag({"a"})) # {"c", "b"} + THEN Assert(FALSE, "Failed Test 2") + ELSE Print("Test 2 OK", TRUE) + + /\ IF BagToSet(B3) # BagToSet(B1) + THEN Assert(FALSE, "Failed Test 3") + ELSE Print("Test 3 OK", TRUE) + + /\ IF BagIn("a", B1 -- SetToBag({"a"})) + THEN Assert(FALSE, "Failed Test 4") + ELSE Print("Test 4 OK", TRUE) + + /\ IF ~BagIn("a", B2) + THEN Assert(FALSE, "Failed Test 5") + ELSE Print("Test 5 OK", TRUE) + + /\ IF EmptyBag # B1 -- B1 + THEN Assert(FALSE, "Failed Test 6") + ELSE Print("Test 6 OK", TRUE) + + /\ IF ~(B1 \sqsubseteq B2) + THEN Assert(FALSE, "Failed Test 7") + ELSE Print("Test 7 OK", TRUE) + + /\ IF ~(B2 \sqsubseteq B3) + THEN Assert(FALSE, "Failed Test 8") + ELSE Print("Test 8 OK", TRUE) + + /\ IF B2 \sqsubseteq B1 + THEN Assert(FALSE, "Failed Test 9") + ELSE Print("Test 9 OK", TRUE) + + /\ IF BagCup(B1, SetToBag({"d"})) \sqsubseteq B1 + THEN Assert(FALSE, "Failed Test 10") + ELSE Print("Test 10 OK", TRUE) + + /\ IF ~(B1 \sqsubseteq (B1 ++ SetToBag({"d"})) ) + THEN Assert(FALSE, "Failed Test 11") + ELSE Print("Test 11 OK", TRUE) + + /\ IF BagToSet(B2 ++ SetToBag({"d"})) # S \cup {"d"} + THEN Assert(FALSE, "Failed Test 12") + ELSE Print("Test 12 OK", TRUE) + + /\ IF Cardinality(SubBag(B1)) # Cardinality(SUBSET S) + THEN Assert(FALSE, "Failed Test 13") + ELSE Print("Test 13 OK", TRUE) + + /\ IF Cardinality(SubBag(B3)) # 64 + THEN Assert(FALSE, "Failed Test 14") + ELSE Print("Test 14 OK", TRUE) + + /\ IF CopiesIn("fa", BagOfAll(F1, B1)) # 2 + THEN Assert(FALSE, "Failed Test 15") + ELSE Print("Test 15 OK", TRUE) + + /\ IF CopiesIn("a", BagOfAll(F1, B1)) # 0 + THEN Assert(FALSE, "Failed Test 16") + ELSE Print("Test 16 OK", TRUE) + + /\ IF CopiesIn("fa", BagOfAll(F1, B3)) # 6 + THEN Assert(FALSE, "Failed Test 17") + ELSE Print("Test 17 OK", TRUE) + + /\ IF BagCardinality(B1) # 3 + THEN Assert(FALSE, "Failed Test 18") + ELSE Print("Test 18 OK", TRUE) + + /\ IF BagCardinality(B3) # 9 + THEN Assert(FALSE, "Failed Test 19") + ELSE Print("Test 19 OK", TRUE) + +========================================================================== + + +============================================================================= diff --git a/tlatools/test-model/suite/test23bis.cfg b/tlatools/test-model/suite/test23bis.cfg new file mode 100644 index 0000000000000000000000000000000000000000..6b7101b241f4a4d2db964521a677ea247a42e163 --- /dev/null +++ b/tlatools/test-model/suite/test23bis.cfg @@ -0,0 +1,5 @@ +INIT Init +NEXT Next +INVARIANT Inv +CONSTANT + SubBag <- MCSubBag diff --git a/tlatools/test-model/suite/test23bis.tla b/tlatools/test-model/suite/test23bis.tla new file mode 100644 index 0000000000000000000000000000000000000000..c8324c3c20a6582895b47c8015d9ed47cc972a3b --- /dev/null +++ b/tlatools/test-model/suite/test23bis.tla @@ -0,0 +1,146 @@ +--------------- MODULE test23 ------------- + +EXTENDS Naturals, FiniteSets, Bags, TLC + +VARIABLES x + +(* S == {"a"} *) +S == {"a", "b", "c"} + +B1 == SetToBag(S) +B2 == BagCup(SetToBag(S), SetToBag({"a"})) +B3 == [i \in S |-> 3] +B4 == BagUnion({B1, B2, B3}) +B9 == BagCup(SetToBag(S), SetToBag({"a"})) + +F1(e) == CASE e = "a" -> "fa" + [] e = "b" -> "fa" + [] e = "c" -> "fc" + [] e = "d" -> "fc" + +Init == /\ Print("Computing Init", TRUE) + /\ x = 1 + +Next == UNCHANGED x + +Inv == + /\ Print("Computing Inv", TRUE) + /\ Print(<<"B1", B1>>, TRUE) + /\ Print(SetToBag({"a"}), TRUE) + + /\ LET B7 == (DOMAIN B1) \cup (DOMAIN SetToBag({"a"})) + e1(e, BB) == (IF e \in DOMAIN BB THEN BB[e] ELSE 0) + IN /\ IF B7 = {} + THEN Assert(FALSE, "Failed Test 0a") + ELSE Print("Test 0a OK", TRUE) + + /\ IF [e \in B7 |-> 2]["a"] # 2 + THEN Assert(FALSE, "Failed Test 0b") + ELSE Print("Test 0b OK", TRUE) + + /\ IF [e \in B7 |-> e1(e, B1)]["a"] # 1 + THEN Assert(FALSE, "Failed Test 0c") + ELSE Print("Test 0c OK", TRUE) + + /\ IF [e \in B7 |-> e1(e, SetToBag({"a"}))]["a"] # 1 + THEN Assert(FALSE, "Failed Test 0d") + ELSE Print("Test 0d OK", TRUE) + + /\ IF [e \in B7 |-> e1(e, B1) + e1(e,SetToBag({"a"}))]["a"] # 2 + THEN Assert(FALSE, "Failed Test 0e") + ELSE Print("Test 0e OK", TRUE) + + /\ IF B9["a"] # 2 + THEN Assert(FALSE, "Failed Test 1aa") + ELSE Print("Test 1aa OK", TRUE) + /\ Print("Finished test 1aa", TRUE) + + /\ IF B2["a"] # 2 + THEN Assert(FALSE, "Failed Test 1a") + ELSE Print("Test 1a OK", TRUE) + /\ Print("Finished test 1a", TRUE) + + /\ IF BagToSet(B9) # S + THEN Assert(FALSE, "Failed Test 1b") + ELSE Print("Test 1b OK", TRUE) + + /\ IF BagToSet(B2) # S + THEN Assert(FALSE, "Failed Test 1c") + ELSE Print("Test 1c OK", TRUE) + + /\ IF BagToSet(B1 -- SetToBag({"a"})) # {"c", "b"} + THEN Assert(FALSE, "Failed Test 2") + ELSE Print("Test 2 OK", TRUE) + + /\ IF BagToSet(B3) # BagToSet(B1) + THEN Assert(FALSE, "Failed Test 3") + ELSE Print("Test 3 OK", TRUE) + + /\ IF BagIn("a", B1 -- SetToBag({"a"})) + THEN Assert(FALSE, "Failed Test 4") + ELSE Print("Test 4 OK", TRUE) + + /\ IF ~BagIn("a", B2) + THEN Assert(FALSE, "Failed Test 5") + ELSE Print("Test 5 OK", TRUE) + + /\ IF EmptyBag # B1 -- B1 + THEN Assert(FALSE, "Failed Test 6") + ELSE Print("Test 6 OK", TRUE) + + /\ IF ~(B1 \sqsubseteq B2) + THEN Assert(FALSE, "Failed Test 7") + ELSE Print("Test 7 OK", TRUE) + + /\ IF ~(B2 \sqsubseteq B3) + THEN Assert(FALSE, "Failed Test 8") + ELSE Print("Test 8 OK", TRUE) + + /\ IF B2 \sqsubseteq B1 + THEN Assert(FALSE, "Failed Test 9") + ELSE Print("Test 9 OK", TRUE) + + /\ IF BagCup(B1, SetToBag({"d"})) \sqsubseteq B1 + THEN Assert(FALSE, "Failed Test 10") + ELSE Print("Test 10 OK", TRUE) + + /\ IF ~(B1 \sqsubseteq (B1 ++ SetToBag({"d"})) ) + THEN Assert(FALSE, "Failed Test 11") + ELSE Print("Test 11 OK", TRUE) + + /\ IF BagToSet(B2 ++ SetToBag({"d"})) # S \cup {"d"} + THEN Assert(FALSE, "Failed Test 12") + ELSE Print("Test 12 OK", TRUE) + + /\ IF Cardinality(SubBag(B1)) # Cardinality(SUBSET S) + THEN Assert(FALSE, "Failed Test 13") + ELSE Print("Test 13 OK", TRUE) + + /\ IF Cardinality(SubBag(B3)) # 64 + THEN Assert(FALSE, "Failed Test 14") + ELSE Print("Test 14 OK", TRUE) + + /\ IF CopiesIn("fa", BagOfAll(F1, B1)) # 2 + THEN Assert(FALSE, "Failed Test 15") + ELSE Print("Test 15 OK", TRUE) + + /\ IF CopiesIn("a", BagOfAll(F1, B1)) # 0 + THEN Assert(FALSE, "Failed Test 16") + ELSE Print("Test 16 OK", TRUE) + + /\ IF CopiesIn("fa", BagOfAll(F1, B3)) # 6 + THEN Assert(FALSE, "Failed Test 17") + ELSE Print("Test 17 OK", TRUE) + + /\ IF BagCardinality(B1) # 3 + THEN Assert(FALSE, "Failed Test 18") + ELSE Print("Test 18 OK", TRUE) + + /\ IF BagCardinality(B3) # 9 + THEN Assert(FALSE, "Failed Test 19") + ELSE Print("Test 19 OK", TRUE) + +========================================================================== + + +============================================================================= diff --git a/tlatools/test-model/suite/test24.cfg b/tlatools/test-model/suite/test24.cfg new file mode 100644 index 0000000000000000000000000000000000000000..d191341c2404a4660de5819024419df4563278cd --- /dev/null +++ b/tlatools/test-model/suite/test24.cfg @@ -0,0 +1,9 @@ + INIT + Init + + NEXT + Next + + + INVARIANT Inv + diff --git a/tlatools/test-model/suite/test24.tla b/tlatools/test-model/suite/test24.tla new file mode 100644 index 0000000000000000000000000000000000000000..9e24ff44d5f5d98b015f01bf310a2cf96da1ba5e --- /dev/null +++ b/tlatools/test-model/suite/test24.tla @@ -0,0 +1,71 @@ +--------------- MODULE test24 ------------- + +(* Test of UNCHANGED *) + +EXTENDS Naturals, TLC + +VARIABLES x, y + +Init == /\ x = [i \in 1..3 |-> i] + /\ y = 1 +Next == + + \/ /\ UNCHANGED <<x, y>> + /\ \A i \in 1..3 : (x[i])' = x[i] + /\ Print("Test 1 OK", TRUE) + + \/ /\ UNCHANGED <<x, y>> + /\ \A i \in 1..3 : UNCHANGED x[i] + /\ Print("Test 2 OK", TRUE) + + \/ /\ UNCHANGED <<x, y>> + /\ \A i \in 1..3 : UNCHANGED [a |-> x[i], b |-> x[i]+1] + /\ Print("Test 3 OK", TRUE) + + \/ /\ UNCHANGED <<x, y>> + /\ IF UNCHANGED \E i \in 1..3 : x[i]=3 + THEN Print("Test 4 OK", TRUE) + ELSE Assert(FALSE, "Test 4 Failed") + + \/ /\ UNCHANGED <<x, y>> + /\ IF UNCHANGED \A i \in 1..3 : x[i]=3 + THEN Print("Test 5 OK", TRUE) + ELSE Assert(FALSE, "Test 5 Failed") + + \/ /\ UNCHANGED <<x, y>> + /\ IF UNCHANGED CHOOSE i \in DOMAIN x : x[i] = 2 + THEN Print("Test 6 OK", TRUE) + ELSE Assert(FALSE, "Test 6 Failed") + + \/ /\ UNCHANGED <<x, y>> + /\ IF UNCHANGED {x[i] : i \in 1..2} + THEN Print("Test 7 OK", TRUE) + ELSE Assert(FALSE, "Test 7 Failed") + + \/ /\ UNCHANGED <<x, y>> + /\ IF UNCHANGED {i \in 3..5 : \E j \in 1..3 : x[j]=i} + THEN Print("Test 8 OK", TRUE) + ELSE Assert(FALSE, "Test 8 Failed") + + \/ /\ UNCHANGED <<x, y>> + /\ IF UNCHANGED DOMAIN x + THEN Print("Test 9 OK", TRUE) + ELSE Assert(FALSE, "Test 9 Failed") + + \/ /\ (y=1) /\ (y' = y+1) + /\ UNCHANGED <<x>> + /\ IF UNCHANGED {x} + THEN Print("Test 10 OK", TRUE) + ELSE Assert(FALSE, "Test 10 Failed") + + \/ /\ (y=1) /\ (y' = y+1) + /\ UNCHANGED <<x>> + /\ IF UNCHANGED {1, 2, y} + THEN Print("Test 11 OK", TRUE) + ELSE Assert(FALSE, "Test 11 Failed") + +Inv == TRUE +========================================================================== + + +============================================================================= diff --git a/tlatools/test-model/suite/test25.cfg b/tlatools/test-model/suite/test25.cfg new file mode 100644 index 0000000000000000000000000000000000000000..4ffaa30df7d616b53602ccd0633e211c7a778449 --- /dev/null +++ b/tlatools/test-model/suite/test25.cfg @@ -0,0 +1,12 @@ + INIT Init + + NEXT Next + + INVARIANT Inv + + CONSTANT + s1 = s1 + t1 = t1 + S = {s1, s2, s3} + T = {t1, t2} + c = c diff --git a/tlatools/test-model/suite/test25.tla b/tlatools/test-model/suite/test25.tla new file mode 100644 index 0000000000000000000000000000000000000000..f948d11468a1a99c96d324f902df107300c2d010 --- /dev/null +++ b/tlatools/test-model/suite/test25.tla @@ -0,0 +1,30 @@ +---------------------- MODULE test25 ---------------------- + +(* test of fingerprinting elements of [S -> [B -> C]] *) + +EXTENDS Naturals, Sequences, TLC + +CONSTANT S, T, s1, t1, c + +VARIABLES x, y, z, w + +Init == /\ Print("Should find only one state", TRUE) + /\ x = [i \in 1..3 |-> [j \in {4,5} |-> i+j]] + /\ y = [a |-> <<"b", "c">>, b |-> <<"c", "d", "e">>] + /\ z = [i \in S |-> [j \in {2,3} |-> j+1]] + /\ w = [i \in S |-> [j \in T |-> c]] + +Inv == TRUE + +Next == + \/ UNCHANGED <<x, y, z, w>> + + \/ /\ x' = << [j \in {4,5} |-> 1+j], [j \in {4,5} |-> 2+j], [j \in {4,5} |-> 3+j]>> + /\ y' = [i \in {"a", "b"} |-> + IF i = "a" THEN [j \in {1,2} |-> IF j = 1 THEN "b" ELSE "c"] + ELSE [<<"x", "d", "e">> EXCEPT ![1]= "c"]] + /\ z' = [[z EXCEPT ![s1] = [@ EXCEPT ![3] = 77]] EXCEPT ![s1][3] = 4] + /\ w' = [[w EXCEPT ![s1][t1] = s1] EXCEPT ![s1][t1] = c] + + +=========================================================== diff --git a/tlatools/test-model/suite/test26.cfg b/tlatools/test-model/suite/test26.cfg new file mode 100644 index 0000000000000000000000000000000000000000..e2bf187389678424ffd4335584ec990172d1030d --- /dev/null +++ b/tlatools/test-model/suite/test26.cfg @@ -0,0 +1,3 @@ +INIT Init +NEXT Next +INVARIANT Inv diff --git a/tlatools/test-model/suite/test26.tla b/tlatools/test-model/suite/test26.tla new file mode 100644 index 0000000000000000000000000000000000000000..c5c569383bf9e97e33e8d3cebab1ca759fabe894 --- /dev/null +++ b/tlatools/test-model/suite/test26.tla @@ -0,0 +1,47 @@ +--------------- MODULE test26 ------------- + +(* Test of LET *) + +EXTENDS TLC, Naturals, Sequences + +VARIABLE x + +Init == x = 0 + +FcnSubst(f, e(_)) == + (* A recursive definition of [i \in DOMAIN f |-> e(i)] *) + LET Range == {f[i] : i \in DOMAIN f} + FSet == UNION { [U -> Range] : U \in SUBSET DOMAIN f} + FSub[g \in FSet] == + IF g = << >> + THEN g + ELSE LET el == CHOOSE el \in DOMAIN g : TRUE + IN [ i \in DOMAIN g |-> + IF i = el + THEN e(i) + ELSE FSub[[j \in (DOMAIN g) \ {el} |-> f[j]]][i]] + IN FSub[f] + + +Inv == + + /\ LET A == {} + B == A + IN IF B = {} + THEN Print("Test 1 OK", TRUE) + ELSE Assert(FALSE, "Test 1 Failed") + + /\ LET A == {} + B == A + C[i \in B] == 17 + IN IF C = << >> + THEN Print("Test 2 OK", TRUE) + ELSE Assert(FALSE, "Test 2 Failed") + + /\ LET e(i) == i+1 + IN IF FcnSubst(<<1,2,3>>, e) = <<2,3,4>> + THEN Print("Test 3 OK", TRUE) + ELSE Assert(FALSE, "Test 3 Failed") + +Next == UNCHANGED x +========================================= diff --git a/tlatools/test-model/suite/test27.cfg b/tlatools/test-model/suite/test27.cfg new file mode 100644 index 0000000000000000000000000000000000000000..1b488723acf2908759c2ebde0150fa06a8606089 --- /dev/null +++ b/tlatools/test-model/suite/test27.cfg @@ -0,0 +1,12 @@ +INIT Init +NEXT Next +INVARIANT Inv +CONSTANT + Data = {d1} + InitData = {d1} + Addr = {a1, a2} + N = 2 + InitData = {d1} + InLen = 2 + OutLen = 1 + ND = ND diff --git a/tlatools/test-model/suite/test27.tla b/tlatools/test-model/suite/test27.tla new file mode 100644 index 0000000000000000000000000000000000000000..f6e7a57e64ab2d70606c70c0ab6d85850af373d9 --- /dev/null +++ b/tlatools/test-model/suite/test27.tla @@ -0,0 +1,121 @@ +--------------------------- MODULE test27 ---------------------------------- +(***************************************************************************) +(* A fingerprint test that models LazyCache. *) +(***************************************************************************) + +EXTENDS Naturals, Sequences, FiniteSets, TLC + +CONSTANT Addr, (* A set *) + Data, (* A set *) + InitData, (* A subset of Data *) + ND, (* A model value *) + N, (* The number of processors *) + InLen, OutLen (* Maximum lengths of in and out *) + +VARIABLE mem, c, in, out + +Proc == 1..N + + +Exp(a, m) == + (*************************************************************************) + (* Recursive definition of a^n. *) + (*************************************************************************) + LET E[n \in Nat] == IF n = 0 THEN 1 ELSE a * E[n-1] + IN E[m] + +CountSeq(S, len) == + (*************************************************************************) + (* Number of elements in Seq(S) of length \leq len. *) + (*************************************************************************) + LET CS[n \in Nat] == + (*******************************************************************) + (* Recursive definition of CountSeq(S, n). There are *) + (* (Cardinality(S))^n sequences of length n. *) + (*******************************************************************) + IF n = 0 THEN 1 + ELSE Exp(Cardinality(S), n) + CS[n-1] + IN CS[len] + + +Op == (Data \X Addr) \cup {<<d, a, "*">> : d \in Data, a \in Addr} + (***********************************************) + (* Have to write Data \X Addr \X {"*"} *) + (* in this way. *) + (***********************************************) + +Restrict(f) == + { g \in [Addr -> Data \cup { ND }] : + \A a \in Addr: g[a] \in { f[a] , ND } } + +Init == /\ Print(LET D == Cardinality(Data) + A == Cardinality(Addr) + P == Cardinality(Proc) + IN <<"Number of distinct states =", + Exp(D, A) (* No. of values of mem *) + * Exp(D+1, P * A) (* No. of values of c *) + * Exp(CountSeq(Op, InLen), P) (* No. of values of in *) + * Exp(CountSeq(Data \X Addr, OutLen), P) (* No. of values of out *) + >>, TRUE) + /\ mem \in [Addr -> InitData] + /\ c \in [Proc -> Restrict(mem)] + /\ in = [i \in Proc |-> <<>>] + /\ out = [i \in Proc |-> <<>>] + +Inv == + /\ IF mem \in [Addr -> Data] + THEN TRUE + ELSE /\ Print("Conj 1 False", TRUE) + /\ FALSE + /\ IF c \in [Proc -> [Addr -> (Data \cup {ND})] ] + THEN TRUE + ELSE /\ Print("Conj 2 False", TRUE) + /\ FALSE + /\ IF in \in [Proc -> Seq(Op)] + THEN TRUE + ELSE /\ Print("Conj 3 False", TRUE) + /\ FALSE + /\ IF \A p \in Proc: Len(in[p]) \leq InLen + THEN TRUE + ELSE /\ Print("Conj 4 False", TRUE) + /\ FALSE + /\ IF out \in [Proc -> Seq(Data \X Addr)] + THEN TRUE + ELSE /\ Print("Conj 5 False", TRUE) + /\ FALSE + /\ IF \A p \in Proc: Len(out[p]) \leq OutLen + THEN TRUE + ELSE /\ Print("Conj 6 False", TRUE) + /\ FALSE + +Next == + \/ UNCHANGED <<mem, c, in, out>> + + \/ /\ \E a \in Addr, d \in Data : mem' = [mem EXCEPT ![a] = d] + /\ UNCHANGED <<c, in, out>> + + \/ \E p \in Proc : + + \/ /\ \E a \in Addr, d \in Data \cup {ND} : + c' = [c EXCEPT ![p][a] = d] + /\ UNCHANGED <<mem, in, out>> + + \/ /\ Len(out[p]) < OutLen + /\ \E o \in Data \X Addr : out' = [out EXCEPT ![p] = @ \o <<o>>] + /\ UNCHANGED <<mem, c, in>> + + \/ /\ Len(in[p]) < InLen + /\ \E o \in Op : in' = [in EXCEPT ![p] = @ \o <<o>>] + /\ UNCHANGED <<mem, c, out>> + + \/ /\ out[p] # << >> + /\ out' = [out EXCEPT ![p] = Tail(@)] + /\ UNCHANGED <<mem, c, in>> + + \/ /\ in[p] # << >> + /\ in' = [in EXCEPT ![p] = Tail(@)] + /\ UNCHANGED <<mem, c, out>> + + + +============================================================================= diff --git a/tlatools/test-model/suite/test28.cfg b/tlatools/test-model/suite/test28.cfg new file mode 100644 index 0000000000000000000000000000000000000000..b0ee260b28eef2026bbc2f6cb01c89fd3d01d947 --- /dev/null +++ b/tlatools/test-model/suite/test28.cfg @@ -0,0 +1,8 @@ + INIT + Init + + NEXT + Next + + INVARIANT Inv + diff --git a/tlatools/test-model/suite/test28.tla b/tlatools/test-model/suite/test28.tla new file mode 100644 index 0000000000000000000000000000000000000000..55b36d6e15561e17f719ae2cc07ed3e8490563c6 --- /dev/null +++ b/tlatools/test-model/suite/test28.tla @@ -0,0 +1,31 @@ +--------------- MODULE test28 ------------- +\* Some random test + +EXTENDS TLC, Naturals, Sequences + +VARIABLE x + +BdedSeq(S, n) == UNION {[1..i -> S] : i \in 0..n} + +Init == /\ x = 2 + /\ x > 1 + /\ Print("Test 1 OK", TRUE) + /\ 1..x = {1,2} + /\ Print("Test 2 OK", TRUE) + /\ {i + 1 : i \in {x}} = {3} + /\ Print("Test 3 OK", TRUE) + /\ {i \in {x} : i = 2} = {2} + /\ Print("Test 4 OK", TRUE) + /\ \E i \in {x} : i = 2 + /\ Print("Test 5 OK", TRUE) + /\ \E i \in 1..x : i < x + /\ Print("Test 6 OK", TRUE) + /\ IF BdedSeq({x}, 2) # {<<>>, <<2>>, <<2,2>>} + THEN Print("Failed Test 7", TRUE) + ELSE Print("Test 7 OK", TRUE) + +Next == UNCHANGED x + +Inv == TRUE + +========================================= diff --git a/tlatools/test-model/suite/test29.cfg b/tlatools/test-model/suite/test29.cfg new file mode 100644 index 0000000000000000000000000000000000000000..b0ee260b28eef2026bbc2f6cb01c89fd3d01d947 --- /dev/null +++ b/tlatools/test-model/suite/test29.cfg @@ -0,0 +1,8 @@ + INIT + Init + + NEXT + Next + + INVARIANT Inv + diff --git a/tlatools/test-model/suite/test29.tla b/tlatools/test-model/suite/test29.tla new file mode 100644 index 0000000000000000000000000000000000000000..86a5fbbd934b994310ba0b5cb63343fc0e273330 --- /dev/null +++ b/tlatools/test-model/suite/test29.tla @@ -0,0 +1,30 @@ +--------------- MODULE test29 ------------- + +\* Testing proper evaluation of primed variables in action + +EXTENDS TLC, Naturals, Sequences + +VARIABLE x + + +Init == x = 2 + +Next == /\ x' = 2 + /\ x' > 1 + /\ Print("Test 1 OK", TRUE) + /\ 1..x' = {1,2} + /\ Print("Test 2 OK", TRUE) + /\ {i + 1 : i \in {x'}} = {3} + /\ Print("Test 3 OK", TRUE) + /\ {i \in {x'} : i = 2} = {2} + /\ Print("Test 4 OK", TRUE) + /\ \E i \in {x'} : i = 2 + /\ Print("Test 5 OK", TRUE) + /\ \E i \in 1..x' : i < x' + /\ Print("Test 6 OK", TRUE) + + + +Inv == TRUE + +========================================= diff --git a/tlatools/test-model/suite/test3.cfg b/tlatools/test-model/suite/test3.cfg new file mode 100644 index 0000000000000000000000000000000000000000..d32cfa879ee7389879ef2ad6bdf490a5e474c707 --- /dev/null +++ b/tlatools/test-model/suite/test3.cfg @@ -0,0 +1,12 @@ + INIT + Init + + NEXT + Next + + INVARIANT Inv + + CONSTANT + a = a + b = b + c = c diff --git a/tlatools/test-model/suite/test3.tla b/tlatools/test-model/suite/test3.tla new file mode 100644 index 0000000000000000000000000000000000000000..cba6800e84ce49d26b2d17f42587e1514d1e18d5 --- /dev/null +++ b/tlatools/test-model/suite/test3.tla @@ -0,0 +1,121 @@ +--------------- MODULE test3 ------------- + +(* Test of function application. *) + +EXTENDS Naturals, TLC + +VARIABLE x +CONSTANT a, b, c + +Type == x \in {1} +Inv == + /\ TRUE + + /\ IF [i \in {3, 2, 1} |-> i+1][3] # 4 + THEN Assert(FALSE, "Test 1") + ELSE Print("Test 1", TRUE) + + /\ IF <<a, b, c>>[2] # b + THEN Assert(FALSE, "Test 2") + ELSE Print("Test 2", TRUE) + + /\ IF [a |-> 1, b |-> 42, c |-> 99]["b"] # 42 + THEN Assert(FALSE, "Test 3" ) + ELSE Print("Test 3" , TRUE) + + /\ IF [i \in Nat |-> i+1][3] # 4 + THEN Assert(FALSE, "Test 4" ) + ELSE Print("Test 4" , TRUE) + + /\ IF [[i \in Nat |-> i+1] EXCEPT ![42] = 3, ![42] = 5][42] # 5 + THEN Assert(FALSE, "Test 5" ) + ELSE Print("Test 5" , TRUE) + + /\ IF [[i \in Nat |-> i+1] EXCEPT ![42] = 3, ![42] = 5][79] # 80 + THEN Assert(FALSE, "Test 6" ) + ELSE Print("Test 6" , TRUE) + + /\ IF [i \in Nat |-> [j \in Nat |-> 2*i + j]][5][3] # 13 + THEN Assert(FALSE, "Test 7" ) + ELSE Print("Test 7" , TRUE) + + /\ IF [[i \in Nat |-> [j \in Nat |-> 2*i + j]] EXCEPT ![5] = 11][5] # 11 + THEN Assert(FALSE, "Test 8" ) + ELSE Print("Test 8" , TRUE) + /\ IF [[i \in Nat |-> [j \in Nat |-> 2*i + j]] EXCEPT ![5][3] = @+1][5][3] # 14 + THEN Assert(FALSE, "Test 9" ) + ELSE Print("Test 9" , TRUE) + + /\ IF [[i \in Nat |-> [j \in Nat |-> 2*i + j]] + EXCEPT ![5][4] = @+2, ![5][3] = @+1][5][3] # 14 + THEN Assert(FALSE, "Test 10" ) + ELSE Print("Test 10" , TRUE) + + /\ IF [[i \in Nat |-> [j \in Nat |-> 2*i + j]] + EXCEPT ![5][3] = @+1, ![5][4] = @+2][5][3] # 14 + THEN Assert(FALSE, "Test 11" ) + ELSE Print("Test 11" , TRUE) + + /\ IF [[i \in Nat |-> [j \in Nat |-> 2*i + j]] + EXCEPT ![5][3] = @+1, ![5][3] = @+2][5][3] # 16 + THEN Assert(FALSE, "Test 12" ) + ELSE Print("Test 12" , TRUE) + + /\ IF [i \in Nat, j \in STRING |-> <<i+1, j>>][22, "b"] # <<23, "b">> + THEN Assert(FALSE, "Test 13" ) + ELSE Print("Test 13" , TRUE) + + /\ IF [i \in Nat, j \in STRING |-> <<i+1, j>>][<<22, "b">>] # <<23, "b">> + THEN Assert(FALSE, "Test 14" ) + ELSE Print("Test 14" , TRUE) + + /\ IF [[i \in Nat, j \in STRING |-> <<i+1, j>>] + EXCEPT ![22, "c"] = 15] [22, "b"] # <<23, "b">> + THEN Assert(FALSE, "Test 15" ) + ELSE Print("Test 15" , TRUE) + + /\ IF [[i \in Nat, j \in STRING |-> <<i+1, j>>] + EXCEPT ![22, "c"] = 15] [22, "c"] # 15 + THEN Assert(FALSE, "Test 16" ) + ELSE Print("Test 16" , TRUE) + + /\ IF [[i \in Nat, j \in STRING |-> <<i+1, j>>] + EXCEPT ![22, "c"] = 15] [<<22, "c">>] # 15 + THEN Assert(FALSE, "Test 17" ) + ELSE Print("Test 17" , TRUE) + + /\ IF [[i \in Nat, j \in STRING |-> <<i+1, j>>] + EXCEPT ![22, "c"] = 15, ![22, "d"] = 33] [22, "c"] # 15 + THEN Assert(FALSE, "Test 18" ) + ELSE Print("Test 18" , TRUE) + + /\ IF [[i \in Nat, j \in STRING |-> <<i+1, j>>] + EXCEPT ![22, "d"] = 33, ![22, "c"] = 15] [22, "c"] # 15 + THEN Assert(FALSE, "Test 19" ) + ELSE Print("Test 19" , TRUE) + + /\ IF [[i \in Nat, j \in STRING |-> <<i+1, j>>] + EXCEPT ![22, "d"] = 33, ![22, "d"] = 15] [22, "d"] # 15 + THEN Assert(FALSE, "Test 20" ) + ELSE Print("Test 20" , TRUE) + + /\ IF [[i \in Nat, j \in STRING |-> <<i+1, j>>] + EXCEPT ![22, "d"] = @[1]] [22, "d"] # 23 + THEN Assert(FALSE, "Test 21" ) + ELSE Print("Test 21" , TRUE) + + + /\ IF [[i \in Nat, j \in Nat |-> <<i+1, j>>] + EXCEPT ![22, 3] = @[1]] [22, 3] # 23 + THEN Assert(FALSE, "Test 22" ) + ELSE Print("Test 22" , TRUE) + + /\ Print("Tests completed", TRUE) + + + /\ Print("Tests completed", TRUE) + +Init == x = 1 + +Next == UNCHANGED x +========================================= diff --git a/tlatools/test-model/suite/test30-true.cfg b/tlatools/test-model/suite/test30-true.cfg new file mode 100644 index 0000000000000000000000000000000000000000..392d8232df0571fe24d200364209c46f8ae4dc1a --- /dev/null +++ b/tlatools/test-model/suite/test30-true.cfg @@ -0,0 +1,15 @@ + INIT + Init + + NEXT + Next + + INVARIANT Inv + + CONSTANT + P <- PRep + ++ <- PlusPlus + PLen <- Len + Plus <- + + Seq <- MCSeq + \o <- MCCat \ No newline at end of file diff --git a/tlatools/test-model/suite/test30-true.tla b/tlatools/test-model/suite/test30-true.tla new file mode 100644 index 0000000000000000000000000000000000000000..39aa0937add02d93fbb437e307f2fc24759b7155 --- /dev/null +++ b/tlatools/test-model/suite/test30-true.tla @@ -0,0 +1,47 @@ +--------------- MODULE test31 ------------- + +(* Test replacement of and by infix operators and overridden operators. *) + +EXTENDS TLC, Naturals, Sequences + +VARIABLE x + +CONSTANT P(_,_), _++_ , Plus(_, _), PLen(_) + +PlusPlus(a, b) == <<a, b>> + +PRep(a, b) == {a, b} + +MCSeq(a) = {a} + +MCCat(a, b) == a + b + +Init == /\ x = 1 + /\ IF P(2, x+3) = {2, 4} + THEN Print("Test 1 OK", TRUE) + ELSE Assert(FALSE, "Test 1 Failed") + /\ IF (2++(x+3)) = <<2, 4>> + THEN Print("Test 2 OK", TRUE) + ELSE Assert(FALSE, "Test 2 Failed") + /\ IF PLen(<<1, 2, 3>>) = 3 + THEN Print("Test 3 OK", TRUE) + ELSE Assert(FALSE, "Test 3 Failed") + /\ IF Plus(2, x+3) = 6 + THEN Print("Test 4 OK", TRUE) + ELSE Assert(FALSE, "Test 4 Failed") + /\ IF Seq(22) = {22} + THEN Print("Test 5 OK", TRUE) + ELSE Assert(FALSE, "Test 5 Failed") + /\ IF 1 \o 2 = 3 (* Huh???? *) + THEN Print("Test 6 OK", TRUE) + ELSE Assert(FALSE, "Test 6 Failed") + /\ LET a \prec b == a < b + IN IF 1 \prec 2 + THEN Print("Test 7 OK", TRUE) + ELSE Assert(FALSE, "Test 7 Failed") + +Next == UNCHANGED x + +Inv == TRUE + +========================================= diff --git a/tlatools/test-model/suite/test30.cfg b/tlatools/test-model/suite/test30.cfg new file mode 100644 index 0000000000000000000000000000000000000000..eecec9a4f071796a4236f4bf1bf5a1a1e115560e --- /dev/null +++ b/tlatools/test-model/suite/test30.cfg @@ -0,0 +1,15 @@ + INIT + Init + + NEXT + Next + + INVARIANT Inv + + CONSTANT + P <- PRep + PLen <- Len + Seq <- MCSeq + ++ <- PlusPlus + Plus <- + + \o <- + \ No newline at end of file diff --git a/tlatools/test-model/suite/test30.tla b/tlatools/test-model/suite/test30.tla new file mode 100644 index 0000000000000000000000000000000000000000..9be35d89a743e1d9f919d6ded96a944984e268c1 --- /dev/null +++ b/tlatools/test-model/suite/test30.tla @@ -0,0 +1,47 @@ +--------------- MODULE test30 ------------- + +(* Test replacement of and by infix operators and overridden operators. *) + +EXTENDS TLC, Naturals, Sequences + +VARIABLE x + +CONSTANT P(_,_), _++_ , Plus(_, _), PLen(_) + +PlusPlus(a, b) == <<a, b>> + +PRep(a, b) == {a, b} + +MCSeq(a) == {a} + +MCCat(a, b) == a + b + +Init == /\ x = 1 + /\ IF P(2, x+3) = {2, 4} + THEN Print("Test 1 OK", TRUE) + ELSE Assert(FALSE, "Test 1 Failed") + /\ IF (2++(x+3)) = <<2, 4>> + THEN Print("Test 2 OK", TRUE) + ELSE Assert(FALSE, "Test 2 Failed") + /\ IF PLen(<<1, 2, 3>>) = 3 + THEN Print("Test 3 OK", TRUE) + ELSE Assert(FALSE, "Test 3 Failed") + /\ IF Plus(2, x+3) = 6 + THEN Print("Test 4 OK", TRUE) + ELSE Assert(FALSE, "Test 4 Failed") + /\ IF Seq(22) = {22} + THEN Print("Test 5 OK", TRUE) + ELSE Assert(FALSE, "Test 5 Failed") + /\ IF 1 \o 2 = 3 (* Huh???? *) + THEN Print("Test 6 OK", TRUE) + ELSE Assert(FALSE, "Test 6 Failed") + /\ LET a \prec b == a < b + IN IF 1 \prec 2 + THEN Print("Test 7 OK", TRUE) + ELSE Assert(FALSE, "Test 7 Failed") + +Next == UNCHANGED x + +Inv == TRUE + +========================================= diff --git a/tlatools/test-model/suite/test31.cfg b/tlatools/test-model/suite/test31.cfg new file mode 100644 index 0000000000000000000000000000000000000000..c73ef51536cac7f74a6e6d78154edf3a7c89d042 --- /dev/null +++ b/tlatools/test-model/suite/test31.cfg @@ -0,0 +1,9 @@ + INIT + Init + + NEXT + Next + + INVARIANT Inv + + \ No newline at end of file diff --git a/tlatools/test-model/suite/test31.tla b/tlatools/test-model/suite/test31.tla new file mode 100644 index 0000000000000000000000000000000000000000..2b7a423e5886ff892a1ef38240362c49d658af25 --- /dev/null +++ b/tlatools/test-model/suite/test31.tla @@ -0,0 +1,20 @@ +--------------- MODULE test31 ------------- + +EXTENDS TLC, Naturals, Sequences, FiniteSets + +VARIABLE x, y + +Init == /\ Print("This test should generate 3 distinct state", TRUE) + /\ x = 1 + /\ y = 1 + +Next == \/ /\ Print("1", TRUE) + /\ x' \in {1, 2, 3} + /\ y' = 1 + /\ Print(<<"Generated state", x', y'>>, TRUE) + + \/ UNCHANGED <<x, y>> + +Inv == TRUE + +========================================= diff --git a/tlatools/test-model/suite/test32.cfg b/tlatools/test-model/suite/test32.cfg new file mode 100644 index 0000000000000000000000000000000000000000..c73ef51536cac7f74a6e6d78154edf3a7c89d042 --- /dev/null +++ b/tlatools/test-model/suite/test32.cfg @@ -0,0 +1,9 @@ + INIT + Init + + NEXT + Next + + INVARIANT Inv + + \ No newline at end of file diff --git a/tlatools/test-model/suite/test32.tla b/tlatools/test-model/suite/test32.tla new file mode 100644 index 0000000000000000000000000000000000000000..87a6664bf99a0b9f191409038712442737a9c418 --- /dev/null +++ b/tlatools/test-model/suite/test32.tla @@ -0,0 +1,32 @@ +--------------- MODULE test32 ------------- + +(* Test of [A]_e and <<A>>_e *) + +EXTENDS TLC, Naturals, Sequences, FiniteSets + +VARIABLE x, y + +Init == /\ x = 1 + /\ y = 1 + +Act1 == /\ x' = 1 + /\ y' = 1 + +Next == \/ /\ UNCHANGED <<x, y>> + /\ IF [x # x]_<<x,y>> + THEN Print("Test1 OK", TRUE) + ELSE Assert(FALSE, "Test 1 Failed") + /\ IF <<Act1>>_<<x,y>> + THEN Assert(FALSE, "Test 2 Failed") + ELSE Print("Test2 OK", TRUE) + + \/ /\ <<x'= 1 /\ y'=1>>_y + /\ Assert(FALSE, "Test 3 Failed") + + \/ /\ Print("Test 4 started -- should finish", TRUE) + /\ [FALSE]_<<x,y>> + /\ Print("Test 4 completed" , TRUE) + +Inv == TRUE + +========================================= diff --git a/tlatools/test-model/suite/test33.cfg b/tlatools/test-model/suite/test33.cfg new file mode 100644 index 0000000000000000000000000000000000000000..c73ef51536cac7f74a6e6d78154edf3a7c89d042 --- /dev/null +++ b/tlatools/test-model/suite/test33.cfg @@ -0,0 +1,9 @@ + INIT + Init + + NEXT + Next + + INVARIANT Inv + + \ No newline at end of file diff --git a/tlatools/test-model/suite/test33.tla b/tlatools/test-model/suite/test33.tla new file mode 100644 index 0000000000000000000000000000000000000000..9e003bb6c16936af9961f44fd48f0c48d1235449 --- /dev/null +++ b/tlatools/test-model/suite/test33.tla @@ -0,0 +1,23 @@ +--------------- MODULE test33 ------------- + +(* Test of large sets of functions *) + +EXTENDS TLC, Naturals + +VARIABLE x + +Init == /\ Print(<<"Computing", 7^6, "States">>, TRUE) + /\ x \in [1..6 -> 1..7] + +Next == UNCHANGED x + +Inv == TRUE +========================================= + +Works for 7^6 = 117649 states on dix in 43.049u 3.729s 1:16.97 60.7% + (1529 states/sec) +Works for 6^7 = 279936 states on dix in 113.333u 19.494s 3:37.24 61.1% + (1289 states/sec) +works for 7^7 = 823543 states on rowdy in 655.835u 158.050s 11:03.11 122.7% + (1242 states/sec) +Doesn't work for 7^7 = 823543 states on dix diff --git a/tlatools/test-model/suite/test34.cfg b/tlatools/test-model/suite/test34.cfg new file mode 100644 index 0000000000000000000000000000000000000000..df8f60f24b3d0c5ab136521162c93d401c5e7be4 --- /dev/null +++ b/tlatools/test-model/suite/test34.cfg @@ -0,0 +1,12 @@ + INIT + Init + + NEXT + Next + + INVARIANT Inv + + CONSTANT a = a + b = b + c = c + \ No newline at end of file diff --git a/tlatools/test-model/suite/test34.tla b/tlatools/test-model/suite/test34.tla new file mode 100644 index 0000000000000000000000000000000000000000..9dc008ae6c95f853eca91dc9f4dd8263026c4456 --- /dev/null +++ b/tlatools/test-model/suite/test34.tla @@ -0,0 +1,25 @@ +--------------- MODULE test34 ------------- + +(* Test Model Values *) + +EXTENDS TLC, Naturals, Sequences, FiniteSets + +CONSTANTS a, b, c + +VARIABLE x + +Init == x = 1 + +Next == UNCHANGED x + +Inv == /\ IF a \in STRING THEN Assert(FALSE, "Test 1 failed") + ELSE Print("Test 1 OK", TRUE) + /\ IF a = {1, 2} THEN Assert(FALSE, "Test 2 failed") + ELSE Print("Test 2 OK", TRUE) + /\ IF a \in {i \in Nat : i > 7} + THEN Assert(FALSE, "Test 3 failed") + ELSE Print("Test 3 OK", TRUE) + /\ IF a \in {b, c} THEN Assert(FALSE, "Test 4 failed") + ELSE Print("Test 4 OK", TRUE) + +========================================= diff --git a/tlatools/test-model/suite/test35.cfg b/tlatools/test-model/suite/test35.cfg new file mode 100644 index 0000000000000000000000000000000000000000..5e8cedc4cac917a8a41ec9ad1e980ca0e367180c --- /dev/null +++ b/tlatools/test-model/suite/test35.cfg @@ -0,0 +1,11 @@ + INIT + Init + + NEXT + Next + + INVARIANT Inv + + + + \ No newline at end of file diff --git a/tlatools/test-model/suite/test35.tla b/tlatools/test-model/suite/test35.tla new file mode 100644 index 0000000000000000000000000000000000000000..bae48a67643eb3f980d2c0880ad1ad91815c8e6f --- /dev/null +++ b/tlatools/test-model/suite/test35.tla @@ -0,0 +1,96 @@ +------------------------------ MODULE test35 ----------------------------- +(* Test of standard Sequences module. *) + +EXTENDS Sequences, Integers, TLC + +VARIABLES x + +Init == x = 0 + +Next == UNCHANGED x + +Inv == + /\ IF <<1, 2, 3>> \in Seq(Nat) + THEN Print("Test 1 OK", TRUE) + ELSE Assert(FALSE, "Test 1 Failed") + + /\ IF <<1, -2, 3>> \notin Seq(Nat) + THEN Print("Test 2 OK", TRUE) + ELSE Assert(FALSE, "Test 2 Failed") + + /\ IF <<1, 2, 3>> \o <<4, 5>> = <<1, 2, 3, 4, 5>> + THEN Print("Test 3 OK", TRUE) + ELSE Assert(FALSE, "Test 3 Failed") + + /\ IF << >> \o << >> = << >> + THEN Print("Test 4 OK", TRUE) + ELSE Assert(FALSE, "Test 4 Failed") + + /\ IF << >> \o <<1, 2 >> = <<1, 2 >> + THEN Print("Test 5 OK", TRUE) + ELSE Assert(FALSE, "Test 5 Failed") + + /\ IF Len(<< >>) = 0 + THEN Print("Test 6 OK", TRUE) + ELSE Assert(FALSE, "Test 6 Failed") + + /\ IF Len(<<1, 2, 3>>) = 3 + THEN Print("Test 7 OK", TRUE) + ELSE Assert(FALSE, "Test 7 Failed") + + /\ IF Append(<<1, 2, 3>>, 4) = <<1, 2, 3, 4>> + THEN Print("Test 8 OK", TRUE) + ELSE Assert(FALSE, "Test 8 Failed") + + /\ IF Append(<< >>, "a") = <<"a">> + THEN Print("Test 9 OK", TRUE) + ELSE Assert(FALSE, "Test 9 Failed") + + /\ IF Head(<< 1 >>) = 1 + THEN Print("Test 10 OK", TRUE) + ELSE Assert(FALSE, "Test 10 Failed") + + /\ IF Head(<<1, 2, 3>>) = 1 + THEN Print("Test 11 OK", TRUE) + ELSE Assert(FALSE, "Test 11 Failed") + + /\ IF Tail(<<1>>) = << >> + THEN Print("Test 12 OK", TRUE) + ELSE Assert(FALSE, "Test 12 Failed") + + /\ IF Tail (<<1, 2, 3>>) = <<2, 3>> + THEN Print("Test 13 OK", TRUE) + ELSE Assert(FALSE, "Test 13 Failed") + +(* /\ IF SubSeq(<<1, 2, 3, 4, 5>>, 2, 4) = <<2, 3, 4>> + THEN Print("Test 14 OK", TRUE) + ELSE Assert(FALSE, "Test 14 Failed") + + /\ IF SubSeq(<<1, 2, 3, 4, 5>>, 1, 1) = <<1>> + THEN Print("Test 15 OK", TRUE) + ELSE Assert(FALSE, "Test 15 Failed") *) + + /\ IF SubSeq(<<1, 2, 3, 4, 5>>, 2, 1) = <<>> + THEN Print("Test 16 OK", TRUE) + ELSE Assert(FALSE, "Test 16 Failed") + +(* /\ IF SubSeq(<<1, 2, 3, 4, 5>>, 5, 5) = <<5>> + THEN Print("Test 17 OK", TRUE) + ELSE Assert(FALSE, "Test 17 Failed") *) + + /\ IF LET T(a) == a \in Nat + IN SelectSeq(<<-1, 2, -3, 4, -5, 6>>, T) = <<2, 4, 6>> + THEN Print("Test 18 OK", TRUE) + ELSE Assert(FALSE, "Test 18 Failed") + + /\ IF LET T(a) == a \in Nat + IN SelectSeq(<<0, -1, 2, -3, 4, -5>>, T) = <<0, 2, 4>> + THEN Print("Test 19 OK", TRUE) + ELSE Assert(FALSE, "Test 19 Failed") + + /\ IF LET T(a) == a \in Nat + IN SelectSeq(<<-1, -3, -5>>, T) = << >> + THEN Print("Test 20 OK", TRUE) + ELSE Assert(FALSE, "Test 20 Failed") + +============================================================================= diff --git a/tlatools/test-model/suite/test36.cfg b/tlatools/test-model/suite/test36.cfg new file mode 100644 index 0000000000000000000000000000000000000000..5e8cedc4cac917a8a41ec9ad1e980ca0e367180c --- /dev/null +++ b/tlatools/test-model/suite/test36.cfg @@ -0,0 +1,11 @@ + INIT + Init + + NEXT + Next + + INVARIANT Inv + + + + \ No newline at end of file diff --git a/tlatools/test-model/suite/test36.tla b/tlatools/test-model/suite/test36.tla new file mode 100644 index 0000000000000000000000000000000000000000..fc4f689d0def508b0e72d6ec01ddccf30383e7bb --- /dev/null +++ b/tlatools/test-model/suite/test36.tla @@ -0,0 +1,90 @@ +----------------------------- MODULE test36 -------------------------------- + +(* Test of standard Bags module *) + +EXTENDS Naturals, TLC, Sequences, FiniteSets, Bags + +Bag1 == SetToBag({"a", "b"}) +Bag2 == Bag1 (+) SetToBag({"a"}) + +VARIABLES x +Init == x = 0 +Next == x'=x + +Inv == + /\ IF IsABag( Bag1 ) + THEN Print("Test 1 OK", TRUE) + ELSE Assert(FALSE, "Test 1 Failed") + + /\ IF BagToSet(Bag2) = {"a", "b"} + THEN Print("Test 2 OK", TRUE) + ELSE Assert(FALSE, "Test 2 Failed") + + /\ IF BagIn("a", Bag2) + THEN Print("Test 3 OK", TRUE) + ELSE Assert(FALSE, "Test 3 Failed") + + /\ IF ~BagIn("c", Bag2) + THEN Print("Test 4 OK", TRUE) + ELSE Assert(FALSE, "Test 4 Failed") + + /\ IF BagToSet(EmptyBag) = {} + THEN Print("Test 5 OK", TRUE) + ELSE Assert(FALSE, "Test 5 Failed") + + /\ IF EmptyBag (+) Bag2 = Bag2 + THEN Print("Test 6 OK", TRUE) + ELSE Assert(FALSE, "Test 6 Failed") + + /\ IF Bag2 (-) SetToBag({"a"}) = Bag1 + THEN Print("Test 7 OK", TRUE) + ELSE Assert(FALSE, "Test 7 Failed") + + /\ IF Bag1 (-) SetToBag({"a"}) = SetToBag({"b"}) + THEN Print("Test 8 OK", TRUE) + ELSE Assert(FALSE, "Test 8 Failed") + + /\ IF BagUnion({Bag1, Bag2}) = Bag1 (+) Bag2 + THEN Print("Test 9 OK", TRUE) + ELSE Assert(FALSE, "Test 9 Failed") + + /\ IF Bag1 \sqsubseteq Bag2 + THEN Print("Test 10 OK", TRUE) + ELSE Assert(FALSE, "Test 10 Failed") + + /\ Print("TLC Fails Tests 11 & 12 because SubBag not implemented", TRUE) +(* /\ IF Bag1 \in SubBag(Bag2) + THEN Print("Test 11 OK", TRUE) + ELSE Assert(FALSE, "Test 11 Failed") + + /\ IF Cardinality(SubBag(Bag2)) = 6 + THEN Print("Test 12 OK", TRUE) + ELSE Assert(FALSE, "Test 12 Failed") *) + + /\ IF LET f(a) == <<"c", a>> + IN CopiesIn(<<"c", "a">>, BagOfAll(f, Bag2)) = 2 + THEN Print("Test 13 OK", TRUE) + ELSE Assert(FALSE, "Test 13 Failed") + + /\ IF BagCardinality(Bag2) = 3 + THEN Print("Test 14 OK", TRUE) + ELSE Assert(FALSE, "Test 14 Failed") + + /\ IF CopiesIn("a", Bag2) = 2 + THEN Print("Test 15 OK", TRUE) + ELSE Assert(FALSE, "Test 15 Failed") + + /\ IF CopiesIn("c", Bag2) = 0 + THEN Print("Test 16 OK", TRUE) + ELSE Assert(FALSE, "Test 16 Failed") + + + +============================================================================= + +(* Last modified on Thu Aug 08 13:28:29 PT 2002 by lamport *) + + 6 Apr 99 : Modified version for standard module set + 7 Dec 98 : Corrected error found by Stephan Merz. + 6 Dec 98 : Modified comments based on suggestions by Lyle Ramshaw. + 5 Dec 98 : Initial version. \ No newline at end of file diff --git a/tlatools/test-model/suite/test37.cfg b/tlatools/test-model/suite/test37.cfg new file mode 100644 index 0000000000000000000000000000000000000000..5e8cedc4cac917a8a41ec9ad1e980ca0e367180c --- /dev/null +++ b/tlatools/test-model/suite/test37.cfg @@ -0,0 +1,11 @@ + INIT + Init + + NEXT + Next + + INVARIANT Inv + + + + \ No newline at end of file diff --git a/tlatools/test-model/suite/test37.tla b/tlatools/test-model/suite/test37.tla new file mode 100644 index 0000000000000000000000000000000000000000..bedc7c94e11d48293bf475f18968fec21770d45b --- /dev/null +++ b/tlatools/test-model/suite/test37.tla @@ -0,0 +1,29 @@ +---------------------------- MODULE test37 ----------------------------- + +(* Test of standard FiniteSets module *) + +EXTENDS FiniteSets, TLC + +VARIABLES x + +Init == x = 0 +Next == x'=x +Inv == + /\ IF IsFiniteSet({"a", "b"}) + THEN Print("Test 1 OK", TRUE) + ELSE Assert(FALSE, "Test 1 Failed") + + /\ IF IsFiniteSet({}) + THEN Print("Test 2 OK", TRUE) + ELSE Assert(FALSE, "Test 2 Failed") + + /\ IF Cardinality({}) = 0 + THEN Print("Test 3 OK", TRUE) + ELSE Assert(FALSE, "Test 3 Failed") + + /\ IF Cardinality({"a", "b"}) = 2 + THEN Print("Test 4 OK", TRUE) + ELSE Assert(FALSE, "Test 4 Failed") + + +============================================================================= diff --git a/tlatools/test-model/suite/test38.cfg b/tlatools/test-model/suite/test38.cfg new file mode 100644 index 0000000000000000000000000000000000000000..067eacc43100c3c5c3c09d818e2ed16510c3f573 --- /dev/null +++ b/tlatools/test-model/suite/test38.cfg @@ -0,0 +1,15 @@ + INIT + Init + + NEXT + Next + + CONSTANT c = c + + INVARIANT Inv + + + + + + \ No newline at end of file diff --git a/tlatools/test-model/suite/test38.tla b/tlatools/test-model/suite/test38.tla new file mode 100644 index 0000000000000000000000000000000000000000..580c1ba6cfdf2bae4368c3e69a47d072d9063773 --- /dev/null +++ b/tlatools/test-model/suite/test38.tla @@ -0,0 +1,16 @@ +---------------------------- MODULE test38 ----------------------------- + +(* Test of EXTENDS *) + +EXTENDS test38a, TLC + +VARIABLES x +\* CONSTANT c + +Init == MInit(x) +Next == x'=c +Inv == IF (x=c) THEN Print("Test OK", TRUE) + ELSE Print("Test Failed", TRUE) + + +============================================================================= diff --git a/tlatools/test-model/suite/test38a.tla b/tlatools/test-model/suite/test38a.tla new file mode 100644 index 0000000000000000000000000000000000000000..537c7c2415fba686c4fd29e3a4bc2ad6f6810923 --- /dev/null +++ b/tlatools/test-model/suite/test38a.tla @@ -0,0 +1,10 @@ +---------------------------- MODULE test38a ----------------------------- + +(* Test of EXTENDS *) + +\* VARIABLES x multiple declarations no longer allowed. + CONSTANT c + +MInit(x) == x = c + +============================================================================= diff --git a/tlatools/test-model/suite/test39.cfg b/tlatools/test-model/suite/test39.cfg new file mode 100644 index 0000000000000000000000000000000000000000..0cc6c2124ed19c2b3250dc1dfa1ab569eca3c252 --- /dev/null +++ b/tlatools/test-model/suite/test39.cfg @@ -0,0 +1,13 @@ + INIT + Init + + NEXT + Next + + CONSTRAINT Constraint + + INVARIANT Inv + + + + \ No newline at end of file diff --git a/tlatools/test-model/suite/test39.tla b/tlatools/test-model/suite/test39.tla new file mode 100644 index 0000000000000000000000000000000000000000..6e13785f161b10c66d29d5c8e1d8ca036d7ff516 --- /dev/null +++ b/tlatools/test-model/suite/test39.tla @@ -0,0 +1,36 @@ +--------------- MODULE test39 ------------- + +(* Test priming and operator arguments *) + +EXTENDS TLC, Integers, Sequences, FiniteSets + +VARIABLE x, y + +Init == /\ x = [i \in {1,2} |-> i] + /\ y = 1 + +Action1(c,d) == c' = [c EXCEPT ![1] = d'] +Action2(c,d) == c' = [c EXCEPT ![1] = d] + + +Next == \/ /\ y = 1 + /\ y' = 2 + /\ Action1(x, y) + /\ IF x[1]' = 2 + THEN Print("Test1 OK", TRUE) + ELSE Assert(FALSE, "Test1 Failed") + + \/ /\ y = 1 + /\ y' = 2 + /\ Action2(x, y) + /\ IF x[1]' = 1 + THEN Print("Test2 OK", TRUE) + ELSE Assert(FALSE, "Test2 Failed") + + \/ UNCHANGED <<x, y>> + +Inv == TRUE + + +Constraint == TRUE +========================================= diff --git a/tlatools/test-model/suite/test4.cfg b/tlatools/test-model/suite/test4.cfg new file mode 100644 index 0000000000000000000000000000000000000000..b0ee260b28eef2026bbc2f6cb01c89fd3d01d947 --- /dev/null +++ b/tlatools/test-model/suite/test4.cfg @@ -0,0 +1,8 @@ + INIT + Init + + NEXT + Next + + INVARIANT Inv + diff --git a/tlatools/test-model/suite/test4.tla b/tlatools/test-model/suite/test4.tla new file mode 100644 index 0000000000000000000000000000000000000000..74529b488e490a575ed3f055105325faa103a418 --- /dev/null +++ b/tlatools/test-model/suite/test4.tla @@ -0,0 +1,72 @@ +--------------- MODULE test4 ------------- + +(* Test of fingerprinting of sets. *) + +EXTENDS Naturals, TLC, Sequences + +VARIABLE x, y, z, w + +Type == + /\ x \in {{1, 2, 3}} + /\ y \in {{"a", "b", "c"}} + /\ z \in {[a : {1, 2, 3}, b : {1, 2, 3}, c : {1, 2, 3}]} + /\ w \in {[{1, 2, 3} -> {1, 2, 3}]} + + +Init == + /\ Print("Should find only one distinct state", TRUE) + /\ x = {1, 2, 3} + /\ y = {"a", "b", "c"} + /\ z = [a : {1, 2, 3}, b : {1, 2, 3}, c : {1, 2, 3}] + /\ w = [{1, 2, 3} -> {1, 2, 3}] + +Inv == + /\ TRUE + /\ x = {1, 2, 3} + /\ y = {"a", "b", "c"} + /\ z = [a : {1, 2, 3}, b : {1, 2, 3}, c : {1, 2, 3}] + /\ w = [{1, 2, 3} -> {1, 2, 3}] + + +Next == + \/ /\ x' = {3, 3, 2, 1} + /\ UNCHANGED <<y, z, w>> + /\ Print("Test 1", TRUE) + + \/ /\ x' = 1..3 + /\ UNCHANGED <<y, z, w>> + /\ Print("Test 2", TRUE) + + \/ /\ x' = {i \in {5, 4, 3, 3, 2, 2, 1} : i \leq 3} + /\ UNCHANGED <<y, z, w>> + /\ Print("Test 2", TRUE) + + \/ /\ x' = {i-3 : i \in 4..6} + /\ UNCHANGED <<y, z, w>> + /\ Print("Test 2", TRUE) + + \/ /\ x' = {i-3 : i \in {6, 6, 5, 4, 4, 5, 5}} + /\ UNCHANGED <<y, z, w>> + /\ Print("Test 2", TRUE) + + \/ /\ x' = { f[i] : i \in 1..3, f \in [{1,2,3} -> {1,2,3}] } + /\ UNCHANGED <<y, z, w>> + /\ Print("Test 3", TRUE) + + \/ /\ y' = DOMAIN [a |-> 1, b |-> 2, c |-> 3] + /\ UNCHANGED <<x, z, w>> + /\ Print("Test 4", TRUE) + \/ /\ z' = [{"a", "b", "c"} -> {1, 2, 3}] + /\ UNCHANGED <<y, x, w>> + /\ Print("Test 5", TRUE) + + \/ /\ w' = [{3,1, 2} -> {1, 3, 2}] + /\ UNCHANGED <<y, x, z>> + /\ Print("Test 6", TRUE) + + \/ /\ w' = [{3,1, 3, 3, 3, 2} -> {2, 2, 1, 3, 2}] + /\ UNCHANGED <<y, x, z>> + /\ Print("Test 6", TRUE) + + +============================================ diff --git a/tlatools/test-model/suite/test40.cfg b/tlatools/test-model/suite/test40.cfg new file mode 100644 index 0000000000000000000000000000000000000000..5e8cedc4cac917a8a41ec9ad1e980ca0e367180c --- /dev/null +++ b/tlatools/test-model/suite/test40.cfg @@ -0,0 +1,11 @@ + INIT + Init + + NEXT + Next + + INVARIANT Inv + + + + \ No newline at end of file diff --git a/tlatools/test-model/suite/test40.tla b/tlatools/test-model/suite/test40.tla new file mode 100644 index 0000000000000000000000000000000000000000..e4d631d9757c5abcbf85f142bfe9e4c7c31b2040 --- /dev/null +++ b/tlatools/test-model/suite/test40.tla @@ -0,0 +1,101 @@ +------------------------------ MODULE test40 ----------------------------- +(* Test of standard Naturals module. *) + +EXTENDS Naturals, TLC + +VARIABLES x + +Init == x = 0 + +Next == UNCHANGED x + +Inv == + /\ IF 2^10 + 2^10 = 2^11 + THEN Print("Test 1 OK", TRUE) + ELSE Assert(FALSE, "Test 1 Failed") + + /\ IF 0^20 = 0 + THEN Print("Test 2 OK", TRUE) + ELSE Assert(FALSE, "Test 2 Failed") + + /\ IF 2^21 - 2^20 = 2^20 + THEN Print("Test 2a OK", TRUE) + ELSE Assert(FALSE, "Test 2a Failed") + + /\ IF 2 - 3 \notin Nat + THEN Print("Test 3 OK", TRUE) + ELSE Assert(FALSE, "Test 3 Failed") + + /\ IF 123 * 345 = 42435 + THEN Print("Test 4 OK", TRUE) + ELSE Assert(FALSE, "Test 4 Failed") + + /\ IF 123 < 124 + THEN Print("Test 5 OK", TRUE) + ELSE Assert(FALSE, "Test 5 Failed") + + /\ IF 12345 > 12344 + THEN Print("Test 6 OK", TRUE) + ELSE Assert(FALSE, "Test 6 Failed") + + /\ IF 123 \leq 123 + THEN Print("Test 7 OK", TRUE) + ELSE Assert(FALSE, "Test 7 Failed") + + /\ IF 12345 \geq 12345 + THEN Print("Test 8 OK", TRUE) + ELSE Assert(FALSE, "Test 8 Failed") + + /\ IF 123 \leq 124 + THEN Print("Test 9 OK", TRUE) + ELSE Assert(FALSE, "Test 9 Failed") + + /\ IF 12344 \geq 12333 + THEN Print("Test 10 OK", TRUE) + ELSE Assert(FALSE, "Test 10 Failed") + + /\ IF 145939 = 487 * (145939 \div 487) + (145939 % 487) + THEN Print("Test 11 OK", TRUE) + ELSE Assert(FALSE, "Test 11 Failed") + + /\ IF 139982 \div 1 = 139982 + THEN Print("Test 12 OK", TRUE) + ELSE Assert(FALSE, "Test 12 Failed") + + /\ IF 123099 % 1 = 0 + THEN Print("Test 13 OK", TRUE) + ELSE Assert(FALSE, "Test 13 Failed") + + /\ IF 0 % 345 = 0 + THEN Print("Test 14 OK", TRUE) + ELSE Assert(FALSE, "Test 14 Failed") + + /\ IF 24 % 9 = 6 + THEN Print("Test 15 OK", TRUE) + ELSE Assert(FALSE, "Test 15 Failed") + + /\ IF 4566799 = 423 * (4566799 \div 423) + (4566799 % 423) + THEN Print("Test 16 OK", TRUE) + ELSE Assert(FALSE, "Test 16 Failed") + + /\ IF 2222222 = 18 * (2222222 \div 18) + (2222222 % 18) + THEN Print("Test 17 OK", TRUE) + ELSE Assert(FALSE, "Test 17 Failed") + + /\ IF 3 .. 2 = {} + THEN Print("Test 18 OK", TRUE) + ELSE Assert(FALSE, "Test 18 Failed") + + /\ IF 2..4 = {2, 3, 4} + THEN Print("Test 19 OK", TRUE) + ELSE Assert(FALSE, "Test 19 Failed") + + /\ IF (0-1) * (0-3) = 3 + THEN Print("Test 20 OK", TRUE) + ELSE Assert(FALSE, "Test 20 Failed") + + /\ IF (0-1) % 3 = 2 + THEN Print("Test 21 OK", TRUE) + ELSE Assert(FALSE, "Test 21 Failed") + +============================================================================= diff --git a/tlatools/test-model/suite/test41.cfg b/tlatools/test-model/suite/test41.cfg new file mode 100644 index 0000000000000000000000000000000000000000..5e8cedc4cac917a8a41ec9ad1e980ca0e367180c --- /dev/null +++ b/tlatools/test-model/suite/test41.cfg @@ -0,0 +1,11 @@ + INIT + Init + + NEXT + Next + + INVARIANT Inv + + + + \ No newline at end of file diff --git a/tlatools/test-model/suite/test41.tla b/tlatools/test-model/suite/test41.tla new file mode 100644 index 0000000000000000000000000000000000000000..3101ecb97f7f4712251af5cf238c5842afc4eae9 --- /dev/null +++ b/tlatools/test-model/suite/test41.tla @@ -0,0 +1,130 @@ +------------------------------ MODULE test41 ----------------------------- +(* Test of standard Integers module. *) + +EXTENDS Integers, TLC + +VARIABLES x + +Init == x = 0 + +Next == UNCHANGED x + +Inv == + /\ IF 2^10 + 2^10 = 2^11 + THEN Print("Test 1 OK", TRUE) + ELSE Assert(FALSE, "Test 1 Failed") + + /\ IF 0^20 = 0 + THEN Print("Test 2 OK", TRUE) + ELSE Assert(FALSE, "Test 2 Failed") + + /\ IF 2 - 3 \in Int + THEN Print("Test 3 OK", TRUE) + ELSE Assert(FALSE, "Test 3 Failed") + + /\ IF (-123) * (-345) = 42435 + THEN Print("Test 4 OK", TRUE) + ELSE Assert(FALSE, "Test 4 Failed") + + /\ IF -123 > -124 + THEN Print("Test 5 OK", TRUE) + ELSE Assert(FALSE, "Test 5 Failed") + + /\ IF -12345 < -12344 + THEN Print("Test 6 OK", TRUE) + ELSE Assert(FALSE, "Test 6 Failed") + + /\ IF -123 \leq -123 + THEN Print("Test 7 OK", TRUE) + ELSE Assert(FALSE, "Test 7 Failed") + + /\ IF -12345 \geq -12345 + THEN Print("Test 8 OK", TRUE) + ELSE Assert(FALSE, "Test 8 Failed") + + /\ IF -123 \geq -124 + THEN Print("Test 9 OK", TRUE) + ELSE Assert(FALSE, "Test 9 Failed") + + /\ IF -12344 \leq 12333 + THEN Print("Test 10 OK", TRUE) + ELSE Assert(FALSE, "Test 10 Failed") + + /\ IF -145939 = 487 * ((-145939) \div 487) + ((-145939) % 487) + THEN Print("Test 11 OK", TRUE) + ELSE Assert(FALSE, "Test 11 Failed") + + /\ IF -139982 \div 1 = -139982 + THEN Print("Test 12 OK", TRUE) + ELSE Assert(FALSE, "Test 12 Failed") + + /\ IF -123099 % 1 = 0 + THEN Print("Test 13 OK", TRUE) + ELSE Assert(FALSE, "Test 13 Failed") + + /\ IF (-1) % 345 = 344 + THEN Print("Test 14 OK", TRUE) + ELSE Assert(FALSE, "Test 14 Failed") + + /\ IF (-24) % 9 = 3 + THEN Print("Test 15 OK", TRUE) + ELSE Assert(FALSE, "Test 15 Failed") + + /\ IF -4566799 = 423 * ((-4566799) \div 423) + ((-4566799) % 423) + THEN Print("Test 16 OK", TRUE) + ELSE Assert(FALSE, "Test 16 Failed") + + /\ IF -2222222 = 18 * ((-2222222) \div 18) + (-2222222 % 18) + THEN Print("Test 17 OK", TRUE) + ELSE Assert(FALSE, "Test 17 Failed") + + /\ IF (-2) .. (-3) = {} + THEN Print("Test 18 OK", TRUE) + ELSE Assert(FALSE, "Test 18 Failed") + + /\ IF (-4)..(-2) = {-2, -3, -4} + THEN Print("Test 19 OK", TRUE) + ELSE Assert(FALSE, "Test 19 Failed") + + /\ IF 2*3+4 = 10 + THEN Print("Test 20 OK", TRUE) + ELSE Assert(FALSE, "Test 20 Failed") + + /\ IF 2^3*3 = 24 + THEN Print("Test 21 OK", TRUE) + ELSE Assert(FALSE, "Test 21 Failed") + + + /\ IF 4+2*3 = 10 + THEN Print("Test 22 OK", TRUE) + ELSE Assert(FALSE, "Test 22 Failed") + + + /\ IF 3*2^3 = 24 + THEN Print("Test 23 OK", TRUE) + ELSE Assert(FALSE, "Test 23 Failed") + + + /\ IF 1 + -2 = -1 + THEN Print("Test 24 OK", TRUE) + ELSE Assert(FALSE, "Test 24 Failed") + + + /\ IF 5 + 5 \div 2 = 7 + THEN Print("Test 25 OK", TRUE) + ELSE Assert(FALSE, "Test 25 Failed") + + /\ IF 5 \div 2 + 2 = 4 + THEN Print("Test 26 OK", TRUE) + ELSE Assert(FALSE, "Test 26 Failed") + + /\ IF 3 - 2 * 5 = -7 + THEN Print("Test 27 OK", TRUE) + ELSE Assert(FALSE, "Test 27 Failed") + + /\ IF 4 * 2 % 3 = 2 + THEN Print("Test 28 OK", TRUE) + ELSE Assert(FALSE, "Test 28 Failed") + + +============================================================================= diff --git a/tlatools/test-model/suite/test42.cfg b/tlatools/test-model/suite/test42.cfg new file mode 100644 index 0000000000000000000000000000000000000000..5e8cedc4cac917a8a41ec9ad1e980ca0e367180c --- /dev/null +++ b/tlatools/test-model/suite/test42.cfg @@ -0,0 +1,11 @@ + INIT + Init + + NEXT + Next + + INVARIANT Inv + + + + \ No newline at end of file diff --git a/tlatools/test-model/suite/test42.tla b/tlatools/test-model/suite/test42.tla new file mode 100644 index 0000000000000000000000000000000000000000..2768d3996b52860a848531b47edca7c6b9e69590 --- /dev/null +++ b/tlatools/test-model/suite/test42.tla @@ -0,0 +1,44 @@ +--------------- MODULE test42 ------------- + +(* Test of operator arguments *) + +EXTENDS TLC, Integers, Sequences + +VARIABLE x + +a ++ b == a + b + +Foo(a, _--_) == a -- 0 + + +Init == x = 1 + + +Next == x' = x + +FooBar(a, Op(_,_)) == Op(a, 0) + +Plus(a, b) == a + b + +Inv == /\ IF FooBar(7, Plus) = 7 + THEN Print("Test 1 OK", TRUE) + ELSE Assert(FALSE, "Test 1 Failed") + + /\ IF FooBar(7,+) = 7 + THEN Print("Test 2 OK", TRUE) + ELSE Assert(FALSE, "Test 2 Failed") + + /\ IF Foo(7, *) = 0 + THEN Print("Test 3 OK", TRUE) + ELSE Assert(FALSE, "Test 3 Failed") + + /\ IF Foo(7,+) = 7 + THEN Print("Test 4 OK", TRUE) + ELSE Assert(FALSE, "Test 4 Failed") + + /\ IF Foo(7,++) = 7 + THEN Print("Test 5 OK", TRUE) + ELSE Assert(FALSE, "Test 5 Failed") + +Constraint == TRUE +========================================= diff --git a/tlatools/test-model/suite/test43.cfg b/tlatools/test-model/suite/test43.cfg new file mode 100644 index 0000000000000000000000000000000000000000..5e8cedc4cac917a8a41ec9ad1e980ca0e367180c --- /dev/null +++ b/tlatools/test-model/suite/test43.cfg @@ -0,0 +1,11 @@ + INIT + Init + + NEXT + Next + + INVARIANT Inv + + + + \ No newline at end of file diff --git a/tlatools/test-model/suite/test43.tla b/tlatools/test-model/suite/test43.tla new file mode 100644 index 0000000000000000000000000000000000000000..65ed2fe94fc9b732312cf42bcc8cc21651f3f407 --- /dev/null +++ b/tlatools/test-model/suite/test43.tla @@ -0,0 +1,83 @@ +--------------- MODULE test43 ------------- + +(* Test of TLC module *) + +EXTENDS TLC, Integers, Sequences + +VARIABLE x + +FApply(f, Op(_,_), Identity) == + LET fa[S \in SUBSET DOMAIN f] == + IF S = { } THEN Identity + ELSE LET s == CHOOSE s \in S : TRUE + IN Op(f[s], fa[S \ {s}]) + + IN fa[DOMAIN f] + +BoundedSeq(S, n) == UNION {[1..i -> S] : i \in 0..n} +Plus(a, b) == a + b + +Times(a, b) == a * b + +Init == x = 1 + +Next == x' = x + +Inv == + + /\ IF FApply([i \in {"a", "b", "c"} |-> 3], Times, 1) = 27 + THEN Print("Test 1 OK", TRUE) + ELSE Assert(FALSE, "Test 1 Failed") + + /\ IF FApply(<<3, 4, 5, 6>> , Plus, 0) = 18 + THEN Print("Test 2 OK", TRUE) + ELSE Assert(FALSE, "Test 2 Failed") + + /\ IF FApply(<<3>> , Plus, 0) = 3 + THEN Print("Test 3 OK", TRUE) + ELSE Assert(FALSE, "Test 3 Failed") + + /\ IF FApply(<<>> , Plus, 7) = 7 + THEN Print("Test 4 OK", TRUE) + ELSE Assert(FALSE, "Test 4 Failed") + + /\ IF <<1, 2>> \in BoundedSeq(1..3, 3) + THEN Print("Test 5 OK", TRUE) + ELSE Assert(FALSE, "Test 5 Failed") + + /\ IF <<1, 2, 3>> \notin BoundedSeq(1..3, 2) + THEN Print("Test 6 OK", TRUE) + ELSE Assert(FALSE, "Test 6 Failed") + + /\ IF <<-1, 2>> \notin BoundedSeq(1..4, 6) + THEN Print("Test 7 OK", TRUE) + ELSE Assert(FALSE, "Test 7 Failed") + + /\ IF << >> \in BoundedSeq({"a", "b"}, 2) + THEN Print("Test 8 OK", TRUE) + ELSE Assert(FALSE, "Test 8 Failed") + + /\ IF \A s \in BoundedSeq({"a", "b", "c"}, 2) : + Len(s) \leq 2 + THEN Print("Test 9 OK", TRUE) + ELSE Assert(FALSE, "Test 10 Failed") + + /\ IF SortSeq(<<3, 2, 4, 2>>, \leq) = <<2, 2, 3, 4>> + THEN Print("Test 10 OK", TRUE) + ELSE Assert(FALSE, "Test 12 Failed") + + /\ IF SortSeq(<<3, 2, 4, 2>>, <) = <<2, 2, 3, 4>> + THEN Print("Test 11 OK", TRUE) + ELSE Assert(FALSE, "Test 13 Failed") + + /\ IF SortSeq(<<>>, >) = <<>> + THEN Print("Test 12 OK", TRUE) + ELSE Assert(FALSE, "Test 14 Failed") + + /\ IF SortSeq(<<1>>, >) = <<1>> + THEN Print("Test 13 OK", TRUE) + ELSE Assert(FALSE, "Test 15 Failed") + + +Constraint == TRUE +========================================= diff --git a/tlatools/test-model/suite/test44.cfg b/tlatools/test-model/suite/test44.cfg new file mode 100644 index 0000000000000000000000000000000000000000..5e8cedc4cac917a8a41ec9ad1e980ca0e367180c --- /dev/null +++ b/tlatools/test-model/suite/test44.cfg @@ -0,0 +1,11 @@ + INIT + Init + + NEXT + Next + + INVARIANT Inv + + + + \ No newline at end of file diff --git a/tlatools/test-model/suite/test44.tla b/tlatools/test-model/suite/test44.tla new file mode 100644 index 0000000000000000000000000000000000000000..5370ca0aa508575ac0fa71fe4c5bb12103e9469b --- /dev/null +++ b/tlatools/test-model/suite/test44.tla @@ -0,0 +1,479 @@ +--------------- MODULE test44 ------------- + +(* Test of operator precedence and associativity . *) + +EXTENDS TLC, Integers, Sequences + +VARIABLE x + +Init == x = 1 + + +Next == x' = x + + +a | b == <<a, b>> +a || b == <<a, b>> +a & b == <<a, b>> +a && b == <<a, b>> +a $ b == <<a, b>> +a $$ b == <<a, b>> +a ?? b == <<a, b>> +a %% b == <<a, b>> +a ## b == <<a, b>> +a ++ b == <<a, b>> +a -- b == <<a, b>> +a ** b == <<a, b>> +a (+) b == <<a, b>> +a (-) b == <<a, b>> +a (.) b == <<a, b>> + +a (\X) b == <<a, b>> + +\* a \otimes b == <<a, b>> +a \uplus b == <<a, b>> +a \sqcap b == <<a, b>> +a \sqcup b == <<a, b>> +a \star b == <<a, b>> +a \bigcirc b == <<a, b>> +a \bullet b == <<a, b>> + +a ... b == <<a, b>> +a // b == <<a, b>> +a ^^ b == <<a, b>> +a !! b == <<a, b>> +a |- b == <<a, b>> +a |= b == <<a, b>> +a -| b == <<a, b>> +a =| b == <<a, b>> + +a <: b == <<a, b>> + + +a := b == <<a, b>> +a ::= b == <<a, b>> +a (/) b == <<a, b>> +\* a \oslash b == <<a, b>> +a \wr b == <<a, b>> +a \prec b == <<a, b>> +a \succ b == <<a, b>> +a \preceq b == <<a, b>> +a \succeq b == <<a, b>> +a \sim b == <<a, b>> +a \simeq b == <<a, b>> +a \ll b == <<a, b>> +a \gg b == <<a, b>> +a \asymp b == <<a, b>> +a \subset b == <<a, b>> +a \supset b == <<a, b>> +a \supseteq b == <<a, b>> +a \approx b == <<a, b>> +a \cong b == <<a, b>> +a \sqsubset b == <<a, b>> +a \sqsupset b == <<a, b>> +a \sqsubseteq b == <<a, b>> +a \sqsupseteq b == <<a, b>> +a \doteq b == <<a, b>> +a \propto b == <<a, b>> + + +Inv == + /\ IF (3 - 2 - 1) = 0 + THEN Print("Test 1 OK", TRUE) + ELSE Assert(FALSE, "Test 1 Failed") + + /\ IF 1 | 2 | 3 = <<<<1, 2>>, 3>> + THEN Print("Test 2 OK", TRUE) + ELSE Assert(FALSE, "Test 2 Failed") + + /\ IF 1 || 2 || 3 = <<<<1, 2>>, 3>> + THEN Print("Test 3 OK", TRUE) + ELSE Assert(FALSE, "Test 3 Failed") + + /\ IF 1 & 2 & 3 = <<<<1, 2>>, 3>> + THEN Print("Test 4 OK", TRUE) + ELSE Assert(FALSE, "Test 4 Failed") + + /\ IF 1 && 2 && 3 = <<<<1, 2>>, 3>> + THEN Print("Test 5 OK", TRUE) + ELSE Assert(FALSE, "Test 5 Failed") + + /\ IF 1 $ 2 $ 3 = <<<<1, 2>>, 3>> + THEN Print("Test 6 OK", TRUE) + ELSE Assert(FALSE, "Test 6 Failed") + + /\ IF 1 $$ 2 $$ 3 = <<<<1, 2>>, 3>> + THEN Print("Test 7 OK", TRUE) + ELSE Assert(FALSE, "Test 7 Failed") + + /\ IF 1 ?? 2 ?? 3 = <<<<1, 2>>, 3>> + THEN Print("Test 8 OK", TRUE) + ELSE Assert(FALSE, "Test 8 Failed") + + /\ IF TRUE + THEN Print("Test 9 OK", TRUE) + ELSE Assert(FALSE, "Test 9 Failed") + + /\ IF 1 %% 2 %% 3 = <<<<1, 2>>, 3>> + THEN Print("Test 10 OK", TRUE) + ELSE Assert(FALSE, "Test 10 Failed") + + /\ IF 1 ## 2 ## 3 = <<<<1, 2>>, 3>> + THEN Print("Test 11 OK", TRUE) + ELSE Assert(FALSE, "Test 11 Failed") + + /\ IF 1 ++ 2 ++ 3 = <<<<1, 2>>, 3>> + THEN Print("Test 12 OK", TRUE) + ELSE Assert(FALSE, "Test 12 Failed") + + /\ IF 1 -- 2 -- 3 = <<<<1, 2>>, 3>> + THEN Print("Test 13 OK", TRUE) + ELSE Assert(FALSE, "Test 13 Failed") + + /\ IF 1 ** 2 ** 3 = <<<<1, 2>>, 3>> + THEN Print("Test 14 OK", TRUE) + ELSE Assert(FALSE, "Test 14 Failed") + + /\ IF 1 (+) 2 (+) 3 = <<<<1, 2>>, 3>> + THEN Print("Test 15 OK", TRUE) + ELSE Assert(FALSE, "Test 15 Failed") + + /\ IF 1 (-) 2 (-) 3 = <<<<1, 2>>, 3>> + THEN Print("Test 16 OK", TRUE) + ELSE Assert(FALSE, "Test 16 Failed") + + /\ IF 1 (.) 2 (.) 3 = <<<<1, 2>>, 3>> + THEN Print("Test 17 OK", TRUE) + ELSE Assert(FALSE, "Test 17 Failed") + + /\ IF 1 (\X) 2 (\X) 3 = <<<<1, 2>>, 3>> + THEN Print("Test 18 OK", TRUE) + ELSE Assert(FALSE, "Test 18 Failed") + + /\ IF 1 \oplus 2 \oplus 3 = <<<<1, 2>>, 3>> + THEN Print("Test 19 OK", TRUE) + ELSE Assert(FALSE, "Test 19 Failed") + + /\ IF 1 \ominus 2 \ominus 3 = <<<<1, 2>>, 3>> + THEN Print("Test 20 OK", TRUE) + ELSE Assert(FALSE, "Test 20 Failed") + + /\ IF 1 \odot 2 \odot 3 = <<<<1, 2>>, 3>> + THEN Print("Test 21 OK", TRUE) + ELSE Assert(FALSE, "Test 21 Failed") + + /\ IF 1 \otimes 2 \otimes 3 = <<<<1, 2>>, 3>> + THEN Print("Test 22 OK", TRUE) + ELSE Assert(FALSE, "Test 22 Failed") + + /\ IF 1 \uplus 2 \uplus 3 = <<<<1, 2>>, 3>> + THEN Print("Test 23 OK", TRUE) + ELSE Assert(FALSE, "Test 23 Failed") + + + /\ IF 1 \sqcap 2 \sqcap 3 = <<<<1, 2>>, 3>> + THEN Print("Test 24 OK", TRUE) + ELSE Assert(FALSE, "Test 24 Failed") + + /\ IF 1 \sqcup 2 \sqcup 3 = <<<<1, 2>>, 3>> + THEN Print("Test 25 OK", TRUE) + ELSE Assert(FALSE, "Test 25 Failed") + + /\ IF 1 \star 2 \star 3 = <<<<1, 2>>, 3>> + THEN Print("Test 26 OK", TRUE) + ELSE Assert(FALSE, "Test 26 Failed") + + /\ IF <<1>> \o <<2>> \o <<3>> = <<1, 2, 3>> + THEN Print("Test 27 OK", TRUE) + ELSE Assert(FALSE, "Test 27 Failed") + + /\ IF 1 \bigcirc 2 \bigcirc 3 = <<<<1, 2>>, 3>> + THEN Print("Test 28 OK", TRUE) + ELSE Assert(FALSE, "Test 28 Failed") + + /\ IF 1 \bullet 2 \bullet 3 = <<<<1, 2>>, 3>> + THEN Print("Test 29 OK", TRUE) + ELSE Assert(FALSE, "Test 29 Failed") + + /\ IF 1 ... 2 = <<1, 2>> + THEN Print("Test 30 OK", TRUE) + ELSE Assert(FALSE, "Test 30 Failed") + + /\ IF 1 // 2 = <<1, 2>> + THEN Print("Test 31 OK", TRUE) + ELSE Assert(FALSE, "Test 31 Failed") + + /\ IF 1 ^^ 2 = <<1, 2>> + THEN Print("Test 32 OK", TRUE) + ELSE Assert(FALSE, "Test 32 Failed") + + /\ IF 1 !! 2 = <<1, 2>> + THEN Print("Test 33 OK", TRUE) + ELSE Assert(FALSE, "Test 33 Failed") + + /\ IF (1 |- 2) = <<1, 2>> + THEN Print("Test 34 OK", TRUE) + ELSE Assert(FALSE, "Test 34 Failed") + + /\ IF (1 |= 2) = <<1, 2>> + THEN Print("Test 35 OK", TRUE) + ELSE Assert(FALSE, "Test 35 Failed") + + /\ IF (1 -| 2) = <<1, 2>> + THEN Print("Test 36 OK", TRUE) + ELSE Assert(FALSE, "Test 36 Failed") + + /\ IF (1 =| 2) = <<1, 2>> + THEN Print("Test 37 OK", TRUE) + ELSE Assert(FALSE, "Test 37 Failed") + + /\ IF 1 <: 2 = <<1, 2>> + THEN Print("Test 38 OK", TRUE) + ELSE Assert(FALSE, "Test 38 Failed") + + /\ IF TRUE + THEN Print("Test 39 OK", TRUE) + ELSE Assert(FALSE, "Test 39 Failed") + + /\ IF (1 := 2) = <<1, 2>> + THEN Print("Test 40 OK", TRUE) + ELSE Assert(FALSE, "Test 40 Failed") + + /\ IF (1 ::= 2) = <<1, 2>> + THEN Print("Test 41 OK", TRUE) + ELSE Assert(FALSE, "Test 41 Failed") + + /\ IF 1 (/) 2 = <<1, 2>> + THEN Print("Test 42 OK", TRUE) + ELSE Assert(FALSE, "Test 42 Failed") + + /\ IF 1 \oslash 2 = <<1, 2>> + THEN Print("Test 43 OK", TRUE) + ELSE Assert(FALSE, "Test 43 Failed") + + /\ IF 1 \wr 2 = <<1, 2>> + THEN Print("Test 44 OK", TRUE) + ELSE Assert(FALSE, "Test 44 Failed") + + /\ IF (1 \prec 2) = <<1, 2>> + THEN Print("Test 45 OK", TRUE) + ELSE Assert(FALSE, "Test 45 Failed") + + /\ IF (1 \succ 2) = <<1, 2>> + THEN Print("Test 46 OK", TRUE) + ELSE Assert(FALSE, "Test 46 Failed") + + /\ IF (1 \preceq 2) = <<1, 2>> + THEN Print("Test 47 OK", TRUE) + ELSE Assert(FALSE, "Test 47 Failed") + + /\ IF (1 \succeq 2) = <<1, 2>> + THEN Print("Test 48 OK", TRUE) + ELSE Assert(FALSE, "Test 48 Failed") + + /\ IF (1 \sim 2) = <<1, 2>> + THEN Print("Test 49 OK", TRUE) + ELSE Assert(FALSE, "Test 49 Failed") + + /\ IF (1 \simeq 2) = <<1, 2>> + THEN Print("Test 50 OK", TRUE) + ELSE Assert(FALSE, "Test 50 Failed") + + /\ IF (1 \ll 2) = <<1, 2>> + THEN Print("Test 51 OK", TRUE) + ELSE Assert(FALSE, "Test 51 Failed") + + /\ IF (1 \gg 2) = <<1, 2>> + THEN Print("Test 52 OK", TRUE) + ELSE Assert(FALSE, "Test 52 Failed") + + /\ IF (1 \asymp 2) = <<1, 2>> + THEN Print("Test 53 OK", TRUE) + ELSE Assert(FALSE, "Test 53 Failed") + + /\ IF (1 \subset 2) = <<1, 2>> + THEN Print("Test 54 OK", TRUE) + ELSE Assert(FALSE, "Test 54 Failed") + + /\ IF (1 \supset 2) = <<1, 2>> + THEN Print("Test 55 OK", TRUE) + ELSE Assert(FALSE, "Test 55 Failed") + + /\ IF (1 \supseteq 2) = <<1, 2>> + THEN Print("Test 56 OK", TRUE) + ELSE Assert(FALSE, "Test 56 Failed") + + /\ IF (1 \approx 2) = <<1, 2>> + THEN Print("Test 57 OK", TRUE) + ELSE Assert(FALSE, "Test 57 Failed") + + /\ IF (1 \cong 2) = <<1, 2>> + THEN Print("Test 58 OK", TRUE) + ELSE Assert(FALSE, "Test 58 Failed") + + /\ IF (1 \sqsubset 2) = <<1, 2>> + THEN Print("Test 59 OK", TRUE) + ELSE Assert(FALSE, "Test 59 Failed") + + /\ IF (1 \sqsupset 2) = <<1, 2>> + THEN Print("Test 60 OK", TRUE) + ELSE Assert(FALSE, "Test 60 Failed") + + /\ IF (1 \sqsubseteq 2) = <<1, 2>> + THEN Print("Test 61 OK", TRUE) + ELSE Assert(FALSE, "Test 61 Failed") + + /\ IF (1 \sqsupseteq 2) = <<1, 2>> + THEN Print("Test 62 OK", TRUE) + ELSE Assert(FALSE, "Test 62 Failed") + + /\ IF (1 \doteq 2) = <<1, 2>> + THEN Print("Test 63 OK", TRUE) + ELSE Assert(FALSE, "Test 63 Failed") + + /\ IF (1 \propto 2) = <<1, 2>> + THEN Print("Test 64 OK", TRUE) + ELSE Assert(FALSE, "Test 64 Failed") + + /\ IF << <<1, 2>>, 3 >> = 1 & 2 ... 3 + THEN Print("Test 65 OK", TRUE) + ELSE Assert(FALSE, "Test 65 Failed") + + + /\ IF << <<1, 2>>, 3 >> = 1 && 2 ... 3 + THEN Print("Test 66 OK", TRUE) + ELSE Assert(FALSE, "Test 66 Failed") + + + /\ IF << <<1, 2>>, 3 >> = 1 ** 2 ... 3 + THEN Print("Test 67 OK", TRUE) + ELSE Assert(FALSE, "Test 67 Failed") + + /\ IF << <<1, 2>>, 3 >> = 1 // 2 ... 3 + THEN Print("Test 68 OK", TRUE) + ELSE Assert(FALSE, "Test 68 Failed") + + /\ IF << <<1, 2>>, 3 >> = 1 ^^ 2 ... 3 + THEN Print("Test 69 OK", TRUE) + ELSE Assert(FALSE, "Test 69 Failed") + + /\ IF << <<1, 2>>, 3 >> = 1 (/) 2 ... 3 + THEN Print("Test 70 OK", TRUE) + ELSE Assert(FALSE, "Test 70 Failed") + + /\ IF << <<1, 2>>, 3 >> = 1 (.) 2 ... 3 + THEN Print("Test 71 OK", TRUE) + ELSE Assert(FALSE, "Test 71 Failed") + + /\ IF << <<1, 2>>, 3 >> = 1 \star 2 ... 3 + THEN Print("Test 72 OK", TRUE) + ELSE Assert(FALSE, "Test 72 Failed") + + /\ IF << <<1, 2>>, 3 >> = 1 \bigcirc 2 ... 3 + THEN Print("Test 73 OK", TRUE) + ELSE Assert(FALSE, "Test 73 Failed") + + /\ IF << <<1, 2>>, 3 >> = 1 \bullet 2 ... 3 + THEN Print("Test 74 OK", TRUE) + ELSE Assert(FALSE, "Test 74 Failed") + + + /\ IF << <<1, 2>>, 3 >> = (1 $ 2 \sim 3) + THEN Print("Test 75 OK", TRUE) + ELSE Assert(FALSE, "Test 75 Failed") + + + /\ IF << <<1, 2>>, 3 >> = (1 $$ 2 \simeq 3) + THEN Print("Test 76 OK", TRUE) + ELSE Assert(FALSE, "Test 76 Failed") + + + /\ IF << <<1, 2>>, 3 >> = (1 $ 2 \gg 3) + THEN Print("Test 77 OK", TRUE) + ELSE Assert(FALSE, "Test 77 Failed") + + /\ IF << <<1, 2>>, 3 >> = (1 -- 2 ++ 3) + THEN Print("Test 78 OK", TRUE) + ELSE Assert(FALSE, "Test 78 Failed") + + /\ IF << <<1, 2>>, 3 >> = (1 -- 2 <: 3) + THEN Print("Test 79 OK", TRUE) + ELSE Assert(FALSE, "Test 79 Failed") + + + /\ IF << <<1, 2>>, 3 >> = (1 !! 2 \ll 3) + THEN Print("Test 80 OK", TRUE) + ELSE Assert(FALSE, "Test 80 Failed") + + /\ IF << <<1, 2>>, 3 >> = 1 (-) 2 (+) 3 + THEN Print("Test 81 OK", TRUE) + ELSE Assert(FALSE, "Test 81 Failed") + + /\ IF << <<1, 2>>, 3 >> = (1 ++ 2 |- 3) + THEN Print("Test 82 OK", TRUE) + ELSE Assert(FALSE, "Test 82 Failed") + + /\ IF << <<1, 2>>, 3 >> = 1 (.) 2 (-) 3 + THEN Print("Test 83 OK", TRUE) + ELSE Assert(FALSE, "Test 83 Failed") + + /\ IF << <<1, 2>>, 3 >> = (1 \bullet 2 \prec 3) + THEN Print("Test 84 OK", TRUE) + ELSE Assert(FALSE, "Test 84 Failed") + + /\ IF << <<1, 2>>, 3 >> = (1 \uplus 2 \approx 3) + THEN Print("Test 85 OK", TRUE) + ELSE Assert(FALSE, "Test 85 Failed") + + + /\ IF << <<1, 2>>, 3 >> = (1 ++ 2 \sim 3) + THEN Print("Test 86 OK", TRUE) + ELSE Assert(FALSE, "Test 86 Failed") + + /\ IF << <<1, 2>>, 3 >> = (1 (/) 2 (-) 3) + THEN Print("Test 87 OK", TRUE) + ELSE Assert(FALSE, "Test 87 Failed") + + + +Constraint == TRUE +========================================= + +<< {"..."}, "Infix", <<P.dotdot, P.dotdot>>, "none" >>, +<< {"//"}, "Infix", <<P.times, P.times>>, "none" >>, +<< {"^^"}, "Infix", <<P.exponent, P.exponent>>, "none" >>, +<< {"!!"}, "Infix", <<P.dotdot, P.times>>, "none" >>, +<< {"|-"}, "Infix", <<P.equals, P.equals>>, "none" >>, +<< {"|="}, "Infix", <<P.equals, P.equals>>, "none" >>, +<< {"-|"}, "Infix", <<P.equals, P.equals>>, "none" >>, +<< {"=|"}, "Infix", <<P.equals, P.equals>>, "none" >>, +<< {"<:"}, "Infix", <<P.colongt, P.colongt>>, "none" >>, +<< {":>"}, "Infix", <<P.colongt, P.colongt>>, "none" >>, +<< {":="}, "Infix", <<P.equals, P.equals>>, "none" >>, +<< {"::="}, "Infix", <<P.equals, P.equals>>, "none" >>, +<< {"(/)", "\oslash"}, "Infix", <<P.times, P.times>>, "none" >>, +<< {"\wr"}, "Infix", <<P.dotdot, P.exponent>>, "none" >>, +<< {"\prec"}, "Infix", <<P.equals, P.equals>>, "none" >>, +<< {"\succ"}, "Infix", <<P.equals, P.equals>>, "none" >>, +<< {"\preceq"}, "Infix", <<P.equals, P.equals>>, "none" >>, +<< {"\succeq"}, "Infix", <<P.equals, P.equals>>, "none" >>, +<< {"\sim"}, "Infix", <<P.equals, P.equals>>, "none" >>, +<< {"\simeq"}, "Infix", <<P.equals, P.equals>>, "none" >>, +<< {"\ll"}, "Infix", <<P.equals, P.equals>>, "none" >>, +<< {"\gg"}, "Infix", <<P.equals, P.equals>>, "none" >>, +<< {"\asymp"}, "Infix", <<P.equals, P.equals>>, "none" >>, +<< {"\subset"}, "Infix", <<P.equals, P.equals>>, "none" >>, +<< {"\supset"}, "Infix", <<P.equals, P.equals>>, "none" >>, +<< {"\supseteq"}, "Infix", <<P.equals, P.equals>>, "none" >>, +<< {"\approx"}, "Infix", <<P.equals, P.equals>>, "none" >>, +<< {"\cong"}, "Infix", <<P.equals, P.equals>>, "none" >>, +<< {"\sqsubset"}, "Infix", <<P.equals, P.equals>>, "none" >>, +<< {"\sqsupset"}, "Infix", <<P.equals, P.equals>>, "none" >>, +<< {"\sqsubseteq"}, "Infix", <<P.equals, P.equals>>, "none" >>, +<< {"\sqsupseteq"}, "Infix", <<P.equals, P.equals>>, "none" >>, +<< {"\doteq"}, "Infix", <<P.equals, P.equals>>, "none" >>, +<< {"\propto"}, "Infix", <<P.equals, P.equals>>, "none" >> + + + + diff --git a/tlatools/test-model/suite/test45.cfg b/tlatools/test-model/suite/test45.cfg new file mode 100644 index 0000000000000000000000000000000000000000..5e8cedc4cac917a8a41ec9ad1e980ca0e367180c --- /dev/null +++ b/tlatools/test-model/suite/test45.cfg @@ -0,0 +1,11 @@ + INIT + Init + + NEXT + Next + + INVARIANT Inv + + + + \ No newline at end of file diff --git a/tlatools/test-model/suite/test45.tla b/tlatools/test-model/suite/test45.tla new file mode 100644 index 0000000000000000000000000000000000000000..66d29e9fe2cda1723e527fde5992e8a5e32e0cef --- /dev/null +++ b/tlatools/test-model/suite/test45.tla @@ -0,0 +1,92 @@ +------------------------------ MODULE test45 ----------------------------- +(* Test of recursive function definitions. *) + +EXTENDS Naturals, TLC + +VARIABLES x + +Init == x = 2 + +Next == UNCHANGED x + +f[i \in 0..5] == [j \in 0..5 |-> + IF j = 0 + THEN [k \in 0..i |-> + IF k = 0 THEN i + ELSE f[i][1] + 1] + ELSE IF i = 0 + THEN j + ELSE f[i-1][j] + 1 ] + +g[i \in 0..5] == [j \in 0..i |-> + IF j = 0 + THEN IF i = 0 THEN 0 + ELSE g[i-1][i-1] + ELSE g[i][j-1]+x] + + +h[i \in 0..3, j \in 0..3] == [k \in 0..3 |-> + IF k = 0 + THEN IF j = 0 + THEN IF i = 0 THEN 0 + ELSE h[i-1,j][k] + 1 + ELSE h[i,j-1][k]+1 + ELSE h[i,j][k-1] + 1] + +(* g[0][0] = 0 + g[0][j] = j * x + g[i][j] = j * x + g[i-1][i-1] + = (j + i-1) * x + g[i-2][i-2] + = (j + i + i-1) * x + g[i-3][i-2] + = ... *) + +Inv == /\ IF f[4][3] = 7 + THEN Print("Test 1 OK", TRUE) + ELSE Assert(FALSE, "Test 1 Failed") + + /\ IF f[4][0][2] = 6 + THEN Print("Test 2 OK", TRUE) + ELSE Assert(FALSE, "Test 2 Failed") + + /\ IF DOMAIN g[3] = 0..3 + THEN Print("Test 3 OK", TRUE) + ELSE Assert(FALSE, "Test 3 Failed") + + /\ IF DOMAIN f[5][0] = 0..5 + THEN Print("Test 4 OK", TRUE) + ELSE Assert(FALSE, "Test 4 Failed") + + /\ IF g[0][0] = 0 + THEN Print("Test 5 OK", TRUE) + ELSE Assert(FALSE, "Test 5 Failed") + + /\ IF g[3][3] = 12 + THEN Print("Test 6 OK", TRUE) + ELSE Assert(FALSE, "Test 6 Failed") + + /\ IF g[3][1] = 8 + THEN Print("Test 7 OK", TRUE) + ELSE Assert(FALSE, "Test 7 Failed") + + /\ IF g[2][0] = 2 + THEN Print("Test 8 OK", TRUE) + ELSE Assert(FALSE, "Test 8 Failed") + + /\ IF DOMAIN h[1,2] = 0..3 + THEN Print("Test 9 OK", TRUE) + ELSE Assert(FALSE, "Test 9 Failed") + + /\ IF DOMAIN h = (0..3) \X (0..3) + THEN Print("Test 10 OK", TRUE) + ELSE Assert(FALSE, "Test 10 Failed") + + /\ IF h[2, 3] = [i \in 0..3 |-> i+5] + THEN Print("Test 11 OK", TRUE) + ELSE Assert(FALSE, "Test 11 Failed") + + /\ IF h[1, 2][3] = 6 + THEN Print("Test 12 OK", TRUE) + ELSE Assert(FALSE, "Test 12 Failed") + + +============================================================================= diff --git a/tlatools/test-model/suite/test46.cfg b/tlatools/test-model/suite/test46.cfg new file mode 100644 index 0000000000000000000000000000000000000000..29941a19ca7c8e1d69ca3edf5999b370184f8bb6 --- /dev/null +++ b/tlatools/test-model/suite/test46.cfg @@ -0,0 +1,13 @@ +CONSTANTS + Reg = {r1} + Adr = {a1} + Val = {v1,v2} + Proc = {p1, p2} + +INIT Init + +NEXT Next + +CONSTRAINT Constraint + +INVARIANT TypeInvariant diff --git a/tlatools/test-model/suite/test46.tla b/tlatools/test-model/suite/test46.tla new file mode 100644 index 0000000000000000000000000000000000000000..1968254c3759950d0ff42992aea0633029c169b6 --- /dev/null +++ b/tlatools/test-model/suite/test46.tla @@ -0,0 +1,49 @@ +--------------------- MODULE test46 ----------------------- + +(* This found a bug in Version 1.03 of 18 Oct 1999 *) + +EXTENDS Naturals, Sequences, FiniteSets, TLC +CONSTANT Reg, Adr, Val, Proc +VARIABLE reqQ, reqOrder + +Request == [adr : Adr, val : Val, op : {"Rd", "Wr"}] +FreeRegVal == [adr : Adr, val : Val, op : {"Free"}] + +reqId == UNION { [proc : {p}, idx : DOMAIN reqQ[p]] : p \in Proc } + +oi \prec oj == <<oi, oj>> \in reqOrder + +TypeInvariant == + /\ reqQ \in [Proc -> Seq(Request \cup [reg : Reg])] + /\ reqOrder \in SUBSET (reqId \X reqId) + +Init == /\ reqQ = [p \in Proc |-> << >>] + /\ reqOrder = {} + +GoodReqOrder == + /\ \A oi, oj, ok \in reqId : (oi \prec oj) /\ (oj \prec ok) => (oi \prec ok) + /\ \A oi \in reqId : ~(oi \prec oi) + /\ \A oi, oj \in reqId : + (oi.proc = oj.proc) /\ (oi.idx < oj.idx) => (oi \prec oj) + +----------------------------------------------------------------------------- +UpdateReqOrder == + /\ reqOrder' \in SUBSET(reqId' \X reqId') + /\ reqOrder \subseteq reqOrder' + /\ GoodReqOrder' + +IssueRequest(proc, req, reg) == + (*************************************************************************) + (* The action by which processor proc issues request req in register *) + (* reg. *) + (*************************************************************************) + /\ reqQ' = [reqQ EXCEPT ![proc] = Append(@, [reg |-> reg])] + /\ UpdateReqOrder + + +Next == \E proc \in Proc, reg \in Reg : + \E req \in Request : IssueRequest(proc, req, reg) + + +Constraint == \A p \in Proc : Len(reqQ[p]) < 2 +============================================================================= diff --git a/tlatools/test-model/suite/test47.cfg b/tlatools/test-model/suite/test47.cfg new file mode 100644 index 0000000000000000000000000000000000000000..5e8cedc4cac917a8a41ec9ad1e980ca0e367180c --- /dev/null +++ b/tlatools/test-model/suite/test47.cfg @@ -0,0 +1,11 @@ + INIT + Init + + NEXT + Next + + INVARIANT Inv + + + + \ No newline at end of file diff --git a/tlatools/test-model/suite/test47.tla b/tlatools/test-model/suite/test47.tla new file mode 100644 index 0000000000000000000000000000000000000000..ba1931feae621a40e0d124b820b5217cf591e2ab --- /dev/null +++ b/tlatools/test-model/suite/test47.tla @@ -0,0 +1,12 @@ +------- MODULE test47 ----- + +\* Checks EXTENDing of both Naturals and Integers + +EXTENDS test47a , Integers + +Init == x = -1 + +Next == x'= IF x = -3 THEN x ELSE x - 1 + +Inv == TRUE +========================== diff --git a/tlatools/test-model/suite/test47a.tla b/tlatools/test-model/suite/test47a.tla new file mode 100644 index 0000000000000000000000000000000000000000..6fcfebb7d549774fc7f5f52eae4fbcc40256a492 --- /dev/null +++ b/tlatools/test-model/suite/test47a.tla @@ -0,0 +1,7 @@ +------------- MODULE test47a -------------- + +EXTENDS Naturals, TLC + + +VARIABLE x +============================================= diff --git a/tlatools/test-model/suite/test48.cfg b/tlatools/test-model/suite/test48.cfg new file mode 100644 index 0000000000000000000000000000000000000000..e3e8c3d2505a0163440775d92531a92d4321811c --- /dev/null +++ b/tlatools/test-model/suite/test48.cfg @@ -0,0 +1,2 @@ +INIT Init +NEXT Next diff --git a/tlatools/test-model/suite/test48.tla b/tlatools/test-model/suite/test48.tla new file mode 100644 index 0000000000000000000000000000000000000000..b2841590f92154b4a528b147e8d47276e49ad883 --- /dev/null +++ b/tlatools/test-model/suite/test48.tla @@ -0,0 +1,148 @@ +--------------- MODULE test48 ------------- + +(* test definability of all user-definable infix, prefix, and postfix + operators *) + +EXTENDS TLC + +VARIABLE x + + +z - y == z +z + y == z +z * y == z +z / y == z +z ^ y == z +z < y == z +z > y == z +z .. y == z +z ... y == z +z | y == z +z || y == z +z & y == z +z && y == z +z $ y == z +z $$ y == z +z ?? y == z +z %% y == z +z ## y == z +z ++ y == z +z -- y == z +z ** y == z +z // y == z +z ^^ y == z +\* z @@ y == z \* THIS IS DEFINED IN THE TLC MODULE +z !! y == z +z |- y == z +z |= y == z +z -| y == z +z =| y == z +z <: y == z +\* z :> y == z \* THIS IS DEFINED IN THE TLC MODULE +z := y == z +z ::= y == z +z \uplus y == z +z \sqcap y == z +z \sqcup y == z +z \div y == z +z \wr y == z +z \star y == z +z \bigcirc y == z +z \bullet y == z +z \prec y == z +z \succ y == z +z \preceq y == z +z \succeq y == z +z \sim y == z +z \simeq y == z +z \ll y == z +z \gg y == z +z \asymp y == z +z \subset y == z +z \supset y == z +z \supseteq y == z +z \approx y == z +z \cong y == z +z \sqsubset y == z +z \sqsupset y == z +z \sqsubseteq y == z +z \sqsupseteq y == z +z \doteq y == z +z \propto y == z + +z ^+ == z +z ^* == z +z ^# == z +-. z == z + + +Init == + /\ x = 0 + +Next == +/\ x = 0 +/\ + \/ x' = x - 1 + \/ x' = x + 1 + \/ x' = x * 1 + \/ x' = x / 1 + \/ x' = x ^ 1 + \/ x' = (x < 1) + \/ x' = (x > 1) + \/ x' = x .. 1 + \/ x' = x ... 1 + \/ x' = x | 1 + \/ x' = x || 1 + \/ x' = x & 1 + \/ x' = x && 1 + \/ x' = x $ 1 + \/ x' = x $$ 1 + \/ x' = x ?? 1 + \/ x' = x %% 1 + \/ x' = x ## 1 + \/ x' = x ++ 1 + \/ x' = x -- 1 + \/ x' = x ** 1 + \/ x' = x // 1 + \/ x' = x ^^ 1 + \/ x' = x !! 1 + \/ x' = (x |- 1) + \/ x' = (x |= 1) + \/ x' = (x -| 1) + \/ x' = (x =| 1) + \/ x' = (x <: 1) + \/ x' = (x := 1) + \/ x' = (x ::= 1) + \/ x' = (x \uplus 1) + \/ x' = (x \sqcap 1) + \/ x' = (x \sqcup 1) + \/ x' = x \div 1 + \/ x' = x \wr 1 + \/ x' = x \star 1 + \/ x' = x \bigcirc 1 + \/ x' = x \bullet 1 + \/ x' = (x \prec 1 ) + \/ x' = (x \succ 1) + \/ x' = (x \preceq 1) + \/ x' = (x \succeq 1) + \/ x' = (x \sim 1) + \/ x' = (x \simeq 1) + \/ x' = (x \ll 1) + \/ x' = (x \gg 1) + \/ x' = (x \asymp 1) + \/ x' = (x \subset 1) + \/ x' = (x \supset 1) + \/ x' = (x \supseteq 1) + \/ x' = (x \approx 1) + \/ x' = (x \cong 1) + \/ x' = (x \sqsubset 1) + \/ x' = (x \sqsupset 1) + \/ x' = (x \sqsubseteq 1) + \/ x' = (x \sqsupseteq 1) + \/ x' = (x \doteq 1) + \/ x' = (x \propto 1) + \/ x' = x ^+ + \/ x' = x ^* + \/ x' = x ^# + \/ x' = -x +============================================================================= diff --git a/tlatools/test-model/suite/test49.cfg b/tlatools/test-model/suite/test49.cfg new file mode 100644 index 0000000000000000000000000000000000000000..21d01a242b550020aa2f6bbd1102bc40ef14e051 --- /dev/null +++ b/tlatools/test-model/suite/test49.cfg @@ -0,0 +1,3 @@ +INIT Init +NEXT Next + diff --git a/tlatools/test-model/suite/test49.tla b/tlatools/test-model/suite/test49.tla new file mode 100644 index 0000000000000000000000000000000000000000..b14cd37f18677ded82092280125ebbc9d1fc47ac --- /dev/null +++ b/tlatools/test-model/suite/test49.tla @@ -0,0 +1,36 @@ +--------------- MODULE test49 ------------- + +(* test definability of operators with aliases *) + + +EXTENDS TLC + +VARIABLE x + +u (+) v == u +u (-) v == u +u (.) v == u +u (/) v == u +u (\X) v == u +u \o v == u +u % v == u +u \leq v == u +u \geq v == u + + +Init == + /\ x = 0 + +Next == + /\ x = 0 + /\ + \/ x' = x (+) 1 + \/ x' = x (-) 1 + \/ x' = x (.) 1 + \/ x' = x (/) 1 + \/ x' = x (\X) 1 + \/ x' = x \o 1 + \/ x' = x % 1 + \/ x' = (x \leq 1) + \/ x' = (x \geq 1 ) +======================================= diff --git a/tlatools/test-model/suite/test49a.cfg b/tlatools/test-model/suite/test49a.cfg new file mode 100644 index 0000000000000000000000000000000000000000..e3e8c3d2505a0163440775d92531a92d4321811c --- /dev/null +++ b/tlatools/test-model/suite/test49a.cfg @@ -0,0 +1,2 @@ +INIT Init +NEXT Next diff --git a/tlatools/test-model/suite/test49a.tla b/tlatools/test-model/suite/test49a.tla new file mode 100644 index 0000000000000000000000000000000000000000..49ae3003ae37d4e6391b9302d7733ef7c9c3508c --- /dev/null +++ b/tlatools/test-model/suite/test49a.tla @@ -0,0 +1,39 @@ +--------------- MODULE test49a ------------- + +(* test definability of operators with aliases *) + + +EXTENDS TLC + +VARIABLE x + +u (+) v == u +u (-) v == u +u (.) v == u +u (/) v == u +u (\X) v == u \* parser can't cope with this +u \o v == u +u % v == u +u \leq v == u +u \geq v == u + + +Init == + /\ x = 0 + /\ Print("******************************************************", TRUE) + /\ Print("Several tests have been commented out because of bugs.", TRUE) + /\ Print("******************************************************", TRUE) + +Next == + /\ x = 0 + /\ + \/ x' = x (+) 1 \* caused TLC bug + \/ x' = x (-) 1 \* caused TLC bug + \/ x' = x (.) 1 \* caused TLC bug + \/ x' = x (/) 1 \* caused TLC bug + \/ x' = x (\X) 1 \* parser can't cope with this. + \/ x' = x \o 1 + \/ x' = x % 1 + \/ x' = (x \leq 1) + \/ x' = (x \geq 1 ) +======================================= diff --git a/tlatools/test-model/suite/test49b.cfg b/tlatools/test-model/suite/test49b.cfg new file mode 100644 index 0000000000000000000000000000000000000000..e3e8c3d2505a0163440775d92531a92d4321811c --- /dev/null +++ b/tlatools/test-model/suite/test49b.cfg @@ -0,0 +1,2 @@ +INIT Init +NEXT Next diff --git a/tlatools/test-model/suite/test49b.tla b/tlatools/test-model/suite/test49b.tla new file mode 100644 index 0000000000000000000000000000000000000000..c8ba49fee511f4e27e6e180f246053bceaae400e --- /dev/null +++ b/tlatools/test-model/suite/test49b.tla @@ -0,0 +1,39 @@ +--------------- MODULE test49b ------------- + +(* test definability of operators with aliases *) + + +EXTENDS TLC + +VARIABLE x + +u \oplus v == u +u \ominus v == u +u \odot v == u +u \oslash v == u +u \otimes v == u +u \circ v == u +u \mod v == u +u =< v == u +u >= v == u + + +Init == + /\ x = 0 + /\ Print("******************************************************", TRUE) + /\ Print("Several tests have been commented out because of bugs.", TRUE) + /\ Print("******************************************************", TRUE) + +Next == + /\ x = 0 + /\ + \/ x' = x \oplus 1 + \/ x' = x \ominus 1 + \/ x' = x \odot 1 + \/ x' = x \oslash 1 + \/ x' = x \otimes 1 +\* \/ x' = x \circ 1 +\* \/ x' = x \mod 1 \* causes TLC bug +\* \/ x' = (x =< 1) \* causes TLC bug +\* \/ x' = (x >= 1 ) \* causes TLC bug +======================================= diff --git a/tlatools/test-model/suite/test49c.cfg b/tlatools/test-model/suite/test49c.cfg new file mode 100644 index 0000000000000000000000000000000000000000..e3e8c3d2505a0163440775d92531a92d4321811c --- /dev/null +++ b/tlatools/test-model/suite/test49c.cfg @@ -0,0 +1,2 @@ +INIT Init +NEXT Next diff --git a/tlatools/test-model/suite/test49c.tla b/tlatools/test-model/suite/test49c.tla new file mode 100644 index 0000000000000000000000000000000000000000..828c88c2dfccb916a3e61ff20d367b49112f075e --- /dev/null +++ b/tlatools/test-model/suite/test49c.tla @@ -0,0 +1,39 @@ +--------------- MODULE test49c ------------- + +(* test definability of operators with aliases *) + + +EXTENDS TLC + +VARIABLE x + +u \oplus v == u +u \ominus v == u +u \odot v == u +u \oslash v == u +u \otimes v == u +u \o v == u +u \mod v == u +u =< v == u +u >= v == u + + +Init == + /\ x = 0 + /\ Print("*************************************************************", TRUE) + /\ Print("Almost all the tests have been commented out because of bugs.", TRUE) + /\ Print("*************************************************************", TRUE) + +Next == + /\ x = 0 + /\ +\* \/ x' = x (+) 1 \* causes TLC bug +\* \/ x' = x (-) 1 \* causes TLC bug +\* \/ x' = x (.) 1 \* causes TLC bug +\* \/ x' = x (/) 1 \* causes TLC bug +\* \/ x' = x (\X) 1 \* parser can't handle this +\* \/ x' = x \circ 1 \* causes TLC bug + \/ x' = x % 1 +\* \/ x' = (x =< 1) \* TLC bug : sets x' to TRUE +\* \/ x' = (x >= 1 ) \* TLC bug : sets x' to FALSE +======================================= diff --git a/tlatools/test-model/suite/test5.cfg b/tlatools/test-model/suite/test5.cfg new file mode 100644 index 0000000000000000000000000000000000000000..b0ee260b28eef2026bbc2f6cb01c89fd3d01d947 --- /dev/null +++ b/tlatools/test-model/suite/test5.cfg @@ -0,0 +1,8 @@ + INIT + Init + + NEXT + Next + + INVARIANT Inv + diff --git a/tlatools/test-model/suite/test5.tla b/tlatools/test-model/suite/test5.tla new file mode 100644 index 0000000000000000000000000000000000000000..ec9791eadf878ba2080f061af7d9f48a0292e466 --- /dev/null +++ b/tlatools/test-model/suite/test5.tla @@ -0,0 +1,113 @@ +--------------- MODULE test5 ------------- + +(* test of Cartesian Product. *) + +EXTENDS Integers, TLC + +VARIABLE x +Type == x \in {1} +Init == x = 1 +Next == UNCHANGED x + +Inv == + /\ IF {1, 2} \X {"a", "b"} # {<<1, "a">>, <<2, "a">>, <<2, "b">>, <<1, "b">>} + THEN Assert(FALSE, "Test 1 failed") + ELSE Print("Test 1 OK", TRUE) + + /\ IF [p \in Nat \X Int |-> p[1] - 2*p[2]][2, -3] # 8 + THEN Assert(FALSE, "Test 2 failed") + ELSE Print("Test 2 OK", TRUE) + + /\ IF [p \in Int \X Int |-> p[1] - 2*p[2]][2, -3] # 8 + THEN Assert(FALSE, "Test 3 failed") + ELSE Print("Test 3 OK", TRUE) + + /\ IF <<1, 3>> \notin Nat \X Nat + THEN Assert(FALSE, "Test 4 failed") + ELSE Print("Test 4 OK", TRUE) + + /\ IF <<1, 3>> \notin (Int \X Int) + THEN Assert(FALSE, "Test 5 failed") + ELSE Print("Test 5 OK", TRUE) + + /\ IF [1..2 -> {1, 2, 3}] \cap ({1} \X {2}) # {<<1,2>>} + THEN Assert(FALSE, "Test 6 failed") + ELSE Print("Test 6 OK", TRUE) + + /\ IF {<<1, 4>>, <<2,3>>} \notin SUBSET ({1,2} \X {3,4}) + THEN Assert(FALSE, "Test 7 failed") + ELSE Print("Test 7 OK", TRUE) + + /\ IF <<1,3>> \notin {1,2,3} \X {2,3,4} + THEN Assert(FALSE, "Test 8 failed") + ELSE Print("Test 8 OK", TRUE) + + /\ IF {1, 2} \X {"a", "b"} \X {"c"} # + {<<1, "a", "c">>, <<2, "a", "c">>, <<2, "b", "c">>, <<1, "b", "c">>} + THEN Assert(FALSE, "Test 9 failed") + ELSE Print("Test 9 OK", TRUE) + + /\ IF [p \in Nat \X Int \X Nat |-> p[1] - 2*p[2] + p[3]][2, -3, 4] # 12 + THEN Assert(FALSE, "Test 10 failed") + ELSE Print("Test 10 OK", TRUE) + + /\ IF [p \in Int \X Int \X Int |-> p[1] - 2*p[2] - p[3]][2, -3, -4] # 12 + THEN Assert(FALSE, "Test 11 failed") + ELSE Print("Test 11 OK", TRUE) + + /\ IF <<1, 2, 3, 4>> \notin Nat \X Nat \X Nat \X Nat + THEN Assert(FALSE, "Test 12 failed") + ELSE Print("Test 12 OK", TRUE) + + /\ IF <<1, 2, 3, 4>> \notin (Int \X Int \X Int \X Int) + THEN Assert(FALSE, "Test 13 failed") + ELSE Print("Test 13 OK", TRUE) + + /\ IF [1..2 -> {1, 2, 3}] \cap ({1} \X {2}) # {<<1,2>>} + THEN Assert(FALSE, "Test 14 failed") + ELSE Print("Test 14 OK", TRUE) + + /\ IF {<<1, 4, 2>>, <<2,3,3>>} \notin SUBSET ({1,2} \X {3,4} \X {2, 3}) + THEN Assert(FALSE, "Test 15 failed") + ELSE Print("Test 15 OK", TRUE) + + /\ IF <<1,3, 5>> \notin {1,2,3} \X {2,3,4} \X {5,6} + THEN Assert(FALSE, "Test 16 failed") + ELSE Print("Test 16 OK", TRUE) + /\ \* Test 17 added 4 June 2014 + IF \/ {} \X {1,2} # {2,3,4} \X {} + \/ {1, 2} \X {} \X {3,4} # {2} \X {4,4} \X {} + \/ {1} \X {2} \X {} # {} \X {3,3} + THEN Assert(FALSE, "Test 17 failed") + ELSE Print("Test 17 OK", TRUE) + /\ \* Test 18 added 12 Jun 2014 + IF \/ [a: {12}, b : {}] # [c: {22}, d: {xyz \in {1,2,3} : xyz < 0}] + \/ [a: {12}, b : {}] # [c: {22}, d: 2..1] + \/ [a: {12}, b : {}] # [c: {22}, d: {1,2,3} \cap 4..6] + \/ [a: {12}, b : {}] # [c: {22}, d: {} \cup {xz \in {1,2,3} : xz < 0}] + \/ [a: {12}, b : {}] # [c: {22}, d: {2,3,4} \X {}] + \/ [a: {12}, b : {}] # [c: {22}, d: [a: {}, b : {1}]] + \/ [a: {12}, b : {}] = [c: {22}, d: SUBSET {}] + \/ [a: {12}, b : {}] # [c: {22}, d: UNION {}] + \/ [a: {12}, b : {}] # [c: {22}, d: {2,3,4} \ 1..5] + \/ [a: {12}, b : {}] # [c: {22}, d: [{1,2,3} -> {}]] + \/ [a: {12}, b : {}] # [c: {22}, d: {}] + \/ [a: {12}, b : {}] # [c: {22}, d: {xyz+1 : xyz \in 2..1}] + THEN Assert(FALSE, "Test 18 failed") + ELSE Print("Test 18 OK", TRUE) + /\ \* Test 19 added 12 Jun 2014 + IF \/ {1} \X {2} \X {} # {22} \X {xyz \in {1,2,3} : xyz < 0} + \/ {1} \X {2} \X {} # {22} \X (2..1) + \/ {1} \X {2} \X {} # {22} \X ({1,2,3} \cap (4..6)) + \/ {1} \X {2} \X {} # {22} \X ({} \cup {xz \in {1,2,3} : xz < 0}) + \/ {1} \X {2} \X {} # {22} \X ({2,3,4} \X {}) + \/ {1} \X {2} \X {} # {22} \X ([a: {}, b : {1}]) + \/ {1} \X {2} \X {} = {22} \X (SUBSET {}) + \/ {1} \X {2} \X {} # {22} \X (UNION {}) + \/ {1} \X {2} \X {} # {22} \X ({2,3,4} \ (1..5)) + \/ {1} \X {2} \X {} # {22} \X ([{1,2,3} -> {}]) + \/ {1} \X {2} \X {} # {22} \X ({}) + \/ {1} \X {2} \X {} # {22} \X ({xyz+1 : xyz \in 2..1}) + THEN Assert(FALSE, "Test 19 failed") + ELSE Print("Test 19 OK", TRUE) +========================================= diff --git a/tlatools/test-model/suite/test50.cfg b/tlatools/test-model/suite/test50.cfg new file mode 100644 index 0000000000000000000000000000000000000000..5e8cedc4cac917a8a41ec9ad1e980ca0e367180c --- /dev/null +++ b/tlatools/test-model/suite/test50.cfg @@ -0,0 +1,11 @@ + INIT + Init + + NEXT + Next + + INVARIANT Inv + + + + \ No newline at end of file diff --git a/tlatools/test-model/suite/test50.tla b/tlatools/test-model/suite/test50.tla new file mode 100644 index 0000000000000000000000000000000000000000..15eecda99a8928eba06fbe67bf6a3f835ea7bc11 --- /dev/null +++ b/tlatools/test-model/suite/test50.tla @@ -0,0 +1,53 @@ +--------------- MODULE test50 ------------- + +(* test usability of the following aliases with the standard modules: + <= and >= of Naturals + \circ of Sequences + (+) and (-) of Bags + +*) + + +EXTENDS Naturals, Sequences, Bags, TLC + +Bag1 == SetToBag({"a", "b"}) +Bag2 == Bag1 \oplus SetToBag({"a"}) \* causes TLC bug +Bag3 == Bag2 \ominus Bag1 +Bag2a == Bag1 (+) SetToBag({"a"}) +Bag3a == Bag2 (-) Bag1 + + +VARIABLES x +Init == x = 0 +Next == x'=x + +Inv == + /\ IF Bag2 = Bag2a + THEN Print("Test 1 OK", TRUE) + ELSE Assert(FALSE, "Test 1 Failed") + + /\ IF Bag3 = Bag3a + THEN Print("Test 2 OK", TRUE) + ELSE Assert(FALSE, "Test 2 Failed") + + /\ IF <<"a">> \o <<"b">> = <<"a">> \circ <<"b">> + THEN Print("Test 3 OK", TRUE) + ELSE Assert(FALSE, "Test 3 Failed") + + /\ IF 1 \leq 2 + THEN Print("Test 4 OK", TRUE) + ELSE Assert(FALSE, "Test 4 Failed") + + /\ IF 1 <= 2 + THEN Print("Test 5 OK", TRUE) + ELSE Assert(FALSE, "Test 5 Failed") + + /\ IF 2 \geq 1 + THEN Print("Test 6 OK", TRUE) + ELSE Assert(FALSE, "Test 6 Failed") + + /\ IF 2 >= 1 + THEN Print("Test 7 OK", TRUE) + ELSE Assert(FALSE, "Test 7 Failed") + +======================================= diff --git a/tlatools/test-model/suite/test51.cfg b/tlatools/test-model/suite/test51.cfg new file mode 100644 index 0000000000000000000000000000000000000000..8d1c8b69c3fce7bea45c73efd06983e3c419a92f --- /dev/null +++ b/tlatools/test-model/suite/test51.cfg @@ -0,0 +1 @@ + diff --git a/tlatools/test-model/suite/test51.tla b/tlatools/test-model/suite/test51.tla new file mode 100644 index 0000000000000000000000000000000000000000..1c8976bc7e53c74505bc38652e13bbed4a9a708c --- /dev/null +++ b/tlatools/test-model/suite/test51.tla @@ -0,0 +1,495 @@ +---------------------- MODULE test51 ----------------------- +\* Test of handling of infix operator symbols + +EXTENDS TLC + +\* Test parser on action/temporal infix operators + +FooA == INSTANCE test51a +FooB == INSTANCE test51b + +TempTest(_+_) == TRUE + FALSE +Foo1 == TempTest(-+->) +Foo2 == TempTest(~>) +Foo3 == TempTest(\cdot) + +\* Definining all undefined infix operators + +a + b == {a, b} +a - b == {a, b} +a * b == {a, b} +a / b == {a, b} +a ..b == {a, b} +a < b == {a, b} +a > b == {a, b} +a \leq b == {a, b} +a \geq b == {a, b} +a % b == {a, b} +a ^b == {a, b} +a \o b == {a, b} +a ++ b == {a, b} +a \div b == {a, b} +a ...b == {a, b} +a -- b == {a, b} +a (+)b == {a, b} +a (-)b == {a, b} +a (\X)b == {a, b} +a (/)b == {a, b} +a (.)b == {a, b} +a ** b == {a, b} +a \sqcap b == {a, b} +a // b == {a, b} +a \prec b == {a, b} +a \succ b == {a, b} +a \preceq b == {a, b} +a \succeq b == {a, b} +a \sqcup b == {a, b} +a ^^b == {a, b} +a \ll b == {a, b} +a \gg b == {a, b} +a <: b == {a, b} +a & b == {a, b} +a && b == {a, b} +a \sqsubset b == {a, b} +a \sqsupset b == {a, b} +a \sqsubseteq b == {a, b} +a \sqsupseteq b == {a, b} +a | b == {a, b} +a || b == {a, b} +a \subset b == {a, b} +a \supset b == {a, b} +a \supseteq b == {a, b} +a \star b == {a, b} +a %% b == {a, b} +a |- b == {a, b} +a -|b == {a, b} +a |= b == {a, b} +a =|b == {a, b} +a \bullet b == {a, b} +a ##b == {a, b} +a \sim b == {a, b} +a \simeq b == {a, b} +a \approx b == {a, b} +a \cong b == {a, b} +a $b == {a, b} +a $$b == {a, b} +a := b == {a, b} +a ::= b == {a, b} +a \asymp b == {a, b} +a \doteq b == {a, b} +a ?? b == {a, b} +a !! b == {a, b} +a \propto b == {a, b} +a \wr b == {a, b} +a \uplus b == {a, b} +a \bigcirc b == {a, b} + +TestOpArg(Foo(_,_)) == Foo("a", "b") + +TestTLCOps(colongt(_,_), atat(_,_)) == + atat( colongt(1, "ab"), colongt(2, "cd")) + \* 1 :> "ab" @@ 2 :> "cd" + +TestDefinedSetOp(Foo(_,_)) == Foo({"a", "b"}, {"b", "c"}) +TestDefinedBooleanOp(Foo(_,_)) == Foo(TRUE,FALSE) +TestIn(Foo(_,_)) == Foo("a", {"b", "c"}) + +ASSUME + /\ IF TestOpArg(+ ) = {"a", "b"} + THEN Print("Test 1 OK", TRUE) + ELSE Assert(FALSE, "Test 1 Failed") + /\ IF TestOpArg( - ) = {"a", "b"} + THEN Print("Test 2 OK", TRUE) + ELSE Assert(FALSE, "Test 2 Failed") + /\ IF TestOpArg( * ) = {"a", "b"} + THEN Print("Test 3 OK", TRUE) + ELSE Assert(FALSE, "Test 3 Failed") + /\ IF TestOpArg(/ ) = {"a", "b"} + THEN Print("Test 4 OK", TRUE) + ELSE Assert(FALSE, "Test 4 Failed") + /\ IF TestOpArg(..) = {"a", "b"} + THEN Print("Test 5 OK", TRUE) + ELSE Assert(FALSE, "Test 5 Failed") + /\ IF TestOpArg(< ) = {"a", "b"} + THEN Print("Test 6 OK", TRUE) + ELSE Assert(FALSE, "Test 6 Failed") + /\ IF TestOpArg(> ) = {"a", "b"} + THEN Print("Test 7 OK", TRUE) + ELSE Assert(FALSE, "Test 7 Failed") + /\ IF TestOpArg(\leq ) = {"a", "b"} + THEN Print("Test 8 OK", TRUE) + ELSE Assert(FALSE, "Test 8 Failed") + /\ IF TestOpArg(=<) = {"a", "b"} + THEN Print("Test 9 OK", TRUE) + ELSE Assert(FALSE, "Test 9 Failed") + /\ IF TestOpArg(<=) = {"a", "b"} + THEN Print("Test 10 OK", TRUE) + ELSE Assert(FALSE, "Test 10 Failed") + /\ IF TestOpArg(\geq ) = {"a", "b"} + THEN Print("Test 11 OK", TRUE) + ELSE Assert(FALSE, "Test 11 Failed") + /\ IF TestOpArg(>=) = {"a", "b"} + THEN Print("Test 12 OK", TRUE) + ELSE Assert(FALSE, "Test 12 Failed") + /\ IF TestOpArg(% ) = {"a", "b"} + THEN Print("Test 13 OK", TRUE) + ELSE Assert(FALSE, "Test 13 Failed") + /\ IF TestOpArg(^) = {"a", "b"} + THEN Print("Test 14 OK", TRUE) + ELSE Assert(FALSE, "Test 14 Failed") + /\ IF TestOpArg(\circ ) = {"a", "b"} + THEN Print("Test 15 OK", TRUE) + ELSE Assert(FALSE, "Test 15 Failed") + /\ IF TestOpArg(\o) = {"a", "b"} + THEN Print("Test 16 OK", TRUE) + ELSE Assert(FALSE, "Test 16 Failed") + /\ IF TestOpArg(++ ) = {"a", "b"} + THEN Print("Test 17 OK", TRUE) + ELSE Assert(FALSE, "Test 17 Failed") + /\ IF TestOpArg(\div ) = {"a", "b"} + THEN Print("Test 18 OK", TRUE) + ELSE Assert(FALSE, "Test 18 Failed") + /\ IF TestOpArg(...) = {"a", "b"} + THEN Print("Test 19 OK", TRUE) + ELSE Assert(FALSE, "Test 19 Failed") + /\ IF TestOpArg(-- ) = {"a", "b"} + THEN Print("Test 20 OK", TRUE) + ELSE Assert(FALSE, "Test 20 Failed") + /\ IF TestOpArg(\oplus ) = {"a", "b"} + THEN Print("Test 21 OK", TRUE) + ELSE Assert(FALSE, "Test 21 Failed") + /\ IF TestOpArg((+)) = {"a", "b"} + THEN Print("Test 22 OK", TRUE) + ELSE Assert(FALSE, "Test 22 Failed") + /\ IF TestOpArg(\ominus ) = {"a", "b"} + THEN Print("Test 23 OK", TRUE) + ELSE Assert(FALSE, "Test 23 Failed") + /\ IF TestOpArg((-)) = {"a", "b"} + THEN Print("Test 24 OK", TRUE) + ELSE Assert(FALSE, "Test 24 Failed") + /\ IF TestOpArg(\otimes ) = {"a", "b"} + THEN Print("Test 25 OK", TRUE) + ELSE Assert(FALSE, "Test 25 Failed") + /\ IF TestOpArg((\X)) = {"a", "b"} + THEN Print("Test 26 OK", TRUE) + ELSE Assert(FALSE, "Test 26 Failed") + /\ IF TestOpArg(\oslash ) = {"a", "b"} + THEN Print("Test 27 OK", TRUE) + ELSE Assert(FALSE, "Test 27 Failed") + /\ IF TestOpArg((/)) = {"a", "b"} + THEN Print("Test 28 OK", TRUE) + ELSE Assert(FALSE, "Test 28 Failed") + /\ IF TestOpArg(\odot ) = {"a", "b"} + THEN Print("Test 29 OK", TRUE) + ELSE Assert(FALSE, "Test 29 Failed") + /\ IF TestOpArg((.)) = {"a", "b"} + THEN Print("Test 30 OK", TRUE) + ELSE Assert(FALSE, "Test 30 Failed") + /\ IF TestOpArg( ** ) = {"a", "b"} + THEN Print("Test 31 OK", TRUE) + ELSE Assert(FALSE, "Test 31 Failed") + /\ IF TestOpArg(\sqcap ) = {"a", "b"} + THEN Print("Test 32 OK", TRUE) + ELSE Assert(FALSE, "Test 32 Failed") + /\ IF TestOpArg(// ) = {"a", "b"} + THEN Print("Test 33 OK", TRUE) + ELSE Assert(FALSE, "Test 33 Failed") + /\ IF TestOpArg(\prec ) = {"a", "b"} + THEN Print("Test 34 OK", TRUE) + ELSE Assert(FALSE, "Test 34 Failed") + /\ IF TestOpArg(\succ ) = {"a", "b"} + THEN Print("Test 35 OK", TRUE) + ELSE Assert(FALSE, "Test 35 Failed") + /\ IF TestOpArg(\preceq ) = {"a", "b"} + THEN Print("Test 36 OK", TRUE) + ELSE Assert(FALSE, "Test 36 Failed") + /\ IF TestOpArg(\succeq ) = {"a", "b"} + THEN Print("Test 37 OK", TRUE) + ELSE Assert(FALSE, "Test 37 Failed") + /\ IF TestOpArg(\sqcup ) = {"a", "b"} + THEN Print("Test 38 OK", TRUE) + ELSE Assert(FALSE, "Test 38 Failed") + /\ IF TestOpArg(^^) = {"a", "b"} + THEN Print("Test 39 OK", TRUE) + ELSE Assert(FALSE, "Test 39 Failed") + /\ IF TestOpArg(\ll ) = {"a", "b"} + THEN Print("Test 40 OK", TRUE) + ELSE Assert(FALSE, "Test 40 Failed") + /\ IF TestOpArg(\gg ) = {"a", "b"} + THEN Print("Test 41 OK", TRUE) + ELSE Assert(FALSE, "Test 41 Failed") + /\ IF TestOpArg(<: ) = {"a", "b"} + THEN Print("Test 42 OK", TRUE) + ELSE Assert(FALSE, "Test 42 Failed") + /\ IF TestOpArg(& ) = {"a", "b"} + THEN Print("Test 43 OK", TRUE) + ELSE Assert(FALSE, "Test 43 Failed") + /\ IF TestOpArg(&& ) = {"a", "b"} + THEN Print("Test 44 OK", TRUE) + ELSE Assert(FALSE, "Test 44 Failed") + /\ IF TestOpArg(\sqsubset ) = {"a", "b"} + THEN Print("Test 45 OK", TRUE) + ELSE Assert(FALSE, "Test 45 Failed") + /\ IF TestOpArg(\sqsupset ) = {"a", "b"} + THEN Print("Test 46 OK", TRUE) + ELSE Assert(FALSE, "Test 46 Failed") + /\ IF TestOpArg(\sqsubseteq ) = {"a", "b"} + THEN Print("Test 47 OK", TRUE) + ELSE Assert(FALSE, "Test 47 Failed") + /\ IF TestOpArg(\sqsupseteq ) = {"a", "b"} + THEN Print("Test 48 OK", TRUE) + ELSE Assert(FALSE, "Test 48 Failed") + /\ IF TestOpArg(| ) = {"a", "b"} + THEN Print("Test 49 OK", TRUE) + ELSE Assert(FALSE, "Test 49 Failed") + /\ IF TestOpArg(|| ) = {"a", "b"} + THEN Print("Test 50 OK", TRUE) + ELSE Assert(FALSE, "Test 50 Failed") + /\ IF TestOpArg(\subset ) = {"a", "b"} + THEN Print("Test 51 OK", TRUE) + ELSE Assert(FALSE, "Test 51 Failed") + /\ IF TestOpArg(\supset ) = {"a", "b"} + THEN Print("Test 52 OK", TRUE) + ELSE Assert(FALSE, "Test 52 Failed") + /\ IF TestOpArg(\supseteq ) = {"a", "b"} + THEN Print("Test 53 OK", TRUE) + ELSE Assert(FALSE, "Test 53 Failed") + /\ IF TestOpArg(\star ) = {"a", "b"} + THEN Print("Test 54 OK", TRUE) + ELSE Assert(FALSE, "Test 54 Failed") + /\ IF TestOpArg(%% ) = {"a", "b"} + THEN Print("Test 55 OK", TRUE) + ELSE Assert(FALSE, "Test 55 Failed") + /\ IF TestOpArg(|- ) = {"a", "b"} + THEN Print("Test 56 OK", TRUE) + ELSE Assert(FALSE, "Test 56 Failed") + /\ IF TestOpArg(-|) = {"a", "b"} + THEN Print("Test 57 OK", TRUE) + ELSE Assert(FALSE, "Test 57 Failed") + /\ IF TestOpArg(|= ) = {"a", "b"} + THEN Print("Test 58 OK", TRUE) + ELSE Assert(FALSE, "Test 58 Failed") + /\ IF TestOpArg(=|) = {"a", "b"} + THEN Print("Test 59 OK", TRUE) + ELSE Assert(FALSE, "Test 59 Failed") + /\ IF TestOpArg(\bullet ) = {"a", "b"} + THEN Print("Test 60 OK", TRUE) + ELSE Assert(FALSE, "Test 60 Failed") + /\ IF TestOpArg(##) = {"a", "b"} + THEN Print("Test 61 OK", TRUE) + ELSE Assert(FALSE, "Test 61 Failed") + /\ IF TestOpArg(\sim ) = {"a", "b"} + THEN Print("Test 62 OK", TRUE) + ELSE Assert(FALSE, "Test 62 Failed") + /\ IF TestOpArg(\simeq ) = {"a", "b"} + THEN Print("Test 63 OK", TRUE) + ELSE Assert(FALSE, "Test 63 Failed") + /\ IF TestOpArg(\approx ) = {"a", "b"} + THEN Print("Test 64 OK", TRUE) + ELSE Assert(FALSE, "Test 64 Failed") + /\ IF TestOpArg(\cong ) = {"a", "b"} + THEN Print("Test 65 OK", TRUE) + ELSE Assert(FALSE, "Test 65 Failed") + /\ IF TestOpArg($) = {"a", "b"} + THEN Print("Test 66 OK", TRUE) + ELSE Assert(FALSE, "Test 66 Failed") + /\ IF TestOpArg($$) = {"a", "b"} + THEN Print("Test 67 OK", TRUE) + ELSE Assert(FALSE, "Test 67 Failed") + /\ IF TestOpArg(:= ) = {"a", "b"} + THEN Print("Test 68 OK", TRUE) + ELSE Assert(FALSE, "Test 68 Failed") + /\ IF TestOpArg(::= ) = {"a", "b"} + THEN Print("Test 69 OK", TRUE) + ELSE Assert(FALSE, "Test 69 Failed") + /\ IF TestOpArg(\asymp ) = {"a", "b"} + THEN Print("Test 70 OK", TRUE) + ELSE Assert(FALSE, "Test 70 Failed") + /\ IF TestOpArg(\doteq ) = {"a", "b"} + THEN Print("Test 71 OK", TRUE) + ELSE Assert(FALSE, "Test 71 Failed") + /\ IF TestOpArg(?? ) = {"a", "b"} + THEN Print("Test 72 OK", TRUE) + ELSE Assert(FALSE, "Test 72 Failed") + /\ IF TestOpArg(!! ) = {"a", "b"} + THEN Print("Test 73 OK", TRUE) + ELSE Assert(FALSE, "Test 73 Failed") + /\ IF TestOpArg(\propto ) = {"a", "b"} + THEN Print("Test 74 OK", TRUE) + ELSE Assert(FALSE, "Test 74 Failed") + /\ IF TestOpArg(\wr ) = {"a", "b"} + THEN Print("Test 75 OK", TRUE) + ELSE Assert(FALSE, "Test 75 Failed") + /\ IF TestOpArg(\uplus ) = {"a", "b"} + THEN Print("Test 76 OK", TRUE) + ELSE Assert(FALSE, "Test 76 Failed") + /\ IF TestOpArg(\bigcirc ) = {"a", "b"} + THEN Print("Test 77 OK", TRUE) + ELSE Assert(FALSE, "Test 77 Failed") + /\ IF TestTLCOps(:>, @@) = 1 :> "ab" @@ 2 :> "cd" + THEN Print("Test 78 OK", TRUE) + ELSE Assert(FALSE, "Test 78 Failed") + /\ IF TestDefinedBooleanOp(=>) = (TRUE => FALSE) + THEN Print("Test 79 OK", TRUE) + ELSE Assert(FALSE, "Test 79 Failed") + /\ IF TestDefinedBooleanOp(\equiv) = (TRUE \equiv FALSE) + THEN Print("Test 80 OK", TRUE) + ELSE Assert(FALSE, "Test 80 Failed") + /\ IF TestDefinedBooleanOp(<=>) = (TRUE <=> FALSE) + THEN Print("Test 81 OK", TRUE) + ELSE Assert(FALSE, "Test 81 Failed") +\* /\ Print("Test 82 fails because of parser bug", TRUE) + /\ IF TestDefinedBooleanOp(/\) = (TRUE /\ FALSE) + THEN Print("Test 82 OK", TRUE) + ELSE Assert(FALSE, "Test 82 Failed") + /\ IF TestDefinedBooleanOp(\land) = (TRUE \land FALSE) + THEN Print("Test 83 OK", TRUE) + ELSE Assert(FALSE, "Test 83 Failed") +\* /\ Print("Test 84 fails because of parser bug", TRUE) + /\ IF TestDefinedBooleanOp(\/) = (TRUE \/ FALSE) + THEN Print("Test 84 OK", TRUE) + ELSE Assert(FALSE, "Test 84 Failed") + /\ IF TestDefinedBooleanOp(\lor) = (TRUE \lor FALSE) + THEN Print("Test 85 OK", TRUE) + ELSE Assert(FALSE, "Test 85 Failed") + /\ IF TestDefinedSetOp(#) = ({"a", "b"} # {"b", "c"}) + THEN Print("Test 86 OK", TRUE) + ELSE Assert(FALSE, "Test 86 Failed") + /\ IF TestDefinedSetOp(/=) = ({"a", "b"} /= {"b", "c"}) + THEN Print("Test 87 OK", TRUE) + ELSE Assert(FALSE, "Test 87 Failed") + /\ IF TestIn(\in) = ("a"\in {"b", "c"}) + THEN Print("Test 88 OK", TRUE) + ELSE Assert(FALSE, "Test 88 Failed") + /\ IF TestIn(\notin) = ("a" \notin {"b", "c"}) + THEN Print("Test 89 OK", TRUE) + ELSE Assert(FALSE, "Test 89 Failed") + /\ IF TestDefinedSetOp(\subseteq) = ({"a", "b"} \subseteq {"b", "c"}) + THEN Print("Test 90 OK", TRUE) + ELSE Assert(FALSE, "Test 90 Failed") + /\ IF TestDefinedSetOp(\) = ({"a", "b"} \ {"b", "c"}) + THEN Print("Test 91 OK", TRUE) + ELSE Assert(FALSE, "Test 91 Failed") + /\ IF TestDefinedSetOp(\cap) = ({"a", "b"} \cap {"b", "c"}) + THEN Print("Test 92 OK", TRUE) + ELSE Assert(FALSE, "Test 92 Failed") + /\ IF TestDefinedSetOp(\intersect) = ({"a", "b"} \intersect {"b", "c"}) + THEN Print("Test 93 OK", TRUE) + ELSE Assert(FALSE, "Test 93 Failed") + /\ IF TestDefinedSetOp(\cup) = ({"a", "b"} \cup {"b", "c"}) + THEN Print("Test 94 OK", TRUE) + ELSE Assert(FALSE, "Test 94 Failed") + /\ IF TestDefinedSetOp(\union) = ({"a", "b"} \union {"b", "c"}) + THEN Print("Test 95 OK", TRUE) + ELSE Assert(FALSE, "Test 95 Failed") + + +============================================================== +78 +ALL UNDEFINED INFIX OPERATORS + ++ +- +* +/ +.. +< +> +\leq +=< +<= +\geq +>= +% +^ +\circ +\o +++ +\div +... +-- +\oplus +(+) +\ominus +(-) +\otimes +(\X) +\oslash +(/) +\odot +(.) +** +\sqcap +// +\prec +\succ +\preceq +\succeq +\sqcup +^^ +\ll +\gg +<: +& +&& +\sqsubset +\sqsupset +\sqsubseteq +\sqsupseteq +| +|| +\subset +\supset +\supseteq +\star +%% +|- +-| +|= +=| +\bullet +## +\sim +\simeq +\approx +\cong +$ +$$ +:= +::= +\asymp +\doteq +?? +!! +\propto +\wr +\uplus +\bigcirc + + +BUILT-IN BOOLEANS +=> +\equiv +<=> +/\ +\land +\/ +\lor + +BUILT-IN SET OPERATORS +# +/= +\in +\notin +\subseteq +\ +\cap +\intersect +\cup +\union + +OTHER + +-+-> +~> +\cdot diff --git a/tlatools/test-model/suite/test51a.tla b/tlatools/test-model/suite/test51a.tla new file mode 100644 index 0000000000000000000000000000000000000000..60ad0e2ab62f2eacf5e53750e7756146a60ea403 --- /dev/null +++ b/tlatools/test-model/suite/test51a.tla @@ -0,0 +1,13 @@ +------------------- MODULE test51a ------------------------- + +\* Test definability of some aliases + +a =< b == {a, b} +a >=b == {a, b} +a \circ b == {a, b} +a \oplus b == {a, b} +a \ominus b == {a, b} +a \otimes b == {a, b} +a \oslash b == {a, b} +a \odot b == {a, b} +============================================================================= diff --git a/tlatools/test-model/suite/test51b.tla b/tlatools/test-model/suite/test51b.tla new file mode 100644 index 0000000000000000000000000000000000000000..2c21874d170bd255904e7f7e66218df301c8c19b --- /dev/null +++ b/tlatools/test-model/suite/test51b.tla @@ -0,0 +1,4 @@ +---------------- MODULE test51b ----------------- +a <= b == {a, b} + +================================================================== diff --git a/tlatools/test-model/suite/test52.cfg b/tlatools/test-model/suite/test52.cfg new file mode 100644 index 0000000000000000000000000000000000000000..942df65a55fdabbd31d3495a5c5124ef762101af --- /dev/null +++ b/tlatools/test-model/suite/test52.cfg @@ -0,0 +1,6 @@ +SPECIFICATION Spec +PROPERTY Property +\* The Invariant is commented because of a known bug in TLC. +\* If this bug is ever addressed, Invariant can be re-enabled +\* and the test test52enabled be deleted. +\*INVARIANT Invariant diff --git a/tlatools/test-model/test52.tla b/tlatools/test-model/suite/test52.tla similarity index 93% rename from tlatools/test-model/test52.tla rename to tlatools/test-model/suite/test52.tla index 10b5c5b0d6eb3cb35cdb085398ea3c01400659a3..0cf62f56535b99a564eee848238b31b57ea68d98 100644 --- a/tlatools/test-model/test52.tla +++ b/tlatools/test-model/suite/test52.tla @@ -1,71 +1,71 @@ ----------------------- MODULE test52 ----------------------- - -\* Test of prefix and postfix operators as arguments - -EXTENDS TLC - -VARIABLES x, y - -BoxTest(-._) == -(x=0) -Foo1 == BoxTest([]) -Foo2 == BoxTest(<>) - --. a == {{"a"}, {"b"}} \ a -a ^+ == {{"a"}, {"b"}} \ a -a ^* == {{"a"}, {"b"}} \ a -a ^# == {{"a"}, {"b"}} \ a - -SetOpTest(Foo(_)) == Foo({{"b"}, {"c"}}) -DomainTest(Foo(_)) == Foo([i\in {"a", "b"} |-> i]) - -ASSUME - /\ Print("Test1 Begun", TRUE) - /\ SetOpTest(-.) = - {{"b"}, {"c"}} - /\ SetOpTest(^+) = {{"b"}, {"c"}} ^+ - /\ SetOpTest(^*) = {{"b"}, {"c"}} ^* - /\ SetOpTest(^#) = {{"b"}, {"c"}} ^# - /\ SetOpTest(SUBSET) = SUBSET {{"b"}, {"c"}} - /\ SetOpTest(UNION) = UNION {{"b"}, {"c"}} - /\ DomainTest(DOMAIN) = DOMAIN [i\in {"a", "b"} |-> i] - /\ Print("Test1 Finished", TRUE) - - -Init == /\ x = {"a"} - /\ y = {"a"} -Next == /\ x'= x \cup {"a"} - /\ y'= y \cup {"b"} -Spec == Init /\ [][Next]_x - -EnabledTest(Foo(_)) == Foo(x'=x) -UnchangedTest(Foo(_)) == Foo(x) -PrimeTest(Foo(_)) == Foo(x) = x - -Invariant == - /\ EnabledTest(ENABLED) - -ImpliedAction == - /\ UnchangedTest(UNCHANGED) - /\ PrimeTest(') - -Property == [][ImpliedAction]_<<x, y>> - -================================================================== -\* Prefix Operators -~ -ENABLED -UNCHANGED -[] -<> -SUBSET -UNION -DOMAIN -- - -\* Postfix Operators -\* Predefined -' - -\* Undefined -^+ -^* -^# +---------------------- MODULE test52 ----------------------- + +\* Test of prefix and postfix operators as arguments + +EXTENDS TLC + +VARIABLES x, y + +BoxTest(-._) == -(x=0) +Foo1 == BoxTest([]) +Foo2 == BoxTest(<>) + +-. a == {{"a"}, {"b"}} \ a +a ^+ == {{"a"}, {"b"}} \ a +a ^* == {{"a"}, {"b"}} \ a +a ^# == {{"a"}, {"b"}} \ a + +SetOpTest(Foo(_)) == Foo({{"b"}, {"c"}}) +DomainTest(Foo(_)) == Foo([i\in {"a", "b"} |-> i]) + +ASSUME + /\ Print("Test1 Begun", TRUE) + /\ SetOpTest(-.) = - {{"b"}, {"c"}} + /\ SetOpTest(^+) = {{"b"}, {"c"}} ^+ + /\ SetOpTest(^*) = {{"b"}, {"c"}} ^* + /\ SetOpTest(^#) = {{"b"}, {"c"}} ^# + /\ SetOpTest(SUBSET) = SUBSET {{"b"}, {"c"}} + /\ SetOpTest(UNION) = UNION {{"b"}, {"c"}} + /\ DomainTest(DOMAIN) = DOMAIN [i\in {"a", "b"} |-> i] + /\ Print("Test1 Finished", TRUE) + + +Init == /\ x = {"a"} + /\ y = {"a"} +Next == /\ x'= x \cup {"a"} + /\ y'= y \cup {"b"} +Spec == Init /\ [][Next]_<<x, y>> + +EnabledTest(Foo(_)) == Foo(x'=x) +UnchangedTest(Foo(_)) == Foo(x) +PrimeTest(Foo(_)) == Foo(x) = x + +Invariant == + /\ EnabledTest(ENABLED) + +ImpliedAction == + /\ UnchangedTest(UNCHANGED) + /\ PrimeTest(') + +Property == [][ImpliedAction]_<<x, y>> + +================================================================== +\* Prefix Operators +~ +ENABLED +UNCHANGED +[] +<> +SUBSET +UNION +DOMAIN +- + +\* Postfix Operators +\* Predefined +' + +\* Undefined +^+ +^* +^# diff --git a/tlatools/test-model/suite/test53.cfg b/tlatools/test-model/suite/test53.cfg new file mode 100644 index 0000000000000000000000000000000000000000..e3e8c3d2505a0163440775d92531a92d4321811c --- /dev/null +++ b/tlatools/test-model/suite/test53.cfg @@ -0,0 +1,2 @@ +INIT Init +NEXT Next diff --git a/tlatools/test-model/suite/test53.tla b/tlatools/test-model/suite/test53.tla new file mode 100644 index 0000000000000000000000000000000000000000..a395492b848682f035d04f7db5a95832f09854ac --- /dev/null +++ b/tlatools/test-model/suite/test53.tla @@ -0,0 +1,36 @@ +--------------- MODULE test53 ------------- + +(* test definability of operators with aliases *) + + +EXTENDS TLC + +VARIABLE x + +u \oplus v == u +u \ominus v == u +u \odot v == u +u \oslash v == u +u \otimes v == u +u \circ v == u +u % v == u +u =< v == u +u >= v == u + + +Init == + /\ x = 0 + +Next == + /\ x = 0 + /\ + \/ x' = x \oplus 1 + \/ x' = x \ominus 1 + \/ x' = x \odot 1 + \/ x' = x \oslash 1 + \/ x' = x \otimes 1 + \/ x' = x \circ 1 + \/ x' = x % 1 \* causes TLC bug + \/ x' = (x =< 1) \* causes TLC bug + \/ x' = (x >= 1 ) \* causes TLC bug +======================================= diff --git a/tlatools/test-model/suite/test54.cfg b/tlatools/test-model/suite/test54.cfg new file mode 100644 index 0000000000000000000000000000000000000000..e3e8c3d2505a0163440775d92531a92d4321811c --- /dev/null +++ b/tlatools/test-model/suite/test54.cfg @@ -0,0 +1,2 @@ +INIT Init +NEXT Next diff --git a/tlatools/test-model/suite/test54.tla b/tlatools/test-model/suite/test54.tla new file mode 100644 index 0000000000000000000000000000000000000000..ae7c1670ea8e68ee6685af8c45d3696d661fa36b --- /dev/null +++ b/tlatools/test-model/suite/test54.tla @@ -0,0 +1,36 @@ +--------------- MODULE test54 ------------- + +(* test definability of operators with aliases *) + + +EXTENDS TLC + +VARIABLE x + +u \oplus v == u +u \ominus v == u +u \odot v == u +u \oslash v == u +u \otimes v == u +u \o v == u +u % v == u +u =< v == u +u >= v == u + + +Init == + /\ x = 0 + +Next == + /\ x = 0 + /\ + \/ x' = x (+) 1 \* causes TLC bug + \/ x' = x (-) 1 \* causes TLC bug + \/ x' = x (.) 1 \* causes TLC bug + \/ x' = x (/) 1 \* causes TLC bug + \/ x' = x (\X) 1 \* parser can't handle this + \/ x' = x \circ 1 \* causes TLC bug + \/ x' = x % 1 + \/ x' = (x =< 1) \* TLC bug : sets x' to TRUE + \/ x' = (x >= 1 ) \* TLC bug : sets x' to FALSE +======================================= diff --git a/tlatools/test-model/test55.cfg b/tlatools/test-model/suite/test55.cfg similarity index 95% rename from tlatools/test-model/test55.cfg rename to tlatools/test-model/suite/test55.cfg index fe7d864f88046c4367c05c018a558362c39fe97c..305c3a68fbd27fb38d68161f05488ca00c1b8202 100644 --- a/tlatools/test-model/test55.cfg +++ b/tlatools/test-model/suite/test55.cfg @@ -1,3 +1,3 @@ -SPECIFICATION Spec -INVARIANT Invariant -PROPERTY InitProperty +SPECIFICATION Spec +INVARIANT Invariant +PROPERTY InitProperty diff --git a/tlatools/test-model/test55.tla b/tlatools/test-model/suite/test55.tla similarity index 95% rename from tlatools/test-model/test55.tla rename to tlatools/test-model/suite/test55.tla index fd95511e3caad8f32b2dd3be3e3fe8cfa02a4f21..3b94b2d51d54bd4283f59c7737a8e369b478416e 100644 --- a/tlatools/test-model/test55.tla +++ b/tlatools/test-model/suite/test55.tla @@ -1,22 +1,22 @@ -------------------------------- MODULE test55 ------------------------------- -\* Test of ENABLED with INSTANCE semantics -EXTENDS Naturals - -VARIABLE z - -I == INSTANCE test55a WITH x <- z, y <- z - -Init == z = 0 -Next == z' = 1 - z - -Spec == Init /\ [][Next]_z /\ WF_z(Next) -Invariant == /\ I!F - /\ ~I!G - -InitProperty == I!Init -SafeProperty == I!SafeSpec -LiveProperty == I!LiveSpec - -============================================================================= - - +------------------------------- MODULE test55 ------------------------------- +\* Test of ENABLED with INSTANCE semantics +EXTENDS Naturals + +VARIABLE z + +I == INSTANCE test55a WITH x <- z, y <- z + +Init == z = 0 +Next == z' = 1 - z + +Spec == Init /\ [][Next]_z /\ WF_z(Next) +Invariant == /\ I!F + /\ ~I!G + +InitProperty == I!Init +SafeProperty == I!SafeSpec +LiveProperty == I!LiveSpec + +============================================================================= + + diff --git a/tlatools/test-model/test55a.tla b/tlatools/test-model/suite/test55a.tla similarity index 96% rename from tlatools/test-model/test55a.tla rename to tlatools/test-model/suite/test55a.tla index bd17db17c8ca799dc92596fd99f1300a7bc8b231..ad6aee45a926630145f1336ac71eb2fccdeb2ff2 100644 --- a/tlatools/test-model/test55a.tla +++ b/tlatools/test-model/suite/test55a.tla @@ -1,14 +1,14 @@ -------------------------------- MODULE test55a ------------------------------ -EXTENDS Naturals -VARIABLES x, y - -F == ENABLED (x'=0 /\ y'=1) -G == ENABLED ((x # y) /\ (x'=0) /\ (y'=1)) - -Init == (x = 0) /\ (y = 0) -Next == /\ x' = 1 - x - /\ y' = y - -SafeSpec == Init /\ [][Next]_<<x,y>> -LiveSpec == SafeSpec /\ WF_<<x,y>>(Next) -============================================================================= +------------------------------- MODULE test55a ------------------------------ +EXTENDS Naturals +VARIABLES x, y + +F == ENABLED (x'=0 /\ y'=1) +G == ENABLED ((x # y) /\ (x'=0) /\ (y'=1)) + +Init == (x = 0) /\ (y = 0) +Next == /\ x' = 1 - x + /\ y' = y + +SafeSpec == Init /\ [][Next]_<<x,y>> +LiveSpec == SafeSpec /\ WF_<<x,y>>(Next) +============================================================================= diff --git a/tlatools/test-model/test56.cfg b/tlatools/test-model/suite/test56.cfg similarity index 97% rename from tlatools/test-model/test56.cfg rename to tlatools/test-model/suite/test56.cfg index 59df66b1045c7bbcce67167852fd154f3d81c41e..8a93632b965223198ef42bd956c09e80e1a9dfb5 100644 --- a/tlatools/test-model/test56.cfg +++ b/tlatools/test-model/suite/test56.cfg @@ -1,2 +1,2 @@ -SPECIFICATION Spec +SPECIFICATION Spec PROPERTY Property \ No newline at end of file diff --git a/tlatools/test-model/test56.tla b/tlatools/test-model/suite/test56.tla similarity index 96% rename from tlatools/test-model/test56.tla rename to tlatools/test-model/suite/test56.tla index 4f204445c4a3d8157babd576089f7b0e5cbf62f1..e44515da94c7d77852bfd7b3994f8e0ceac11bbb 100644 --- a/tlatools/test-model/test56.tla +++ b/tlatools/test-model/suite/test56.tla @@ -1,18 +1,18 @@ -------------------------------- MODULE test56 ------------------------------- -\* Test of subtle INSTANCE semantics -EXTENDS Naturals -VARIABLE x - -I == INSTANCE test56a WITH u <- x - -NotIBangH == (x'=x) /\ I!G(x, x'=x+1) - -Init == \E i \in 1..3 : x = i -Next == x' = 9-x -Spec == Init /\ [][Next]_x - -Property == [][I!H = NotIBangH]_x - -============================================================================= -I!H = (x'=x) /\ ENABLED (u'=x+1 \/ (u'=x /\ u'=x) \/ (u'=x /\ u'=x)) -NotIBangH = (x'=x) /\ ENABLED (x'=x+1 \/ (u'=x /\ x'=x) \/ (u'=x /\ x'=u)) +------------------------------- MODULE test56 ------------------------------- +\* Test of subtle INSTANCE semantics +EXTENDS Naturals +VARIABLE x + +I == INSTANCE test56a WITH u <- x + +NotIBangH == (x'=x) /\ I!G(x, x'=x+1) + +Init == \E i \in 1..3 : x = i +Next == x' = 9-x +Spec == Init /\ [][Next]_x + +Property == [][I!H = NotIBangH]_x + +============================================================================= +I!H = (x'=x) /\ ENABLED (u'=x+1 \/ (u'=x /\ u'=x) \/ (u'=x /\ u'=x)) +NotIBangH = (x'=x) /\ ENABLED (x'=x+1 \/ (u'=x /\ x'=x) \/ (u'=x /\ x'=u)) diff --git a/tlatools/test-model/test56a.tla b/tlatools/test-model/suite/test56a.tla similarity index 98% rename from tlatools/test-model/test56a.tla rename to tlatools/test-model/suite/test56a.tla index 521cf1046b8d7dc9b71f0117916beb05f98b665e..449dd447ff88465b7a710a7eb77853c563fce942 100644 --- a/tlatools/test-model/test56a.tla +++ b/tlatools/test-model/suite/test56a.tla @@ -1,7 +1,7 @@ -------------------------------- MODULE test56a ------------------------------ -\* Test of subtle INSTANCE semantics -EXTENDS Naturals -VARIABLE u -G(v, A) == ENABLED (A \/ (u'=u /\ v'=v) \/ (v'=u /\ u'=v)) -H == (u'=u) /\ G(u, u' = u + 1) +------------------------------- MODULE test56a ------------------------------ +\* Test of subtle INSTANCE semantics +EXTENDS Naturals +VARIABLE u +G(v, A) == ENABLED (A \/ (u'=u /\ v'=v) \/ (v'=u /\ u'=v)) +H == (u'=u) /\ G(u, u' = u + 1) ============================================================================= \ No newline at end of file diff --git a/tlatools/test-model/suite/test57.cfg b/tlatools/test-model/suite/test57.cfg new file mode 100644 index 0000000000000000000000000000000000000000..e9294d0442019c6b2d506c2a5118da6ffb19b55a --- /dev/null +++ b/tlatools/test-model/suite/test57.cfg @@ -0,0 +1,3 @@ +SPECIFICATION Spec +INVARIANT Invariant0 Invariant2 Invariant3 Invariant1 +PROPERTY Property \ No newline at end of file diff --git a/tlatools/test-model/test57.tla b/tlatools/test-model/suite/test57.tla similarity index 96% rename from tlatools/test-model/test57.tla rename to tlatools/test-model/suite/test57.tla index 18539eb3e576a32528123ddd14aea25dc3a0c219..08adb6d0e2ac94935105328a0935bc037cbec538 100644 --- a/tlatools/test-model/test57.tla +++ b/tlatools/test-model/suite/test57.tla @@ -1,19 +1,20 @@ -------------------------------- MODULE test57 ------------------------------- -\* Test of subtle INSTANCE semantics -EXTENDS Naturals -VARIABLE x - -I == INSTANCE test57a WITH u <- x, v <- x - -Init == \E i \in 1..3 : x = i -Next == x' = 9-x -Spec == Init /\ [][Next]_x - -Invariant0 == I!B(Next) -Invariant1 == I!C -Invariant2 == ~ I!B(I!A) -Property == [][~I!A]_x -============================================================================= -I!C = ENABLED((u'=x) /\ (v' = x+1)) -I!B(I!A) = ENABLED ((x'=x) /\ (x'=x+1)) - +------------------------------- MODULE test57 ------------------------------- +\* Test of subtle INSTANCE semantics +EXTENDS Naturals +VARIABLE x + +I == INSTANCE test57a WITH u <- x, v <- x + +Init == \E i \in 1..3 : x = i +Next == x' = 9-x +Spec == Init /\ [][Next]_x + +Invariant0 == I!B(Next) +Invariant1 == I!C +Invariant2 == ~ I!B(I!A) +Invariant3 == I!D +Property == [][~I!A]_x +============================================================================= +I!C = ENABLED((u'=x) /\ (v' = x+1)) +I!B(I!A) = ENABLED ((x'=x) /\ (x'=x+1)) + diff --git a/tlatools/test-model/test57a.tla b/tlatools/test-model/suite/test57a.tla similarity index 94% rename from tlatools/test-model/test57a.tla rename to tlatools/test-model/suite/test57a.tla index d90b00c1be9dcd095ff9389681ef82062ee665cf..4fde946c021c6d5e2ed2267ef65fa1fdb6376616 100644 --- a/tlatools/test-model/test57a.tla +++ b/tlatools/test-model/suite/test57a.tla @@ -1,8 +1,9 @@ -------------------------------- MODULE test57a ------------------------------ -\* Test of subtle INSTANCE semantics -EXTENDS Naturals -VARIABLES u, v -A == (u'=u) /\ (v'=v+1) -B(d) == ENABLED d -C == B(A) +------------------------------- MODULE test57a ------------------------------ +\* Test of subtle INSTANCE semantics +EXTENDS Naturals +VARIABLES u, v +A == (u'=u) /\ (v'=v+1) +B(d) == ENABLED d +C == B(A) +D == ENABLED A ============================================================================= \ No newline at end of file diff --git a/tlatools/test-model/suite/test58.cfg b/tlatools/test-model/suite/test58.cfg new file mode 100644 index 0000000000000000000000000000000000000000..8d1c8b69c3fce7bea45c73efd06983e3c419a92f --- /dev/null +++ b/tlatools/test-model/suite/test58.cfg @@ -0,0 +1 @@ + diff --git a/tlatools/test-model/suite/test58.tla b/tlatools/test-model/suite/test58.tla new file mode 100644 index 0000000000000000000000000000000000000000..8fff7e580b1e748907f8d3de6e2a3554031c506f --- /dev/null +++ b/tlatools/test-model/suite/test58.tla @@ -0,0 +1,16 @@ +------------------- MODULE test58 -------------------- +\* Test of binary, octal, and hexadecimal numbers + +EXTENDS Naturals + +ASSUME + /\ 63 = \b111111 + /\ 63 = \o77 + /\ 63 = \h3F + /\ 63 = \B111111 + /\ 63 = \O77 + /\ 63 = \H3f + /\ 63 = \H3F + /\ 63 = \h3f + +======================================================= diff --git a/tlatools/test-model/test59.cfg b/tlatools/test-model/suite/test59.cfg similarity index 94% rename from tlatools/test-model/test59.cfg rename to tlatools/test-model/suite/test59.cfg index 9816c74f9b53e2266134e30e7da07fa5392c5384..8e570acdc96c3b5f8e28b4ecccd0273858365db2 100644 --- a/tlatools/test-model/test59.cfg +++ b/tlatools/test-model/suite/test59.cfg @@ -1,2 +1,2 @@ -SPECIFICATION Spec -PROPERTY Liveness +SPECIFICATION Spec +PROPERTY Liveness diff --git a/tlatools/test-model/test59.tla b/tlatools/test-model/suite/test59.tla similarity index 96% rename from tlatools/test-model/test59.tla rename to tlatools/test-model/suite/test59.tla index 8a1b24ddf2904342cb22317bb7122f775a6de463..f1e1d2dff5279079471423bfa94b06a8450547d1 100644 --- a/tlatools/test-model/test59.tla +++ b/tlatools/test-model/suite/test59.tla @@ -1,18 +1,18 @@ -------------------------------- MODULE test59 ------------------------------- -EXTENDS Naturals -\* Test of INSTANCE inside LET - -VARIABLE y - -Next == LET x == y+1 - M == INSTANCE test59a - IN y' = (M!xplus1 - 1) % 5 - -Init == y = 0 - -Spec == Init /\ [][Next]_y /\ WF_y(Next) - -Invariant == y \in 0..4 -Liveness == []<>(y = 4) - +------------------------------- MODULE test59 ------------------------------- +EXTENDS Naturals +\* Test of INSTANCE inside LET + +VARIABLE y + +Next == LET x == y+1 + M == INSTANCE test59a + IN y' = (M!xplus1 - 1) % 5 + +Init == y = 0 + +Spec == Init /\ [][Next]_y /\ WF_y(Next) + +Invariant == y \in 0..4 +Liveness == []<>(y = 4) + ============================================================================= \ No newline at end of file diff --git a/tlatools/test-model/test59a.tla b/tlatools/test-model/suite/test59a.tla similarity index 96% rename from tlatools/test-model/test59a.tla rename to tlatools/test-model/suite/test59a.tla index 2963511f41478b31003db2ae4ff96c17207f1f9d..a5c42c5d2c8769bffa23a0d871a100c4d0799fda 100644 --- a/tlatools/test-model/test59a.tla +++ b/tlatools/test-model/suite/test59a.tla @@ -1,9 +1,9 @@ -------------------------------- MODULE test59a ------------------------------- -EXTENDS Naturals -\* Test of INSTANCE inside LET - -VARIABLE x - -xplus1 == x+1 - +------------------------------- MODULE test59a ------------------------------- +EXTENDS Naturals +\* Test of INSTANCE inside LET + +VARIABLE x + +xplus1 == x+1 + ============================================================================= \ No newline at end of file diff --git a/tlatools/test-model/suite/test6.cfg b/tlatools/test-model/suite/test6.cfg new file mode 100644 index 0000000000000000000000000000000000000000..b0ee260b28eef2026bbc2f6cb01c89fd3d01d947 --- /dev/null +++ b/tlatools/test-model/suite/test6.cfg @@ -0,0 +1,8 @@ + INIT + Init + + NEXT + Next + + INVARIANT Inv + diff --git a/tlatools/test-model/suite/test6.tla b/tlatools/test-model/suite/test6.tla new file mode 100644 index 0000000000000000000000000000000000000000..2665292fa01e629dede7c238430f0597b8ff27e6 --- /dev/null +++ b/tlatools/test-model/suite/test6.tla @@ -0,0 +1,132 @@ +--------------- MODULE test6 ------------- + +(* test of Propositional Logic. *) + +EXTENDS Integers, TLC + +VARIABLE x, y, z, w +Type == /\ x \in BOOLEAN + /\ y \in BOOLEAN + /\ z \in BOOLEAN + /\ w = 0 +Init == /\ x = TRUE + /\ y = TRUE + /\ z = FALSE + /\ w = 0 +Next == /\ \/ (w = 1) /\ Assert(FALSE, "This is a bug") + \/ (w=0) + /\ (w=0) \* \/ Assert(FALSE, "This is a bug, too") + \* Commented out because I now don't think it's a bug. + /\ UNCHANGED <<w, x, y, z>> + +Inv == + + /\ IF ~(x /\ y /\ ~z) + THEN Assert(FALSE, "Test 1 failed") + ELSE Print("Test 1 OK", TRUE) + + /\ IF ~(~z /\ y) + THEN Assert(FALSE, "Test 2 failed") + ELSE Print("Test 2 OK", TRUE) + + /\ IF ~(x \/ ~y \/ z) + THEN Assert(FALSE, "Test 3 failed") + ELSE Print("Test 3 OK", TRUE) + + /\ IF ~(z \/ ~y \/ x) + THEN Assert(FALSE, "Test 4 failed") + ELSE Print("Test 4 OK", TRUE) + + /\ IF ~(x /\ y) + THEN Assert(FALSE, "Test 5 failed") + ELSE Print("Test 5 OK", TRUE) + + /\ IF (x /\ z) + THEN Assert(FALSE, "Test 6 failed") + ELSE Print("Test 6 OK", TRUE) + + /\ IF (z /\ x) + THEN Assert(FALSE, "Test 7 failed") + ELSE Print("Test 7 OK", TRUE) + + /\ IF (z /\ ~x) + THEN Assert(FALSE, "Test 8 failed") + ELSE Print("Test 8 OK", TRUE) + + /\ IF (z /\ (x=1)) + THEN Assert(FALSE, "Test 9 failed") + ELSE Print("Test 9 OK", TRUE) + + /\ IF ~(x \/ (z=1)) + THEN Assert(FALSE, "Test 10 failed") + ELSE Print("Test 10 OK", TRUE) + + /\ IF ~(x => y) + THEN Assert(FALSE, "Test 11 failed") + ELSE Print("Test 11 OK", TRUE) + + /\ IF ~(~x => y) + THEN Assert(FALSE, "Test 12 failed") + ELSE Print("Test 12 OK", TRUE) + + /\ IF ~(~x => z) + THEN Assert(FALSE, "Test 13 failed") + ELSE Print("Test 13 OK", TRUE) + + /\ IF (x => z) + THEN Assert(FALSE, "Test 14 failed") + ELSE Print("Test 14 OK", TRUE) + + /\ IF ~x + THEN Assert(FALSE, "Test 15 failed") + ELSE Print("Test 15 OK", TRUE) + + /\ IF ~~z + THEN Assert(FALSE, "Test 16 failed") + ELSE Print("Test 16 OK", TRUE) + + /\ IF ~(x <=> y) + THEN Assert(FALSE, "Test 17 failed") + ELSE Print("Test 17 OK", TRUE) + + /\ IF ~(~x <=> z) + THEN Assert(FALSE, "Test 18 failed") + ELSE Print("Test 18 OK", TRUE) + + /\ IF (x <=> z) + THEN Assert(FALSE, "Test 19 failed") + ELSE Print("Test 19 OK", TRUE) + + /\ IF (z <=> x) + THEN Assert(FALSE, "Test 20 failed") + ELSE Print("Test 20 OK", TRUE) + + /\ IF ~(x \equiv y) + THEN Assert(FALSE, "Test 21 failed") + ELSE Print("Test 21 OK", TRUE) + + /\ IF ~(~x \equiv z) + THEN Assert(FALSE, "Test 22 failed") + ELSE Print("Test 22 OK", TRUE) + + /\ IF (x \equiv z) + THEN Assert(FALSE, "Test 23 failed") + ELSE Print("Test 23 OK", TRUE) + + /\ IF (z \equiv x) + THEN Assert(FALSE, "Test 24 failed") + ELSE Print("Test 24 OK", TRUE) + + /\ IF TRUE \notin BOOLEAN + THEN Assert(FALSE, "Test 25 failed") + ELSE Print("Test 25 OK", TRUE) + + /\ IF FALSE \notin BOOLEAN + THEN Assert(FALSE, "Test 26 failed") + ELSE Print("Test 26 OK", TRUE) + + /\ IF {TRUE, FALSE} # BOOLEAN + THEN Assert(FALSE, "Test 27 failed") + ELSE Print("Test 27 OK", TRUE) + +========================================= diff --git a/tlatools/test-model/suite/test60.cfg b/tlatools/test-model/suite/test60.cfg new file mode 100644 index 0000000000000000000000000000000000000000..8d1c8b69c3fce7bea45c73efd06983e3c419a92f --- /dev/null +++ b/tlatools/test-model/suite/test60.cfg @@ -0,0 +1 @@ + diff --git a/tlatools/test-model/suite/test60.tla b/tlatools/test-model/suite/test60.tla new file mode 100644 index 0000000000000000000000000000000000000000..0fef36a2cd81fa358d639907cb7dee87e547659f --- /dev/null +++ b/tlatools/test-model/suite/test60.tla @@ -0,0 +1,14 @@ +-------------------- MODULE test60 -------------------- +(***************************************************************************) +(* Test of handling strings as sequences. *) +(***************************************************************************) + +EXTENDS Sequences, TLC + +ASSUME "abc" \o "def" = "abcdef" +ASSUME Len("abcdef") = 6 +ASSUME "a\\b\"c\nd" = "a\\b\"" \o "c\nd" +ASSUME Len("a\\b\"c\nd") = 7 + +======================================== + diff --git a/tlatools/test-model/suite/test61.cfg b/tlatools/test-model/suite/test61.cfg new file mode 100644 index 0000000000000000000000000000000000000000..d2c069de399c1fe1495c4a2ca92e0320fd53347d --- /dev/null +++ b/tlatools/test-model/suite/test61.cfg @@ -0,0 +1,2 @@ +SPECIFICATION ISpec +INVARIANT Invariant diff --git a/tlatools/test-model/suite/test61.tla b/tlatools/test-model/suite/test61.tla new file mode 100644 index 0000000000000000000000000000000000000000..bb8de9ead6edc804da3c2325d4a7f3db36a63b9d --- /dev/null +++ b/tlatools/test-model/suite/test61.tla @@ -0,0 +1,21 @@ +-------------------- MODULE test61 -------------------- +(***************************************************************************) +(* Test of parametrized INSTANCE *) +(***************************************************************************) +\* Because of bug, it Handles JSpec but not ISpec + +EXTENDS Naturals + +VARIABLE y + +IM(x) == INSTANCE test61a + +ISpec == IM(y)!Spec + +Invariant == y \in 0..4 + +JM == INSTANCE test61a WITH x <- y +JSpec == JM!Spec + +======================================== + diff --git a/tlatools/test-model/suite/test61a.tla b/tlatools/test-model/suite/test61a.tla new file mode 100644 index 0000000000000000000000000000000000000000..c7f83a41bb788675f361125b200224a40b167f73 --- /dev/null +++ b/tlatools/test-model/suite/test61a.tla @@ -0,0 +1,11 @@ +-------------------- MODULE test61a -------------------- +(***************************************************************************) +(* Test of parametrized INSTANCE *) +(***************************************************************************) + +EXTENDS Naturals + +VARIABLE x + +Spec == (x = 0) /\ [][x'=(x+1)%5]_x +======================================== diff --git a/tlatools/test-model/suite/test62.cfg b/tlatools/test-model/suite/test62.cfg new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/tlatools/test-model/suite/test62.cfg @@ -0,0 +1 @@ + diff --git a/tlatools/test-model/suite/test62.tla b/tlatools/test-model/suite/test62.tla new file mode 100644 index 0000000000000000000000000000000000000000..ccff29bbedd611998e86e8be370b1917fd446e04 --- /dev/null +++ b/tlatools/test-model/suite/test62.tla @@ -0,0 +1,5 @@ +----------------------- MODULE test62 -------------------------- +EXTENDS TLC +ASSUME Print("Should print this", TRUE) +ASSUME PrintT("And this") +================================================================= diff --git a/tlatools/test-model/test63.cfg b/tlatools/test-model/suite/test63.cfg similarity index 100% rename from tlatools/test-model/test63.cfg rename to tlatools/test-model/suite/test63.cfg diff --git a/tlatools/test-model/test63.tla b/tlatools/test-model/suite/test63.tla similarity index 100% rename from tlatools/test-model/test63.tla rename to tlatools/test-model/suite/test63.tla diff --git a/tlatools/test-model/suite/test63a.cfg b/tlatools/test-model/suite/test63a.cfg new file mode 100644 index 0000000000000000000000000000000000000000..9608bf7492bdbbbdf9966ffa01eb5eac559a8584 --- /dev/null +++ b/tlatools/test-model/suite/test63a.cfg @@ -0,0 +1,2 @@ +SPECIFICATION HC + diff --git a/tlatools/test-model/test63a.tla b/tlatools/test-model/suite/test63a.tla similarity index 100% rename from tlatools/test-model/test63a.tla rename to tlatools/test-model/suite/test63a.tla diff --git a/tlatools/test-model/suite/test64.cfg b/tlatools/test-model/suite/test64.cfg new file mode 100644 index 0000000000000000000000000000000000000000..4d07be4bdd383dc53e46a5cbb701afa9dba3c30d --- /dev/null +++ b/tlatools/test-model/suite/test64.cfg @@ -0,0 +1 @@ +SPECIFICATION Spec diff --git a/tlatools/test-model/suite/test64.tla b/tlatools/test-model/suite/test64.tla new file mode 100644 index 0000000000000000000000000000000000000000..5e66d028eafb7af9c2594c394f3cc4a10f874203 --- /dev/null +++ b/tlatools/test-model/suite/test64.tla @@ -0,0 +1,8 @@ +-------------------------------- MODULE test64 -------------------------------- +VARIABLE y + +I == INSTANCE test64a WITH x <- y + \* Changed from Test64a on 12 June 2014 because that's now illegal + +Spec == I!Spec +======================================================================= diff --git a/tlatools/test-model/suite/test64a.cfg b/tlatools/test-model/suite/test64a.cfg new file mode 100644 index 0000000000000000000000000000000000000000..4d07be4bdd383dc53e46a5cbb701afa9dba3c30d --- /dev/null +++ b/tlatools/test-model/suite/test64a.cfg @@ -0,0 +1 @@ +SPECIFICATION Spec diff --git a/tlatools/test-model/suite/test64a.tla b/tlatools/test-model/suite/test64a.tla new file mode 100644 index 0000000000000000000000000000000000000000..c8a0994d90665a3bb9c5e1fb80d527b77fa61f6f --- /dev/null +++ b/tlatools/test-model/suite/test64a.tla @@ -0,0 +1,8 @@ +----------------------- MODULE test64a ----------------------- +VARIABLE x + +ASet == {r \in [{0} -> {0}] : x} + +Spec == x = TRUE /\ [][x' = (ASet = {})]_x +============================================================= + \ No newline at end of file diff --git a/tlatools/test-model/suite/test65.cfg b/tlatools/test-model/suite/test65.cfg new file mode 100644 index 0000000000000000000000000000000000000000..1446df75569d1fca6a285c43b13fb3d99699e1f8 --- /dev/null +++ b/tlatools/test-model/suite/test65.cfg @@ -0,0 +1 @@ +CONSTANT c = "a" diff --git a/tlatools/test-model/suite/test65.tla b/tlatools/test-model/suite/test65.tla new file mode 100644 index 0000000000000000000000000000000000000000..ea1615d4a5ebee5d42b25f3db547d961ebcfad95 --- /dev/null +++ b/tlatools/test-model/suite/test65.tla @@ -0,0 +1,8 @@ +-------------------------------- MODULE test65 -------------------------------- +EXTENDS test65a + +I == INSTANCE test65a WITH c <- 42 +J == INSTANCE test65a WITH c <- 44 +ASSUME /\ <<I!foo, J!foo>> = <<42, 44>> + /\ foo = c +======================================================================= diff --git a/tlatools/test-model/suite/test65a.cfg b/tlatools/test-model/suite/test65a.cfg new file mode 100644 index 0000000000000000000000000000000000000000..1446df75569d1fca6a285c43b13fb3d99699e1f8 --- /dev/null +++ b/tlatools/test-model/suite/test65a.cfg @@ -0,0 +1 @@ +CONSTANT c = "a" diff --git a/tlatools/test-model/suite/test65a.tla b/tlatools/test-model/suite/test65a.tla new file mode 100644 index 0000000000000000000000000000000000000000..c82bd7513ae6ec612280ac37a5063262cbec4369 --- /dev/null +++ b/tlatools/test-model/suite/test65a.tla @@ -0,0 +1,7 @@ +----------------------- MODULE test65a ----------------------- +CONSTANT c + +bar == c +foo == bar +============================================================= + \ No newline at end of file diff --git a/tlatools/test-model/suite/test7.cfg b/tlatools/test-model/suite/test7.cfg new file mode 100644 index 0000000000000000000000000000000000000000..b0ee260b28eef2026bbc2f6cb01c89fd3d01d947 --- /dev/null +++ b/tlatools/test-model/suite/test7.cfg @@ -0,0 +1,8 @@ + INIT + Init + + NEXT + Next + + INVARIANT Inv + diff --git a/tlatools/test-model/suite/test7.tla b/tlatools/test-model/suite/test7.tla new file mode 100644 index 0000000000000000000000000000000000000000..b4468cd6736c8d1024be6b6860821c3416d53b46 --- /dev/null +++ b/tlatools/test-model/suite/test7.tla @@ -0,0 +1,100 @@ +--------------- MODULE test7 ------------- + +(* test of Predicate Logic and CHOOSE *) + +EXTENDS Integers, TLC + +VARIABLE x +Type == x \in BOOLEAN +Init == x = TRUE +Next == UNCHANGED x + +Inv == + + /\ IF ~ \E i \in {1,2,3} : i=2 + THEN Assert(FALSE, "Test 1 Failed") + ELSE Print("Test 1 OK", TRUE) + + /\ IF ~ \exists i \in {1,2,3} : i=2 + THEN Assert(FALSE, "Test 1a Failed") + ELSE Print("Test 1a OK", TRUE) + + /\ IF \A i \in {3,3,3,3,2,1} : i=3 + THEN Assert(FALSE, "Test 2 Failed") + ELSE Print("Test 2 OK", TRUE) + + /\ IF \forall i \in {3,3,3,3,2,1} : i=3 + THEN Assert(FALSE, "Test 2a Failed") + ELSE Print("Test 2a OK", TRUE) + + /\ IF \E i \in {1,2,3} : FALSE + THEN Assert(FALSE, "Test 3 Failed") + ELSE Print("Test 3 OK", TRUE) + + /\ IF ~\A i \in {1,2,3} : TRUE + THEN Assert(FALSE, "Test 4 Failed") + ELSE Print("Test 4 OK", TRUE) + + /\ IF \E i \in {} : TRUE + THEN Assert(FALSE, "Test 5 Failed") + ELSE Print("Test 5 OK", TRUE) + + /\ IF \exists i \in {} : TRUE + THEN Assert(FALSE, "Test 5a Failed") + ELSE Print("Test 5a OK", TRUE) + + /\ IF ~\A i \in {} : FALSE + THEN Assert(FALSE, "Test 6 Failed") + ELSE Print("Test 6 OK", TRUE) + + /\ IF ~\forall i \in {} : FALSE + THEN Assert(FALSE, "Test 6a Failed") + ELSE Print("Test 6a OK", TRUE) + + /\ IF ~\E <<i, j>> \in {1,2} \X {3,4}, + k, l \in {4, 5}, m \in {6,7} : i + j + k + l + m = 23 + THEN Assert(FALSE, "Test 7 Failed") + ELSE Print("Test 7 OK", TRUE) + + /\ IF ~\A <<i, j>> \in {1,2} \X {3,4}, + k, l \in {4, 5}, m \in {6,7} : i + j + k + l + m > 17 + THEN Assert(FALSE, "Test 8 Failed") + ELSE Print("Test 8 OK", TRUE) + + /\ IF (CHOOSE i \in {1,2,3} : i > 2) # 3 + THEN Assert(FALSE, "Test 9 Failed") + ELSE Print("Test 9 OK", TRUE) + + /\ IF (CHOOSE i \in {1,2,3, 4} : i > 2) \leq 2 + THEN Assert(FALSE, "Test 10 Failed") + ELSE Print("Test 10 OK", TRUE) + + /\ IF <<1, 3>> # CHOOSE <<i, j>> \in {1,2} \X {3,4} : (i < 2) /\ (j = i+2) + THEN Assert(FALSE, "Test 11 Failed") + ELSE Print("Test 11 OK", TRUE) + + /\ IF ~(\A i \in {1,2}, <<j, k>> \in {3,4} \X {5, 6}, m, n \in {7,8} : + i+j+k+m+n \geq 23) + THEN Assert(FALSE, "Test 12 Failed") + ELSE Print("Test 12 OK", TRUE) + + /\ IF \E i \in {1,2}, <<j, k>> \in {3,4} \X {5, 6}, m, n \in {7,8} : + i+j+k+m+n < 23 + THEN Assert(FALSE, "Test 13 Failed") + ELSE Print("Test 13 OK", TRUE) + + + /\ IF \A i \in CHOOSE j \in {{Nat}, {Int}} : \E k \in j : -1 \in k : + -4 \in i + THEN Print("Test 14 OK", TRUE) + ELSE Assert(FALSE, "Test 14 Failed") +========================================= + +The following test is not expected to work because +TLC does not preserve the semantics of CHOOSE + + /\ IF (CHOOSE i \in {1,2,3, 4} : i > 2) + # (CHOOSE i \in {4, 1, 3 , 2} : i > 2) + THEN Assert(FALSE, "Test 11 Failed") + ELSE Print("Test 11 OK", TRUE) + diff --git a/tlatools/test-model/suite/test8.cfg b/tlatools/test-model/suite/test8.cfg new file mode 100644 index 0000000000000000000000000000000000000000..b0ee260b28eef2026bbc2f6cb01c89fd3d01d947 --- /dev/null +++ b/tlatools/test-model/suite/test8.cfg @@ -0,0 +1,8 @@ + INIT + Init + + NEXT + Next + + INVARIANT Inv + diff --git a/tlatools/test-model/suite/test8.tla b/tlatools/test-model/suite/test8.tla new file mode 100644 index 0000000000000000000000000000000000000000..fe958975938226c2b001df313438f5d7368f01d0 --- /dev/null +++ b/tlatools/test-model/suite/test8.tla @@ -0,0 +1,102 @@ +--------------- MODULE test8 ------------- + +(* Test of set operators \cup, \cap, \subseteq, \ *) + +EXTENDS Integers, TLC, Sequences + +VARIABLE x +Type == x \in BOOLEAN +Init == x = TRUE +Next == UNCHANGED x + +Inv == + + /\ IF {1, 2, 3} \cup {3, 4, 5} # 1..5 + THEN Assert(FALSE, "Test 1 Failed") + ELSE Print("Test 1 OK", TRUE) + + /\ IF {1, 2, 3} \cap {3, 4, 5} # {3} + THEN Assert(FALSE, "Test 2 Failed") + ELSE Print("Test 2 OK", TRUE) + + /\ IF -5 \notin Int \cup Nat + THEN Assert(FALSE, "Test 3 Failed") + ELSE Print("Test 3 OK", TRUE) + + /\ IF -5 \notin Nat \cup Int + THEN Assert(FALSE, "Test 4 Failed") + ELSE Print("Test 4 OK", TRUE) + + /\ IF {1, 2, 3, 4} \cap {i \in Int : i > 2} # {3, 4} + THEN Assert(FALSE, "Test 5 Failed") + ELSE Print("Test 5 OK", TRUE) + + /\ IF {1, 2, 3} \cap {} # {} + THEN Assert(FALSE, "Test 6 Failed") + ELSE Print("Test 6 OK", TRUE) + + /\ IF {} \cap {1, 2, 3} # {} + THEN Assert(FALSE, "Test 7 Failed") + ELSE Print("Test 7 OK", TRUE) + + /\ IF {1, 2, 3, 4} \ {i \in Int : i > 2} # {2, 1} + THEN Assert(FALSE, "Test 8 Failed") + ELSE Print("Test 8 OK", TRUE) + + /\ IF {1, 2, 3} \ {} # {3,1,2} + THEN Assert(FALSE, "Test 9 Failed") + ELSE Print("Test 9 OK", TRUE) + + /\ IF {} \ {1, 2, 3} # {} + THEN Assert(FALSE, "Test 10 Failed") + ELSE Print("Test 10 OK", TRUE) + + /\ IF -5 \in {-5} \ Int + THEN Assert(FALSE, "Test 11 Failed") + ELSE Print("Test 11 OK", TRUE) + + /\ IF <<1, 2>> \in Seq({1,2,3}) \ Seq({1,2}) + THEN Assert(FALSE, "Test 12 Failed") + ELSE Print("Test 12 OK", TRUE) + + /\ IF <<1, 2>> \notin Seq({1,2,3}) + THEN Assert(FALSE, "Test 12a Failed") + ELSE Print("Test 12a OK", TRUE) + + /\ IF <<1, 2>> \notin Seq({1,2,3}) \cap Seq({0,1,2}) + THEN Assert(FALSE, "Test 13 Failed") + ELSE Print("Test 13 OK", TRUE) + + /\ IF <<1, 2>> \notin Seq({0,1}) \cup Seq({1,2,3}) + THEN Assert(FALSE, "Test 14 Failed") + ELSE Print("Test 14 OK", TRUE) + + /\ IF <<1, 2>> \in [1..2 ->{1,2,3}] \ [1..2 ->{1,2}] + THEN Assert(FALSE, "Test 16 Failed") + ELSE Print("Test 16 OK", TRUE) + + /\ IF <<1, 2>> \notin [1..2 ->{1,2,3}] + THEN Assert(FALSE, "Test 17 Failed") + ELSE Print("Test 17 OK", TRUE) + + /\ IF <<1, 2>> \notin [1..2 ->{1,2,3}] \cap [1..2 ->{0,1,2}] + THEN Assert(FALSE, "Test 18 Failed") + ELSE Print("Test 18 OK", TRUE) + + /\ IF <<1, 2>> \notin [1..2 ->{0,1}] \cup [1..2 ->{1,2,3}] + THEN Assert(FALSE, "Test 19 Failed") + ELSE Print("Test 19 OK", TRUE) + + /\ IF ~({1,2,3} \subseteq Int) + THEN Assert(FALSE, "Test 20 Failed") + ELSE Print("Test 20 OK", TRUE) + + /\ IF {1,2,3} \subseteq {i \in Int : i < 3} + THEN Assert(FALSE, "Test 21 Failed") + ELSE Print("Test 21 OK", TRUE) + + /\ IF <<1,2>> \in ({1,2} \X Nat) \cup {<<3,4>>, <<5,6>>} + THEN Print("Test 22 OK", TRUE) + ELSE Assert(FALSE, "Test 22 Failed") + +========================================= diff --git a/tlatools/test-model/suite/test9.cfg b/tlatools/test-model/suite/test9.cfg new file mode 100644 index 0000000000000000000000000000000000000000..b0ee260b28eef2026bbc2f6cb01c89fd3d01d947 --- /dev/null +++ b/tlatools/test-model/suite/test9.cfg @@ -0,0 +1,8 @@ + INIT + Init + + NEXT + Next + + INVARIANT Inv + diff --git a/tlatools/test-model/suite/test9.tla b/tlatools/test-model/suite/test9.tla new file mode 100644 index 0000000000000000000000000000000000000000..baac310c2b3fc7bd5b29fe68216a884c2a1f2ca3 --- /dev/null +++ b/tlatools/test-model/suite/test9.tla @@ -0,0 +1,73 @@ +--------------- MODULE test9 ------------- + +(* Test of set constructors {x \in S : P(x)} and {e(x) : x \in S} *) + +EXTENDS Integers, TLC, FiniteSets + +VARIABLE x +Type == x \in BOOLEAN +Init == x = TRUE +Next == UNCHANGED x + +Inv == + + /\ IF {i \in {1,3,3,3,4} : i > 1} # {4,3} + THEN Assert(FALSE, "Test 1 Failed") + ELSE Print("Test 1 OK", TRUE) + + /\ IF {<<i, j>> \in {1,2} \X {2,3} : j > i} # {<<1,2>>, <<1,3>>, <<2,3>>} + THEN Assert(FALSE, "Test 2 Failed") + ELSE Print("Test 2 OK", TRUE) + + /\ IF 2 \notin {i \in Int : i > 1} + THEN Assert(FALSE, "Test 3 Failed") + ELSE Print("Test 3 OK", TRUE) + + /\ IF <<3,4>> \notin {<<i,j>> \in Int \X Int : j > i} + THEN Assert(FALSE, "Test 4 Failed") + ELSE Print("Test 4 OK", TRUE) + + /\ IF {i+j+k+l+m : i, j \in {1, 2, 3}, + <<k, l>> \in {4,5} \X {6,7}, m \in {8}} # 20..26 + THEN Assert(FALSE, "Test 5 Failed") + ELSE Print("Test 5 OK", TRUE) + + /\ IF 24 \notin {i+j+k+l+m : i, j \in {1, 2, 3}, + <<k, l>> \in {4,5} \X {6,7}, m \in {8}} + THEN Assert(FALSE, "Test 6 Failed") + ELSE Print("Test 6 OK", TRUE) + + /\ IF {i+j : i \in {1,2,3}, j \in {}} # {} + THEN Assert(FALSE, "Test 7 Failed") + ELSE Print("Test 7 OK", TRUE) + + /\ IF {i \in {} : i > 2} # {} + THEN Assert(FALSE, "Test 8 Failed") + ELSE Print("Test 8 OK", TRUE) + + /\ IF {<<i, j>> \in {1,3} \X {} : i > 2} # {} + THEN Assert(FALSE, "Test 9 Failed") + ELSE Print("Test 9 OK", TRUE) + + /\ IF <<1,2>> \notin {f \in UNION {[S -> {1,2,3}] : + S \in SUBSET {1,2,3}} : f[2] = 2} + THEN Assert(FALSE, "Test 10 Failed") + ELSE Print("Test 10 OK", TRUE) + + /\ IF Cardinality({f \in [{1,2,3} -> {1,2,3}] : f[2] > 1}) # 18 + THEN Assert(FALSE, "Test 11 Failed") + ELSE Print("Test 11 OK", TRUE) + + /\ IF Cardinality([{1,2,3} -> {1,2,3,4}]) # 64 + THEN Assert(FALSE, "Test 12 Failed") + ELSE Print("Test 12 OK", TRUE) + + /\ IF Cardinality([ {5, 5, 5, 3, 5, 2} -> {1, 3, 3, 3} ]) # 8 + THEN Assert(FALSE, "Test 13 Failed") + ELSE Print("Test 13 OK", TRUE) + + /\ IF [ {5, 5, 5, 3, 5, 2} -> {1, 3, 3, 3} ] # [{2,3,5} -> {1,3}] + THEN Assert(FALSE, "Test 14 Failed") + ELSE Print("Test 14 OK", TRUE) + +========================================= diff --git a/tlatools/test-model/suite/test99.cfg b/tlatools/test-model/suite/test99.cfg new file mode 100644 index 0000000000000000000000000000000000000000..5e8cedc4cac917a8a41ec9ad1e980ca0e367180c --- /dev/null +++ b/tlatools/test-model/suite/test99.cfg @@ -0,0 +1,11 @@ + INIT + Init + + NEXT + Next + + INVARIANT Inv + + + + \ No newline at end of file diff --git a/tlatools/test-model/suite/test99.tla b/tlatools/test-model/suite/test99.tla new file mode 100644 index 0000000000000000000000000000000000000000..b020e7eaae3d0e0d844dd2704ef4d4eac81c68cd --- /dev/null +++ b/tlatools/test-model/suite/test99.tla @@ -0,0 +1,89 @@ +------------------------------ MODULE test99 ----------------------------- +(* Test of standard Naturals module. *) + +EXTENDS Integers, TLC + +VARIABLES x + +Init == x = 0 + +Next == UNCHANGED x + +Inv == +(* /\ IF 2^10 + 2^10 = 2^11 + THEN Print("Test 1 OK", TRUE) + ELSE Assert(FALSE, "Test 1 Failed") + + /\ IF 0^20 = 0 + THEN Print("Test 2 OK", TRUE) + ELSE Assert(FALSE, "Test 2 Failed") *) + + /\ IF 2 - 3 \notin Nat + THEN Print("Test 3 OK", TRUE) + ELSE Assert(FALSE, "Test 3 Failed") + + /\ IF 123 * 345 = 42435 + THEN Print("Test 4 OK", TRUE) + ELSE Assert(FALSE, "Test 4 Failed") + + /\ IF 123 < 124 + THEN Print("Test 5 OK", TRUE) + ELSE Assert(FALSE, "Test 5 Failed") + + /\ IF 12345 > 12344 + THEN Print("Test 6 OK", TRUE) + ELSE Assert(FALSE, "Test 6 Failed") + + /\ IF 123 \leq 123 + THEN Print("Test 7 OK", TRUE) + ELSE Assert(FALSE, "Test 7 Failed") + + /\ IF 12345 \geq 12345 + THEN Print("Test 8 OK", TRUE) + ELSE Assert(FALSE, "Test 8 Failed") + + /\ IF 123 \leq 124 + THEN Print("Test 9 OK", TRUE) + ELSE Assert(FALSE, "Test 9 Failed") + + /\ IF 12344 \geq 12333 + THEN Print("Test 10 OK", TRUE) + ELSE Assert(FALSE, "Test 10 Failed") + + /\ IF 145939 = 487 * (145939 \div 487) + (145939 % 487) + THEN Print("Test 11 OK", TRUE) + ELSE Assert(FALSE, "Test 11 Failed") + + /\ IF 139982 \div 1 = 139982 + THEN Print("Test 12 OK", TRUE) + ELSE Assert(FALSE, "Test 12 Failed") + + /\ IF 123099 % 1 = 0 + THEN Print("Test 13 OK", TRUE) + ELSE Assert(FALSE, "Test 13 Failed") + + /\ IF 0 % 345 = 0 + THEN Print("Test 14 OK", TRUE) + ELSE Assert(FALSE, "Test 14 Failed") + + /\ IF 24 % 9 = 6 + THEN Print("Test 15 OK", TRUE) + ELSE Assert(FALSE, "Test 15 Failed") + + /\ IF 4566799 = 423 * (4566799 \div 423) + (4566799 % 423) + THEN Print("Test 16 OK", TRUE) + ELSE Assert(FALSE, "Test 16 Failed") + + /\ IF 2222222 = 18 * (2222222 \div 18) + (2222222 % 18) + THEN Print("Test 17 OK", TRUE) + ELSE Assert(FALSE, "Test 17 Failed") + + /\ IF 3 .. 2 = {} + THEN Print("Test 18 OK", TRUE) + ELSE Assert(FALSE, "Test 18 Failed") + + /\ IF 2..4 = {2, 3, 4} + THEN Print("Test 19 OK", TRUE) + ELSE Assert(FALSE, "Test 19 Failed") + +============================================================================= diff --git a/tlatools/test-model/suite/test999.cfg b/tlatools/test-model/suite/test999.cfg new file mode 100644 index 0000000000000000000000000000000000000000..eecec9a4f071796a4236f4bf1bf5a1a1e115560e --- /dev/null +++ b/tlatools/test-model/suite/test999.cfg @@ -0,0 +1,15 @@ + INIT + Init + + NEXT + Next + + INVARIANT Inv + + CONSTANT + P <- PRep + PLen <- Len + Seq <- MCSeq + ++ <- PlusPlus + Plus <- + + \o <- + \ No newline at end of file diff --git a/tlatools/test-model/suite/test999.tla b/tlatools/test-model/suite/test999.tla new file mode 100644 index 0000000000000000000000000000000000000000..d28a8963809b6279c5d49487d512fcd9a7c15f4d --- /dev/null +++ b/tlatools/test-model/suite/test999.tla @@ -0,0 +1,47 @@ +--------------- MODULE test999 ------------- + +(* Test replacement of and by infix operators and overridden operators. *) + +EXTENDS TLC, Naturals, Sequences + +VARIABLE x + +CONSTANT P(_,_), _++_ , Plus(_, _), PLen(_) + +PlusPlus(a, b) == <<a, b>> + +PRep(a, b) == {a, b} + +MCSeq(a) == {a} + +MCCat(a, b) == a + b + +Init == /\ x = 1 + /\ IF P(2, x+3) = {2, 4} + THEN Print("Test 1 OK", TRUE) + ELSE Assert(FALSE, "Test 1 Failed") + /\ IF (2++(x+3)) = <<2, 4>> + THEN Print("Test 2 OK", TRUE) + ELSE Assert(FALSE, "Test 2 Failed") + /\ IF PLen(<<1, 2, 3>>) = 3 + THEN Print("Test 3 OK", TRUE) + ELSE Assert(FALSE, "Test 3 Failed") + /\ IF Plus(2, x+3) = 6 + THEN Print("Test 4 OK", TRUE) + ELSE Assert(FALSE, "Test 4 Failed") + /\ IF Seq(22) = {22} + THEN Print("Test 5 OK", TRUE) + ELSE Assert(FALSE, "Test 5 Failed") + /\ IF 1 \o 2 = 3 (* Huh???? *) + THEN Print("Test 6 OK", TRUE) + ELSE Assert(FALSE, "Test 6 Failed") + /\ LET a \prec b == a < b + IN IF 1 \prec 2 + THEN Print("Test 7 OK", TRUE) + ELSE Assert(FALSE, "Test 7 Failed") + +Next == UNCHANGED x + +Inv == TRUE + +========================================= diff --git a/tlatools/test-model/suite/testa.cfg b/tlatools/test-model/suite/testa.cfg new file mode 100644 index 0000000000000000000000000000000000000000..5e8cedc4cac917a8a41ec9ad1e980ca0e367180c --- /dev/null +++ b/tlatools/test-model/suite/testa.cfg @@ -0,0 +1,11 @@ + INIT + Init + + NEXT + Next + + INVARIANT Inv + + + + \ No newline at end of file diff --git a/tlatools/test-model/suite/testa.tla b/tlatools/test-model/suite/testa.tla new file mode 100644 index 0000000000000000000000000000000000000000..a535e319b8e45f214181388511f29114c626b2f0 --- /dev/null +++ b/tlatools/test-model/suite/testa.tla @@ -0,0 +1,5 @@ +------- MODULE testa ----- + +EXTENDS test, Integers + +========================== diff --git a/tlatools/test-model/suite/testfoo.cfg b/tlatools/test-model/suite/testfoo.cfg new file mode 100644 index 0000000000000000000000000000000000000000..2a209c125237fe03cfb6a69cde7bd574d490c9ca --- /dev/null +++ b/tlatools/test-model/suite/testfoo.cfg @@ -0,0 +1,11 @@ + INIT + Init + + NEXT + Next + + + + + + \ No newline at end of file diff --git a/tlatools/test-model/suite/testfoo.tla b/tlatools/test-model/suite/testfoo.tla new file mode 100644 index 0000000000000000000000000000000000000000..19ee2927f791114501c75d8bcfe2a00b47930110 --- /dev/null +++ b/tlatools/test-model/suite/testfoo.tla @@ -0,0 +1,9 @@ +------------- MODULE test -------------- +EXTENDS Naturals + +VARIABLES x + +Init == x \in SUBSET (1..32) + +Next == x' = 1 +============================================= diff --git a/tlatools/test-model/suite/testt37.tla b/tlatools/test-model/suite/testt37.tla new file mode 100644 index 0000000000000000000000000000000000000000..f1f6fbbaf012b6ad9b450c2b4f30a940fbcedf27 --- /dev/null +++ b/tlatools/test-model/suite/testt37.tla @@ -0,0 +1,26 @@ +---------------------------- MODULE test37 ----------------------------- +EXTENDS FiniteSets, TLC + +VARIABLES x + +Init == x = 0 +Next == x'=x +Inv == + /\ IF IsFiniteSet({"a", "b"}) + THEN Print("Test 1 OK", TRUE) + ELSE Assert(FALSE, "Test 1 Failed") + + /\ IF IsFiniteSet({}) + THEN Print("Test 2 OK", TRUE) + ELSE Assert(FALSE, "Test 2 Failed") + + /\ IF Cardinality({}) = 0 + THEN Print("Test 3 OK", TRUE) + ELSE Assert(FALSE, "Test 3 Failed") + + /\ IF Cardinality({"a", "b"}) = 2 + THEN Print("Test 4 OK", TRUE) + ELSE Assert(FALSE, "Test 4 Failed") + + +============================================================================= diff --git a/tlatools/test-model/symmetry/April20a.tla b/tlatools/test-model/symmetry/April20a.tla new file mode 100644 index 0000000000000000000000000000000000000000..07c10a69a59e08ee76dd3699026556ffca70a101 --- /dev/null +++ b/tlatools/test-model/symmetry/April20a.tla @@ -0,0 +1,24 @@ +----------------------------- MODULE April20a ----------------------------- +EXTENDS Integers + +CONSTANT S + +VARIABLE x, y +vars == <<x, y>> + +Init == (x = 0) /\ (y=0) + +Next == \/ /\ y=0 + /\ y'=1 + /\ x' \in S + \/ /\ y=1 + /\ y'=2 + /\ x' \in S \ {x} + \/ /\ y = 2 + /\ y'=0 + /\ x' = 0 + +Spec == Init /\ [][Next]_vars + +Live == ~ \A i \in S : \E j \in S \ {i} : []<><<x'=i /\ x=j>>_x +============================================================================= diff --git a/tlatools/test-model/symmetry/April20aMC.cfg b/tlatools/test-model/symmetry/April20aMC.cfg new file mode 100644 index 0000000000000000000000000000000000000000..4975b08bd040c40a68167c9fd5f5eec29ee0e88f --- /dev/null +++ b/tlatools/test-model/symmetry/April20aMC.cfg @@ -0,0 +1,15 @@ +\* MV CONSTANT declarations +CONSTANTS +m1 = m1 +m2 = m2 +\* MV CONSTANT definitions +CONSTANT +S <- const_146115895642519000 +\* SYMMETRY definition +SYMMETRY symm_146115895643520000 +\* SPECIFICATION definition +SPECIFICATION +spec_146115895644521000 +\* PROPERTY definition +PROPERTY +prop_146115895645522000 diff --git a/tlatools/test-model/symmetry/April20aMC.tla b/tlatools/test-model/symmetry/April20aMC.tla new file mode 100644 index 0000000000000000000000000000000000000000..278a73f775e200b7fb275a6576dd83d52a6bcf35 --- /dev/null +++ b/tlatools/test-model/symmetry/April20aMC.tla @@ -0,0 +1,27 @@ +---- MODULE April20aMC ---- +EXTENDS April20a, TLC + +\* MV CONSTANT declarations@modelParameterConstants +CONSTANTS +m1, m2 +---- + +\* MV CONSTANT definitions S +const_146115895642519000 == +{m1, m2} +---- + +\* SYMMETRY definition +symm_146115895643520000 == +Permutations(const_146115895642519000) +---- + +\* SPECIFICATION definition @modelBehaviorSpec:0 +spec_146115895644521000 == +Spec +---- +\* PROPERTY definition @modelCorrectnessProperties:0 +prop_146115895645522000 == +Live +---- +============================================================================= diff --git a/tlatools/test-model/symmetry/April20b.tla b/tlatools/test-model/symmetry/April20b.tla new file mode 100644 index 0000000000000000000000000000000000000000..c0d1e888f6932bdbb22fafc7637cac6fc84241a3 --- /dev/null +++ b/tlatools/test-model/symmetry/April20b.tla @@ -0,0 +1,24 @@ +----------------------------- MODULE April20b ----------------------------- +EXTENDS Integers, Sequences + +CONSTANT S + +VARIABLE x, y +vars == <<x, y>> + +Init == (x = << >>) /\ (y=0) + +Next == \/ /\ y=0 + /\ y'=1 + /\ \E s \in S : x' = <<s>> + \/ /\ y=1 + /\ y'=2 + /\ \E s \in S \ {x[1]} : x' = Append(x, s) + \/ /\ y = 2 + /\ y'=0 + /\ x' = << >> + +Spec == Init /\ [][Next]_vars + +Live == ~ \A i \in S : \E j \in S \ {i} : []<>(x = <<i, j>>) +============================================================================= diff --git a/tlatools/test-model/symmetry/April20bMC.cfg b/tlatools/test-model/symmetry/April20bMC.cfg new file mode 100644 index 0000000000000000000000000000000000000000..850ed89ce61152d41aff311467b5eaf1f40668c2 --- /dev/null +++ b/tlatools/test-model/symmetry/April20bMC.cfg @@ -0,0 +1,15 @@ +\* MV CONSTANT declarations +CONSTANTS +m1 = m1 +m2 = m2 +\* MV CONSTANT definitions +CONSTANT +S <- const_146115906599338000 +\* SYMMETRY definition +SYMMETRY symm_146115906600339000 +\* SPECIFICATION definition +SPECIFICATION +spec_146115906601340000 +\* PROPERTY definition +PROPERTY +prop_146115906602341000 diff --git a/tlatools/test-model/symmetry/April20bMC.tla b/tlatools/test-model/symmetry/April20bMC.tla new file mode 100644 index 0000000000000000000000000000000000000000..b2b4059482829ba9fa897613d9fd3424c68d0e36 --- /dev/null +++ b/tlatools/test-model/symmetry/April20bMC.tla @@ -0,0 +1,27 @@ +---- MODULE April20bMC ---- +EXTENDS April20b, TLC + +\* MV CONSTANT declarations@modelParameterConstants +CONSTANTS +m1, m2 +---- + +\* MV CONSTANT definitions S +const_146115906599338000 == +{m1, m2} +---- + +\* SYMMETRY definition +symm_146115906600339000 == +Permutations(const_146115906599338000) +---- + +\* SPECIFICATION definition @modelBehaviorSpec:0 +spec_146115906601340000 == +Spec +---- +\* PROPERTY definition @modelCorrectnessProperties:0 +prop_146115906602341000 == +Live +---- +============================================================================= diff --git a/tlatools/test-model/symmetry/April21.tla b/tlatools/test-model/symmetry/April21.tla new file mode 100644 index 0000000000000000000000000000000000000000..e161d36f1abcac6bdf2040b46b3d6f63302a056c --- /dev/null +++ b/tlatools/test-model/symmetry/April21.tla @@ -0,0 +1,15 @@ +----------------------------- MODULE April21 ----------------------------- +EXTENDS Integers + +CONSTANT S + +VARIABLE x + +Init == x \in S + +Next == x' \in S + +Spec == Init /\ [][Next]_x + +Live == \E i \in S : <>[][x # i]_x +============================================================================= diff --git a/tlatools/test-model/symmetry/April21MC.cfg b/tlatools/test-model/symmetry/April21MC.cfg new file mode 100644 index 0000000000000000000000000000000000000000..4975b08bd040c40a68167c9fd5f5eec29ee0e88f --- /dev/null +++ b/tlatools/test-model/symmetry/April21MC.cfg @@ -0,0 +1,15 @@ +\* MV CONSTANT declarations +CONSTANTS +m1 = m1 +m2 = m2 +\* MV CONSTANT definitions +CONSTANT +S <- const_146115895642519000 +\* SYMMETRY definition +SYMMETRY symm_146115895643520000 +\* SPECIFICATION definition +SPECIFICATION +spec_146115895644521000 +\* PROPERTY definition +PROPERTY +prop_146115895645522000 diff --git a/tlatools/test-model/symmetry/April21MC.tla b/tlatools/test-model/symmetry/April21MC.tla new file mode 100644 index 0000000000000000000000000000000000000000..a3005a9817fa1b7f7a6aa0d940bb3c70d29d6b17 --- /dev/null +++ b/tlatools/test-model/symmetry/April21MC.tla @@ -0,0 +1,27 @@ +---- MODULE April21MC ---- +EXTENDS April21, TLC + +\* MV CONSTANT declarations@modelParameterConstants +CONSTANTS +m1, m2 +---- + +\* MV CONSTANT definitions S +const_146115895642519000 == +{m1, m2} +---- + +\* SYMMETRY definition +symm_146115895643520000 == +Permutations(const_146115895642519000) +---- + +\* SPECIFICATION definition @modelBehaviorSpec:0 +spec_146115895644521000 == +Spec +---- +\* PROPERTY definition @modelCorrectnessProperties:0 +prop_146115895645522000 == +Live +---- +============================================================================= diff --git a/tlatools/test-model/symmetry/April22.tla b/tlatools/test-model/symmetry/April22.tla new file mode 100644 index 0000000000000000000000000000000000000000..33bcdfd724e834db56863c4a030277e1e63b8956 --- /dev/null +++ b/tlatools/test-model/symmetry/April22.tla @@ -0,0 +1,15 @@ +----------------------------- MODULE April22 ----------------------------- +EXTENDS Integers + +CONSTANT S + +VARIABLE x + +Init == x \in S + +Next == x' \in S + +Spec == Init /\ [][Next]_x /\ WF_x(Next) + +Live == \A i \in S : []<>(x=i) +============================================================================= diff --git a/tlatools/test-model/symmetry/April22MC.cfg b/tlatools/test-model/symmetry/April22MC.cfg new file mode 100644 index 0000000000000000000000000000000000000000..4975b08bd040c40a68167c9fd5f5eec29ee0e88f --- /dev/null +++ b/tlatools/test-model/symmetry/April22MC.cfg @@ -0,0 +1,15 @@ +\* MV CONSTANT declarations +CONSTANTS +m1 = m1 +m2 = m2 +\* MV CONSTANT definitions +CONSTANT +S <- const_146115895642519000 +\* SYMMETRY definition +SYMMETRY symm_146115895643520000 +\* SPECIFICATION definition +SPECIFICATION +spec_146115895644521000 +\* PROPERTY definition +PROPERTY +prop_146115895645522000 diff --git a/tlatools/test-model/symmetry/April22MC.tla b/tlatools/test-model/symmetry/April22MC.tla new file mode 100644 index 0000000000000000000000000000000000000000..f6536924153fa30d97cabc30fccb41e74b278056 --- /dev/null +++ b/tlatools/test-model/symmetry/April22MC.tla @@ -0,0 +1,27 @@ +---- MODULE April22MC ---- +EXTENDS April22, TLC + +\* MV CONSTANT declarations@modelParameterConstants +CONSTANTS +m1, m2 +---- + +\* MV CONSTANT definitions S +const_146115895642519000 == +{m1, m2} +---- + +\* SYMMETRY definition +symm_146115895643520000 == +Permutations(const_146115895642519000) +---- + +\* SPECIFICATION definition @modelBehaviorSpec:0 +spec_146115895644521000 == +Spec +---- +\* PROPERTY definition @modelCorrectnessProperties:0 +prop_146115895645522000 == +Live +---- +============================================================================= diff --git a/tlatools/test-model/symmetry/April25.tla b/tlatools/test-model/symmetry/April25.tla new file mode 100644 index 0000000000000000000000000000000000000000..9dc40bc60ffe1e58b1caf9dd71c34ddd8447bb7e --- /dev/null +++ b/tlatools/test-model/symmetry/April25.tla @@ -0,0 +1,77 @@ +----------------------------- MODULE April25 ----------------------------- +EXTENDS Integers + +CONSTANT S + +VARIABLE x + +Init == x \in S + +Next == x' \in S + +Spec == Init /\ [][Next]_x + +(* + There exists (at least) an element e in S for which variable x in all + suffixes of all behaviors is never assigned e. Prefixes can be anything. + There can be other elements in S which show a different behavior + (even though this is not be the case when the spec/model declares S + to be symmetric). + + A counterexample - to such a liveness claim - is an infinite suffix path + with the property that x gets assigned e. This assignment can occur 1 to + N times within the infinite suffix path. Any SCC is an infinite suffix + path. + + Assume S={a,b}: + + This behavior satisfies Live with e bound to b (in Omega notation) + ... > (x=a)^w + + Conversely, this behavior satisfies Live with e bound to a + ... > (x=b)^w + + Thus, the only infinite path that violates Live is where x is assigned + both elements of the set S. This behavior violates Live for both + values a and b. + ... > (x=a > x=b)^w + + Under no symmetry and S consisting out of two elements, the corresponding + behavior graph - which shows the above violation - has two nodes. The two + nodes are strongly connected. TLC searches the behavior graph for an SCC + whose path dissatisfies all action predicates (arc labels) - indicating + whether x' # x for each of the values of S evaluates to true. + See April25NoSymmetry.dot for an illustration. The illustration omits + self loops as they are irrelevant in this context. + + TLC generates two initial states (x=a and x=b) and for each, explores + its successor states. The successors for x=a are {x=a, x=b} and for x=b + {x=b, x=a}. Thus, the action predicate x'=e is evaluated for element + a *and* b. + + Gen_init = {x=a, x=b} + Gen_succ(x=a) = {x=a, x=b} + Gen_succ(x=b) = {x=a, x=b} + Arcs_sat = {x=a -> x=b, x=b -> x=a} + + + With symmetry declared on S, the behavior graph is reduced to a single + node. This node is logically equivalent to all states of the symmetry + set (which is indicated by the label in April25WithSymmetry.dot). + Consequently, TLC only generates successor states for a single element + of the symmetry set. Thus, it evaluates the action predicate for the + chosen element of S only and just adds its corresponding arcs to the + behavior graph. + + Let the initial state for which successor states are generated be x=a. + Its successors are x=a and x=b. TLC therefore only adds the arc + x=a -> x=b to the behavior graph which satisfies the action predicate + for (-[x#a]_x). + + Gen_init = {x=a, x=b} + Gen_succ(x=a) = {x=a, x=b} + Gen_succ(x=b) never explored due to symmetry of x=a and x=b. + Arcs_sat = {x=a -> x=b} +*) +Live == \E e \in S : <>[][x # e]_x +============================================================================= diff --git a/tlatools/test-model/symmetry/April25MC.cfg b/tlatools/test-model/symmetry/April25MC.cfg new file mode 100644 index 0000000000000000000000000000000000000000..4975b08bd040c40a68167c9fd5f5eec29ee0e88f --- /dev/null +++ b/tlatools/test-model/symmetry/April25MC.cfg @@ -0,0 +1,15 @@ +\* MV CONSTANT declarations +CONSTANTS +m1 = m1 +m2 = m2 +\* MV CONSTANT definitions +CONSTANT +S <- const_146115895642519000 +\* SYMMETRY definition +SYMMETRY symm_146115895643520000 +\* SPECIFICATION definition +SPECIFICATION +spec_146115895644521000 +\* PROPERTY definition +PROPERTY +prop_146115895645522000 diff --git a/tlatools/test-model/symmetry/April25MC.tla b/tlatools/test-model/symmetry/April25MC.tla new file mode 100644 index 0000000000000000000000000000000000000000..afb14df4b32ecba252df13fe5e791bcf0767da4a --- /dev/null +++ b/tlatools/test-model/symmetry/April25MC.tla @@ -0,0 +1,27 @@ +---- MODULE April25MC ---- +EXTENDS April25, TLC + +\* MV CONSTANT declarations@modelParameterConstants +CONSTANTS +m1, m2 +---- + +\* MV CONSTANT definitions S +const_146115895642519000 == +{m1, m2} +---- + +\* SYMMETRY definition +symm_146115895643520000 == +Permutations(const_146115895642519000) +---- + +\* SPECIFICATION definition @modelBehaviorSpec:0 +spec_146115895644521000 == +Spec +---- +\* PROPERTY definition @modelCorrectnessProperties:0 +prop_146115895645522000 == +Live +---- +============================================================================= diff --git a/tlatools/test-model/symmetry/April25NoSymmetry.dot b/tlatools/test-model/symmetry/April25NoSymmetry.dot new file mode 100644 index 0000000000000000000000000000000000000000..8fe72c150a6b9677c1585a10ad63b57bee89b77a --- /dev/null +++ b/tlatools/test-model/symmetry/April25NoSymmetry.dot @@ -0,0 +1,6 @@ +digraph DiskGraph { +3247490433077925545 -> 5068397444161466873 [label="[ft]"]; +5068397444161466873 -> 3247490433077925545 [label="[tf]"]; +3247490433077925545 [label="x = m1"]; +5068397444161466873 [label="x = m2"]; +} \ No newline at end of file diff --git a/tlatools/test-model/symmetry/April25WithSymmetry.dot b/tlatools/test-model/symmetry/April25WithSymmetry.dot new file mode 100644 index 0000000000000000000000000000000000000000..4942746ec112ea1f5a41a7cec744dbb702e3b990 --- /dev/null +++ b/tlatools/test-model/symmetry/April25WithSymmetry.dot @@ -0,0 +1,4 @@ +digraph DiskGraph { +3247490433077925545 [label="x = m1 /\\ x = m2"]; +3247490433077925545 -> 3247490433077925545 [label="[ft]"]; +} \ No newline at end of file diff --git a/tlatools/test-model/symmetry/April29.tla b/tlatools/test-model/symmetry/April29.tla new file mode 100644 index 0000000000000000000000000000000000000000..f7e0cea8a3bc9f11c02cb73256c89822fc1adaae --- /dev/null +++ b/tlatools/test-model/symmetry/April29.tla @@ -0,0 +1,23 @@ +--------------------------- MODULE April29 --------------------------- +CONSTANT S + +VARIABLES x,y +vars == <<x,y>> + +Init == x \in S /\ y = 0 + +NextA == /\ y = 0 + /\ y' = 1 + /\ x' = x + +NextB == /\ y = 1 + /\ y' = 1 + /\ x' = x +Spec == Init /\ [][NextA \/ NextB]_vars /\ WF_vars(NextA \/ NextB) + +NextC == /\ y = 1 + /\ y' = 1 + /\ \E i \in (S \ {x}): x' = i +SpecD == Init /\ [][NextA \/ NextB \/ NextC]_vars /\ WF_vars(NextA \/ NextB \/ NextC) + +============================================================================= diff --git a/tlatools/test-model/symmetry/April29MC.cfg b/tlatools/test-model/symmetry/April29MC.cfg new file mode 100644 index 0000000000000000000000000000000000000000..fa4c00c2199b3370c2b04f8053176aeb78be16bc --- /dev/null +++ b/tlatools/test-model/symmetry/April29MC.cfg @@ -0,0 +1,15 @@ +\* MV CONSTANT declarations +CONSTANTS +a = a +b = b +\* MV CONSTANT definitions +CONSTANT +S <- const_146183936175119000 +\* SYMMETRY definition +SYMMETRY symm_146183936176120000 +\* SPECIFICATION definition +SPECIFICATION +spec_146183936177121000 +\* PROPERTY definition +PROPERTY +prop_146183936178122000 diff --git a/tlatools/test-model/symmetry/April29MC.tla b/tlatools/test-model/symmetry/April29MC.tla new file mode 100644 index 0000000000000000000000000000000000000000..7000424b8ffc7d234eaaffc50e0f3fba44b3c856 --- /dev/null +++ b/tlatools/test-model/symmetry/April29MC.tla @@ -0,0 +1,27 @@ +---- MODULE April29MC ---- +EXTENDS April29, TLC + +\* MV CONSTANT declarations@modelParameterConstants +CONSTANTS +a, b +---- + +\* MV CONSTANT definitions S +const_146183936175119000 == +{a, b} +---- + +\* SYMMETRY definition +symm_146183936176120000 == +Permutations(const_146183936175119000) +---- + +\* SPECIFICATION definition @modelBehaviorSpec:0 +spec_146183936177121000 == +Spec +---- +\* PROPERTY definition @modelCorrectnessProperties:0 +prop_146183936178122000 == +[]<>(x=a) /\ []<>(x=b) +---- +============================================================================= diff --git a/tlatools/test-model/symmetry/April29dMC.cfg b/tlatools/test-model/symmetry/April29dMC.cfg new file mode 100644 index 0000000000000000000000000000000000000000..c4252e0458359333615cd48b7973a8b1a05d8e21 --- /dev/null +++ b/tlatools/test-model/symmetry/April29dMC.cfg @@ -0,0 +1,15 @@ +\* MV CONSTANT declarations +CONSTANTS +a = a +b = b +\* MV CONSTANT definitions +CONSTANT +S <- const_146183938495726000 +\* SYMMETRY definition +SYMMETRY symm_146183938496727000 +\* SPECIFICATION definition +SPECIFICATION +spec_146183938497728000 +\* PROPERTY definition +PROPERTY +prop_146183938498729000 diff --git a/tlatools/test-model/symmetry/April29dMC.tla b/tlatools/test-model/symmetry/April29dMC.tla new file mode 100644 index 0000000000000000000000000000000000000000..cf342a8b9f24c4a985004e1c7cafff69f628e099 --- /dev/null +++ b/tlatools/test-model/symmetry/April29dMC.tla @@ -0,0 +1,27 @@ +---- MODULE April29dMC ---- +EXTENDS April29, TLC + +\* MV CONSTANT declarations@modelParameterConstants +CONSTANTS +a, b +---- + +\* MV CONSTANT definitions S +const_146183938495726000 == +{a, b} +---- + +\* SYMMETRY definition +symm_146183938496727000 == +Permutations(const_146183938495726000) +---- + +\* SPECIFICATION definition @modelBehaviorSpec:0 +spec_146183938497728000 == +SpecD +---- +\* PROPERTY definition @modelCorrectnessProperties:0 +prop_146183938498729000 == +[]<>(x=a) /\ []<>(x=b) +---- +============================================================================= diff --git a/tlatools/test-model/symmetry/ChooseTableauSymmetry.tla b/tlatools/test-model/symmetry/ChooseTableauSymmetry.tla new file mode 100644 index 0000000000000000000000000000000000000000..14ae2fe6835294f1df023f4a019bef612cc9351f --- /dev/null +++ b/tlatools/test-model/symmetry/ChooseTableauSymmetry.tla @@ -0,0 +1,29 @@ +-------------------------- MODULE ChooseTableauSymmetry -------------------------- +CONSTANT Val +VARIABLE arr + +Init == arr = [v \in Val |-> "ready"] + +Ready(v) == /\ arr[v] = "ready" + /\ arr' = [arr EXCEPT ![v]= "busy"] + +Busy(v) == /\ arr[v] = "busy" + /\ arr' = [arr EXCEPT ![v]= "done"] + +Done(v) == /\ arr[v] = "done" + /\ arr' = [arr EXCEPT ![v]= "ready"] + +Next == \E v \in Val : Ready(v) \/ Busy(v) \/ Done(v) + +Spec == Init /\[][Next]_<<arr>> /\ WF_<<arr>>(Next) + +Some == CHOOSE e \in Val: TRUE +Other(v) == CHOOSE e \in Val \ {v}: TRUE + +Liveness == LET v == Some + IN (arr[v] = "busy") ~> (arr[v] = "ready") + +LivenessO == LET v == Other(Some) + IN (arr[v] = "busy") ~> (arr[v] = "ready") + +============================================================================= diff --git a/tlatools/test-model/symmetry/ChooseTableauSymmetryMC.cfg b/tlatools/test-model/symmetry/ChooseTableauSymmetryMC.cfg new file mode 100644 index 0000000000000000000000000000000000000000..823aee7b2c78dafe6e5cc779c7a2de726802bbba --- /dev/null +++ b/tlatools/test-model/symmetry/ChooseTableauSymmetryMC.cfg @@ -0,0 +1,15 @@ +\* MV CONSTANT declarations +CONSTANTS +a = a +b = b +\* MV CONSTANT definitions +CONSTANT +Val <- const_144481269486926000 +\* SYMMETRY definition +SYMMETRY symm_144481269487927000 +\* SPECIFICATION definition +SPECIFICATION +spec_144481269488928000 +\* PROPERTY definition +PROPERTY +prop_144481269489929000 diff --git a/tlatools/test-model/symmetry/ChooseTableauSymmetryMC.tla b/tlatools/test-model/symmetry/ChooseTableauSymmetryMC.tla new file mode 100644 index 0000000000000000000000000000000000000000..36b43db6bfdb214fcdf67d5a716386dccea6eff4 --- /dev/null +++ b/tlatools/test-model/symmetry/ChooseTableauSymmetryMC.tla @@ -0,0 +1,27 @@ +---- MODULE ChooseTableauSymmetryMC ---- +EXTENDS ChooseTableauSymmetry, TLC + +\* MV CONSTANT declarations@modelParameterConstants +CONSTANTS +a, b +---- + +\* MV CONSTANT definitions Val +const_144481269486926000 == +{a, b} +---- + +\* SYMMETRY definition +symm_144481269487927000 == +Permutations(const_144481269486926000) +---- + +\* SPECIFICATION definition @modelBehaviorSpec:0 +spec_144481269488928000 == +Spec +---- +\* PROPERTY definition @modelCorrectnessProperties:0 +prop_144481269489929000 == +Liveness +---- +============================================================================= diff --git a/tlatools/test-model/symmetry/ChooseTableauSymmetryMCa.cfg b/tlatools/test-model/symmetry/ChooseTableauSymmetryMCa.cfg new file mode 100644 index 0000000000000000000000000000000000000000..823aee7b2c78dafe6e5cc779c7a2de726802bbba --- /dev/null +++ b/tlatools/test-model/symmetry/ChooseTableauSymmetryMCa.cfg @@ -0,0 +1,15 @@ +\* MV CONSTANT declarations +CONSTANTS +a = a +b = b +\* MV CONSTANT definitions +CONSTANT +Val <- const_144481269486926000 +\* SYMMETRY definition +SYMMETRY symm_144481269487927000 +\* SPECIFICATION definition +SPECIFICATION +spec_144481269488928000 +\* PROPERTY definition +PROPERTY +prop_144481269489929000 diff --git a/tlatools/test-model/symmetry/ChooseTableauSymmetryMCa.tla b/tlatools/test-model/symmetry/ChooseTableauSymmetryMCa.tla new file mode 100644 index 0000000000000000000000000000000000000000..5f807d01b9677db2b1351565608f6bbeb74dbc86 --- /dev/null +++ b/tlatools/test-model/symmetry/ChooseTableauSymmetryMCa.tla @@ -0,0 +1,27 @@ +---- MODULE ChooseTableauSymmetryMCa ---- +EXTENDS ChooseTableauSymmetry, TLC + +\* MV CONSTANT declarations@modelParameterConstants +CONSTANTS +a, b +---- + +\* MV CONSTANT definitions Val +const_144481269486926000 == +{a, b} +---- + +\* SYMMETRY definition +symm_144481269487927000 == +Permutations(const_144481269486926000) +---- + +\* SPECIFICATION definition @modelBehaviorSpec:0 +spec_144481269488928000 == +Spec +---- +\* PROPERTY definition @modelCorrectnessProperties:0 +prop_144481269489929000 == +LivenessO +---- +============================================================================= diff --git a/tlatools/test-model/symmetry/ErrorTraceConstruction.tla b/tlatools/test-model/symmetry/ErrorTraceConstruction.tla new file mode 100644 index 0000000000000000000000000000000000000000..a07f60b96cb6b4cdf21efb21b1e6b0406fd30124 --- /dev/null +++ b/tlatools/test-model/symmetry/ErrorTraceConstruction.tla @@ -0,0 +1,49 @@ +------------------------- MODULE ErrorTraceConstruction ------------------------- +EXTENDS Integers, TLC + +S == {0,1} +Other(n) == CHOOSE x \in S \ {n} : TRUE + +VARIABLES x, y + +Init == x = 0 /\ y = 0 + +N0 == /\ y = 0 + /\ y' = y+1 + /\ x' = x +N1 == /\ y = 1 + /\ y' = y+1 + /\ x' = x +N2 == /\ y = 2 + /\ y' = y+1 + /\ x' = x +N3 == /\ y = 3 + /\ y' = y+1 + /\ x' = x +N4 == /\ y = 4 + /\ y' = y+1 + /\ x' = Other(x) \* flip x to violate Prop1 +N5 == /\ y = 5 + /\ y' = y+1 + /\ x' = Other(x) \* flip x back to reduce the number of overall states +N6 == /\ y = 6 + /\ y' = y+1 + /\ x' = x +N7 == /\ y = 7 + /\ y' = 3 \* loop to N3 + /\ x' = x + +Next == \/ N0 + \/ N1 + \/ N2 + \/ N3 + \/ N4 + \/ N5 + \/ N6 + \/ N7 + +Spec == Init /\ [][Next]_<<x, y>> /\ WF_<<x,y>>(Next) + +Prop == <>[][x'=x]_<<x, y>> + +============================================================================= diff --git a/tlatools/test-model/symmetry/ErrorTraceConstructionMC.cfg b/tlatools/test-model/symmetry/ErrorTraceConstructionMC.cfg new file mode 100644 index 0000000000000000000000000000000000000000..8a9daaec38db0a8f607f72ea76dad6e58afc573a --- /dev/null +++ b/tlatools/test-model/symmetry/ErrorTraceConstructionMC.cfg @@ -0,0 +1,6 @@ +\* SPECIFICATION definition +SPECIFICATION +spec_14411937652404000 +\* PROPERTY definition +PROPERTY +prop_14411937652505000 diff --git a/tlatools/test-model/symmetry/ErrorTraceConstructionMC.tla b/tlatools/test-model/symmetry/ErrorTraceConstructionMC.tla new file mode 100644 index 0000000000000000000000000000000000000000..17c7bd11b547e285884077de5a4bf8ea6beeb271 --- /dev/null +++ b/tlatools/test-model/symmetry/ErrorTraceConstructionMC.tla @@ -0,0 +1,12 @@ +---- MODULE ErrorTraceConstructionMC ---- +EXTENDS ErrorTraceConstruction, TLC + +\* SPECIFICATION definition @modelBehaviorSpec:0 +spec_14411937652404000 == +Spec +---- +\* PROPERTY definition @modelCorrectnessProperties:0 +prop_14411937652505000 == +Prop +---- +============================================================================= diff --git a/tlatools/test-model/symmetry/ErrorTraceConstructionPhases.png b/tlatools/test-model/symmetry/ErrorTraceConstructionPhases.png new file mode 100644 index 0000000000000000000000000000000000000000..9a87f95c1080372fea4548fa09851cdb997b9303 Binary files /dev/null and b/tlatools/test-model/symmetry/ErrorTraceConstructionPhases.png differ diff --git a/tlatools/test-model/symmetry/LongMC.cfg b/tlatools/test-model/symmetry/LongMC.cfg new file mode 100644 index 0000000000000000000000000000000000000000..edacc0bc0474c6c8a2fe290ac875c9641fed7e4c --- /dev/null +++ b/tlatools/test-model/symmetry/LongMC.cfg @@ -0,0 +1,15 @@ +\* MV CONSTANT declarations +CONSTANTS +a = a +b = b +\* MV CONSTANT definitions +CONSTANT +S <- const_143263724205431000 +\* SYMMETRY definition +SYMMETRY symm_143263724206532000 +\* SPECIFICATION definition +SPECIFICATION +spec_143263724207533000 +\* PROPERTY definition +PROPERTY +prop_143263724208534000 diff --git a/tlatools/test-model/symmetry/LongMC.tla b/tlatools/test-model/symmetry/LongMC.tla new file mode 100644 index 0000000000000000000000000000000000000000..d606b420e83bc9e9b8049cfa86388162b90d1620 --- /dev/null +++ b/tlatools/test-model/symmetry/LongMC.tla @@ -0,0 +1,27 @@ +---- MODULE LongMC ---- +EXTENDS SymmetryLivenessLong, TLC + +\* MV CONSTANT declarations@modelParameterConstants +CONSTANTS +a, b +---- + +\* MV CONSTANT definitions S +const_143263724205431000 == +{a, b} +---- + +\* SYMMETRY definition +symm_143263724206532000 == +Permutations(const_143263724205431000) +---- + +\* SPECIFICATION definition @modelBehaviorSpec:0 +spec_143263724207533000 == +Spec2 +---- +\* PROPERTY definition @modelCorrectnessProperties:0 +prop_143263724208534000 == +Prop1 +---- +============================================================================= diff --git a/tlatools/test-model/symmetry/LongMCa.cfg b/tlatools/test-model/symmetry/LongMCa.cfg new file mode 100644 index 0000000000000000000000000000000000000000..edacc0bc0474c6c8a2fe290ac875c9641fed7e4c --- /dev/null +++ b/tlatools/test-model/symmetry/LongMCa.cfg @@ -0,0 +1,15 @@ +\* MV CONSTANT declarations +CONSTANTS +a = a +b = b +\* MV CONSTANT definitions +CONSTANT +S <- const_143263724205431000 +\* SYMMETRY definition +SYMMETRY symm_143263724206532000 +\* SPECIFICATION definition +SPECIFICATION +spec_143263724207533000 +\* PROPERTY definition +PROPERTY +prop_143263724208534000 diff --git a/tlatools/test-model/symmetry/LongMCa.tla b/tlatools/test-model/symmetry/LongMCa.tla new file mode 100644 index 0000000000000000000000000000000000000000..dadfdfbb68daff61b295a9146aafc17171114beb --- /dev/null +++ b/tlatools/test-model/symmetry/LongMCa.tla @@ -0,0 +1,27 @@ +---- MODULE LongMCa ---- +EXTENDS SymmetryLivenessLong, TLC + +\* MV CONSTANT declarations@modelParameterConstants +CONSTANTS +a, b +---- + +\* MV CONSTANT definitions S +const_143263724205431000 == +{a, b} +---- + +\* SYMMETRY definition +symm_143263724206532000 == +Permutations(const_143263724205431000) +---- + +\* SPECIFICATION definition @modelBehaviorSpec:0 +spec_143263724207533000 == +Spec1 +---- +\* PROPERTY definition @modelCorrectnessProperties:0 +prop_143263724208534000 == +Prop1 +---- +============================================================================= diff --git a/tlatools/test-model/symmetry/MC.cfg b/tlatools/test-model/symmetry/MC.cfg new file mode 100644 index 0000000000000000000000000000000000000000..ed287f880ec454f7d7e3019a83302a49af39c8fd --- /dev/null +++ b/tlatools/test-model/symmetry/MC.cfg @@ -0,0 +1,16 @@ +\* MV CONSTANT declarations +CONSTANTS +a = a +b = b +\* MV CONSTANT definitions +CONSTANT +S <- const_143263724205431000 +\* SYMMETRY definition +SYMMETRY symm_143263724206532000 +\* SPECIFICATION definition +SPECIFICATION +spec_143263724207533000 +\* PROPERTY definition +PROPERTY +prop_143263724208534000 +\* Generated on Tue May 26 12:47:22 CEST 2015 \ No newline at end of file diff --git a/tlatools/test-model/symmetry/MC.tla b/tlatools/test-model/symmetry/MC.tla new file mode 100644 index 0000000000000000000000000000000000000000..b98e4a2db2d1f62ecc6558a4ee461562d3b4f7fc --- /dev/null +++ b/tlatools/test-model/symmetry/MC.tla @@ -0,0 +1,29 @@ +---- MODULE MC ---- +EXTENDS SymmetryLiveness3, TLC + +\* MV CONSTANT declarations@modelParameterConstants +CONSTANTS +a, b +---- + +\* MV CONSTANT definitions S +const_143263724205431000 == +{a, b} +---- + +\* SYMMETRY definition +symm_143263724206532000 == +Permutations(const_143263724205431000) +---- + +\* SPECIFICATION definition @modelBehaviorSpec:0 +spec_143263724207533000 == +Spec2 +---- +\* PROPERTY definition @modelCorrectnessProperties:0 +prop_143263724208534000 == +Prop1 +---- +============================================================================= +\* Modification History +\* Created Tue May 26 12:47:22 CEST 2015 by markus diff --git a/tlatools/test-model/symmetry/MC_Graph.png b/tlatools/test-model/symmetry/MC_Graph.png new file mode 100644 index 0000000000000000000000000000000000000000..ca330f79ea241615b55cca76c3b6ab3c3acd8ead Binary files /dev/null and b/tlatools/test-model/symmetry/MC_Graph.png differ diff --git a/tlatools/test-model/symmetry/MCa.cfg b/tlatools/test-model/symmetry/MCa.cfg new file mode 100644 index 0000000000000000000000000000000000000000..ed287f880ec454f7d7e3019a83302a49af39c8fd --- /dev/null +++ b/tlatools/test-model/symmetry/MCa.cfg @@ -0,0 +1,16 @@ +\* MV CONSTANT declarations +CONSTANTS +a = a +b = b +\* MV CONSTANT definitions +CONSTANT +S <- const_143263724205431000 +\* SYMMETRY definition +SYMMETRY symm_143263724206532000 +\* SPECIFICATION definition +SPECIFICATION +spec_143263724207533000 +\* PROPERTY definition +PROPERTY +prop_143263724208534000 +\* Generated on Tue May 26 12:47:22 CEST 2015 \ No newline at end of file diff --git a/tlatools/test-model/symmetry/MCa.tla b/tlatools/test-model/symmetry/MCa.tla new file mode 100644 index 0000000000000000000000000000000000000000..0520dcaadc07e35e01de4649b9a747e5efa81e83 --- /dev/null +++ b/tlatools/test-model/symmetry/MCa.tla @@ -0,0 +1,29 @@ +---- MODULE MCa ---- +EXTENDS SymmetryLiveness3, TLC + +\* MV CONSTANT declarations@modelParameterConstants +CONSTANTS +a, b +---- + +\* MV CONSTANT definitions S +const_143263724205431000 == +{a, b} +---- + +\* SYMMETRY definition +symm_143263724206532000 == +Permutations(const_143263724205431000) +---- + +\* SPECIFICATION definition @modelBehaviorSpec:0 +spec_143263724207533000 == +Spec1 +---- +\* PROPERTY definition @modelCorrectnessProperties:0 +prop_143263724208534000 == +Prop1 +---- +============================================================================= +\* Modification History +\* Created Tue May 26 12:47:22 CEST 2015 by markus diff --git a/tlatools/test-model/symmetry/MCa_Graph.png b/tlatools/test-model/symmetry/MCa_Graph.png new file mode 100644 index 0000000000000000000000000000000000000000..c4944d2074755519584364eef815feca0f8d6c7a Binary files /dev/null and b/tlatools/test-model/symmetry/MCa_Graph.png differ diff --git a/tlatools/test-model/symmetry/May09.tla b/tlatools/test-model/symmetry/May09.tla new file mode 100644 index 0000000000000000000000000000000000000000..600ec7b10a4247105b3309763f922925b0b3ac03 --- /dev/null +++ b/tlatools/test-model/symmetry/May09.tla @@ -0,0 +1,29 @@ +------------------------- MODULE May09 ------------------------- +EXTENDS Integers +CONSTANT S + +VARIABLES x,y +vars == <<x,y>> + +Init == x \in S /\ y = 0 + +NextA == /\ y = 0 + /\ y' = 1 + /\ x' = x + +NextB == /\ y >= 1 + /\ y < 5 + /\ y' = y + 1 + /\ x' = x + +NextC == /\ y = 5 + /\ y' = 1 + /\ x' = x +Spec == Init /\ [][NextA \/ NextB \/ NextC]_vars /\ WF_vars(NextA \/ NextB \/ NextC) + +NextD == /\ y = 5 + /\ y' = 1 + /\ \E i \in (S \ {x}) : x' = i +SpecD == Init /\ [][NextA \/ NextB \/ NextD]_vars /\ WF_vars(NextA \/ NextB \/ NextD) + +============================================================================= diff --git a/tlatools/test-model/symmetry/May09MC.cfg b/tlatools/test-model/symmetry/May09MC.cfg new file mode 100644 index 0000000000000000000000000000000000000000..fa4c00c2199b3370c2b04f8053176aeb78be16bc --- /dev/null +++ b/tlatools/test-model/symmetry/May09MC.cfg @@ -0,0 +1,15 @@ +\* MV CONSTANT declarations +CONSTANTS +a = a +b = b +\* MV CONSTANT definitions +CONSTANT +S <- const_146183936175119000 +\* SYMMETRY definition +SYMMETRY symm_146183936176120000 +\* SPECIFICATION definition +SPECIFICATION +spec_146183936177121000 +\* PROPERTY definition +PROPERTY +prop_146183936178122000 diff --git a/tlatools/test-model/symmetry/May09MC.tla b/tlatools/test-model/symmetry/May09MC.tla new file mode 100644 index 0000000000000000000000000000000000000000..31fc76203af855b7aaf0a50da3a18ae0c28bcdd5 --- /dev/null +++ b/tlatools/test-model/symmetry/May09MC.tla @@ -0,0 +1,27 @@ +---- MODULE May09MC ---- +EXTENDS May09, TLC + +\* MV CONSTANT declarations@modelParameterConstants +CONSTANTS +a, b +---- + +\* MV CONSTANT definitions S +const_146183936175119000 == +{a, b} +---- + +\* SYMMETRY definition +symm_146183936176120000 == +Permutations(const_146183936175119000) +---- + +\* SPECIFICATION definition @modelBehaviorSpec:0 +spec_146183936177121000 == +Spec +---- +\* PROPERTY definition @modelCorrectnessProperties:0 +prop_146183936178122000 == +[]<>(x=a) /\ []<>(x=b) +---- +============================================================================= diff --git a/tlatools/test-model/symmetry/May09dMC.cfg b/tlatools/test-model/symmetry/May09dMC.cfg new file mode 100644 index 0000000000000000000000000000000000000000..fa4c00c2199b3370c2b04f8053176aeb78be16bc --- /dev/null +++ b/tlatools/test-model/symmetry/May09dMC.cfg @@ -0,0 +1,15 @@ +\* MV CONSTANT declarations +CONSTANTS +a = a +b = b +\* MV CONSTANT definitions +CONSTANT +S <- const_146183936175119000 +\* SYMMETRY definition +SYMMETRY symm_146183936176120000 +\* SPECIFICATION definition +SPECIFICATION +spec_146183936177121000 +\* PROPERTY definition +PROPERTY +prop_146183936178122000 diff --git a/tlatools/test-model/symmetry/May09dMC.tla b/tlatools/test-model/symmetry/May09dMC.tla new file mode 100644 index 0000000000000000000000000000000000000000..f829b215add2d9f174db5b93a76a33828a463672 --- /dev/null +++ b/tlatools/test-model/symmetry/May09dMC.tla @@ -0,0 +1,27 @@ +---- MODULE May09dMC ---- +EXTENDS May09, TLC + +\* MV CONSTANT declarations@modelParameterConstants +CONSTANTS +a, b +---- + +\* MV CONSTANT definitions S +const_146183936175119000 == +{a, b} +---- + +\* SYMMETRY definition +symm_146183936176120000 == +Permutations(const_146183936175119000) +---- + +\* SPECIFICATION definition @modelBehaviorSpec:0 +spec_146183936177121000 == +SpecD +---- +\* PROPERTY definition @modelCorrectnessProperties:0 +prop_146183936178122000 == +[]<>(x=a) /\ []<>(x=b) +---- +============================================================================= diff --git a/tlatools/test-model/symmetry/NQ/MC.cfg b/tlatools/test-model/symmetry/NQ/MC.cfg new file mode 100644 index 0000000000000000000000000000000000000000..6c49e8e3722e6db56fb627cc0d6e35119a30e55a --- /dev/null +++ b/tlatools/test-model/symmetry/NQ/MC.cfg @@ -0,0 +1,49 @@ +\* MV CONSTANT declarations +CONSTANTS +e1 = e1 +e2 = e2 +\* MV CONSTANT declarations +CONSTANTS +i1 = i1 +i2 = i2 +\* MV CONSTANT declarations +CONSTANTS +d1 = d1 +\* MV CONSTANT declarations +CONSTANTS +v1 = v1 +v2 = v2 +\* MV CONSTANT definitions +CONSTANT +EnQers <- const_14467714432321108000 +\* MV CONSTANT definitions +CONSTANT +Ids <- const_14467714432421109000 +\* MV CONSTANT definitions +CONSTANT +DeQers <- const_14467714432521110000 +\* MV CONSTANT definitions +CONSTANT +Data <- const_14467714432621111000 +\* SYMMETRY definition +SYMMETRY symm_14467714432721112000 +\* CONSTANT definitions +CONSTANT +InitData <- const_14467714432821113000 +\* CONSTANT definition +CONSTANT +Done = Done +Busy = Busy +NotAnId = NotAnId +NotData = NotData +NotAnElement = NotAnElement +\* SPECIFICATION definition +SPECIFICATION +spec_14467714433421119000 +\* INVARIANT definition +INVARIANT +inv_14467714433521120000 +\* PROPERTY definition +PROPERTY +prop_14467714433621121000 +\* Generated on Thu Nov 05 16:57:23 PST 2015 \ No newline at end of file diff --git a/tlatools/test-model/symmetry/NQ/MC.tla b/tlatools/test-model/symmetry/NQ/MC.tla new file mode 100644 index 0000000000000000000000000000000000000000..78bbe6b0fee0b7560146e68c33eaae4b0f0a675f --- /dev/null +++ b/tlatools/test-model/symmetry/NQ/MC.tla @@ -0,0 +1,68 @@ +---- MODULE MC ---- +EXTENDS NQSpec, TLC + +\* MV CONSTANT declarations@modelParameterConstants +CONSTANTS +e1, e2 +---- + +\* MV CONSTANT declarations@modelParameterConstants +CONSTANTS +i1, i2 +---- + +\* MV CONSTANT declarations@modelParameterConstants +CONSTANTS +d1 +---- + +\* MV CONSTANT declarations@modelParameterConstants +CONSTANTS +v1, v2 +---- + +\* MV CONSTANT definitions EnQers +const_14467714432321108000 == +{e1, e2} +---- + +\* MV CONSTANT definitions Ids +const_14467714432421109000 == +{i1, i2} +---- + +\* MV CONSTANT definitions DeQers +const_14467714432521110000 == +{d1} +---- + +\* MV CONSTANT definitions Data +const_14467714432621111000 == +{v1, v2} +---- + +\* SYMMETRY definition +symm_14467714432721112000 == +Permutations(const_14467714432321108000) \union Permutations(const_14467714432521110000) +---- + +\* CONSTANT definitions @modelParameterConstants:4InitData +const_14467714432821113000 == +v1 +---- + +\* SPECIFICATION definition @modelBehaviorSpec:0 +spec_14467714433421119000 == +Spec +---- +\* INVARIANT definition @modelCorrectnessInvariants:0 +inv_14467714433521120000 == +TypeOK +---- +\* PROPERTY definition @modelCorrectnessProperties:0 +prop_14467714433621121000 == +(\E e \in elts : e.data = v2) ~> (\E d \in DeQers : deq[d] = v2) +---- +============================================================================= +\* Modification History +\* Created Thu Nov 05 16:57:23 PST 2015 by lamport diff --git a/tlatools/test-model/symmetry/NQ/MCa.cfg b/tlatools/test-model/symmetry/NQ/MCa.cfg new file mode 100644 index 0000000000000000000000000000000000000000..b27f9c68e77d42d8c8a70f4e8b5d7170c5ddf283 --- /dev/null +++ b/tlatools/test-model/symmetry/NQ/MCa.cfg @@ -0,0 +1,49 @@ +\* MV CONSTANT declarations +CONSTANTS +e1 = e1 +e2 = e2 +\* MV CONSTANT declarations +CONSTANTS +i1 = i1 +i2 = i2 +\* MV CONSTANT declarations +CONSTANTS +d1 = d1 +\* MV CONSTANT declarations +CONSTANTS +v1 = v1 +v2 = v2 +\* MV CONSTANT definitions +CONSTANT +EnQers <- const_14467710209401026000 +\* MV CONSTANT definitions +CONSTANT +Ids <- const_14467710209501027000 +\* MV CONSTANT definitions +CONSTANT +DeQers <- const_14467710209601028000 +\* MV CONSTANT definitions +CONSTANT +Data <- const_14467710209701029000 +\* SYMMETRY definition +SYMMETRY symm_14467710209801030000 +\* CONSTANT definitions +CONSTANT +InitData <- const_14467710209901031000 +\* CONSTANT definition +CONSTANT +Done = Done +Busy = Busy +NotAnId = NotAnId +NotData = NotData +NotAnElement = NotAnElement +\* SPECIFICATION definition +SPECIFICATION +spec_14467710210501037000 +\* INVARIANT definition +INVARIANT +inv_14467710210601038000 +\* PROPERTY definition +PROPERTY +prop_14467710210701039000 +\* Generated on Thu Nov 05 16:50:21 PST 2015 \ No newline at end of file diff --git a/tlatools/test-model/symmetry/NQ/MCa.tla b/tlatools/test-model/symmetry/NQ/MCa.tla new file mode 100644 index 0000000000000000000000000000000000000000..76e9f8de9d9e3def7841e3fcee240631ea6972fd --- /dev/null +++ b/tlatools/test-model/symmetry/NQ/MCa.tla @@ -0,0 +1,68 @@ +---- MODULE MCa ---- +EXTENDS NQSpec, TLC + +\* MV CONSTANT declarations@modelParameterConstants +CONSTANTS +e1, e2 +---- + +\* MV CONSTANT declarations@modelParameterConstants +CONSTANTS +i1, i2 +---- + +\* MV CONSTANT declarations@modelParameterConstants +CONSTANTS +d1 +---- + +\* MV CONSTANT declarations@modelParameterConstants +CONSTANTS +v1, v2 +---- + +\* MV CONSTANT definitions EnQers +const_14467710209401026000 == +{e1, e2} +---- + +\* MV CONSTANT definitions Ids +const_14467710209501027000 == +{i1, i2} +---- + +\* MV CONSTANT definitions DeQers +const_14467710209601028000 == +{d1} +---- + +\* MV CONSTANT definitions Data +const_14467710209701029000 == +{v1, v2} +---- + +\* SYMMETRY definition +symm_14467710209801030000 == +Permutations(const_14467710209401026000) \union Permutations(const_14467710209601028000) +---- + +\* CONSTANT definitions @modelParameterConstants:4InitData +const_14467710209901031000 == +v1 +---- + +\* SPECIFICATION definition @modelBehaviorSpec:0 +spec_14467710210501037000 == +Spec +---- +\* INVARIANT definition @modelCorrectnessInvariants:0 +inv_14467710210601038000 == +TypeOK +---- +\* PROPERTY definition @modelCorrectnessProperties:0 +prop_14467710210701039000 == +(\E e \in EnQers : enq[e] = v2) ~> (\E d \in DeQers : deq[d] = v2) +---- +============================================================================= +\* Modification History +\* Created Thu Nov 05 16:50:21 PST 2015 by lamport diff --git a/tlatools/test-model/symmetry/NQ/NQSpec.tla b/tlatools/test-model/symmetry/NQ/NQSpec.tla new file mode 100644 index 0000000000000000000000000000000000000000..09774ed31418f2d0b6d970ac2b57a317332c2557 --- /dev/null +++ b/tlatools/test-model/symmetry/NQ/NQSpec.tla @@ -0,0 +1,84 @@ +------------------------------- MODULE NQSpec ------------------------------- +EXTENDS Integers, Sequences + +CONSTANTS EnQers, DeQers, Data, InitData, Ids + +ASSUME InitData \in Data + +Done == CHOOSE D : D \notin Data +Busy == CHOOSE D : D \notin Data +NotAnId == CHOOSE i : i \notin Ids +NotData == CHOOSE D : D \notin Data +Elements == [data : Data, id : Ids] +NotAnElement == CHOOSE E : E \notin Elements + +VARIABLES enq, deq, elts, after, adding +vars == <<enq, deq, elts, after, adding>> + + + +TypeOK == /\ enq \in [EnQers -> Data \cup {Done}] + /\ deq \in [DeQers -> Data \cup {Busy}] + /\ elts \in SUBSET Elements + /\ after \in [elts -> SUBSET elts] + /\ adding \in [EnQers -> Elements \cup {NotAnElement}] + +Init == /\ enq = [e \in EnQers |-> Done] + /\ deq = [d \in DeQers |-> InitData] + /\ elts = {} + /\ after = << >> + /\ adding = [e \in EnQers |-> NotAnElement] +----------------------------------------------------------------------------- + +Assign(var, idx, val) == var' = [var EXCEPT ![idx] = val] + +UnusedIds == Ids \ {el.id : el \in elts} + +AddElt(el) == + /\ elts' = elts \cup {el} + /\ after' = [x \in elts' |-> + IF x = el THEN elts \ {adding[e] : e \in EnQers} + ELSE after[x] ] + +RemoveElt(el) == + /\ elts' = elts \ {el} + /\ after' = [x \in elts' |-> after[x] \ {el}] + +minimalElts == {el \in elts : after[el] = {}} + +BeginEnq(e) == /\ enq[e] = Done + /\ \E D \in Data, id \in UnusedIds : + LET el == [data |-> D, id |-> id] + IN /\ Assign(enq, e, D) + /\ AddElt(el) + /\ Assign(adding, e, el) + /\ UNCHANGED deq + +EndEnq(e) == /\ enq[e] # Done + /\ Assign(enq, e, Done) + /\ Assign(adding, e, NotAnElement) + /\ UNCHANGED <<deq, elts, after>> + +\* enq, deq, elts, after, adding +BeginDeq(d) == /\ deq[d] # Busy + /\ Assign(deq, d, Busy) + /\ UNCHANGED <<enq, elts, after, adding>> + +EndDeq(d) == /\ deq[d] = Busy + /\ \E el \in minimalElts : + /\ RemoveElt(el) + /\ Assign(deq, d, el.data) + /\ UNCHANGED <<enq, adding>> + +Next == \/ \E e \in EnQers : BeginEnq(e) \/ EndEnq(e) + \/ \E d \in DeQers : BeginDeq(d) \/ EndDeq(d) + +Liveness == /\ \A e \in EnQers : WF_vars(BeginEnq(e) \/ EndEnq(e)) + /\ \A d \in DeQers : WF_vars(BeginDeq(d) \/ EndDeq(d)) + +Spec == Init /\ [][Next]_vars /\ Liveness + +============================================================================= +\* Modification History +\* Last modified Thu Nov 05 16:16:15 PST 2015 by lamport +\* Created Thu Nov 05 15:07:25 PST 2015 by lamport diff --git a/tlatools/test-model/symmetry/NoSymmetryLivenessTableauMC.cfg b/tlatools/test-model/symmetry/NoSymmetryLivenessTableauMC.cfg new file mode 100644 index 0000000000000000000000000000000000000000..95c4dce4083eb2edfa412ba1459dfb5803ab183a --- /dev/null +++ b/tlatools/test-model/symmetry/NoSymmetryLivenessTableauMC.cfg @@ -0,0 +1,31 @@ +\* MV CONSTANT declarations +CONSTANTS +a = a +b = b +\* MV CONSTANT declarations +CONSTANTS +c = c +d = d +\* MV CONSTANT declarations +CONSTANTS +e = e +f = f +\* MV CONSTANT definitions +CONSTANT +Val <- const_144172196716899000 +\* MV CONSTANT definitions +CONSTANT +Proc <- const_1441721967178100000 +\* MV CONSTANT definitions +CONSTANT +Adr <- const_1441721967189101000 +\* CONSTANT definition +CONSTANT +NoVal = NoVal +\* SPECIFICATION definition +SPECIFICATION +spec_1441721967209103000 +\* PROPERTY definition +PROPERTY +prop_1441721967219104000 +\* Generated on Tue Sep 08 16:19:27 CEST 2015 \ No newline at end of file diff --git a/tlatools/test-model/symmetry/NoSymmetryLivenessTableauMC.tla b/tlatools/test-model/symmetry/NoSymmetryLivenessTableauMC.tla new file mode 100644 index 0000000000000000000000000000000000000000..8427d29d68cd968144d758e9daa70911fa658344 --- /dev/null +++ b/tlatools/test-model/symmetry/NoSymmetryLivenessTableauMC.tla @@ -0,0 +1,42 @@ +---- MODULE NoSymmetryLivenessTableauMC ---- +EXTENDS SymmetryLivenessTableau, TLC + +\* MV CONSTANT declarations@modelParameterConstants +CONSTANTS +a, b +---- + +\* MV CONSTANT declarations@modelParameterConstants +CONSTANTS +c, d +---- + +\* MV CONSTANT declarations@modelParameterConstants +CONSTANTS +e, f +---- + +\* MV CONSTANT definitions Val +const_144172196716899000 == +{a, b} +---- + +\* MV CONSTANT definitions Proc +const_1441721967178100000 == +{c, d} +---- + +\* MV CONSTANT definitions Adr +const_1441721967189101000 == +{e, f} +---- + +\* SPECIFICATION definition @modelBehaviorSpec:0 +spec_1441721967209103000 == +Spec +---- +\* PROPERTY definition @modelCorrectnessProperties:0 +prop_1441721967219104000 == +Liveness +---- +============================================================================= diff --git a/tlatools/test-model/symmetry/OneBitMutex/OneBitMutex.tla b/tlatools/test-model/symmetry/OneBitMutex/OneBitMutex.tla new file mode 100644 index 0000000000000000000000000000000000000000..242889c543ef70f26f7dbdffe82132c8983176dc --- /dev/null +++ b/tlatools/test-model/symmetry/OneBitMutex/OneBitMutex.tla @@ -0,0 +1,141 @@ +---------------------------- MODULE OneBitMutex ---------------------------- +EXTENDS Integers +CONSTANT Procs + +(*************************************************************************** +--algorithm OneBitMutex + { + variable x = [i \in Procs |-> FALSE]; + fair process (p \in Procs) + variables unchecked = {}; + other \in Procs ; + { + ncs:- while (TRUE) + { + e1: x[self] := TRUE ; + unchecked := Procs \ {self}; + e2: while (unchecked # {}) + { + with (i \in unchecked) { other := i } ; + unchecked := unchecked \ {other}; + e3: if (x[other]) + { + if (TRUE) \* Changed from "self > other" to "TRUE" + { + e4: x[self] := FALSE; + e5: await ~x[other]; + goto e1; + } + else + { + e6: await ~x[other]; + } + }; + } ; + cs: skip; + f: x[self] := FALSE + } + } +} + ***************************************************************************) + +\* BEGIN TRANSLATION +VARIABLES x, pc, unchecked, other + +vars == << x, pc, unchecked, other >> + +ProcSet == (Procs) + +Init == (* Global variables *) + /\ x = [i \in Procs |-> FALSE] + (* Process p *) + /\ unchecked = [self \in Procs |-> {}] + /\ other \in [Procs -> Procs] + /\ pc = [self \in ProcSet |-> "ncs"] + +ncs(self) == /\ pc[self] = "ncs" + /\ pc' = [pc EXCEPT ![self] = "e1"] + /\ UNCHANGED << x, unchecked, other >> + +e1(self) == /\ pc[self] = "e1" + /\ x' = [x EXCEPT ![self] = TRUE] + /\ unchecked' = [unchecked EXCEPT ![self] = Procs \ {self}] + /\ pc' = [pc EXCEPT ![self] = "e2"] + /\ other' = other + +e2(self) == /\ pc[self] = "e2" + /\ IF unchecked[self] # {} + THEN /\ \E i \in unchecked[self]: + other' = [other EXCEPT ![self] = i] + /\ unchecked' = [unchecked EXCEPT ![self] = unchecked[self] \ {other'[self]}] + /\ pc' = [pc EXCEPT ![self] = "e3"] + ELSE /\ pc' = [pc EXCEPT ![self] = "cs"] + /\ UNCHANGED << unchecked, other >> + /\ x' = x + +e3(self) == /\ pc[self] = "e3" + /\ IF x[other[self]] + THEN /\ IF TRUE + THEN /\ pc' = [pc EXCEPT ![self] = "e4"] + ELSE /\ pc' = [pc EXCEPT ![self] = "e6"] + ELSE /\ pc' = [pc EXCEPT ![self] = "e2"] + /\ UNCHANGED << x, unchecked, other >> + +e4(self) == /\ pc[self] = "e4" + /\ x' = [x EXCEPT ![self] = FALSE] + /\ pc' = [pc EXCEPT ![self] = "e5"] + /\ UNCHANGED << unchecked, other >> + +e5(self) == /\ pc[self] = "e5" + /\ ~x[other[self]] + /\ pc' = [pc EXCEPT ![self] = "e1"] + /\ UNCHANGED << x, unchecked, other >> + +e6(self) == /\ pc[self] = "e6" + /\ ~x[other[self]] + /\ pc' = [pc EXCEPT ![self] = "e2"] + /\ UNCHANGED << x, unchecked, other >> + +cs(self) == /\ pc[self] = "cs" + /\ TRUE + /\ pc' = [pc EXCEPT ![self] = "f"] + /\ UNCHANGED << x, unchecked, other >> + +f(self) == /\ pc[self] = "f" + /\ x' = [x EXCEPT ![self] = FALSE] + /\ pc' = [pc EXCEPT ![self] = "ncs"] + /\ UNCHANGED << unchecked, other >> + +p(self) == ncs(self) \/ e1(self) \/ e2(self) \/ e3(self) \/ e4(self) + \/ e5(self) \/ e6(self) \/ cs(self) \/ f(self) + +Next == (\E self \in Procs: p(self)) + +Spec == /\ Init /\ [][Next]_vars + /\ \A self \in Procs : WF_vars((pc[self] # "ncs") /\ p(self)) + +\* END TRANSLATION + +----------------------------------------------------------------------------- +TypeOK == /\ pc \in [{0,1} -> {"r", "e1", "e2", "cs"}] + /\ x \in [{0,1} -> BOOLEAN] + +Past(i,j) == \/ ((pc[i] = "e2") /\ (j \notin unchecked[i])) + \/ /\ pc[i] \in {"e3", "e6"} + /\ j \notin unchecked[i] \cup {other[i]} + \/ pc[i] = "cs" + +Inv == /\ TypeOK + /\ \A i \in Procs : + /\ (pc[i] \in {"e2", "e3", "e6", "cs"}) => x[i] + /\ \A j \in Procs \ {i} : + Past(i, j) => ~Past(j, i) /\ x[i] + + +(*************************************************************************** + N-process One-Bit mutual exclusion is _not_ starvation free! + ***************************************************************************) +StarvationFreedom == \A e \in Procs : pc[e] = "e2" ~> pc[e] = "cs" + + +============================================================================= diff --git a/tlatools/test-model/symmetry/OneBitMutex/OneBitMutexMC.cfg b/tlatools/test-model/symmetry/OneBitMutex/OneBitMutexMC.cfg new file mode 100644 index 0000000000000000000000000000000000000000..5b31cea12d04a107fda0619f30b270e06e33179b --- /dev/null +++ b/tlatools/test-model/symmetry/OneBitMutex/OneBitMutexMC.cfg @@ -0,0 +1,15 @@ +\* MV CONSTANT declarations +CONSTANTS +A = A +B = B +\* MV CONSTANT definitions +CONSTANT +Procs <- const_144491423291817000 +\* SYMMETRY definition +SYMMETRY symm_144481269487927000 +\* SPECIFICATION definition +SPECIFICATION +spec_144491423292818000 +\* PROPERTY definition +PROPERTY +prop_144491423293819000 \ No newline at end of file diff --git a/tlatools/test-model/symmetry/OneBitMutex/OneBitMutexMC.tla b/tlatools/test-model/symmetry/OneBitMutex/OneBitMutexMC.tla new file mode 100644 index 0000000000000000000000000000000000000000..e81b50c63ee43ca0d7bd806cfc0f49c0b16ca962 --- /dev/null +++ b/tlatools/test-model/symmetry/OneBitMutex/OneBitMutexMC.tla @@ -0,0 +1,27 @@ +---- MODULE OneBitMutexMC ---- +EXTENDS OneBitMutex, TLC + +\* MV CONSTANT declarations@modelParameterConstants +CONSTANTS +A, B +---- + +\* MV CONSTANT definitions Procs +const_144491423291817000 == +{A, B} +---- + +\* SYMMETRY definition +symm_144481269487927000 == +Permutations(const_144491423291817000) +---- + +\* SPECIFICATION definition @modelBehaviorSpec:0 +spec_144491423292818000 == +Spec +---- +\* PROPERTY definition @modelCorrectnessProperties:0 +prop_144491423293819000 == +StarvationFreedom +---- +============================================================================= diff --git a/tlatools/test-model/symmetry/OneBitMutex/OneBitMutexNoSymmetryMC.cfg b/tlatools/test-model/symmetry/OneBitMutex/OneBitMutexNoSymmetryMC.cfg new file mode 100644 index 0000000000000000000000000000000000000000..aaaa39d3089a0410098ef62bec4ba63d040d030e --- /dev/null +++ b/tlatools/test-model/symmetry/OneBitMutex/OneBitMutexNoSymmetryMC.cfg @@ -0,0 +1,13 @@ +\* MV CONSTANT declarations +CONSTANTS +A = A +B = B +\* MV CONSTANT definitions +CONSTANT +Procs <- const_144491423291817000 +\* SPECIFICATION definition +SPECIFICATION +spec_144491423292818000 +\* PROPERTY definition +PROPERTY +prop_144491423293819000 \ No newline at end of file diff --git a/tlatools/test-model/symmetry/OneBitMutex/OneBitMutexNoSymmetryMC.tla b/tlatools/test-model/symmetry/OneBitMutex/OneBitMutexNoSymmetryMC.tla new file mode 100644 index 0000000000000000000000000000000000000000..e5d1f68dd4e98e537e246aaca1089504e6b5750a --- /dev/null +++ b/tlatools/test-model/symmetry/OneBitMutex/OneBitMutexNoSymmetryMC.tla @@ -0,0 +1,22 @@ +---- MODULE OneBitMutexNoSymmetryMC ---- +EXTENDS OneBitMutex, TLC + +\* MV CONSTANT declarations@modelParameterConstants +CONSTANTS +A, B +---- + +\* MV CONSTANT definitions Procs +const_144491423291817000 == +{A, B} +---- + +\* SPECIFICATION definition @modelBehaviorSpec:0 +spec_144491423292818000 == +Spec +---- +\* PROPERTY definition @modelCorrectnessProperties:0 +prop_144491423293819000 == +StarvationFreedom +---- +============================================================================= diff --git a/tlatools/test-model/symmetry/SymmetryLiveness3.tla b/tlatools/test-model/symmetry/SymmetryLiveness3.tla new file mode 100644 index 0000000000000000000000000000000000000000..2e0cb3f2576ce119eccb5f0fab427ab1352e35b1 --- /dev/null +++ b/tlatools/test-model/symmetry/SymmetryLiveness3.tla @@ -0,0 +1,34 @@ +------------------------- MODULE SymmetryLiveness3 ------------------------- +EXTENDS Integers +CONSTANT S +Other(n) == CHOOSE x \in S \ {n} : TRUE + +VARIABLES x, y + +Init == x \in S /\ y = 0 + +\* MCa (SymmetryModelCheckerTest3a) +N1 == \/ /\ y = 0 + /\ y' = 1 + /\ x' = x + \/ /\ y = 1 + /\ y' = 0 + /\ x' = Other(x) +Spec1 == Init /\ [][N1]_<<x, y>> /\ WF_<<x,y>>(N1) \* Violates Prop1, but TLC produces bogus trace + +\* MC (SymmetryModelCheckerTest3) +\* Contrary to N1, x' either stays unchanged OR assumes Other(x) +N2 == \/ /\ y = 0 + /\ y' = 1 + /\ x' = x + \/ /\ y = 1 + /\ y' = 0 + /\ x' = x + \/ /\ y = 1 + /\ y' = 0 + /\ x' = Other(x) +Spec2 == Init /\ [][N2]_<<x, y>> /\ WF_<<x,y>>(N2) \* Fails to find error in Prop1 + + +Prop1 == <>[][x'=x]_<<x, y>> +============================================================================= diff --git a/tlatools/test-model/symmetry/SymmetryLivenessLong.tla b/tlatools/test-model/symmetry/SymmetryLivenessLong.tla new file mode 100644 index 0000000000000000000000000000000000000000..3606cf58c05ab61b9938dbbe9fc5818edb82dcc8 --- /dev/null +++ b/tlatools/test-model/symmetry/SymmetryLivenessLong.tla @@ -0,0 +1,54 @@ +------------------------- MODULE SymmetryLivenessLong ------------------------- +EXTENDS Integers +CONSTANT S +Other(n) == CHOOSE x \in S \ {n} : TRUE + +VARIABLES x, y + +Init == x \in S /\ y = 0 + +\* Contrary to SymmetryLiveness3, this produces a long trace which results in LiveWorker#bfsPostFix finding a longer path. + +\* MCa (SymmetryModelCheckerTestLonga) +N1 == \/ /\ y = 0 + /\ y' = 1 + /\ x' = x + \/ /\ y = 1 + /\ y' = 2 + /\ x' = x + \/ /\ y = 2 + /\ y' = 3 + /\ x' = x + \/ /\ y = 3 + /\ y' = 4 + /\ x' = x + \/ /\ y = 4 + /\ y' = 0 + /\ x' = Other(x) +Spec1 == Init /\ [][N1]_<<x, y>> /\ WF_<<x,y>>(N1) \* Violates Prop1, but TLC produces bogus trace + +\* MC (SymmetryModelCheckerTestLong) +\* Contrary to N1, x' either stays unchanged OR assumes Other(x) +N2 == \/ /\ y = 0 + /\ y' = 1 + /\ x' = x + \/ /\ y = 1 + /\ y' = 2 + /\ x' = x + \/ /\ y = 2 + /\ y' = 3 + /\ x' = x + \/ /\ y = 3 + /\ y' = 4 + /\ x' = x + \/ /\ y = 4 + /\ y' = 0 + /\ x' = x + \/ /\ y = 4 + /\ y' = 0 + /\ x' = Other(x) +Spec2 == Init /\ [][N2]_<<x, y>> /\ WF_<<x,y>>(N2) \* Fails to find error in Prop1 + + +Prop1 == <>[][x'=x]_<<x, y>> +============================================================================= diff --git a/tlatools/test-model/symmetry/SymmetryLivenessTableau.tla b/tlatools/test-model/symmetry/SymmetryLivenessTableau.tla new file mode 100644 index 0000000000000000000000000000000000000000..a1cc65b0d45bad2696ca299e10fe4b2a6f575bc9 --- /dev/null +++ b/tlatools/test-model/symmetry/SymmetryLivenessTableau.tla @@ -0,0 +1,66 @@ +---------------------- MODULE SymmetryLivenessTableau ---------------------- +VARIABLES mem, ctl, buf +VARIABLE memInt +CONSTANTS Proc, + Adr, + Val + +NoVal == CHOOSE x : x \notin Val +InitMemInt == {<<p, NoVal>> : p \in Proc} +Send(p, d, oldMemInt, newMemInt) == newMemInt = <<p, d>> +Reply(p, d, oldMemInt, newMemInt) == newMemInt = <<p, d>> +(***************************************************************************) +(* We comment out the assumption because TLC cannot handle unbounded *) +(* quantifiers. *) +(***************************************************************************) +\* ASSUME \A p, d, miOld, miNew : +\* /\ Send(p,d,miOld,miNew) \in BOOLEAN +\* /\ Reply(p,d,miOld,miNew) \in BOOLEAN + +----------------------------------------------------------------------------- +MReq == [op : {"Rd"}, adr: Adr] + \cup [op : {"Wr"}, adr: Adr, val : Val] + +-------------------------------------------------------------- +IInit == /\ mem \in [Adr->Val] + /\ ctl = [p \in Proc |-> "rdy"] + /\ buf = [p \in Proc |-> NoVal] + /\ memInt \in InitMemInt + +TypeInvariant == + /\ mem \in [Adr->Val] + /\ ctl \in [Proc -> {"rdy", "busy","done"}] + /\ buf \in [Proc -> MReq \cup Val \cup {NoVal}] + +Req(p) == /\ ctl[p] = "rdy" + /\ \E req \in MReq : + /\ Send(p, req, memInt, memInt') + /\ buf' = [buf EXCEPT ![p] = req] + /\ ctl' = [ctl EXCEPT ![p] = "busy"] + /\ UNCHANGED mem + +Do(p) == + /\ ctl[p] = "busy" + /\ mem' = IF buf[p].op = "Wr" + THEN [mem EXCEPT ![buf[p].adr] = buf[p].val] + ELSE mem + /\ buf' = [buf EXCEPT ![p] = IF buf[p].op = "Wr" + THEN NoVal + ELSE mem[buf[p].adr]] + /\ ctl' = [ctl EXCEPT ![p] = "done"] + /\ UNCHANGED memInt + +Rsp(p) == /\ ctl[p] = "done" + /\ Reply(p, buf[p], memInt, memInt') + /\ ctl' = [ctl EXCEPT ![p]= "rdy"] + /\ UNCHANGED <<mem, buf>> + +INext == \E p \in Proc: Req(p) \/ Do(p) \/ Rsp(p) + +Spec == IInit /\ [][INext]_<<memInt, mem, ctl, buf>> + /\ \A p \in Proc : WF_<<memInt, mem, ctl, buf>>(Do(p) \/ Rsp(p)) + +Liveness == \A p \in Proc : (ctl[p] = "busy") ~> (ctl[p] = "rdy") +-------------------------------------------------------------- +THEOREM Spec => []TypeInvariant +============================================================== \ No newline at end of file diff --git a/tlatools/test-model/symmetry/SymmetryLivenessTableauMC.cfg b/tlatools/test-model/symmetry/SymmetryLivenessTableauMC.cfg new file mode 100644 index 0000000000000000000000000000000000000000..a6a36db92f92499a81c85156f9f49481cf55c8c1 --- /dev/null +++ b/tlatools/test-model/symmetry/SymmetryLivenessTableauMC.cfg @@ -0,0 +1,33 @@ +\* MV CONSTANT declarations +CONSTANTS +a = a +b = b +\* MV CONSTANT declarations +CONSTANTS +c = c +d = d +\* MV CONSTANT declarations +CONSTANTS +e = e +f = f +\* MV CONSTANT definitions +CONSTANT +Val <- const_144172195407986000 +\* MV CONSTANT definitions +CONSTANT +Proc <- const_144172195408987000 +\* MV CONSTANT definitions +CONSTANT +Adr <- const_144172195409988000 +\* SYMMETRY definition +SYMMETRY symm_144172195410989000 +\* CONSTANT definition +CONSTANT +NoVal = NoVal +\* SPECIFICATION definition +SPECIFICATION +spec_144172195413091000 +\* PROPERTY definition +PROPERTY +prop_144172195414092000 +\* Generated on Tue Sep 08 16:19:14 CEST 2015 \ No newline at end of file diff --git a/tlatools/test-model/symmetry/SymmetryLivenessTableauMC.tla b/tlatools/test-model/symmetry/SymmetryLivenessTableauMC.tla new file mode 100644 index 0000000000000000000000000000000000000000..d3fb95c9829c6a4464620a2fabe3b4624756aef7 --- /dev/null +++ b/tlatools/test-model/symmetry/SymmetryLivenessTableauMC.tla @@ -0,0 +1,47 @@ +---- MODULE SymmetryLivenessTableauMC ---- +EXTENDS SymmetryLivenessTableau, TLC + +\* MV CONSTANT declarations@modelParameterConstants +CONSTANTS +a, b +---- + +\* MV CONSTANT declarations@modelParameterConstants +CONSTANTS +c, d +---- + +\* MV CONSTANT declarations@modelParameterConstants +CONSTANTS +e, f +---- + +\* MV CONSTANT definitions Val +const_144172195407986000 == +{a, b} +---- + +\* MV CONSTANT definitions Proc +const_144172195408987000 == +{c, d} +---- + +\* MV CONSTANT definitions Adr +const_144172195409988000 == +{e, f} +---- + +\* SYMMETRY definition +symm_144172195410989000 == +Permutations(const_144172195407986000) \union Permutations(const_144172195408987000) \union Permutations(const_144172195409988000) +---- + +\* SPECIFICATION definition @modelBehaviorSpec:0 +spec_144172195413091000 == +Spec +---- +\* PROPERTY definition @modelCorrectnessProperties:0 +prop_144172195414092000 == +Liveness +---- +============================================================================= diff --git a/tlatools/test-model/symmetry/TableauSymmetry.tla b/tlatools/test-model/symmetry/TableauSymmetry.tla new file mode 100644 index 0000000000000000000000000000000000000000..829677122cabe09df53e3e21e4b139adfc303902 --- /dev/null +++ b/tlatools/test-model/symmetry/TableauSymmetry.tla @@ -0,0 +1,22 @@ +-------------------------- MODULE TableauSymmetry -------------------------- +CONSTANT Val +VARIABLE arr + +Init == arr = [v \in Val |-> "ready"] + +Ready(v) == /\ arr[v] = "ready" + /\ arr' = [arr EXCEPT ![v]= "busy"] + +Busy(v) == /\ arr[v] = "busy" + /\ arr' = [arr EXCEPT ![v]= "done"] + +Done(v) == /\ arr[v] = "done" + /\ arr' = [arr EXCEPT ![v]= "ready"] + +Next == \E v \in Val : Ready(v) \/ Busy(v) \/ Done(v) + +Spec == Init /\[][Next]_<<arr>> /\ WF_<<arr>>(Next) + +Liveness == \A v \in Val : (arr[v] = "busy") ~> (arr[v] = "ready") + +============================================================================= diff --git a/tlatools/test-model/symmetry/TableauSymmetryDisabled.png b/tlatools/test-model/symmetry/TableauSymmetryDisabled.png new file mode 100644 index 0000000000000000000000000000000000000000..ea8343cbd6d4ad9494a0f0b456f6bb5faf58360e Binary files /dev/null and b/tlatools/test-model/symmetry/TableauSymmetryDisabled.png differ diff --git a/tlatools/test-model/symmetry/TableauSymmetryMC.cfg b/tlatools/test-model/symmetry/TableauSymmetryMC.cfg new file mode 100644 index 0000000000000000000000000000000000000000..823aee7b2c78dafe6e5cc779c7a2de726802bbba --- /dev/null +++ b/tlatools/test-model/symmetry/TableauSymmetryMC.cfg @@ -0,0 +1,15 @@ +\* MV CONSTANT declarations +CONSTANTS +a = a +b = b +\* MV CONSTANT definitions +CONSTANT +Val <- const_144481269486926000 +\* SYMMETRY definition +SYMMETRY symm_144481269487927000 +\* SPECIFICATION definition +SPECIFICATION +spec_144481269488928000 +\* PROPERTY definition +PROPERTY +prop_144481269489929000 diff --git a/tlatools/test-model/symmetry/TableauSymmetryMC.tla b/tlatools/test-model/symmetry/TableauSymmetryMC.tla new file mode 100644 index 0000000000000000000000000000000000000000..e44b77ec5c7b59cffa99815735c739c58efd1180 --- /dev/null +++ b/tlatools/test-model/symmetry/TableauSymmetryMC.tla @@ -0,0 +1,27 @@ +---- MODULE TableauSymmetryMC ---- +EXTENDS TableauSymmetry, TLC + +\* MV CONSTANT declarations@modelParameterConstants +CONSTANTS +a, b +---- + +\* MV CONSTANT definitions Val +const_144481269486926000 == +{a, b} +---- + +\* SYMMETRY definition +symm_144481269487927000 == +Permutations(const_144481269486926000) +---- + +\* SPECIFICATION definition @modelBehaviorSpec:0 +spec_144481269488928000 == +Spec +---- +\* PROPERTY definition @modelCorrectnessProperties:0 +prop_144481269489929000 == +Liveness +---- +============================================================================= diff --git a/tlatools/test-model/symmetry/TwoPhaseCommit/DistributedCommit.tla b/tlatools/test-model/symmetry/TwoPhaseCommit/DistributedCommit.tla new file mode 100644 index 0000000000000000000000000000000000000000..89afbcea5c077f6c0c82e1c0427959004d22ec6b --- /dev/null +++ b/tlatools/test-model/symmetry/TwoPhaseCommit/DistributedCommit.tla @@ -0,0 +1,66 @@ +------------------------- MODULE DistributedCommit ------------------------- +(***************************************************************************) +(* This is a high-level TLA+ specification of a distributed commit *) +(* protocol. A set of nodes individually decide whether they wish to *) +(* commit or abort a transaction. Second, the transaction will be *) +(* committed iff every node wishes to commit. *) +(***************************************************************************) +CONSTANT Participant \* set of participating nodes + +VARIABLE pState \* state of each participant + +(***************************************************************************) +(* Possible states of non-coordinator participants. *) +(***************************************************************************) +PState == {"preparing", "readyCommit", "readyAbort", "committed", "aborted"} + +(***************************************************************************) +(* Initially, every participant is preparing the transaction. *) +(***************************************************************************) +Init == pState = [p \in Participant |-> "preparing"] + +(***************************************************************************) +(* A participant decides whether she wishes to commit or abort. *) +(***************************************************************************) +Decide(p) == + /\ pState[p] = "preparing" + /\ \E dec \in {"readyCommit", "readyAbort"} : + pState' = [pState EXCEPT ![p] = dec] + +(***************************************************************************) +(* A participant may definitely commit only if all participants wish *) +(* to do so. *) +(***************************************************************************) +Commit(p) == + /\ \A q \in Participant : pState[q] \in {"readyCommit", "committed"} + /\ pState' = [pState EXCEPT ![p] = "committed"] + +(***************************************************************************) +(* A participant aborts if some participant wishes to do so. *) +(***************************************************************************) +Abort(p) == + /\ \E q \in Participant : pState[q] \in {"readyAbort", "aborted"} + /\ pState' = [pState EXCEPT ![p] = "aborted"] + +(***************************************************************************) +(* The next-state relation is the disjunction of the above actions. *) +(***************************************************************************) +Next == + \E p \in Participant : Decide(p) \/ Commit(p) \/ Abort(p) + +(***************************************************************************) +(* Overall specification. *) +(***************************************************************************) +Spec == Init /\ [][Next]_pState /\ WF_pState(Next) + +----------------------------------------------------------------------------- + +(***************************************************************************) +(* Main safety property: participants may definitely commit only if *) +(* all participants agree. *) +(***************************************************************************) +Safety == + \A p \in Participant : pState[p] = "committed" => + \A q \in Participant : pState[q] \in {"readyCommit", "committed"} + +============================================================================= diff --git a/tlatools/test-model/symmetry/TwoPhaseCommit/MC.cfg b/tlatools/test-model/symmetry/TwoPhaseCommit/MC.cfg new file mode 100644 index 0000000000000000000000000000000000000000..46bf85e8552506cb7dd0e250d429b4a174b52e64 --- /dev/null +++ b/tlatools/test-model/symmetry/TwoPhaseCommit/MC.cfg @@ -0,0 +1,23 @@ +\* MV CONSTANT declarations +CONSTANTS +anna = anna +berta = berta +charlie = charlie +dimitry = dimitry +eugene = eugene +\* MV CONSTANT definitions +CONSTANT +Participant <- const_146894168642461000 +\* SYMMETRY definition +SYMMETRY symm_146894168643562000 +\* SPECIFICATION definition +SPECIFICATION +spec_146894168644663000 +\* INVARIANT definition +INVARIANT +inv_146894168645764000 +inv_146894168646965000 +inv_146894168647966000 +\* PROPERTY definition +PROPERTY +prop_146894168649067000 diff --git a/tlatools/test-model/symmetry/TwoPhaseCommit/MC.tla b/tlatools/test-model/symmetry/TwoPhaseCommit/MC.tla new file mode 100644 index 0000000000000000000000000000000000000000..c195e717456b6454a7aa6297a0b8ba984ffc1c9c --- /dev/null +++ b/tlatools/test-model/symmetry/TwoPhaseCommit/MC.tla @@ -0,0 +1,39 @@ +---- MODULE MC ---- +EXTENDS TwoPhaseCommit, TLC + +\* MV CONSTANT declarations@modelParameterConstants +CONSTANTS +anna, berta, charlie, dimitry, eugene +---- + +\* MV CONSTANT definitions Participant +const_146894168642461000 == +{anna, berta, charlie, dimitry, eugene} +---- + +\* SYMMETRY definition +symm_146894168643562000 == +Permutations(const_146894168642461000) +---- + +\* SPECIFICATION definition @modelBehaviorSpec:0 +spec_146894168644663000 == +Spec +---- +\* INVARIANT definition @modelCorrectnessInvariants:0 +inv_146894168645764000 == +TypeOK +---- +\* INVARIANT definition @modelCorrectnessInvariants:1 +inv_146894168646965000 == +CommitOrAbort +---- +\* INVARIANT definition @modelCorrectnessInvariants:2 +inv_146894168647966000 == +AbortWins +---- +\* PROPERTY definition @modelCorrectnessProperties:0 +prop_146894168649067000 == +Liveness +---- +============================================================================= diff --git a/tlatools/test-model/symmetry/TwoPhaseCommit/TwoPhaseCommit.tla b/tlatools/test-model/symmetry/TwoPhaseCommit/TwoPhaseCommit.tla new file mode 100644 index 0000000000000000000000000000000000000000..2e7833d68a5b7ce65167282b6cd79201ce44eae3 --- /dev/null +++ b/tlatools/test-model/symmetry/TwoPhaseCommit/TwoPhaseCommit.tla @@ -0,0 +1,150 @@ +--------------------------- MODULE TwoPhaseCommit --------------------------- +(***************************************************************************) +(* This is a TLA+ specification of the two-phase commit protocol used for *) +(* distributed data bases. It models only one instance of the protocol, *) +(* i.e. for a single transaction. *) +(* *) +(* We specify only the safety part of the specification: what may occur *) +(* during an execution. This would be complemented by a specification of *) +(* what must eventually occur, specified by suitable fairness conditions. *) +(***************************************************************************) + +CONSTANT Participant \* set of nodes other than the coordinator + +VARIABLES + cState, \* the state of the coordinator + pState, \* the state of the non-coordinator participants + committed, \* participants that the coordinator knows are OK for committing + msgs \* messages sent during the protocol + +vars == <<cState, pState, committed, msgs>> + +(***************************************************************************) +(* Possible states of coordinator. *) +(***************************************************************************) +CState == {"preparing", "committed", "aborted"} + +(***************************************************************************) +(* Possible states of non-coordinator participants. *) +(***************************************************************************) +PState == {"preparing", "readyCommit", "readyAbort", "committed", "aborted"} + +(***************************************************************************) +(* Messages sent during the protocol. *) +(***************************************************************************) +Messages == + \* participant informs coordinator about its decision + [kind : {"commit", "abort"}, part : Participant] \cup + \* coordinator tells participants whether to commit or abort + [kind : {"doCommit", "doAbort"}] + +commit(p) == [kind |-> "commit", part |-> p] +abort(p) == [kind |-> "abort", part |-> p] +doCommit == [kind |-> "doCommit"] +doAbort == [kind |-> "doAbort"] + +(***************************************************************************) +(* The following predicate specifies what values the variables can take *) +(* during an execution of the protocol. *) +(***************************************************************************) +TypeOK == + /\ cState \in CState + /\ pState \in [Participant -> PState] + /\ committed \subseteq Participant + /\ msgs \subseteq Messages + +(***************************************************************************) +(* The initial state of the protocol. *) +(***************************************************************************) +Init == + /\ cState = "preparing" + /\ pState = [p \in Participant |-> "preparing"] + /\ committed = {} + /\ msgs = {} + +(***************************************************************************) +(* The following action formulas describe the possible transitions of *) +(* the nodes. *) +(***************************************************************************) + +(***************************************************************************) +(* A participant decides and informs the coordinator of its decision. *) +(***************************************************************************) +Decide(p) == + /\ pState[p] = "preparing" + /\ \/ pState' = [pState EXCEPT ![p] = "readyCommit"] /\ msgs' = msgs \cup {commit(p)} + \/ pState' = [pState EXCEPT ![p] = "readyAbort"] /\ msgs' = msgs \cup {abort(p)} + /\ UNCHANGED <<cState, committed>> + +(***************************************************************************) +(* A participant receives a commit or abort order from the coordinator. *) +(***************************************************************************) +Execute(p) == + /\ \/ doCommit \in msgs /\ pState' = [pState EXCEPT ![p] = "committed"] + \/ doAbort \in msgs /\ pState' = [pState EXCEPT ![p] = "aborted"] + /\ UNCHANGED <<cState, committed, msgs>> + +(***************************************************************************) +(* The coordinator receives a new commit decision for some participant. *) +(* If all participants wish to commit, it sends an order to commit. *) +(***************************************************************************) +RcvCommit == \E p \in Participant \ committed : + /\ commit(p) \in msgs + /\ committed' = committed \cup {p} + /\ IF committed' = Participant + THEN /\ cState' = "committed" + /\ msgs' = msgs \cup {doCommit} + ELSE UNCHANGED <<cState, msgs>> + /\ pState' = pState + +(***************************************************************************) +(* The coordinator receives an abort decision and sends an order to abort. *) +(***************************************************************************) +RcvAbort == \E p \in Participant : + /\ abort(p) \in msgs + /\ cState' = "aborted" + /\ msgs' = msgs \cup {doAbort} + /\ UNCHANGED <<pState, committed>> + +(***************************************************************************) +(* The overall next-state relation is the disjunction of the action *) +(* formulas defined previously. *) +(***************************************************************************) +Next == + \/ \E p \in Participant : Decide(p) \/ Execute(p) + \/ RcvCommit + \/ RcvAbort + +Spec == Init /\ [][Next]_vars /\ WF_vars(Next) +----------------------------------------------------------------------------- + +(***************************************************************************) +(* Correctness properties. *) +(***************************************************************************) + +(***************************************************************************) +(* The coordinator never sends both a doCommit and a doAbort message. *) +(***************************************************************************) +CommitOrAbort == ~(doCommit \in msgs /\ doAbort \in msgs) + +(***************************************************************************) +(* The coordinator may commit only if all participants wish to commit and *) +(* no participant wishes to abort. *) +(***************************************************************************) +AbortWins == + doCommit \in msgs => + \A p \in Participant : + /\ commit(p) \in msgs /\ pState[p] \in {"readyCommit", "committed"} + /\ abort(p) \notin msgs + +Liveness == + \A p \in Participant : <>(pState[p] \in {"committed", "aborted"}) +----------------------------------------------------------------------------- + +(***************************************************************************) +(* Two-phase commitment implements distributed commitment. *) +(***************************************************************************) +DC == INSTANCE DistributedCommit + +THEOREM Spec => DC!Spec +============================================================================= diff --git a/tlatools/test-model/symmetry/Unsymmetric.tla b/tlatools/test-model/symmetry/Unsymmetric.tla new file mode 100644 index 0000000000000000000000000000000000000000..26b1c5f8b3a63f1234160ef8f1119ab332d37376 --- /dev/null +++ b/tlatools/test-model/symmetry/Unsymmetric.tla @@ -0,0 +1,45 @@ +--------------------------- MODULE Unsymmetric --------------------------- +CONSTANT S +VARIABLE x + +Init == x \in S + +Back == \/ /\ x \in {1,2} + /\ x' \in S + +\* A: +\* +\* A honors S being symmetric. TLC finds and +\* prints the correct counter-example. + +tA == CHOOSE a \in S : TRUE + +NextA == \/ /\ x \in S + /\ IF x = tA THEN x' = 1 + ELSE x' = 2 +SpecA == Init /\ [][NextA \/ Back]_x /\ WF_x(NextA \/ Back) + +\* B: +\* +\* 'tb' breaks symmetry because it explicitly +\* requires an element in S that is different to +\* another S element. This is exactly opposite +\* to what symmetry represents. Hence, the spec +\* is _not_ symmetric and thus TLC fails to find a +\* violation/counter-example (TLC finds the +\* counter-example when S is not declared symmetric. + +tB == LET t1 == CHOOSE a \in S : TRUE + IN CHOOSE a \in S \ {t1} : TRUE + + +NextB == \/ /\ x \in S + /\ IF x = tB THEN x' = 1 + ELSE x' = 2 +SpecB == Init /\ [][NextB \/ Back]_x /\ WF_x(NextB \/ Back) + +\* Liveness + +Prop == []<>(x = 2) +============================================================================ + diff --git a/tlatools/test-model/symmetry/UnsymmetricAWithSymmetry.png b/tlatools/test-model/symmetry/UnsymmetricAWithSymmetry.png new file mode 100644 index 0000000000000000000000000000000000000000..dceb2092db2b2ec7a633f63a20f3e3ac4d7ff2ea Binary files /dev/null and b/tlatools/test-model/symmetry/UnsymmetricAWithSymmetry.png differ diff --git a/tlatools/test-model/symmetry/UnsymmetricAWithoutSymmetry.png b/tlatools/test-model/symmetry/UnsymmetricAWithoutSymmetry.png new file mode 100644 index 0000000000000000000000000000000000000000..47bb11b146cb7a64522bb3df45c769e73d71107a Binary files /dev/null and b/tlatools/test-model/symmetry/UnsymmetricAWithoutSymmetry.png differ diff --git a/tlatools/test-model/symmetry/UnsymmetricBWithSymmetry.png b/tlatools/test-model/symmetry/UnsymmetricBWithSymmetry.png new file mode 100644 index 0000000000000000000000000000000000000000..5c8b7da1242badefaf7eaab33930c4e537727d83 Binary files /dev/null and b/tlatools/test-model/symmetry/UnsymmetricBWithSymmetry.png differ diff --git a/tlatools/test-model/symmetry/UnsymmetricBWithoutSymmetry.png b/tlatools/test-model/symmetry/UnsymmetricBWithoutSymmetry.png new file mode 100644 index 0000000000000000000000000000000000000000..47242297c74fd5ad0abba3e90e3ff1b9996e13f1 Binary files /dev/null and b/tlatools/test-model/symmetry/UnsymmetricBWithoutSymmetry.png differ diff --git a/tlatools/test-model/symmetry/UnsymmetricMCA.cfg b/tlatools/test-model/symmetry/UnsymmetricMCA.cfg new file mode 100644 index 0000000000000000000000000000000000000000..bec8fd9c0fe0a39d9571a24cc1d5070bb5373876 --- /dev/null +++ b/tlatools/test-model/symmetry/UnsymmetricMCA.cfg @@ -0,0 +1,15 @@ +\* MV CONSTANT declarations +CONSTANTS +a = a +b = b +\* MV CONSTANT definitions +CONSTANT +S <- const_1444366015085101000 +\* SYMMETRY definition +SYMMETRY symm_1444366015096102000 +\* SPECIFICATION definition +SPECIFICATION +spec_1444366015106103000 +\* PROPERTY definition +PROPERTY +prop_1444366015116104000 diff --git a/tlatools/test-model/symmetry/UnsymmetricMCA.tla b/tlatools/test-model/symmetry/UnsymmetricMCA.tla new file mode 100644 index 0000000000000000000000000000000000000000..fd772c596d3d202a98694d08942efc73968ed18c --- /dev/null +++ b/tlatools/test-model/symmetry/UnsymmetricMCA.tla @@ -0,0 +1,27 @@ +---- MODULE UnsymmetricMCA ---- +EXTENDS Unsymmetric, TLC + +\* MV CONSTANT declarations@modelParameterConstants +CONSTANTS +a, b +---- + +\* MV CONSTANT definitions S +const_1444366015085101000 == +{a, b} +---- + +\* SYMMETRY definition +symm_1444366015096102000 == +Permutations(const_1444366015085101000) +---- + +\* SPECIFICATION definition @modelBehaviorSpec:0 +spec_1444366015106103000 == +SpecA +---- +\* PROPERTY definition @modelCorrectnessProperties:0 +prop_1444366015116104000 == +Prop +---- +============================================================================= diff --git a/tlatools/test-model/symmetry/UnsymmetricMCB.cfg b/tlatools/test-model/symmetry/UnsymmetricMCB.cfg new file mode 100644 index 0000000000000000000000000000000000000000..bec8fd9c0fe0a39d9571a24cc1d5070bb5373876 --- /dev/null +++ b/tlatools/test-model/symmetry/UnsymmetricMCB.cfg @@ -0,0 +1,15 @@ +\* MV CONSTANT declarations +CONSTANTS +a = a +b = b +\* MV CONSTANT definitions +CONSTANT +S <- const_1444366015085101000 +\* SYMMETRY definition +SYMMETRY symm_1444366015096102000 +\* SPECIFICATION definition +SPECIFICATION +spec_1444366015106103000 +\* PROPERTY definition +PROPERTY +prop_1444366015116104000 diff --git a/tlatools/test-model/symmetry/UnsymmetricMCB.tla b/tlatools/test-model/symmetry/UnsymmetricMCB.tla new file mode 100644 index 0000000000000000000000000000000000000000..b6d8a0fec9d6d3926b50fa55db4ef68673c841a7 --- /dev/null +++ b/tlatools/test-model/symmetry/UnsymmetricMCB.tla @@ -0,0 +1,27 @@ +---- MODULE UnsymmetricMCB ---- +EXTENDS Unsymmetric, TLC + +\* MV CONSTANT declarations@modelParameterConstants +CONSTANTS +a, b +---- + +\* MV CONSTANT definitions S +const_1444366015085101000 == +{a, b} +---- + +\* SYMMETRY definition +symm_1444366015096102000 == +Permutations(const_1444366015085101000) +---- + +\* SPECIFICATION definition @modelBehaviorSpec:0 +spec_1444366015106103000 == +SpecB +---- +\* PROPERTY definition @modelCorrectnessProperties:0 +prop_1444366015116104000 == +Prop +---- +============================================================================= diff --git a/tlatools/test-model/test57.cfg b/tlatools/test-model/test57.cfg deleted file mode 100644 index 2e385425054877319618031959b1771ec5b65ca3..0000000000000000000000000000000000000000 --- a/tlatools/test-model/test57.cfg +++ /dev/null @@ -1,3 +0,0 @@ -SPECIFICATION Spec -INVARIANT Invariant0 Invariant1 Invariant2 -PROPERTY Property \ No newline at end of file diff --git a/tlatools/test-model/test52.cfg b/tlatools/test-model/testinvalidinvariant.cfg similarity index 95% rename from tlatools/test-model/test52.cfg rename to tlatools/test-model/testinvalidinvariant.cfg index f0e860231ad0256721aa61b69aec5db318681a6c..da219465bfd7a82d092808d0c6661eed71f7d66a 100644 --- a/tlatools/test-model/test52.cfg +++ b/tlatools/test-model/testinvalidinvariant.cfg @@ -1,3 +1,3 @@ -SPECIFICATION Spec -PROPERTY Property -INVARIANT Invariant +SPECIFICATION Spec +PROPERTY Property +INVARIANT Invariant diff --git a/tlatools/test-model/testinvalidinvariant.tla b/tlatools/test-model/testinvalidinvariant.tla new file mode 100644 index 0000000000000000000000000000000000000000..e00c9bd9158a664ad6639c0094f6e0d3f014fff7 --- /dev/null +++ b/tlatools/test-model/testinvalidinvariant.tla @@ -0,0 +1,12 @@ +------------------ MODULE testinvalidinvariant -------------------- +EXTENDS Integers + +VARIABLES x +Init == /\ x = 1 +Next == /\ x'= x + 1 +Spec == Init /\ [][Next]_<<x>> + +\* Primed variable is invalid. +Invariant == x' \in Nat + +================================================================== diff --git a/tlatools/test-model/tlc2/overrides/TLCOverrides.class b/tlatools/test-model/tlc2/overrides/TLCOverrides.class new file mode 100644 index 0000000000000000000000000000000000000000..fada9ec400c899edf0c7e43d29d448a39216a5fd Binary files /dev/null and b/tlatools/test-model/tlc2/overrides/TLCOverrides.class differ diff --git a/tlatools/test-model/tlc2/overrides/TLCOverrides.java b/tlatools/test-model/tlc2/overrides/TLCOverrides.java new file mode 100644 index 0000000000000000000000000000000000000000..7dff9fd00a406e7952aecf8e39bb19ee11d763f1 --- /dev/null +++ b/tlatools/test-model/tlc2/overrides/TLCOverrides.java @@ -0,0 +1,40 @@ +/******************************************************************************* + * Copyright (c) 2019 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.overrides; + +import tlc2.tool.EvaluatingValueTest; + +public class TLCOverrides implements ITLCOverrides { + + /* (non-Javadoc) + * @see tlc2.overrides.ITLCOverrides#get() + */ + @Override + public Class[] get() { + return new Class[] { EvaluatingValueTest.class, UserModuleOverrideAnnotationImpl.class }; + } + +} diff --git a/tlatools/test-model/tlc2/overrides/UserModuleOverrideAnnotationImpl.class b/tlatools/test-model/tlc2/overrides/UserModuleOverrideAnnotationImpl.class new file mode 100644 index 0000000000000000000000000000000000000000..dc2d251fc94396f72481e5f609fae6b6307aaf64 Binary files /dev/null and b/tlatools/test-model/tlc2/overrides/UserModuleOverrideAnnotationImpl.class differ diff --git a/tlatools/test-model/tlc2/overrides/UserModuleOverrideAnnotationImpl.java b/tlatools/test-model/tlc2/overrides/UserModuleOverrideAnnotationImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..33048fa6d4d42e59e72e98bbbbdb33ef1025a843 --- /dev/null +++ b/tlatools/test-model/tlc2/overrides/UserModuleOverrideAnnotationImpl.java @@ -0,0 +1,59 @@ +/******************************************************************************* + * Copyright (c) 2019 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.overrides; + +import tlc2.value.impl.BoolValue; +import tlc2.value.impl.Value; + +public class UserModuleOverrideAnnotationImpl { + + @TLAPlusOperator(identifier="Get", module="UserModuleOverrideAnnotation") + public static Value getNumberOne() { + return BoolValue.ValTrue; + } + + @TLAPlusOperator(identifier="Get2", module="UserModuleOverrideAnnotation") + public static Value Get2() { + return BoolValue.ValTrue; + } + + //************ The ones below will cause warnings because they don't match ************// + + @TLAPlusOperator(identifier="Get2", module="UserModuleOverrideAnnotation") + public static Value Get2(Value v1) { + return BoolValue.ValFalse; + } + + @TLAPlusOperator(identifier="NoSuchIdentifier", module="UserModuleOverrideAnnotation") + public static Value noSuchIdentifier() { + return BoolValue.ValFalse; + } + + @TLAPlusOperator(identifier="Get", module="NoSuchModule") + public static Value noSuchModule() { + return BoolValue.ValFalse; + } +} diff --git a/tlatools/test-verify/tlc2/tool/fp/OffHeapDiskFPSetJPFTest.java b/tlatools/test-verify/tlc2/tool/fp/OffHeapDiskFPSetJPFTest.java new file mode 100644 index 0000000000000000000000000000000000000000..0ec0a2225b03b7003b5d6697af8d79e8870e596c --- /dev/null +++ b/tlatools/test-verify/tlc2/tool/fp/OffHeapDiskFPSetJPFTest.java @@ -0,0 +1,138 @@ +/******************************************************************************* + * Copyright (c) 2016 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.fp; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; +import java.util.concurrent.BrokenBarrierException; +import java.util.concurrent.atomic.AtomicInteger; + +import org.junit.Test; + +import gov.nasa.jpf.util.test.TestJPF; + +// This class verifies a core part of tlc2.tool.fp.OffHeapDiskFPSet. It is - more or less - a verbatim copy. +public class OffHeapDiskFPSetJPFTest extends TestJPF { + private static final int PROBE_LIMIT = 2; + + @Test + public void test() throws InterruptedException, BrokenBarrierException { + if (verifyNoPropertyViolation("+listener=.listener.AssertionProperty,.listener.ErrorTraceGenerator")) { + final int size = 3; + final int max = 3; + final DummyOffHeapDiskFPSet fpSet = new DummyOffHeapDiskFPSet(size, max); + + // Two concurrent writers. + for (int i = 0; i < 2; i++) { + Thread worker = new Thread(new Runnable() { + public void run() { + for (long i = 1; i <= max; i++) { + fpSet.memInsert(i); + } + } + }, "Worker"); + worker.start(); + } + assert fpSet.checkInvariant() : "FPSet violates its invariant: " + Arrays.toString(fpSet.array.array); + } + } + + private static class DummyOffHeapDiskFPSet { + + private static final int EMPTY = 0; + + private final OffHeapDiskFPSet.Indexer indexer; + private final DummyLongArray array; + // AtomicInteger should be LongAdder but JPF fails with what seems to be an + // internal bug. + private final AtomicInteger tblCnt; + + public DummyOffHeapDiskFPSet(int positions, long max) { + this.tblCnt = new AtomicInteger(0); + this.array = new DummyLongArray(positions); + this.indexer = new OffHeapDiskFPSet.Indexer(positions, 1, max); + } + + // This is what we want to verify. + public boolean memInsert(final long fp) { + for (int i = 0; i < PROBE_LIMIT; i++) { + final int position = (int) indexer.getIdx(fp, i); + final long expected = array.get(position); + if (expected == EMPTY) { + if (array.trySet(position, expected, fp)) { + tblCnt.incrementAndGet(); + return false; + } else { + i = i - 1; + continue; + } + } + if (expected == fp) { + return true; + } + } + return false; + } + + // Heap-variant of LongArray with synchronized instead of CAS. JPF does + // not seem to support sun.misc.Unsafe. + private static class DummyLongArray { + private final long[] array; + + public DummyLongArray(int positions) { + this.array = new long[positions]; + } + + public long get(int position) { + return this.array[position]; + } + + public synchronized boolean trySet(int position, long expected, long l) { + if (this.array[position] == expected) { + this.array[position] = l; + return true; + } + return false; + } + } + + //**** Assertion Helper ****// + + public synchronized boolean checkInvariant() { + // No duplicates. + int cnt = 0; + final Set<Long> s = new HashSet<Long>(array.array.length); + for (int i = 0; i < array.array.length; i++) { + if (array.array[i] > 0) { + s.add(array.array[i]); + cnt++; + } + } + return cnt == s.size(); + } + } +} diff --git a/tlatools/test-verify/tlc2/tool/queue/StateQueueJFP.launch b/tlatools/test-verify/tlc2/tool/queue/StateQueueJFP.launch new file mode 100644 index 0000000000000000000000000000000000000000..f36bb231b11e3069caeaabd4020d4f7398e19976 --- /dev/null +++ b/tlatools/test-verify/tlc2/tool/queue/StateQueueJFP.launch @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<launchConfiguration type="org.eclipse.jdt.launching.localJavaApplication"> +<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS"> +<listEntry value="/tlatools/lib/jpf.jar"/> +</listAttribute> +<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_TYPES"> +<listEntry value="1"/> +</listAttribute> +<booleanAttribute key="org.eclipse.jdt.launching.ATTR_USE_START_ON_FIRST_THREAD" value="true"/> +<listAttribute key="org.eclipse.jdt.launching.CLASSPATH"> +<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry containerPath="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8" javaProject="tlatools" path="1" type="4"/> "/> +<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry id="org.eclipse.jdt.launching.classpathentry.defaultClasspath"> <memento exportedEntriesOnly="false" project="tlatools"/> </runtimeClasspathEntry> "/> +<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/tlatools/lib/jpf/jgraphx.jar" path="3" type="2"/> "/> +<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/tlatools/lib/jpf/jpf-shell.jar" path="3" type="2"/> "/> +<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/tlatools/lib/jpf/jpf-visual.jar" path="3" type="2"/> "/> +</listAttribute> +<booleanAttribute key="org.eclipse.jdt.launching.DEFAULT_CLASSPATH" value="false"/> +<stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value="gov.nasa.jpf.tool.RunJPF"/> +<stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="${project_loc:tlatools}/test-verify/tlc2/tool/queue/StateQueueJPFStandalone.jpf -show"/> +<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="tlatools"/> +</launchConfiguration> diff --git a/tlatools/test-verify/tlc2/tool/queue/StateQueueJPFStandalone.jpf b/tlatools/test-verify/tlc2/tool/queue/StateQueueJPFStandalone.jpf new file mode 100644 index 0000000000000000000000000000000000000000..ce498466fcdff73f1cf6e367c7d270ff13285f83 --- /dev/null +++ b/tlatools/test-verify/tlc2/tool/queue/StateQueueJPFStandalone.jpf @@ -0,0 +1,22 @@ +## +## Adjust for current system!!! Run in Eclispe with .launch file. +## +basepath=/home/markus/src/TLA/tla/tlatools + + +target=tlc2.tool.queue.StateQueueJPFTest +jpf-visual.sourcepath+=${basepath}/test-verify;${basepath}/src + +# register console errorTracePrinter as a publisher +report.publisher+=,errorTracePrinter +report.errorTracePrinter.class=ErrorTracePrinter + +# print trace when property is violated +report.errorTracePrinter.property_violation=trace + +#turn on the shell +shell=.shell.basicshell.BasicShell + +#turn on the new panel +shell.panels+=,errorTrace +shell.panels.errorTrace=ErrorTracePanel diff --git a/tlatools/test-verify/tlc2/tool/queue/StateQueueJPFTest.java b/tlatools/test-verify/tlc2/tool/queue/StateQueueJPFTest.java index ea4bc80a8bf128715696af493ac21d00eedd1312..f531a1111f9cf0487caab2370d37795d8ba20ab3 100644 --- a/tlatools/test-verify/tlc2/tool/queue/StateQueueJPFTest.java +++ b/tlatools/test-verify/tlc2/tool/queue/StateQueueJPFTest.java @@ -35,6 +35,10 @@ import tlc2.tool.TLCState; public class StateQueueJPFTest extends TestJPF { + public static void main(String[] args) { + new StateQueueJPFTest().test(); + } + @Test public void test() { if (verifyNoPropertyViolation()) { @@ -50,21 +54,23 @@ public class StateQueueJPFTest extends TestJPF { } }, "Main"); main.start(); - - Thread worker = new Thread(new Runnable() { - public void run() { - for (int i = 0; i < 10; i++) { - TLCState state = queue.dequeue(); - if (state == null) { - queue.finishAll(); - return; + + for (int i = 0; i < 3; i++) { + Thread worker = new Thread(new Runnable() { + public void run() { + for (int i = 0; i < 3; i++) { + TLCState state = queue.dequeue(); + if (state == null) { + queue.finishAll(); + return; + } + queue.enqueue(tlcState); } - queue.enqueue(tlcState); + queue.finishAll(); } - queue.finishAll(); - } - }, "Worker"); - worker.start(); + }, "Worker" + i); + worker.start(); + } } } @@ -85,6 +91,10 @@ public class StateQueueJPFTest extends TestJPF { return state; } + TLCState peekInner() { + return state; + } + public void beginChkpt() throws IOException { // checkpointing not being verified } diff --git a/tlatools/test/pcal/AssignmentToUndeclaredVariableTest.java b/tlatools/test/pcal/AssignmentToUndeclaredVariableTest.java new file mode 100644 index 0000000000000000000000000000000000000000..988f67fa208a34a3dbe7c3a135c6646f55dafe57 --- /dev/null +++ b/tlatools/test/pcal/AssignmentToUndeclaredVariableTest.java @@ -0,0 +1,205 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package pcal; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import java.io.IOException; +import java.util.Arrays; + +import org.junit.Test; + +import util.ToolIO; + +public class AssignmentToUndeclaredVariableTest extends PCalTest { + @Test + public void procedure() throws IOException { + assertEquals(trans.STATUS_EXIT_WITH_ERRORS, trans.runMe(new String[] {"-nocfg", + writeTempFile("AssignmentToUndeclaredVariableTest", + "---- MODULE algo ----\n" + + "CONSTANT c\n" + + "(*\n" + + "--algorithm algo {\n" + + " variables v, w;\n" + + " procedure Proc1() \n" + + " {p1 : v := 23;\n" + + " c := 42 }\n" + + " {\n" + + " i: call Proc1();\n" + // Assignment to constant + " }\n" + + "}*)\n" + + "====" + )})); + + assertTrue(Arrays.toString(ToolIO.getAllMessages()), + Arrays.asList(ToolIO.getAllMessages()).contains("\nUnrecoverable error:\n" + + " -- Assignment to undeclared variable c\n" + + " at line 8, column 13.\n")); + } + + @Test + public void process() throws IOException { + assertEquals(trans.STATUS_EXIT_WITH_ERRORS, trans.runMe(new String[] {"-nocfg", + writeTempFile("AssignmentToUndeclaredVariableTest", + "---- MODULE algo ----\n" + + "CONSTANT c\n" + + "(*\n" + + "--algorithm algo {\n" + + " variables v, w;\n" + + " process (proc \\in {1,2})\n" + + " variable loc\n" + + " {\n" + + " lbl1: loc := 42;\n" + + " lbl2: v := 23;\n" + + " lbl3: w := 174;\n" + + " lbl4: c := \"fail\";\n" + // Assignment to constant + " }\n" + + "}*)\n" + + "====" + )})); + + assertTrue(Arrays.toString(ToolIO.getAllMessages()), + Arrays.asList(ToolIO.getAllMessages()).contains("\nUnrecoverable error:\n" + + " -- Assignment to undeclared variable c\n" + + " at line 12, column 10.\n")); + } + + @Test + public void multiAssignment() throws IOException { + assertEquals(trans.STATUS_EXIT_WITH_ERRORS, trans.runMe(new String[] {"-nocfg", + writeTempFile("AssignmentToUndeclaredVariableTest", + "---- MODULE algo ----\n" + + "CONSTANT c\n" + + "(*\n" + + "--algorithm algo {\n" + + " variables v, w;\n" + + " {\n" + + " v := 42 || w := 23;\n" + + " v := 42 || c := 23;\n" + // Assignment to constant + " }\n" + + "}*)\n" + + "====" + )})); + + assertTrue(Arrays.toString(ToolIO.getAllMessages()), + Arrays.asList(ToolIO.getAllMessages()).contains("\nUnrecoverable error:\n" + + " -- Assignment to undeclared variable c\n" + + " at line 8, column 11.\n")); + } + + @Test + public void macro() throws IOException { + assertEquals(trans.STATUS_EXIT_WITH_ERRORS, trans.runMe(new String[] {"-nocfg", + writeTempFile("AssignmentToUndeclaredVariableTest", + "---- MODULE algo ----\n" + + "CONSTANT c\n" + + "(*\n" + + "--algorithm algo {\n" + + " variables v;\n" + + " macro Mac() { v := \"pmac\";\n c := 42; }\n" + + " {\n" + + " Mac();\n" + // Assignment to constant + " }\n" + + "}*)\n" + + "====" + )})); + + assertTrue(Arrays.toString(ToolIO.getAllMessages()), + Arrays.asList(ToolIO.getAllMessages()).contains("\nUnrecoverable error:\n" + + " -- Assignment to undeclared variable c\n" + + " at line 7, column 2 of macro called at line 9, column 3.\n")); + } + + @Test + public void macroParam() throws IOException { + assertEquals(trans.STATUS_EXIT_WITH_ERRORS, trans.runMe(new String[] {"-nocfg", + writeTempFile("AssignmentToUndeclaredVariableTest", + "---- MODULE algo ----\n" + + "CONSTANT c\n" + + "(*\n" + + "--algorithm algo {\n" + + " variables v;\n" + + " macro Mac2(p) { p := \"pmac\"}\n" + + " {\n" + + " lbl1: Mac2(v);\n" + + " lbl2: Mac2(c);\n" + // Assignment to constant + " }\n" + + "}*)\n" + + "====" + )})); + + assertTrue(Arrays.asList(ToolIO.getAllMessages()).contains("\nUnrecoverable error:\n" + + " -- Assignment to undeclared variable c\n" + + " at line 6, column 19 of macro called at line 9, column 9.\n")); + } + + @Test + public void boundIdentifier() throws IOException { + assertEquals(trans.STATUS_EXIT_WITH_ERRORS, trans.runMe(new String[] {"-nocfg", + writeTempFile("AssignmentToUndeclaredVariableTest", + "---- MODULE algo ----\n" + + "CONSTANT c\n" + + "(*\n" + + "--algorithm algo\n" + + " variables v;\n" + + "begin\n" + + " with n \\in {1,2,3} do\n" + + " v := n;\n" + + " n := 42;\n" + // Assignment to bound identifier! + " end with;" + + "end algorithm\n" + + " *)\n" + + "====" + )})); + + assertTrue(Arrays.asList(ToolIO.getAllMessages()).contains("\nUnrecoverable error:\n" + + " -- Assignment to undeclared variable n\n" + + " at line 9, column 7.\n")); + } + + @Test + public void constant() throws IOException { + assertEquals(trans.STATUS_EXIT_WITH_ERRORS, trans.runMe(new String[] {"-nocfg", + writeTempFile("AssignmentToUndeclaredVariableTest", + "---- MODULE algo ----\n" + + "CONSTANT c\n" + + "(*\n" + + "--algorithm algo\n" + + " variables v;\n" + + "begin\n" + + " v := 23;\n" + + " c := 42;\n" + // Assignment to constant! + "end algorithm\n" + + " *)\n" + + "====" + )})); + + assertTrue(Arrays.asList(ToolIO.getAllMessages()).contains("\nUnrecoverable error:\n" + + " -- Assignment to undeclared variable c\n" + + " at line 8, column 4.\n")); + } +} diff --git a/tlatools/test/pcal/BakeryTest.java b/tlatools/test/pcal/BakeryTest.java new file mode 100644 index 0000000000000000000000000000000000000000..1735eb9b5dac6c4eb09480e7ed78470491d08233 --- /dev/null +++ b/tlatools/test/pcal/BakeryTest.java @@ -0,0 +1,51 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package pcal; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.output.EC; + +public class BakeryTest extends PCalModelCheckerTestCase { + + public BakeryTest() { + super("Bakery", "pcal"); + } + + @Test + public void testSpec() { + assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "1")); + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertFalse(recorder.recorded(EC.GENERAL)); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "1183", "668", "0")); + assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "41")); + + assertZeroUncovered(); + } +} diff --git a/tlatools/test/tlc2/tool/liveness/Test059.java b/tlatools/test/pcal/Bug051003Test.java similarity index 79% rename from tlatools/test/tlc2/tool/liveness/Test059.java rename to tlatools/test/pcal/Bug051003Test.java index dc9f162db9e30a5d6cf43ce352309d4f3c6b501f..3f783025f816e2b52ddd6a5a355e1f2c86716594 100644 --- a/tlatools/test/tlc2/tool/liveness/Test059.java +++ b/tlatools/test/pcal/Bug051003Test.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2015 Microsoft Research. All rights reserved. + * Copyright (c) 2018 Microsoft Research. All rights reserved. * * The MIT License (MIT) * @@ -23,22 +23,29 @@ * Contributors: * Markus Alexander Kuppe - initial API and implementation ******************************************************************************/ +package pcal; -package tlc2.tool.liveness; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; import tlc2.output.EC; -public class Test059 extends ModelCheckerTestCase { +public class Bug051003Test extends PCalModelCheckerTestCase { - public Test059() { - super("test59"); + public Bug051003Test() { + super("bug_05_10_03", "pcal"); } - + + @Test public void testSpec() { - // ModelChecker has finished and generated the expected amount of states - assertTrue(recorder.recorded(EC.TLC_FINISHED)); - assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "5")); assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "1")); - assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "6", "5", "0")); + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertFalse(recorder.recorded(EC.GENERAL)); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "6", "4", "0")); + assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "3")); + + assertZeroUncovered(); } } diff --git a/tlatools/test/pcal/Bug051210aTest.java b/tlatools/test/pcal/Bug051210aTest.java new file mode 100644 index 0000000000000000000000000000000000000000..ada455e80a9e3418194f5b4f70144e779ec390b9 --- /dev/null +++ b/tlatools/test/pcal/Bug051210aTest.java @@ -0,0 +1,92 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package pcal; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.output.EC; + +public class Bug051210aTest extends PCalModelCheckerTestCase { + + public Bug051210aTest() { + super("bug_05_12_10a", "pcal"); + } + + @Test + public void testSpec() { + assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "1")); + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertFalse(recorder.recorded(EC.GENERAL)); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "15", "14", "0")); + assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "14")); + + assertUncovered(" line 134, col 25 to line 137, col 42 of module bug_05_12_10a: 0\n" + + " line 138, col 25 to line 138, col 43 of module bug_05_12_10a: 0\n" + + " line 139, col 22 to line 139, col 32 of module bug_05_12_10a: 0\n" + + " line 140, col 22 to line 140, col 29 of module bug_05_12_10a: 0\n" + + " line 147, col 12 to line 147, col 21 of module bug_05_12_10a: 0\n" + + " line 148, col 12 to line 148, col 22 of module bug_05_12_10a: 0\n" + + " line 149, col 12 to line 149, col 74 of module bug_05_12_10a: 0\n" + + " line 153, col 25 to line 153, col 44 of module bug_05_12_10a: 0\n" + + " line 154, col 25 to line 157, col 42 of module bug_05_12_10a: 0\n" + + " line 158, col 22 to line 158, col 32 of module bug_05_12_10a: 0\n" + + " line 159, col 22 to line 159, col 29 of module bug_05_12_10a: 0\n" + + " line 167, col 33 to line 167, col 50 of module bug_05_12_10a: 0\n" + + " line 168, col 33 to line 168, col 43 of module bug_05_12_10a: 0\n" + + " line 173, col 12 to line 173, col 21 of module bug_05_12_10a: 0\n" + + " line 174, col 12 to line 174, col 22 of module bug_05_12_10a: 0\n" + + " line 175, col 12 to line 175, col 74 of module bug_05_12_10a: 0\n" + + " line 196, col 25 to line 196, col 43 of module bug_05_12_10a: 0\n" + + " line 197, col 25 to line 200, col 42 of module bug_05_12_10a: 0\n" + + " line 201, col 22 to line 201, col 33 of module bug_05_12_10a: 0\n" + + " line 202, col 22 to line 202, col 32 of module bug_05_12_10a: 0\n" + + " line 203, col 22 to line 203, col 48 of module bug_05_12_10a: 0\n" + + " line 204, col 11 to line 204, col 64 of module bug_05_12_10a: 0\n" + + " line 207, col 12 to line 207, col 21 of module bug_05_12_10a: 0\n" + + " line 208, col 12 to line 208, col 22 of module bug_05_12_10a: 0\n" + + " line 209, col 12 to line 209, col 74 of module bug_05_12_10a: 0\n" + + " line 232, col 22 to line 232, col 33 of module bug_05_12_10a: 0\n" + + " line 233, col 22 to line 233, col 50 of module bug_05_12_10a: 0\n" + + " line 241, col 12 to line 241, col 19 of module bug_05_12_10a: 0\n" + + " line 242, col 12 to line 242, col 22 of module bug_05_12_10a: 0\n" + + " line 243, col 12 to line 243, col 75 of module bug_05_12_10a: 0\n" + + " line 248, col 11 to line 248, col 30 of module bug_05_12_10a: 0\n" + + " line 249, col 11 to line 249, col 34 of module bug_05_12_10a: 0\n" + + " line 250, col 11 to line 250, col 30 of module bug_05_12_10a: 0\n" + + " line 251, col 11 to line 251, col 64 of module bug_05_12_10a: 0\n" + + " line 256, col 11 to line 256, col 30 of module bug_05_12_10a: 0\n" + + " line 257, col 11 to line 257, col 36 of module bug_05_12_10a: 0\n" + + " line 258, col 11 to line 258, col 30 of module bug_05_12_10a: 0\n" + + " line 259, col 11 to line 259, col 63 of module bug_05_12_10a: 0\n" + + " line 272, col 12 to line 272, col 31 of module bug_05_12_10a: 0\n" + + " line 273, col 12 to line 273, col 35 of module bug_05_12_10a: 0\n" + + " line 274, col 12 to line 274, col 31 of module bug_05_12_10a: 0\n" + + " line 275, col 12 to line 275, col 65 of module bug_05_12_10a: 0"); + } +} diff --git a/tlatools/test/pcal/Bug051216bTest.java b/tlatools/test/pcal/Bug051216bTest.java new file mode 100644 index 0000000000000000000000000000000000000000..2de30905f074678c0ec5eb4fbf0f4456c89024d0 --- /dev/null +++ b/tlatools/test/pcal/Bug051216bTest.java @@ -0,0 +1,51 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package pcal; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.output.EC; + +public class Bug051216bTest extends PCalModelCheckerTestCase { + + public Bug051216bTest() { + super("bug_05_12_16b", "pcal"); + } + + @Test + public void testSpec() { + assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "1")); + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertFalse(recorder.recorded(EC.GENERAL)); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "146", "64", "0")); + assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "10")); + + assertZeroUncovered(); + } +} diff --git a/tlatools/test/pcal/Bug051231Test.java b/tlatools/test/pcal/Bug051231Test.java new file mode 100644 index 0000000000000000000000000000000000000000..b8995126394988c6b6ce8a031d2bf99777739dc9 --- /dev/null +++ b/tlatools/test/pcal/Bug051231Test.java @@ -0,0 +1,51 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package pcal; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.output.EC; + +public class Bug051231Test extends PCalModelCheckerTestCase { + + public Bug051231Test() { + super("bug_05_12_31", "pcal"); + } + + @Test + public void testSpec() { + assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "1")); + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertFalse(recorder.recorded(EC.GENERAL)); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "4", "3", "0")); + assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "3")); + + assertZeroUncovered(); + } +} diff --git a/tlatools/test/pcal/Bug060125Test.java b/tlatools/test/pcal/Bug060125Test.java new file mode 100644 index 0000000000000000000000000000000000000000..725c05b6250335a9fd8581e490db25007804c020 --- /dev/null +++ b/tlatools/test/pcal/Bug060125Test.java @@ -0,0 +1,58 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package pcal; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.output.EC; + +public class Bug060125Test extends PCalModelCheckerTestCase { + + public Bug060125Test() { + super("bug_06_01_25", "pcal"); + } + + @Test + public void testSpec() { + assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "1")); + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertFalse(recorder.recorded(EC.GENERAL)); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "130", "64", "0")); + assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "15")); + + assertUncovered("line 138, col 27 to line 138, col 53 of module bug_06_01_25: 0\n" + + "line 139, col 27 to line 139, col 53 of module bug_06_01_25: 0\n" + + "line 140, col 27 to line 140, col 53 of module bug_06_01_25: 0\n" + + "line 162, col 27 to line 162, col 53 of module bug_06_01_25: 0\n" + + "line 163, col 27 to line 163, col 53 of module bug_06_01_25: 0\n" + + "line 164, col 27 to line 164, col 53 of module bug_06_01_25: 0\n" + + "line 165, col 27 to line 167, col 75 of module bug_06_01_25: 0\n" + + "line 168, col 27 to line 168, col 58 of module bug_06_01_25: 0"); + } +} diff --git a/tlatools/test/pcal/CBakeryTest.java b/tlatools/test/pcal/CBakeryTest.java new file mode 100644 index 0000000000000000000000000000000000000000..31b4ad2dbd6b22f82d16df947adbeabc10b0f84a --- /dev/null +++ b/tlatools/test/pcal/CBakeryTest.java @@ -0,0 +1,51 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package pcal; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.output.EC; + +public class CBakeryTest extends PCalModelCheckerTestCase { + + public CBakeryTest() { + super("CBakery", "pcal"); + } + + @Test + public void testSpec() { + assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "1")); + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertFalse(recorder.recorded(EC.GENERAL)); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "867", "486", "0")); + assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "31")); + + assertZeroUncovered(); + } +} diff --git a/tlatools/test/pcal/CCallReturn1Test.java b/tlatools/test/pcal/CCallReturn1Test.java new file mode 100644 index 0000000000000000000000000000000000000000..4285b5862412f57f1686b1b2df8d137b2d2c5a14 --- /dev/null +++ b/tlatools/test/pcal/CCallReturn1Test.java @@ -0,0 +1,52 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package pcal; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.output.EC; + +public class CCallReturn1Test extends PCalModelCheckerTestCase { + + public CCallReturn1Test() { + super("CCallReturn1", "pcal", new String[] {"-wf", "-termination"}); + } + + @Test + public void testSpec() { + assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "1")); + assertTrue(recorder.recordedWithStringValues(EC.TLC_CHECKING_TEMPORAL_PROPS, "complete", "8")); + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertFalse(recorder.recorded(EC.GENERAL)); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "9", "8", "0")); + assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "8")); + + assertZeroUncovered(); + } +} diff --git a/tlatools/test/pcal/CDiningPhilosophersTest.java b/tlatools/test/pcal/CDiningPhilosophersTest.java new file mode 100644 index 0000000000000000000000000000000000000000..2435f924a0ccd3bfacc94fd034c6489a33a8fa5d --- /dev/null +++ b/tlatools/test/pcal/CDiningPhilosophersTest.java @@ -0,0 +1,51 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package pcal; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.output.EC; + +public class CDiningPhilosophersTest extends PCalModelCheckerTestCase { + + public CDiningPhilosophersTest() { + super("CDiningPhilosophers", "pcal", new String[] {"-sf"}); + } + + @Test + public void testSpec() { + assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "1")); + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertFalse(recorder.recorded(EC.GENERAL)); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "301", "118", "0")); + assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "14")); + + assertZeroUncovered(); + } +} diff --git a/tlatools/test/pcal/CEither1Test.java b/tlatools/test/pcal/CEither1Test.java new file mode 100644 index 0000000000000000000000000000000000000000..270087db26b95f3fb7cd0c3ddb8ec0569cf34938 --- /dev/null +++ b/tlatools/test/pcal/CEither1Test.java @@ -0,0 +1,52 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package pcal; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.output.EC; + +public class CEither1Test extends PCalModelCheckerTestCase { + + public CEither1Test() { + super("CEither1", "pcal", new String[] {"-wf", "-termination"}); + } + + @Test + public void testSpec() { + assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "1")); + assertTrue(recorder.recordedWithStringValues(EC.TLC_CHECKING_TEMPORAL_PROPS, "complete", "7")); + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertFalse(recorder.recorded(EC.GENERAL)); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "9", "7", "0")); + assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "4")); + + assertZeroUncovered(); + } +} diff --git a/tlatools/test/pcal/CMultiprocDefineTest.java b/tlatools/test/pcal/CMultiprocDefineTest.java new file mode 100644 index 0000000000000000000000000000000000000000..d1df2feee8d3cd0a4d944432916a95ab7a2ac70f --- /dev/null +++ b/tlatools/test/pcal/CMultiprocDefineTest.java @@ -0,0 +1,52 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package pcal; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.output.EC; + +public class CMultiprocDefineTest extends PCalModelCheckerTestCase { + + public CMultiprocDefineTest() { + super("CMultiprocDefine", "pcal", new String[] {"-wf", "-termination"}); + } + + @Test + public void testSpec() { + assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "1")); + assertTrue(recorder.recordedWithStringValues(EC.TLC_CHECKING_TEMPORAL_PROPS, "complete", "8")); + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertFalse(recorder.recorded(EC.GENERAL)); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "14", "8", "0")); + assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "4")); + + assertZeroUncovered(); + } +} diff --git a/tlatools/test/pcal/CallGotoUnlabeledTest.java b/tlatools/test/pcal/CallGotoUnlabeledTest.java new file mode 100644 index 0000000000000000000000000000000000000000..b2a535996eecdc771d31f58c92c4bf41c487d657 --- /dev/null +++ b/tlatools/test/pcal/CallGotoUnlabeledTest.java @@ -0,0 +1,39 @@ +package pcal; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import java.util.Arrays; + +import org.junit.Test; + +import tlc2.tool.CommonTestCase; +import util.ToolIO; + +public class CallGotoUnlabeledTest extends PCalTest { + + // https://groups.google.com/forum/#!topic/tlaplus/6M1oFOtN-5k/discussion + + @Test + public void test() { + ToolIO.setMode(ToolIO.TOOL); + + final String fileName = "CallGotoUnlabeledTest.tla"; + + assertEquals(0, trans.runMe(new String[] {"-nocfg", "-unixEOL", "-reportLabels", CommonTestCase.BASE_PATH + fileName})); + final TLAtoPCalMapping mapping = PcalParams.tlaPcalMapping; + assertNotNull(mapping); + + final String[] messages = ToolIO.getAllMessages(); + assertTrue(Arrays.toString(messages), messages.length == 6); + + assertEquals("The following labels were added:", messages[0]); + assertEquals(" Lbl_1 at line 10, column 3", messages[1]); + assertEquals(" Lbl_2 at line 19, column 3", messages[2]); + assertEquals("Parsing completed.", messages[3]); + assertEquals("Translation completed.", messages[4]); + // Ignore last line "New file ...." because it depends on from where the test is executed. +// assertEquals("New file test-model/" + fileName + " written.", messages[5]); + } +} diff --git a/tlatools/test/pcal/CallReturn1Test.java b/tlatools/test/pcal/CallReturn1Test.java new file mode 100644 index 0000000000000000000000000000000000000000..40b0b20d386ab06e9410102977f90771b4777a04 --- /dev/null +++ b/tlatools/test/pcal/CallReturn1Test.java @@ -0,0 +1,52 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package pcal; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.output.EC; + +public class CallReturn1Test extends PCalModelCheckerTestCase { + + public CallReturn1Test() { + super("CallReturn1", "pcal", new String[] {"-wf", "-termination"}); + } + + @Test + public void testSpec() { + assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "1")); + assertTrue(recorder.recordedWithStringValues(EC.TLC_CHECKING_TEMPORAL_PROPS, "complete", "8")); + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertFalse(recorder.recorded(EC.GENERAL)); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "9", "8", "0")); + assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "8")); + + assertZeroUncovered(); + } +} diff --git a/tlatools/test/pcal/CallReturn2Test.java b/tlatools/test/pcal/CallReturn2Test.java new file mode 100644 index 0000000000000000000000000000000000000000..f3be012b0aeec3235a4b28cd6de70318446c71e1 --- /dev/null +++ b/tlatools/test/pcal/CallReturn2Test.java @@ -0,0 +1,63 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package pcal; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.output.EC; + +public class CallReturn2Test extends PCalModelCheckerTestCase { + + public CallReturn2Test() { + super("CallReturn2", "pcal", new String[] {"-wf", "-termination"}); + } + + @Test + public void testSpec() { + assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "1")); + assertTrue(recorder.recordedWithStringValues(EC.TLC_CHECKING_TEMPORAL_PROPS, "complete", "11")); + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertFalse(recorder.recorded(EC.GENERAL)); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "12", "11", "0")); + assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "11")); + + assertUncovered(" line 129, col 10 to line 129, col 15 of module CallReturn2: 0\n" + + " line 130, col 10 to line 130, col 19 of module CallReturn2: 0\n" + + " line 131, col 10 to line 131, col 65 of module CallReturn2: 0\n" + + " line 134, col 13 to line 134, col 18 of module CallReturn2: 0\n" + + " line 135, col 13 to line 138, col 36 of module CallReturn2: 0\n" + + " line 139, col 13 to line 139, col 30 of module CallReturn2: 0\n" + + " line 140, col 10 to line 140, col 19 of module CallReturn2: 0\n" + + " line 141, col 10 to line 141, col 55 of module CallReturn2: 0\n" + + " line 147, col 10 to line 147, col 29 of module CallReturn2: 0\n" + + " line 148, col 10 to line 148, col 27 of module CallReturn2: 0\n" + + " line 149, col 10 to line 149, col 29 of module CallReturn2: 0\n" + + " line 150, col 10 to line 150, col 58 of module CallReturn2: 0"); + } +} diff --git a/tlatools/test/pcal/DetlefSpecTest.java b/tlatools/test/pcal/DetlefSpecTest.java new file mode 100644 index 0000000000000000000000000000000000000000..fdbb095f712f61624bc1fce46d61f0ff294ab5ae --- /dev/null +++ b/tlatools/test/pcal/DetlefSpecTest.java @@ -0,0 +1,51 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package pcal; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.output.EC; + +public class DetlefSpecTest extends PCalModelCheckerTestCase { + + public DetlefSpecTest() { + super("DetlefSpec", "pcal", new String[] {"-wf"}); + } + + @Test + public void testSpec() { + assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "1")); + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertFalse(recorder.recorded(EC.GENERAL)); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "31", "15", "0")); + assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "8")); + + assertZeroUncovered(); + } +} diff --git a/tlatools/test/pcal/DetlefsTest.java b/tlatools/test/pcal/DetlefsTest.java new file mode 100644 index 0000000000000000000000000000000000000000..6d23fbf8dec62a95f5da9452442ebe84316eeba5 --- /dev/null +++ b/tlatools/test/pcal/DetlefsTest.java @@ -0,0 +1,51 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package pcal; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.output.EC; + +public class DetlefsTest extends PCalModelCheckerTestCase { + + public DetlefsTest() { + super("Detlefs", "pcal"); + } + + @Test + public void testSpec() { + assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "1")); + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertFalse(recorder.recorded(EC.GENERAL)); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "2127012", "952912", "0")); + assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "82")); + + assertZeroUncovered(); + } +} diff --git a/tlatools/test/pcal/Dijkstra1Test.java b/tlatools/test/pcal/Dijkstra1Test.java new file mode 100644 index 0000000000000000000000000000000000000000..7be46c0a75c086a374858d8f5f751f1fec6770c6 --- /dev/null +++ b/tlatools/test/pcal/Dijkstra1Test.java @@ -0,0 +1,78 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package pcal; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.output.EC; + +public class Dijkstra1Test extends PCalModelCheckerTestCase { + + public Dijkstra1Test() { + super("Dijkstra1", "pcal", new String[] {"-wf"}); + } + + @Test + public void testSpec() { + assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "625")); + assertTrue(recorder.recordedWithStringValues(EC.TLC_CHECKING_TEMPORAL_PROPS, "complete", "5510")); + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertFalse(recorder.recorded(EC.GENERAL)); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "16775", "5510", "0")); + assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "10")); + + assertZeroUncovered(); + } +} +/* +C:\lamport\tla\pluscal>java -mx1000m -cp "c:/lamport/tla/newtools/tla2-inria-workspace/tla2-inria/tlatools/class" tlc2.TLC -cleanup Dijkstra1.tla +TLC2 Version 2.05 of 18 May 2012 +Running in Model-Checking mode. +Parsing file Dijkstra1.tla +Parsing file C:\lamport\tla\newtools\tla2-inria-workspace\tla2-inria\tlatools\class\tla2sany\StandardModules\FiniteSets.tla +Parsing file C:\lamport\tla\newtools\tla2-inria-workspace\tla2-inria\tlatools\class\tla2sany\StandardModules\Naturals.tla +Parsing file C:\lamport\tla\newtools\tla2-inria-workspace\tla2-inria\tlatools\class\tla2sany\StandardModules\Sequences.tla +Semantic processing of module Naturals +Semantic processing of module Sequences +Semantic processing of module FiniteSets +Semantic processing of module Dijkstra1 +Starting... (2012-08-10 17:39:54) +Implied-temporal checking--satisfiability problem has 1 branches. +Computing initial states... +Finished computing initial states: 625 distinct states generated. +Checking temporal properties for the complete state space... +Model checking completed. No error has been found. + Estimates of the probability that TLC did not check all reachable states + because two distinct states had the same fingerprint: + calculated (optimistic): val = 3.4E-12 + based on the actual fingerprints: val = 5.3E-14 +16775 states generated, 5510 distinct states found, 0 states left on queue. +The depth of the complete state graph search is 10. +Finished. (2012-08-10 17:39:55) +*/ \ No newline at end of file diff --git a/tlatools/test/pcal/DiningPhilosophersTest.java b/tlatools/test/pcal/DiningPhilosophersTest.java new file mode 100644 index 0000000000000000000000000000000000000000..57349d69ab7811ca7b458fc9ea9a731abb851c90 --- /dev/null +++ b/tlatools/test/pcal/DiningPhilosophersTest.java @@ -0,0 +1,52 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package pcal; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.output.EC; + +public class DiningPhilosophersTest extends PCalModelCheckerTestCase { + + public DiningPhilosophersTest() { + super("DiningPhilosophers", "pcal", new String[] {"-sf"}); + } + + @Test + public void testSpec() { + assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "1")); + assertTrue(recorder.recordedWithStringValues(EC.TLC_CHECKING_TEMPORAL_PROPS, "complete", "118")); + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertFalse(recorder.recorded(EC.GENERAL)); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "301", "118", "0")); + assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "14")); + + assertZeroUncovered(); + } +} diff --git a/tlatools/test/pcal/DivergenceTest.java b/tlatools/test/pcal/DivergenceTest.java new file mode 100644 index 0000000000000000000000000000000000000000..a6db76aebaafc8ba75f24406a93fcb53dfb75a97 --- /dev/null +++ b/tlatools/test/pcal/DivergenceTest.java @@ -0,0 +1,315 @@ +/******************************************************************************* + * Copyright (c) 2020 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package pcal; + +import java.io.File; +import java.io.IOException; + +import org.junit.Ignore; +import org.junit.Test; + +import tla2sany.drivers.SANY; +import util.TestPrintStream; +import util.ToolIO; + +// Plz can haz https://openjdk.java.net/jeps/355 ? +public class DivergenceTest extends PCalTest { + @Test + public void divergenceTest00() throws IOException { + final String filename = "divergenceTest001" + System.currentTimeMillis(); + final String absolutePath = writeFile(System.getProperty("java.io.tmpdir") + File.separator + filename, + "---- MODULE " + filename + " ----\n" + + "\n" + + "(*\n" + + "--algorithm a\n" + + "begin\n" + + " skip;\n" + + "end algorithm; *)\n" + + "\\* BEGIN TRANSLATION\n" + + "VARIABLE pc\n" + + "\n" + + "vars == << pc >>\n" + + "\n" + + "Init == /\\ pc = \"Lbl_1\"\n" + + "\n" + + "Lbl_1 == /\\ pc = \"Lbl_1\"\n" + + " /\\ TRUE\n" + + " /\\ pc' = \"Done\"\n" + + "\n" + + "(* Allow infinite stuttering to prevent deadlock on termination. *)\n" + + "Terminating == pc = \"Done\" /\\ UNCHANGED vars\n" + + "\n" + + "Next == Lbl_1\n" + + " \\/ Terminating\n" + + "\n" + + "Spec == Init /\\ [][Next]_vars\n" + + "\n" + + "Termination == <>(pc = \"Done\")\n" + + "\n" + + "\\* END TRANSLATION\n" + + "\n========================="); + // Parse with SANY and check for errors (collects parse errors into ToolIO.out) + final TestPrintStream testPrintStream = new TestPrintStream(); + ToolIO.out = testPrintStream; + SANY.SANYmain(new String[] { absolutePath }); + testPrintStream.assertSubstring("Semantic processing of module " + filename); + testPrintStream.assertNoSubstring("!! WARNING " + filename); + } + + @Test + public void divergenceTest01() throws IOException { + final String filename = "divergenceTest01" + System.currentTimeMillis(); + final String absolutePath = writeFile(System.getProperty("java.io.tmpdir") + File.separator + filename, + "---- MODULE " + filename + " ----\n" + + "\n" + + "(*\n" + + "--algorithm a\n" + + "begin\n" + + " skip;\n" + + "end algorithm; *)\n" + + "\\* BEGIN TRANSLATION (chksum(PCal) \\in STRING /\\ chksum(TLA+) \\in STRING)\n" + + "VARIABLE pc\n" + + "\n" + + "vars == << pc >>\n" + + "\n" + + "Init == /\\ pc = \"Lbl_1\"\n" + + "\n" + + "Lbl_1 == /\\ pc = \"Lbl_1\"\n" + + " /\\ TRUE\n" + + " /\\ pc' = \"Done\"\n" + + "\n" + + "(* Allow infinite stuttering to prevent deadlock on termination. *)\n" + + "Terminating == pc = \"Done\" /\\ UNCHANGED vars\n" + + "\n" + + "Next == Lbl_1\n" + + " \\/ Terminating\n" + + "\n" + + "Spec == Init /\\ [][Next]_vars\n" + + "\n" + + "Termination == <>(pc = \"Done\")\n" + + "\n" + + "\\* END TRANSLATION\n" + + "\n========================="); + // Parse with SANY and check for errors (collects parse errors into ToolIO.out) + final TestPrintStream testPrintStream = new TestPrintStream(); + ToolIO.out = testPrintStream; + SANY.SANYmain(new String[] { absolutePath }); + testPrintStream.assertSubstring("Semantic processing of module " + filename); + testPrintStream.assertNoSubstring("!! WARNING " + filename); + } + + @Test + public void divergenceTest02() throws IOException { + final String filename = "divergenceTest02" + System.currentTimeMillis(); + final String absolutePath = writeFile(System.getProperty("java.io.tmpdir") + File.separator + filename, + "---- MODULE " + filename + " ----\n" + + "\n" + + "(*\n" + + "--algorithm a\n" + + "begin\n" + + " print \"msg\";\n" + // PlusCal diverged + "end algorithm; *)\n" + + "\\* BEGIN TRANSLATION (chksum(PCal) = \"4860ac97\" /\\ chksum(TLA+) = \"af3d9146\")\n" + + "VARIABLE pc\n" + + "\n" + + "vars == << pc >>\n" + + "\n" + + "Init == /\\ pc = \"Lbl_1\"\n" + + "\n" + + "Lbl_1 == /\\ pc = \"Lbl_1\"\n" + + " /\\ TRUE\n" + + " /\\ pc' = \"Done\"\n" + + "\n" + + "(* Allow infinite stuttering to prevent deadlock on termination. *)\n" + + "Terminating == pc = \"Done\" /\\ UNCHANGED vars\n" + + "\n" + + "Next == Lbl_1\n" + + " \\/ Terminating\n" + + "\n" + + "Spec == Init /\\ [][Next]_vars\n" + + "\n" + + "Termination == <>(pc = \"Done\")\n" + + "\n" + + "\\* END TRANSLATION\n" + + "\n========================="); + // Parse with SANY and check for errors (collects parse errors into ToolIO.out) + final TestPrintStream testPrintStream = new TestPrintStream(); + ToolIO.out = testPrintStream; + SANY.SANYmain(new String[] { absolutePath }); + testPrintStream.assertSubstring("Semantic processing of module " + filename); + testPrintStream.assertSubstring(String.format( + "!! WARNING: The PlusCal algorithm in module %s has changed since its last translation.", + filename)); + } + + @Test + public void divergenceTest03() throws IOException { + final String filename = "divergenceTest03" + System.currentTimeMillis(); + final String absolutePath = writeFile(System.getProperty("java.io.tmpdir") + File.separator + filename, + "---- MODULE " + filename + " ----\n" + + "\n" + + "(*\n" + + "--algorithm a\n" + + "begin\n" + + " skip;\n" + + "end algorithm; *)\n" + + "\\* BEGIN TRANSLATION (checksum(PlusCal) = \"4860ac97\" /\\ ChkSum(tla+) = \"af3d9146\")\n" + + "VARIABLE pc\n" + + "\n" + + "vars == << pc >>\n" + + "\n" + + "Init == /\\ pc = \"Lbl_1\"\n" + + "\n" + + "Lbl_1 == /\\ pc = \"Lbl_1\"\n" + + " /\\ TRUE\n" + + " /\\ pc' = \"Done\"\n" + + "\n" + + "(* Allow infinite stuttering to prevent deadlock on termination. *)\n" + + "Terminating == pc = \"Done\" /\\ UNCHANGED vars\n" + + "\n" + + "Next == Lbl_1\n" + // TLA+ diverged. + "\n" + + "Spec == Init /\\ [][Next]_vars\n" + + "\n" + + "Termination == <>(pc = \"Done\")\n" + + "\n" + + "\\* END TRANSLATION\n" + + "\n========================="); + // Parse with SANY and check for errors (collects parse errors into ToolIO.out) + final TestPrintStream testPrintStream = new TestPrintStream(); + ToolIO.out = testPrintStream; + SANY.SANYmain(new String[] { absolutePath }); + testPrintStream.assertSubstring("Semantic processing of module " + filename); + testPrintStream.assertSubstring(String.format( + "!! WARNING: The TLA+ translation in module %s has changed since its last translation.", + filename)); + } + + @Test + public void divergenceTest04() throws IOException { + final String filename = "divergenceTest04" + System.currentTimeMillis(); + final String absolutePath = writeFile(System.getProperty("java.io.tmpdir") + File.separator + filename, + "---- MODULE " + filename + " ----\n" + + "\n" + + "(*\n" + + "--algorithm a\n" + + "begin\n" + + " print \"msg\";\n" + // PlusCal diverged + "end algorithm; *)\n" + + "\\* BEGIN TRANSLATION (checksum(PlusCal) = \"4860ac97\" /\\ ChkSum(tla+) = \"af3d9146\") \n" + + "VARIABLE pc\n" + + "\n" + + "vars == << pc >>\n" + + "\n" + + "Init == /\\ pc = \"Lbl_1\"\n" + + "\n" + + "Lbl_1 == /\\ pc = \"Lbl_1\"\n" + + " /\\ TRUE\n" + + " /\\ pc' = \"Done\"\n" + + "\n" + + "(* Allow infinite stuttering to prevent deadlock on termination. *)\n" + + "Terminating == pc = \"Done\" /\\ UNCHANGED vars\n" + + "\n" + + "Next == Lbl_1\n" + // TLA+ diverged. + "\n" + + "Spec == Init /\\ [][Next]_vars\n" + + "\n" + + "Termination == <>(pc = \"Done\")\n" + + "\n" + + "\\* END TRANSLATION\n" + + "\n========================="); + // Parse with SANY and check for errors (collects parse errors into ToolIO.out) + final TestPrintStream testPrintStream = new TestPrintStream(); + ToolIO.out = testPrintStream; + SANY.SANYmain(new String[] { absolutePath }); + testPrintStream.assertSubstring("Semantic processing of module " + filename); + testPrintStream.assertSubstring(String.format( + "!! WARNING: The PlusCal algorithm and its TLA+ translation in module %s filename since the last translation.", + filename)); + } + @Test + public void divergenceTest05() throws IOException { + final String filename = "divergenceTest04" + System.currentTimeMillis(); + final String absolutePath = writeFile(System.getProperty("java.io.tmpdir") + File.separator + filename, + "---- MODULE " + filename + " ----\n" + + "\n" + + "(*\n" + + "--algorithm a\n" + + "begin\n" + + " print \"msg\";\n" + // PlusCal diverged + "end algorithm; *)\n" + + "\\* BEGIN TRANSLATION (checksum(PlusCal) \\in STRING /\\ ChkSum(tla+) \\in STRING) \n" + + "VARIABLE pc\n" + + "\n" + + "vars == << pc >>\n" + + "\n" + + "Init == /\\ pc = \"Lbl_1\"\n" + + "\n" + + "Lbl_1 == /\\ pc = \"Lbl_1\"\n" + + " /\\ TRUE\n" + + " /\\ pc' = \"Done\"\n" + + "\n" + + "(* Allow infinite stuttering to prevent deadlock on termination. *)\n" + + "Terminating == pc = \"Done\" /\\ UNCHANGED vars\n" + + "\n" + + "Next == Lbl_1\n" + // TLA+ diverged. + "\n" + + "Spec == Init /\\ [][Next]_vars\n" + + "\n" + + "Termination == <>(pc = \"Done\")\n" + + "\n" + + "\\* END TRANSLATION\n" + + "\n========================="); + // Parse with SANY and check for errors (collects parse errors into ToolIO.out) + final TestPrintStream testPrintStream = new TestPrintStream(); + ToolIO.out = testPrintStream; + SANY.SANYmain(new String[] { absolutePath }); + testPrintStream.assertSubstring("Semantic processing of module " + filename); + testPrintStream.assertNoSubstring(String.format( + "!! WARNING:", + filename)); + } + + @Test + public void divergenceTest06() throws IOException { + final String filename = "divergenceTest05" + System.currentTimeMillis(); + final String absolutePath = writeFile(System.getProperty("java.io.tmpdir") + File.separator + filename, + "---- MODULE " + filename + " ----\n" + + "\n" + + "(*\n" + + "--algorithm a\n" + + "begin\n" + + " skip;\n" + + "end algorithm; *)\n" + + "\n========================="); + // Parse with SANY and check for errors (collects parse errors into ToolIO.out) + final TestPrintStream testPrintStream = new TestPrintStream(); + ToolIO.out = testPrintStream; + SANY.SANYmain(new String[] { absolutePath }); + testPrintStream.assertSubstring("Semantic processing of module " + filename); + testPrintStream.assertNoSubstring("!! WARNING " + filename); + } +} diff --git a/tlatools/test/pcal/Either1Test.java b/tlatools/test/pcal/Either1Test.java new file mode 100644 index 0000000000000000000000000000000000000000..47558d6d0d9b30d4fb36a0e12034ce21e1245eeb --- /dev/null +++ b/tlatools/test/pcal/Either1Test.java @@ -0,0 +1,52 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package pcal; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.output.EC; + +public class Either1Test extends PCalModelCheckerTestCase { + + public Either1Test() { + super("Either1", "pcal", new String[] {"-wf", "-termination"}); + } + + @Test + public void testSpec() { + assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "1")); + assertTrue(recorder.recordedWithStringValues(EC.TLC_CHECKING_TEMPORAL_PROPS, "complete", "7")); + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertFalse(recorder.recorded(EC.GENERAL)); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "9", "7", "0")); + assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "4")); + + assertZeroUncovered(); + } +} diff --git a/tlatools/test/pcal/Either2Test.java b/tlatools/test/pcal/Either2Test.java new file mode 100644 index 0000000000000000000000000000000000000000..72809ff31b202d0617108231798953201efa7cbc --- /dev/null +++ b/tlatools/test/pcal/Either2Test.java @@ -0,0 +1,52 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package pcal; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.output.EC; + +public class Either2Test extends PCalModelCheckerTestCase { + + public Either2Test() { + super("Either2", "pcal", new String[] {"-wf", "-termination"}); + } + + @Test + public void testSpec() { + assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "1")); + assertTrue(recorder.recordedWithStringValues(EC.TLC_CHECKING_TEMPORAL_PROPS, "complete", "7")); + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertFalse(recorder.recorded(EC.GENERAL)); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "9", "7", "0")); + assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "4")); + + assertZeroUncovered(); + } +} diff --git a/tlatools/test/pcal/Either3Test.java b/tlatools/test/pcal/Either3Test.java new file mode 100644 index 0000000000000000000000000000000000000000..37494778c877542f53fbc5ab4831e574ccbd50da --- /dev/null +++ b/tlatools/test/pcal/Either3Test.java @@ -0,0 +1,52 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package pcal; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.output.EC; + +public class Either3Test extends PCalModelCheckerTestCase { + + public Either3Test() { + super("Either3", "pcal", new String[] {"-wf", "-termination"}); + } + + @Test + public void testSpec() { + assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "1")); + assertTrue(recorder.recordedWithStringValues(EC.TLC_CHECKING_TEMPORAL_PROPS, "complete", "9")); + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertFalse(recorder.recorded(EC.GENERAL)); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "12", "9", "0")); + assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "4")); + + assertZeroUncovered(); + } +} diff --git a/tlatools/test/pcal/Either4Test.java b/tlatools/test/pcal/Either4Test.java new file mode 100644 index 0000000000000000000000000000000000000000..8d2eabd7bd20b36e5f736f976d81e2fc376502d6 --- /dev/null +++ b/tlatools/test/pcal/Either4Test.java @@ -0,0 +1,52 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package pcal; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.output.EC; + +public class Either4Test extends PCalModelCheckerTestCase { + + public Either4Test() { + super("Either4", "pcal", new String[] {"-wf", "-termination"}); + } + + @Test + public void testSpec() { + assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "1")); + assertTrue(recorder.recordedWithStringValues(EC.TLC_CHECKING_TEMPORAL_PROPS, "complete", "81")); + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertFalse(recorder.recorded(EC.GENERAL)); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "154", "81", "0")); + assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "7")); + + assertZeroUncovered(); + } +} diff --git a/tlatools/test/pcal/Either5Test.java b/tlatools/test/pcal/Either5Test.java new file mode 100644 index 0000000000000000000000000000000000000000..bb283cade8439d6fef412cb4314942ff2bb08828 --- /dev/null +++ b/tlatools/test/pcal/Either5Test.java @@ -0,0 +1,52 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package pcal; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.output.EC; + +public class Either5Test extends PCalModelCheckerTestCase { + + public Either5Test() { + super("Either5", "pcal", new String[] {"-wf", "-termination"}); + } + + @Test + public void testSpec() { + assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "1")); + assertTrue(recorder.recordedWithStringValues(EC.TLC_CHECKING_TEMPORAL_PROPS, "complete", "7")); + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertFalse(recorder.recorded(EC.GENERAL)); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "10", "7", "0")); + assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "3")); + + assertZeroUncovered(); + } +} diff --git a/tlatools/test/pcal/Euclid2Test.java b/tlatools/test/pcal/Euclid2Test.java new file mode 100644 index 0000000000000000000000000000000000000000..d44252a5aa71d5f279f8b01b3960ba0a6567292a --- /dev/null +++ b/tlatools/test/pcal/Euclid2Test.java @@ -0,0 +1,52 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package pcal; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.output.EC; + +public class Euclid2Test extends PCalModelCheckerTestCase { + + public Euclid2Test() { + super("Euclid2", "pcal", new String[] {"-wf", "-termination"}); + } + + @Test + public void testSpec() { + assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "500")); + assertTrue(recorder.recordedWithStringValues(EC.TLC_CHECKING_TEMPORAL_PROPS, "complete", "18852")); + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertFalse(recorder.recorded(EC.GENERAL)); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "19352", "18852", "0")); + assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "90")); + + assertZeroUncovered(); + } +} diff --git a/tlatools/test/pcal/Euclid3Test.java b/tlatools/test/pcal/Euclid3Test.java new file mode 100644 index 0000000000000000000000000000000000000000..1e41f3cc2da343165e01a9159ef4ae506f363d5a --- /dev/null +++ b/tlatools/test/pcal/Euclid3Test.java @@ -0,0 +1,53 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package pcal; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.output.EC; + +public class Euclid3Test extends PCalModelCheckerTestCase { + + public Euclid3Test() { + super("Euclid3", "pcal", new String[] {"-wf", "-termination"}); + } + + @Test + public void testSpec() { + assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "3")); + assertTrue(recorder.recordedWithStringValues(EC.TLC_CHECKING_TEMPORAL_PROPS, "complete", "94")); + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertFalse(recorder.recorded(EC.GENERAL)); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "97", "94", "0")); + assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "50")); + + assertUncovered("line 38, col 35 to line 38, col 40 of module Euclid3: 0\n" + + "line 39, col 35 to line 39, col 40 of module Euclid3: 0"); + } +} diff --git a/tlatools/test/pcal/EuclidTest.java b/tlatools/test/pcal/EuclidTest.java new file mode 100644 index 0000000000000000000000000000000000000000..f865501ea24ae96939cd5613880a86f33291f49a --- /dev/null +++ b/tlatools/test/pcal/EuclidTest.java @@ -0,0 +1,52 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package pcal; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.output.EC; + +public class EuclidTest extends PCalModelCheckerTestCase { + + public EuclidTest() { + super("Euclid", "pcal", new String[] {"-wf", "-termination"}); + } + + @Test + public void testSpec() { + assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "400")); + assertTrue(recorder.recordedWithStringValues(EC.TLC_CHECKING_TEMPORAL_PROPS, "complete", "6352")); + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertFalse(recorder.recorded(EC.GENERAL)); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "6752", "6352", "0")); + assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "42")); + + assertZeroUncovered(); + } +} diff --git a/tlatools/test/pcal/EvenOddBadTest.java b/tlatools/test/pcal/EvenOddBadTest.java new file mode 100644 index 0000000000000000000000000000000000000000..0f8e2d371cf0623db81fe21c980633a443d01ef7 --- /dev/null +++ b/tlatools/test/pcal/EvenOddBadTest.java @@ -0,0 +1,55 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package pcal; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.output.EC; + +public class EvenOddBadTest extends PCalModelCheckerTestCase { + + public EvenOddBadTest() { + super("EvenOddBad", "pcal", new String[] {"-wf", "-termination"}); + } + + @Test + public void testSpec() { + assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "2")); + assertTrue(recorder.recordedWithStringValues(EC.TLC_CHECKING_TEMPORAL_PROPS, "complete", "13")); + assertTrue(recorder.recorded(EC.TLC_CHECKING_TEMPORAL_PROPS_END)); + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertFalse(recorder.recorded(EC.GENERAL)); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "15", "13", "0")); + assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "9")); + + assertUncovered("line 66, col 23 to line 66, col 37 of module EvenOddBad: 0\n" + + "line 67, col 23 to line 67, col 32 of module EvenOddBad: 0\n" + + "line 68, col 23 to line 68, col 50 of module EvenOddBad: 0"); + } +} diff --git a/tlatools/test/pcal/EvenOddTest.java b/tlatools/test/pcal/EvenOddTest.java new file mode 100644 index 0000000000000000000000000000000000000000..be0e4fbee2de067f04c035b8fcf28414b48801a9 --- /dev/null +++ b/tlatools/test/pcal/EvenOddTest.java @@ -0,0 +1,54 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package pcal; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.output.EC; + +public class EvenOddTest extends PCalModelCheckerTestCase { + + public EvenOddTest() { + super("EvenOdd", "pcal", new String[] {"-wf", "-termination"}); + } + + @Test + public void testSpec() { + assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "1")); + assertTrue(recorder.recordedWithStringValues(EC.TLC_CHECKING_TEMPORAL_PROPS, "complete", "13")); + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertFalse(recorder.recorded(EC.GENERAL)); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "14", "13", "0")); + assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "13")); + + assertUncovered("line 67, col 23 to line 67, col 37 of module EvenOdd: 0\n" + + "line 68, col 23 to line 68, col 34 of module EvenOdd: 0\n" + + "line 69, col 23 to line 69, col 50 of module EvenOdd: 0"); + } +} diff --git a/tlatools/test/pcal/Factorial2Test.java b/tlatools/test/pcal/Factorial2Test.java new file mode 100644 index 0000000000000000000000000000000000000000..516a50e7c89923fecd875b5e897e62515609470d --- /dev/null +++ b/tlatools/test/pcal/Factorial2Test.java @@ -0,0 +1,56 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package pcal; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.output.EC; + +public class Factorial2Test extends PCalModelCheckerTestCase { + + public Factorial2Test() { + super("Factorial2", "pcal", new String[] {"-wf", "-termination"}); + } + + @Test + public void testSpec() { + assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "1")); + assertTrue(recorder.recordedWithStringValues(EC.TLC_CHECKING_TEMPORAL_PROPS, "complete", "12")); + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertFalse(recorder.recorded(EC.GENERAL)); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "13", "12", "0")); + assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "12")); + + assertUncovered("line 53, col 21 to line 53, col 40 of module Factorial2: 0\n" + + "line 54, col 21 to line 54, col 38 of module Factorial2: 0\n" + + "line 55, col 21 to line 55, col 44 of module Factorial2: 0\n" + + "line 56, col 21 to line 56, col 40 of module Factorial2: 0\n" + + "line 57, col 21 to line 57, col 52 of module Factorial2: 0"); + } +} diff --git a/tlatools/test/pcal/FactorialTest.java b/tlatools/test/pcal/FactorialTest.java new file mode 100644 index 0000000000000000000000000000000000000000..870caefb161bc6da17b0accd2bc2d817395f31e1 --- /dev/null +++ b/tlatools/test/pcal/FactorialTest.java @@ -0,0 +1,52 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package pcal; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.output.EC; + +public class FactorialTest extends PCalModelCheckerTestCase { + + public FactorialTest() { + super("Factorial", "pcal", new String[] {"-wf", "-termination"}); + } + + @Test + public void testSpec() { + assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "1")); + assertTrue(recorder.recordedWithStringValues(EC.TLC_CHECKING_TEMPORAL_PROPS, "complete", "9")); + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertFalse(recorder.recorded(EC.GENERAL)); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "10", "9", "0")); + assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "9")); + + assertZeroUncovered(); + } +} diff --git a/tlatools/test/pcal/FairSeq2Test.java b/tlatools/test/pcal/FairSeq2Test.java new file mode 100644 index 0000000000000000000000000000000000000000..a41d78c1c8fb99ebe387396cb0de5e823d0ec907 --- /dev/null +++ b/tlatools/test/pcal/FairSeq2Test.java @@ -0,0 +1,52 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package pcal; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.output.EC; + +public class FairSeq2Test extends PCalModelCheckerTestCase { + + public FairSeq2Test() { + super("FairSeq2", "pcal", new String[] {"-wf", "-termination"}); + } + + @Test + public void testSpec() { + assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "1")); + assertTrue(recorder.recordedWithStringValues(EC.TLC_CHECKING_TEMPORAL_PROPS, "complete", "12")); + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertFalse(recorder.recorded(EC.GENERAL)); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "13", "12", "0")); + assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "12")); + + assertZeroUncovered(); + } +} diff --git a/tlatools/test/pcal/FairSeqTest.java b/tlatools/test/pcal/FairSeqTest.java new file mode 100644 index 0000000000000000000000000000000000000000..af5d65c106045f162c9465935cebe52c961d89d2 --- /dev/null +++ b/tlatools/test/pcal/FairSeqTest.java @@ -0,0 +1,52 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package pcal; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.output.EC; + +public class FairSeqTest extends PCalModelCheckerTestCase { + + public FairSeqTest() { + super("FairSeq", "pcal", new String[] {"-wf", "-termination"}); + } + + @Test + public void testSpec() { + assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "1")); + assertTrue(recorder.recordedWithStringValues(EC.TLC_CHECKING_TEMPORAL_PROPS, "complete", "24")); + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertFalse(recorder.recorded(EC.GENERAL)); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "13", "12", "0")); + assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "12")); + + assertZeroUncovered(); + } +} diff --git a/tlatools/test/pcal/FastMutex2Test.java b/tlatools/test/pcal/FastMutex2Test.java new file mode 100644 index 0000000000000000000000000000000000000000..a75d6009d5c6a5aab8450b418f0bc8c811772e19 --- /dev/null +++ b/tlatools/test/pcal/FastMutex2Test.java @@ -0,0 +1,52 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package pcal; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.output.EC; + +public class FastMutex2Test extends PCalModelCheckerTestCase { + + public FastMutex2Test() { + super("FastMutex2", "pcal", new String[] {"-wf"}); + } + + @Test + public void testSpec() { + assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "1")); + assertTrue(recorder.recordedWithStringValues(EC.TLC_CHECKING_TEMPORAL_PROPS, "complete", "1415")); + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertFalse(recorder.recorded(EC.GENERAL)); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "2679", "1415", "0")); + assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "60")); + + assertZeroUncovered(); + } +} diff --git a/tlatools/test/pcal/FastMutex3Test.java b/tlatools/test/pcal/FastMutex3Test.java new file mode 100644 index 0000000000000000000000000000000000000000..1a2c4b21593ee85245e4b8322e6b66ba0a8c4034 --- /dev/null +++ b/tlatools/test/pcal/FastMutex3Test.java @@ -0,0 +1,52 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package pcal; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.output.EC; + +public class FastMutex3Test extends PCalModelCheckerTestCase { + + public FastMutex3Test() { + super("FastMutex3", "pcal", new String[] {"-wf"}); + } + + @Test + public void testSpec() { + assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "1")); + assertTrue(recorder.recordedWithStringValues(EC.TLC_CHECKING_TEMPORAL_PROPS, "complete", "1415")); + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertFalse(recorder.recorded(EC.GENERAL)); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "2679", "1415", "0")); + assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "60")); + + assertZeroUncovered(); + } +} diff --git a/tlatools/test/pcal/FastMutexTest.java b/tlatools/test/pcal/FastMutexTest.java new file mode 100644 index 0000000000000000000000000000000000000000..75a2a9ae2f5d01525935e5500cd814137b4912c0 --- /dev/null +++ b/tlatools/test/pcal/FastMutexTest.java @@ -0,0 +1,52 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package pcal; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.output.EC; + +public class FastMutexTest extends PCalModelCheckerTestCase { + + public FastMutexTest() { + super("FastMutex", "pcal", new String[] {"-wf"}); + } + + @Test + public void testSpec() { + assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "1")); + assertTrue(recorder.recordedWithStringValues(EC.TLC_CHECKING_TEMPORAL_PROPS, "complete", "1415")); + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertFalse(recorder.recorded(EC.GENERAL)); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "2679", "1415", "0")); + assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "60")); + + assertZeroUncovered(); + } +} diff --git a/tlatools/test/pcal/FastMutexWithGoto2Test.java b/tlatools/test/pcal/FastMutexWithGoto2Test.java new file mode 100644 index 0000000000000000000000000000000000000000..4a14a4430ddbacc3c154c6f063c5ac6445618488 --- /dev/null +++ b/tlatools/test/pcal/FastMutexWithGoto2Test.java @@ -0,0 +1,52 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package pcal; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.output.EC; + +public class FastMutexWithGoto2Test extends PCalModelCheckerTestCase { + + public FastMutexWithGoto2Test() { + super("FastMutexWithGoto2", "pcal", new String[] {"-wfNext"}); + } + + @Test + public void testSpec() { + assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "1")); + assertTrue(recorder.recordedWithStringValues(EC.TLC_CHECKING_TEMPORAL_PROPS, "complete", "15900")); + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertFalse(recorder.recorded(EC.GENERAL)); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "42277", "15900", "0")); + assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "47")); + + assertZeroUncovered(); + } +} diff --git a/tlatools/test/pcal/FastMutexWithGotoTest.java b/tlatools/test/pcal/FastMutexWithGotoTest.java new file mode 100644 index 0000000000000000000000000000000000000000..a46bb0059f00927b927b0d5d45a282bc46b3ef66 --- /dev/null +++ b/tlatools/test/pcal/FastMutexWithGotoTest.java @@ -0,0 +1,52 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package pcal; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.output.EC; + +public class FastMutexWithGotoTest extends PCalModelCheckerTestCase { + + public FastMutexWithGotoTest() { + super("FastMutexWithGoto", "pcal"); + } + + @Test + public void testSpec() { + assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "1")); + assertTrue(recorder.recordedWithStringValues(EC.TLC_CHECKING_TEMPORAL_PROPS, "complete", "1415")); + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertFalse(recorder.recorded(EC.GENERAL)); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "2679", "1415", "0")); + assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "58")); + + assertZeroUncovered(); + } +} diff --git a/tlatools/test/pcal/FischerTest.java b/tlatools/test/pcal/FischerTest.java new file mode 100644 index 0000000000000000000000000000000000000000..37c3e49e0c1ffa8bf4d66b574dc9510dee69f547 --- /dev/null +++ b/tlatools/test/pcal/FischerTest.java @@ -0,0 +1,84 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package pcal; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.output.EC; + +public class FischerTest extends PCalModelCheckerTestCase { + + public FischerTest() { + super("Fischer", "pcal", new String[] {"-wf"}); + } + + @Test + public void testSpec() { + assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "1")); + assertTrue(recorder.recordedWithStringValues(EC.TLC_CHECKING_TEMPORAL_PROPS, "complete", "1002")); + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertFalse(recorder.recorded(EC.GENERAL)); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "2487", "1002", "0")); + assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "24")); + + assertZeroUncovered(); + } +} +/* +C:\lamport\tla\pluscal>java -mx1000m -cp "c:/lamport/tla/newtools/tla2-inria-workspace/tla2-inria/tlatools/class" tlc2.TLC -cleanup Fischer.tla +TLC2 Version 2.05 of 18 May 2012 +Running in Model-Checking mode. +Parsing file Fischer.tla +Parsing file C:\lamport\tla\newtools\tla2-inria-workspace\tla2-inria\tlatools\class\tla2sany\StandardModules\Naturals.tla +Parsing file C:\lamport\tla\newtools\tla2-inria-workspace\tla2-inria\tlatools\class\tla2sany\StandardModules\TLC.tla +Parsing file C:\lamport\tla\newtools\tla2-inria-workspace\tla2-inria\tlatools\class\tla2sany\StandardModules\Sequences.tla +Semantic processing of module Naturals +Semantic processing of module Sequences +Semantic processing of module TLC +Semantic processing of module Fischer +Starting... (2012-08-10 17:38:12) +Implied-temporal checking--satisfiability problem has 1 branches. +"Testing Fischer's Mutual Exclusion Algorithm" TRUE +<<" Number of processes = ", 3>> TRUE +<<" Delta = ", 2>> TRUE +<<" Epsilon = ", 3>> TRUE +"Should find a bug if N > 1 and Delta >= Epsilon" TRUE +Computing initial states... +Finished computing initial states: 1 distinct state generated. +Checking temporal properties for the complete state space... +Model checking completed. No error has been found. + Estimates of the probability that TLC did not check all reachable states + because two distinct states had the same fingerprint: + calculated (optimistic): val = 8.1E-14 + based on the actual fingerprints: val = 8.0E-15 +2487 states generated, 1002 distinct states found, 0 states left on queue. +The depth of the complete state graph search is 24. +Finished. (2012-08-10 17:38:13) +InnerLabeledIf.tla +*/ \ No newline at end of file diff --git a/tlatools/test/pcal/Github358.java b/tlatools/test/pcal/Github358.java new file mode 100644 index 0000000000000000000000000000000000000000..c7851681e2c19132e359b4d1a64518eacdd5d602 --- /dev/null +++ b/tlatools/test/pcal/Github358.java @@ -0,0 +1,51 @@ +/******************************************************************************* + * Copyright (c) 2020 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package pcal; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.tool.CommonTestCase; +import util.ToolIO; + +public class Github358 extends PCalTest { + + @Test + public void test() { + assertEquals(trans.STATUS_EXIT_WITH_ERRORS, + trans.runMe(new String[] {"-nocfg", CommonTestCase.BASE_PATH + "Github358.tla"})); + + final String[] messages = ToolIO.getAllMessages(); + assertTrue(messages.length == 1); + + final String msg = messages[0]; + assertEquals("Unrecoverable error:\n" + + " -- Expected \":=\" but found \"skip\"\n" + + " line 5, column 7.", msg.trim()); + } +} diff --git a/tlatools/test/pcal/InnerLabeledIfTest.java b/tlatools/test/pcal/InnerLabeledIfTest.java new file mode 100644 index 0000000000000000000000000000000000000000..f97510f664c2eb732f017220835e9bd18d79b6a5 --- /dev/null +++ b/tlatools/test/pcal/InnerLabeledIfTest.java @@ -0,0 +1,85 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package pcal; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.output.EC; + +public class InnerLabeledIfTest extends PCalModelCheckerTestCase { + + public InnerLabeledIfTest() { + super("InnerLabeledIf", "pcal", new String[] {"-wf", "-termination"}); + } + + @Test + public void testSpec() { + assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "4")); + assertTrue(recorder.recordedWithStringValues(EC.TLC_CHECKING_TEMPORAL_PROPS, "complete", "16")); + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertFalse(recorder.recorded(EC.GENERAL)); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "20", "16", "0")); + assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "4")); + assertZeroUncovered(); + } +} +/* +C:\lamport\tla\pluscal>java -mx1000m -cp "c:/lamport/tla/newtools/tla2-inria-workspace/tla2-inria/tlatools/class" tlc2.TLC -cleanup InnerLabeledIf.tla +TLC2 Version 2.05 of 18 May 2012 +Running in Model-Checking mode. +Parsing file InnerLabeledIf.tla +Parsing file C:\lamport\tla\newtools\tla2-inria-workspace\tla2-inria\tlatools\class\tla2sany\StandardModules\Sequences.tla +Parsing file C:\lamport\tla\newtools\tla2-inria-workspace\tla2-inria\tlatools\class\tla2sany\StandardModules\Naturals.tla +Parsing file C:\lamport\tla\newtools\tla2-inria-workspace\tla2-inria\tlatools\class\tla2sany\StandardModules\TLC.tla +Semantic processing of module Naturals +Semantic processing of module Sequences +Semantic processing of module TLC +Semantic processing of module InnerLabeledIf +Starting... (2012-08-10 17:38:14) +Implied-temporal checking--satisfiability problem has 1 branches. +Computing initial states... +Finished computing initial states: 4 distinct states generated. +"made it to end" +"made it to end" +"made it to end" +"made it to end" +"made it to end" +"made it to end" +"made it to end" +"made it to end" +Checking temporal properties for the complete state space... +Model checking completed. No error has been found. + Estimates of the probability that TLC did not check all reachable states + because two distinct states had the same fingerprint: + calculated (optimistic): val = 3.5E-18 + based on the actual fingerprints: val = 1.7E-17 +20 states generated, 16 distinct states found, 0 states left on queue. +The depth of the complete state graph search is 4. +Finished. (2012-08-10 17:38:14) +*/ diff --git a/tlatools/test/pcal/MPFactorial2Test.java b/tlatools/test/pcal/MPFactorial2Test.java new file mode 100644 index 0000000000000000000000000000000000000000..5acbd064fac3b161549780de5840083a9a1de986 --- /dev/null +++ b/tlatools/test/pcal/MPFactorial2Test.java @@ -0,0 +1,56 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package pcal; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.output.EC; + +public class MPFactorial2Test extends PCalModelCheckerTestCase { + + public MPFactorial2Test() { + super("MPFactorial2", "pcal", new String[] {"-wf", "-termination"}); + } + + @Test + public void testSpec() { + assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "1")); + assertTrue(recorder.recordedWithStringValues(EC.TLC_CHECKING_TEMPORAL_PROPS, "complete", "1728")); + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertFalse(recorder.recorded(EC.GENERAL)); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "4754", "1728", "0")); + assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "34")); + + assertUncovered("line 61, col 27 to line 61, col 74 of module MPFactorial2: 0\n" + + "line 62, col 27 to line 62, col 71 of module MPFactorial2: 0\n" + + "line 63, col 27 to line 63, col 80 of module MPFactorial2: 0\n" + + "line 64, col 27 to line 64, col 77 of module MPFactorial2: 0\n" + + "line 65, col 27 to line 65, col 58 of module MPFactorial2: 0"); + } +} diff --git a/tlatools/test/pcal/MPFactorialTest.java b/tlatools/test/pcal/MPFactorialTest.java new file mode 100644 index 0000000000000000000000000000000000000000..d0abcbfc1eb5696db7d1c97d263b4d175006913c --- /dev/null +++ b/tlatools/test/pcal/MPFactorialTest.java @@ -0,0 +1,52 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package pcal; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.output.EC; + +public class MPFactorialTest extends PCalModelCheckerTestCase { + + public MPFactorialTest() { + super("MPFactorial", "pcal", new String[] {"-wf", "-termination"}); + } + + @Test + public void testSpec() { + assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "1")); + assertTrue(recorder.recordedWithStringValues(EC.TLC_CHECKING_TEMPORAL_PROPS, "complete", "729")); + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertFalse(recorder.recorded(EC.GENERAL)); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "1946", "729", "0")); + assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "25")); + + assertZeroUncovered(); + } +} diff --git a/tlatools/test/pcal/MPNoParamsTest.java b/tlatools/test/pcal/MPNoParamsTest.java new file mode 100644 index 0000000000000000000000000000000000000000..587d42992c0ffe1ed2ed3928cb5252d5d7d51e5c --- /dev/null +++ b/tlatools/test/pcal/MPNoParamsTest.java @@ -0,0 +1,52 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package pcal; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.output.EC; + +public class MPNoParamsTest extends PCalModelCheckerTestCase { + + public MPNoParamsTest() { + super("MPNoParams", "pcal", new String[] {"-wf", "-termination"}); + } + + @Test + public void testSpec() { + assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "1")); + assertTrue(recorder.recordedWithStringValues(EC.TLC_CHECKING_TEMPORAL_PROPS, "complete", "96")); + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertFalse(recorder.recorded(EC.GENERAL)); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "250", "96", "0")); + assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "13")); + + assertZeroUncovered(); + } +} diff --git a/tlatools/test/pcal/MacroQuicksortTest.java b/tlatools/test/pcal/MacroQuicksortTest.java new file mode 100644 index 0000000000000000000000000000000000000000..f132b260dc32947a199089e453e1cec267c0b388 --- /dev/null +++ b/tlatools/test/pcal/MacroQuicksortTest.java @@ -0,0 +1,50 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package pcal; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.output.EC; + +public class MacroQuicksortTest extends PCalModelCheckerTestCase { + + public MacroQuicksortTest() { + super("MacroQuicksort", "pcal", new String[] {"-wf", "-termination"}); + } + + @Test + public void testSpec() { + assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "256")); + assertTrue(recorder.recordedWithStringValues(EC.TLC_CHECKING_TEMPORAL_PROPS, "complete", "31092")); + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertFalse(recorder.recorded(EC.GENERAL)); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "38084", "31092", "0")); + assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "23")); + } +} diff --git a/tlatools/test/pcal/MacroRealQuicksortTest.java b/tlatools/test/pcal/MacroRealQuicksortTest.java new file mode 100644 index 0000000000000000000000000000000000000000..34b9f96c75c266b23dd092b5782464c25c5502aa --- /dev/null +++ b/tlatools/test/pcal/MacroRealQuicksortTest.java @@ -0,0 +1,50 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package pcal; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.output.EC; + +public class MacroRealQuicksortTest extends PCalModelCheckerTestCase { + + public MacroRealQuicksortTest() { + super("MacroRealQuicksort", "pcal", new String[] {"-wf", "-termination"}); + } + + @Test + public void testSpec() { + assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "256")); + assertTrue(recorder.recordedWithStringValues(EC.TLC_CHECKING_TEMPORAL_PROPS, "complete", "27336")); + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertFalse(recorder.recorded(EC.GENERAL)); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "64072", "27336", "0")); + assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "9")); + } +} diff --git a/tlatools/test/pcal/MergeSortTest.java b/tlatools/test/pcal/MergeSortTest.java new file mode 100644 index 0000000000000000000000000000000000000000..994be4e7b6c3c2b14f64b81ba3d5928b11fc3b8d --- /dev/null +++ b/tlatools/test/pcal/MergeSortTest.java @@ -0,0 +1,80 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package pcal; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.output.EC; + +public class MergeSortTest extends PCalModelCheckerTestCase { + + public MergeSortTest() { + super("MergeSort", "pcal", new String[] {"-wf", "-termination"}); + } + + @Test + public void testSpec() { + assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "6")); + assertTrue(recorder.recordedWithStringValues(EC.TLC_CHECKING_TEMPORAL_PROPS, "complete", "80", "")); + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertFalse(recorder.recorded(EC.GENERAL)); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "86", "80", "0")); + assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "18")); + assertUncovered("line 162, col 32 to line 162, col 61 of module MergeSort: 0\n" + + "line 178, col 32 to line 178, col 61 of module MergeSort: 0\n" + + "line 179, col 32 to line 179, col 37 of module MergeSort: 0"); + } +} +/* +C:\lamport\tla\pluscal>java -mx1000m -cp "c:/lamport/tla/newtools/tla2-inria-workspace/tla2-inria/tlatools/class" tlc2.TLC -cleanup MergeSort.tla +TLC2 Version 2.05 of 18 May 2012 +Running in Model-Checking mode. +Parsing file MergeSort.tla +Parsing file C:\lamport\tla\newtools\tla2-inria-workspace\tla2-inria\tlatools\class\tla2sany\StandardModules\Naturals.tla +Parsing file C:\lamport\tla\newtools\tla2-inria-workspace\tla2-inria\tlatools\class\tla2sany\StandardModules\Sequences.tla +Parsing file C:\lamport\tla\newtools\tla2-inria-workspace\tla2-inria\tlatools\class\tla2sany\StandardModules\TLC.tla +Semantic processing of module Naturals +Semantic processing of module Sequences +Semantic processing of module TLC +Semantic processing of module MergeSort +Starting... (2012-08-10 17:38:17) +Implied-temporal checking--satisfiability problem has 1 branches. +<<"Testing Mergesort on all arrays of length <= ", 2>> TRUE +Computing initial states... +Finished computing initial states: 6 distinct states generated. +Checking temporal properties for the complete state space... +Model checking completed. No error has been found. + Estimates of the probability that TLC did not check all reachable states + because two distinct states had the same fingerprint: + calculated (optimistic): val = 2.6E-17 + based on the actual fingerprints: val = 1.6E-16 +86 states generated, 80 distinct states found, 0 states left on queue. +The depth of the complete state graph search is 18. +Finished. (2012-08-10 17:38:17) +*/ diff --git a/tlatools/test/pcal/MissingBodyInWhileTest.java b/tlatools/test/pcal/MissingBodyInWhileTest.java new file mode 100644 index 0000000000000000000000000000000000000000..898c952e8d0de0a34f866022a2a8bf5114cc868d --- /dev/null +++ b/tlatools/test/pcal/MissingBodyInWhileTest.java @@ -0,0 +1,26 @@ +package pcal; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.tool.CommonTestCase; +import util.ToolIO; + +public class MissingBodyInWhileTest extends PCalTest { + + @Test + public void test() { + assertEquals(trans.STATUS_EXIT_WITH_ERRORS, + trans.runMe(new String[] {"-nocfg", CommonTestCase.BASE_PATH + "MissingBodyInWhile.tla"})); + + final String[] messages = ToolIO.getAllMessages(); + assertTrue(messages.length == 1); + + final String msg = messages[0]; + assertEquals("Unrecoverable error:\n" + + " -- Missing body of while statement at\n" + + " line 6, column 14.", msg.trim()); + } +} diff --git a/tlatools/test/pcal/MissingBodyInWithTest.java b/tlatools/test/pcal/MissingBodyInWithTest.java new file mode 100644 index 0000000000000000000000000000000000000000..a57b20270cc55b7b9fdd5c5ab284c2a1471e4013 --- /dev/null +++ b/tlatools/test/pcal/MissingBodyInWithTest.java @@ -0,0 +1,26 @@ +package pcal; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.tool.CommonTestCase; +import util.ToolIO; + +public class MissingBodyInWithTest extends PCalTest { + + @Test + public void test() { + assertEquals(trans.STATUS_EXIT_WITH_ERRORS, + trans.runMe(new String[] { "-nocfg", CommonTestCase.BASE_PATH + "MissingBodyInWith.tla" })); + + final String[] messages = ToolIO.getAllMessages(); + assertTrue(messages.length == 1); + + final String msg = messages[0]; + assertEquals("Unrecoverable error:\n" + + " -- Missing body of with statement\n" + + " at line 5, column 5.", msg.trim()); + } +} diff --git a/tlatools/test/pcal/MultiAssignmentTest.java b/tlatools/test/pcal/MultiAssignmentTest.java new file mode 100644 index 0000000000000000000000000000000000000000..296ae0ae6eddf95ee587df78437ef9690dbeceb9 --- /dev/null +++ b/tlatools/test/pcal/MultiAssignmentTest.java @@ -0,0 +1,77 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package pcal; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.output.EC; + +public class MultiAssignmentTest extends PCalModelCheckerTestCase { + + public MultiAssignmentTest() { + super("MultiAssignment", "pcal", new String[] {"-wf", "-termination"}); + } + + @Test + public void testSpec() { + assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "1")); + assertTrue(recorder.recordedWithStringValues(EC.TLC_CHECKING_TEMPORAL_PROPS, "complete", "27")); + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertFalse(recorder.recorded(EC.GENERAL)); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "56", "27", "0")); + assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "7")); + assertZeroUncovered(); + } +} +/* +C:\lamport\tla\pluscal>java -mx1000m -cp "c:/lamport/tla/newtools/tla2-inria-workspace/tla2-inria/tlatools/class" tlc2.TLC -cleanup MultiAssignment.tla +TLC2 Version 2.05 of 18 May 2012 +Running in Model-Checking mode. +Parsing file MultiAssignment.tla +Parsing file C:\lamport\tla\newtools\tla2-inria-workspace\tla2-inria\tlatools\class\tla2sany\StandardModules\Naturals.tla +Parsing file C:\lamport\tla\newtools\tla2-inria-workspace\tla2-inria\tlatools\class\tla2sany\StandardModules\TLC.tla +Parsing file C:\lamport\tla\newtools\tla2-inria-workspace\tla2-inria\tlatools\class\tla2sany\StandardModules\Sequences.tla +Semantic processing of module Naturals +Semantic processing of module Sequences +Semantic processing of module TLC +Semantic processing of module MultiAssignment +Starting... (2012-08-10 17:38:18) +Implied-temporal checking--satisfiability problem has 1 branches. +Computing initial states... +Finished computing initial states: 1 distinct state generated. +Checking temporal properties for the complete state space... +Model checking completed. No error has been found. + Estimates of the probability that TLC did not check all reachable states + because two distinct states had the same fingerprint: + calculated (optimistic): val = 4.2E-17 + based on the actual fingerprints: val = 1.4E-17 +56 states generated, 27 distinct states found, 0 states left on queue. +The depth of the complete state graph search is 7. +Finished. (2012-08-10 17:38:18) +*/ \ No newline at end of file diff --git a/tlatools/test/pcal/MultiProc2Test.java b/tlatools/test/pcal/MultiProc2Test.java new file mode 100644 index 0000000000000000000000000000000000000000..72d0af22c56cb3558d5bd197c4ec3843ebbca601 --- /dev/null +++ b/tlatools/test/pcal/MultiProc2Test.java @@ -0,0 +1,85 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package pcal; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.output.EC; + +public class MultiProc2Test extends PCalModelCheckerTestCase { + + public MultiProc2Test() { + super("MultiProc2", "pcal", new String[] {"-wf", "-termination"}); + } + + @Test + public void testSpec() { + assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "4")); + assertTrue(recorder.recordedWithStringValues(EC.TLC_CHECKING_TEMPORAL_PROPS, "complete", "504")); + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertFalse(recorder.recorded(EC.GENERAL)); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "1212", "504", "0")); + assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "14")); + assertZeroUncovered(); + } +} +/* +C:\lamport\tla\pluscal>java -mx1000m -cp "c:/lamport/tla/newtools/tla2-inria-workspace/tla2-inria/tlatools/class" tlc2.TLC -cleanup MultiProc2.tla +TLC2 Version 2.05 of 18 May 2012 +Running in Model-Checking mode. +Parsing file MultiProc2.tla +Parsing file C:\lamport\tla\newtools\tla2-inria-workspace\tla2-inria\tlatools\class\tla2sany\StandardModules\Sequences.tla +Parsing file C:\lamport\tla\newtools\tla2-inria-workspace\tla2-inria\tlatools\class\tla2sany\StandardModules\Naturals.tla +Parsing file C:\lamport\tla\newtools\tla2-inria-workspace\tla2-inria\tlatools\class\tla2sany\StandardModules\TLC.tla +Semantic processing of module Naturals +Semantic processing of module Sequences +Semantic processing of module TLC +Semantic processing of module MultiProc2 +Starting... (2012-08-10 17:38:19) +Implied-temporal checking--satisfiability problem has 1 branches. +Computing initial states... +Finished computing initial states: 4 distinct states generated. +13 TRUE +13 TRUE +14 TRUE +14 TRUE +14 TRUE +14 TRUE +15 TRUE +15 TRUE +Checking temporal properties for the complete state space... +Model checking completed. No error has been found. + Estimates of the probability that TLC did not check all reachable states + because two distinct states had the same fingerprint: + calculated (optimistic): val = 1.9E-14 + based on the actual fingerprints: val = 3.7E-13 +1212 states generated, 504 distinct states found, 0 states left on queue. +The depth of the complete state graph search is 14. +Finished. (2012-08-10 17:38:20) +*/ \ No newline at end of file diff --git a/tlatools/test/pcal/MultiprocDefineTest.java b/tlatools/test/pcal/MultiprocDefineTest.java new file mode 100644 index 0000000000000000000000000000000000000000..227303870a8cfb3fbb65a2673cb9c101a93490f3 --- /dev/null +++ b/tlatools/test/pcal/MultiprocDefineTest.java @@ -0,0 +1,75 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package pcal; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.output.EC; + +public class MultiprocDefineTest extends PCalModelCheckerTestCase { + + public MultiprocDefineTest() { + super("MultiprocDefine", "pcal"); + } + + @Test + public void testSpec() { + assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "1")); + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertFalse(recorder.recorded(EC.GENERAL)); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "14", "8", "0")); + assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "4")); + + assertZeroUncovered(); + } +} +/* +C:\lamport\tla\pluscal>java -mx1000m -cp "c:/lamport/tla/newtools/tla2-inria-workspace/tla2-inria/tlatools/class" tlc2.TLC -cleanup MultiprocDefine.tla +TLC2 Version 2.05 of 18 May 2012 +Running in Model-Checking mode. +Parsing file MultiprocDefine.tla +Parsing file C:\lamport\tla\newtools\tla2-inria-workspace\tla2-inria\tlatools\class\tla2sany\StandardModules\Naturals.tla +Parsing file C:\lamport\tla\newtools\tla2-inria-workspace\tla2-inria\tlatools\class\tla2sany\StandardModules\Sequences.tla +Parsing file C:\lamport\tla\newtools\tla2-inria-workspace\tla2-inria\tlatools\class\tla2sany\StandardModules\TLC.tla +Semantic processing of module Naturals +Semantic processing of module Sequences +Semantic processing of module TLC +Semantic processing of module MultiprocDefine +Starting... (2012-08-10 17:40:00) +Computing initial states... +Finished computing initial states: 1 distinct state generated. +Model checking completed. No error has been found. + Estimates of the probability that TLC did not check all reachable states + because two distinct states had the same fingerprint: + calculated (optimistic): val = 2.6E-18 + based on the actual fingerprints: val = 4.8E-19 +14 states generated, 8 distinct states found, 0 states left on queue. +The depth of the complete state graph search is 4. +Finished. (2012-08-10 17:40:00) +*/ \ No newline at end of file diff --git a/tlatools/test/pcal/NestedMacrosTest.java b/tlatools/test/pcal/NestedMacrosTest.java new file mode 100644 index 0000000000000000000000000000000000000000..e8b202d927bcd8e2d9a3ff6b2fbe40b1085d0ce7 --- /dev/null +++ b/tlatools/test/pcal/NestedMacrosTest.java @@ -0,0 +1,52 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package pcal; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.output.EC; + +public class NestedMacrosTest extends PCalModelCheckerTestCase { + + public NestedMacrosTest() { + super("NestedMacros", "pcal", new String[] {"-wf", "-termination"}); + } + + @Test + public void testSpec() { + assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "1")); + assertTrue(recorder.recordedWithStringValues(EC.TLC_CHECKING_TEMPORAL_PROPS, "complete", "9")); + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertFalse(recorder.recorded(EC.GENERAL)); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "14", "9", "0")); + assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "5")); + + assertZeroUncovered(); + } +} diff --git a/tlatools/test/pcal/NoLoop2Test.java b/tlatools/test/pcal/NoLoop2Test.java new file mode 100644 index 0000000000000000000000000000000000000000..62e5cd8e5a40eedc32d5a3127ddf2fbd4a7e6386 --- /dev/null +++ b/tlatools/test/pcal/NoLoop2Test.java @@ -0,0 +1,82 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package pcal; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.output.EC; + +public class NoLoop2Test extends PCalModelCheckerTestCase { + + public NoLoop2Test() { + super("NoLoop2", "pcal", new String[] {"-wf", "-termination"}); + } + + @Test + public void testSpec() { + assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "1")); + assertTrue(recorder.recordedWithStringValues(EC.TLC_CHECKING_TEMPORAL_PROPS, "complete", "9")); + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertFalse(recorder.recorded(EC.GENERAL)); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "11", "9", "0")); + assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "6")); + + assertZeroUncovered(); + } +} +/* +C:\lamport\tla\pluscal>java -mx1000m -cp "c:/lamport/tla/newtools/tla2-inria-workspace/tla2-inria/tlatools/class" tlc2.TLC -cleanup NoLoop2.tla +TLC2 Version 2.05 of 18 May 2012 +Running in Model-Checking mode. +Parsing file NoLoop2.tla +Parsing file C:\lamport\tla\newtools\tla2-inria-workspace\tla2-inria\tlatools\class\tla2sany\StandardModules\Naturals.tla +Parsing file C:\lamport\tla\newtools\tla2-inria-workspace\tla2-inria\tlatools\class\tla2sany\StandardModules\TLC.tla +Parsing file C:\lamport\tla\newtools\tla2-inria-workspace\tla2-inria\tlatools\class\tla2sany\StandardModules\Sequences.tla +Semantic processing of module Naturals +Semantic processing of module Sequences +Semantic processing of module TLC +Semantic processing of module NoLoop2 +Starting... (2012-08-10 17:38:22) +Implied-temporal checking--satisfiability problem has 1 branches. +Computing initial states... +Finished computing initial states: 1 distinct state generated. +7 TRUE +7 TRUE +10 TRUE +10 TRUE +Checking temporal properties for the complete state space... +Model checking completed. No error has been found. + Estimates of the probability that TLC did not check all reachable states + because two distinct states had the same fingerprint: + calculated (optimistic): val = 9.8E-19 + based on the actual fingerprints: val = 1.6E-17 +11 states generated, 9 distinct states found, 0 states left on queue. +The depth of the complete state graph search is 6. +Finished. (2012-08-10 17:38:22) +*/ \ No newline at end of file diff --git a/tlatools/test/pcal/NoLoopTest.java b/tlatools/test/pcal/NoLoopTest.java new file mode 100644 index 0000000000000000000000000000000000000000..9fa96e5109e5f5a1d13eafbf4e5a412449ef1586 --- /dev/null +++ b/tlatools/test/pcal/NoLoopTest.java @@ -0,0 +1,82 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package pcal; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.output.EC; + +public class NoLoopTest extends PCalModelCheckerTestCase { + + public NoLoopTest() { + super("NoLoop", "pcal", new String[] {"-wf", "-termination"}); + } + + @Test + public void testSpec() { + assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "1")); + assertTrue(recorder.recordedWithStringValues(EC.TLC_CHECKING_TEMPORAL_PROPS, "complete", "6")); + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertFalse(recorder.recorded(EC.GENERAL)); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "8", "6", "0")); + assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "4")); + + assertZeroUncovered(); + } +} +/* +C:\lamport\tla\pluscal>java -mx1000m -cp "c:/lamport/tla/newtools/tla2-inria-workspace/tla2-inria/tlatools/class" tlc2.TLC -cleanup NoLoop.tla +TLC2 Version 2.05 of 18 May 2012 +Running in Model-Checking mode. +Parsing file NoLoop.tla +Parsing file C:\lamport\tla\newtools\tla2-inria-workspace\tla2-inria\tlatools\class\tla2sany\StandardModules\Sequences.tla +Parsing file C:\lamport\tla\newtools\tla2-inria-workspace\tla2-inria\tlatools\class\tla2sany\StandardModules\Naturals.tla +Parsing file C:\lamport\tla\newtools\tla2-inria-workspace\tla2-inria\tlatools\class\tla2sany\StandardModules\TLC.tla +Semantic processing of module Naturals +Semantic processing of module Sequences +Semantic processing of module TLC +Semantic processing of module NoLoop +Starting... (2012-08-10 17:38:21) +Implied-temporal checking--satisfiability problem has 1 branches. +Computing initial states... +Finished computing initial states: 1 distinct state generated. +5 TRUE +5 TRUE +7 TRUE +7 TRUE +Checking temporal properties for the complete state space... +Model checking completed. No error has been found. + Estimates of the probability that TLC did not check all reachable states + because two distinct states had the same fingerprint: + calculated (optimistic): val = 6.5E-19 + based on the actual fingerprints: val = 1.5E-18 +8 states generated, 6 distinct states found, 0 states left on queue. +The depth of the complete state graph search is 4. +Finished. (2012-08-10 17:38:21) +*/ \ No newline at end of file diff --git a/tlatools/test/pcal/NoParamsTest.java b/tlatools/test/pcal/NoParamsTest.java new file mode 100644 index 0000000000000000000000000000000000000000..d84a809071b4894a20a59c3283fd428dde03ff0f --- /dev/null +++ b/tlatools/test/pcal/NoParamsTest.java @@ -0,0 +1,80 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package pcal; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.output.EC; + +public class NoParamsTest extends PCalModelCheckerTestCase { + + public NoParamsTest() { + super("NoParams", "pcal", new String[] {"-wf", "-termination"}); + } + + @Test + public void testSpec() { + assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "1")); + assertTrue(recorder.recordedWithStringValues(EC.TLC_CHECKING_TEMPORAL_PROPS, "complete", "6")); + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertFalse(recorder.recorded(EC.GENERAL)); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "7", "6", "0")); + assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "6")); + + assertZeroUncovered(); + } +} +/* +C:\lamport\tla\pluscal>java -mx1000m -cp "c:/lamport/tla/newtools/tla2-inria-workspace/tla2-inria/tlatools/class" tlc2.TLC -cleanup NoParams.tla +TLC2 Version 2.05 of 18 May 2012 +Running in Model-Checking mode. +Parsing file NoParams.tla +Parsing file C:\lamport\tla\newtools\tla2-inria-workspace\tla2-inria\tlatools\class\tla2sany\StandardModules\Sequences.tla +Parsing file C:\lamport\tla\newtools\tla2-inria-workspace\tla2-inria\tlatools\class\tla2sany\StandardModules\Naturals.tla +Parsing file C:\lamport\tla\newtools\tla2-inria-workspace\tla2-inria\tlatools\class\tla2sany\StandardModules\TLC.tla +Semantic processing of module Naturals +Semantic processing of module Sequences +Semantic processing of module TLC +Semantic processing of module NoParams +Starting... (2012-08-10 17:38:23) +Implied-temporal checking--satisfiability problem has 1 branches. +Computing initial states... +Finished computing initial states: 1 distinct state generated. +2 TRUE +2 TRUE +Checking temporal properties for the complete state space... +Model checking completed. No error has been found. + Estimates of the probability that TLC did not check all reachable states + because two distinct states had the same fingerprint: + calculated (optimistic): val = 3.3E-19 + based on the actual fingerprints: val = 1.7E-18 +7 states generated, 6 distinct states found, 0 states left on queue. +The depth of the complete state graph search is 6. +Finished. (2012-08-10 17:38:23) +*/ \ No newline at end of file diff --git a/tlatools/test/pcal/NotSoSimpleLoopTest.java b/tlatools/test/pcal/NotSoSimpleLoopTest.java new file mode 100644 index 0000000000000000000000000000000000000000..4df7ab3718a5b5588e60c453a13f6962ab6de4aa --- /dev/null +++ b/tlatools/test/pcal/NotSoSimpleLoopTest.java @@ -0,0 +1,78 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package pcal; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.output.EC; + +public class NotSoSimpleLoopTest extends PCalModelCheckerTestCase { + + public NotSoSimpleLoopTest() { + super("NotSoSimpleLoop", "pcal", new String[] {"-wf", "-termination"}); + } + + @Test + public void testSpec() { + assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "1")); + assertTrue(recorder.recordedWithStringValues(EC.TLC_CHECKING_TEMPORAL_PROPS, "complete", "13")); + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertFalse(recorder.recorded(EC.GENERAL)); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "14", "13", "0")); + assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "13")); + + assertZeroUncovered(); + } +} +/* +C:\lamport\tla\pluscal>java -mx1000m -cp "c:/lamport/tla/newtools/tla2-inria-workspace/tla2-inria/tlatools/class" tlc2.TLC -cleanup NotSoSimpleLoop.tla +TLC2 Version 2.05 of 18 May 2012 +Running in Model-Checking mode. +Parsing file NotSoSimpleLoop.tla +Parsing file C:\lamport\tla\newtools\tla2-inria-workspace\tla2-inria\tlatools\class\tla2sany\StandardModules\Naturals.tla +Parsing file C:\lamport\tla\newtools\tla2-inria-workspace\tla2-inria\tlatools\class\tla2sany\StandardModules\TLC.tla +Parsing file C:\lamport\tla\newtools\tla2-inria-workspace\tla2-inria\tlatools\class\tla2sany\StandardModules\Sequences.tla +Semantic processing of module Naturals +Semantic processing of module Sequences +Semantic processing of module TLC +Semantic processing of module NotSoSimpleLoop +Starting... (2012-08-10 17:38:24) +Implied-temporal checking--satisfiability problem has 1 branches. +Computing initial states... +Finished computing initial states: 1 distinct state generated. +Checking temporal properties for the complete state space... +Model checking completed. No error has been found. + Estimates of the probability that TLC did not check all reachable states + because two distinct states had the same fingerprint: + calculated (optimistic): val = 7.0E-19 + based on the actual fingerprints: val = 3.3E-18 +14 states generated, 13 distinct states found, 0 states left on queue. +The depth of the complete state graph search is 13. +Finished. (2012-08-10 17:38:25) +*/ \ No newline at end of file diff --git a/tlatools/test/pcal/OptionalSemicolonTest.java b/tlatools/test/pcal/OptionalSemicolonTest.java new file mode 100644 index 0000000000000000000000000000000000000000..d1385820d82cdc1ce8721df17c45b5de54dc3748 --- /dev/null +++ b/tlatools/test/pcal/OptionalSemicolonTest.java @@ -0,0 +1,143 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package pcal; + +import static org.junit.Assert.assertEquals; + +import java.io.File; +import java.io.IOException; + +import org.junit.Test; + +import tla2sany.drivers.SANY; +import util.TestPrintStream; +import util.ToolIO; + +public class OptionalSemicolonTest extends PCalTest { + + /* + * A PlusCal User's Manual P-Syntax: + * "[...] the final semicolon [or comma] is optional in the p-syntax." + */ + + @Test + public void noOptionalSemiColonVariableList1() throws IOException { + // Translate from PCal to TLA+ + final String filename = "MissingSemiColonVariableListTest1" + System.currentTimeMillis(); + final String absolutePath = writeFile(System.getProperty("java.io.tmpdir") + File.separator + filename, + "---- MODULE " + filename + " ----\n" + + "(*\n" + + "--algorithm algo\n" + + "variables foo = 0" + // no optional semicolon + "\n" + + "fair process bug = 0\n" + // notice the "fair" statement + "begin\n" + + "L:\n" + + " skip\n" + + "end process\n" + + "\n" + + "end algorithm *)\n" + + "====" + ); + test(filename, absolutePath); + } + + @Test + public void noOptionalSemiColonVariableList2() throws IOException { + // Translate from PCal to TLA+ + final String filename = "MissingSemiColonVariableListTest2" + System.currentTimeMillis(); + final String absolutePath = writeFile(System.getProperty("java.io.tmpdir") + File.separator + filename, + "---- MODULE " + filename + " ----\n" + + "(*\n" + + "--algorithm algo\n" + + "variables foo = 0 ;" + // optional semicolon + "\n" + + "fair process bug = 0\n" + // notice the "fair" statement + "begin\n" + + "L:\n" + + " skip\n" + + "end process\n" + + "\n" + + "end algorithm *)\n" + + "====" + ); + test(filename, absolutePath); + } + + @Test + public void noOptionalSemiColonVariableList3() throws IOException { + // Translate from PCal to TLA+ + final String filename = "MissingSemiColonVariableListTest3" + System.currentTimeMillis(); + final String absolutePath = writeFile(System.getProperty("java.io.tmpdir") + File.separator + filename, + "---- MODULE " + filename + " ----\n" + + "(*\n" + + "--algorithm algo\n" + + "variables foo = 0 ;" + // optional semicolon + "\n" + + "process bug = 0\n" + // notice the "fair" statement + "begin\n" + + "L:\n" + + " skip\n" + + "end process\n" + + "\n" + + "end algorithm *)\n" + + "====" + ); + test(filename, absolutePath); + } + + @Test + public void noOptionalSemiColonVariableList4() throws IOException { + // Translate from PCal to TLA+ + final String filename = "MissingSemiColonVariableListTest4" + System.currentTimeMillis(); + final String absolutePath = writeFile(System.getProperty("java.io.tmpdir") + File.separator + filename, + "---- MODULE " + filename + " ----\n" + + "(*\n" + + "--algorithm algo\n" + + "variables foo = 0" + // no optional semicolon + "\n" + + "process bug = 0\n" + // no "fair" statement + "begin\n" + + "L:\n" + + " skip\n" + + "end process\n" + + "\n" + + "end algorithm *)\n" + + "====" + ); + test(filename, absolutePath); + } + + private void test(final String filename, final String absolutePath) { + assertEquals(trans.STATUS_EXIT_WITHOUT_ERROR, trans.runMe(new String[] { "-nocfg", absolutePath })); + + // Parse with SANY and check for errors (collects parse errors into ToolIO.out) + final TestPrintStream testPrintStream = new TestPrintStream(); + ToolIO.out = testPrintStream; + SANY.SANYmain(new String[] { absolutePath }); + testPrintStream.assertSubstring("Semantic processing of module " + filename); + } +} diff --git a/tlatools/test/pcal/PCalModelCheckerTestCase.java b/tlatools/test/pcal/PCalModelCheckerTestCase.java new file mode 100644 index 0000000000000000000000000000000000000000..d683d6576dbca9324c86251556047a26c122f387 --- /dev/null +++ b/tlatools/test/pcal/PCalModelCheckerTestCase.java @@ -0,0 +1,87 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package pcal; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import java.io.File; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import org.junit.Before; + +import tlc2.output.EC; +import tlc2.tool.CommonTestCase; +import tlc2.tool.liveness.ModelCheckerTestCase; +import util.TLAConstants; +import util.ToolIO; + +public abstract class PCalModelCheckerTestCase extends ModelCheckerTestCase { + + private final List<String> pcalArgs = new ArrayList<String>(); + + public PCalModelCheckerTestCase(final String spec, final String path) { + this(spec, path, EC.ExitStatus.SUCCESS); + } + + public PCalModelCheckerTestCase(final String spec, final String path, final String[] extraPcalArgs) { + this(spec, path, EC.ExitStatus.SUCCESS); + this.pcalArgs.addAll(Arrays.asList(extraPcalArgs)); + } + + public PCalModelCheckerTestCase(final String spec, final String path, final int exitStatus) { + super(spec, path, exitStatus); + this.pcalArgs.add("-unixEOL"); + } + + @Before + @Override + public void setUp() { + // Make tool capture the output written to ToolIO.out. Otherwise, + // ToolIO#getAllMessages returns an empty array. + ToolIO.setMode(ToolIO.TOOL); + + // Reset ToolIO for each test case. Otherwise, a test case sees the output of + // the previous tests. + ToolIO.reset(); + + this.pcalArgs.add(CommonTestCase.BASE_PATH + File.separator + path + File.separator + spec + + TLAConstants.Files.TLA_EXTENSION); + + // Run PCal translator + assertEquals(0, trans.runMe(pcalArgs.toArray(new String[pcalArgs.size()]))); + assertNotNull(PcalParams.tlaPcalMapping); // successfully translated PCal to TLA+ + + final String[] messages = ToolIO.getAllMessages(); + assertTrue(Arrays.toString(messages), messages.length == 4 || messages.length == 5); + + // Run TLC + super.setUp(); + } +} diff --git a/tlatools/test/pcal/PCalTest.java b/tlatools/test/pcal/PCalTest.java new file mode 100644 index 0000000000000000000000000000000000000000..8db29ff4c37eea12dcbbbd5e22b46c2d4206ce59 --- /dev/null +++ b/tlatools/test/pcal/PCalTest.java @@ -0,0 +1,69 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package pcal; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; + +import org.junit.Before; + +import util.TLAConstants; +import util.ToolIO; + +public abstract class PCalTest { + + @Before + public void setup() { + // Make tool capture the output written to ToolIO.out. Otherwise, + // ToolIO#getAllMessages returns an empty array. + ToolIO.setMode(ToolIO.TOOL); + + // Reset ToolIO for each test case. Otherwise, a test case sees the output of + // the previous tests. + ToolIO.reset(); + } + + protected static String writeFile(String filename, String content) throws IOException { + final Path path = Files.createFile(Paths.get(filename + TLAConstants.Files.TLA_EXTENSION)); + Files.write(path, content.getBytes()); + + final File file = path.toFile(); + file.deleteOnExit(); + return file.getAbsolutePath(); + } + + protected static String writeTempFile(String filename, String content) throws IOException { + final Path path = Files.createTempFile(filename, TLAConstants.Files.TLA_EXTENSION); + Files.write(path, content.getBytes()); + + final File file = path.toFile(); + file.deleteOnExit(); + return file.getAbsolutePath(); + } +} diff --git a/tlatools/test/tlc2/tool/liveness/Test052.java b/tlatools/test/pcal/PcalPaxosTest.java similarity index 78% rename from tlatools/test/tlc2/tool/liveness/Test052.java rename to tlatools/test/pcal/PcalPaxosTest.java index 8ed0bcf4251bb6f9d5985d00b808c1a80955e626..a8526a2e004d6d5c89afb22ce5f707c65093055e 100644 --- a/tlatools/test/tlc2/tool/liveness/Test052.java +++ b/tlatools/test/pcal/PcalPaxosTest.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2015 Microsoft Research. All rights reserved. + * Copyright (c) 2018 Microsoft Research. All rights reserved. * * The MIT License (MIT) * @@ -23,24 +23,27 @@ * Contributors: * Markus Alexander Kuppe - initial API and implementation ******************************************************************************/ +package pcal; -package tlc2.tool.liveness; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; import tlc2.output.EC; -public class Test052 extends ModelCheckerTestCase { +public class PcalPaxosTest extends PCalModelCheckerTestCase { - public Test052() { - super("test52"); + public PcalPaxosTest() { + super("PcalPaxos", "pcal"); } + @Test public void testSpec() { - // ModelChecker has finished and generated the expected amount of states - assertTrue(recorder.recorded(EC.TLC_MODE_MC)); - assertTrue(recorder.recorded(EC.TLC_FINISHED)); - assertTrue(recorder.recorded(EC.TLC_COMPUTING_INIT)); - assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "2")); assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "1")); - assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "3", "2", "0")); + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertFalse(recorder.recorded(EC.GENERAL)); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "", "", "0")); + assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "")); } } diff --git a/tlatools/test/pcal/PetersonTest.java b/tlatools/test/pcal/PetersonTest.java new file mode 100644 index 0000000000000000000000000000000000000000..6928eb13bcd43aa9a38f1e8eef85a0085a99af47 --- /dev/null +++ b/tlatools/test/pcal/PetersonTest.java @@ -0,0 +1,51 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package pcal; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.output.EC; + +public class PetersonTest extends PCalModelCheckerTestCase { + + public PetersonTest() { + super("Peterson", "pcal"); + } + + @Test + public void testSpec() { + assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "1")); + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertFalse(recorder.recorded(EC.GENERAL)); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "85", "42", "0")); + assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "11")); + + assertZeroUncovered(); + } +} diff --git a/tlatools/test/pcal/Quicksort2ProcsTest.java b/tlatools/test/pcal/Quicksort2ProcsTest.java new file mode 100644 index 0000000000000000000000000000000000000000..2f3b17ac4c23650783d1e08e0a730f2a75db0a18 --- /dev/null +++ b/tlatools/test/pcal/Quicksort2ProcsTest.java @@ -0,0 +1,76 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package pcal; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.output.EC; + +public class Quicksort2ProcsTest extends PCalModelCheckerTestCase { + + public Quicksort2ProcsTest() { + super("Quicksort2Procs", "pcal", new String[] {"-wf", "-termination"}); + } + + @Test + public void testSpec() { + assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "27")); + assertTrue(recorder.recordedWithStringValues(EC.TLC_CHECKING_TEMPORAL_PROPS, "complete", "361")); + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertFalse(recorder.recorded(EC.GENERAL)); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "445", "361", "0")); + assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "15")); + + assertZeroUncovered(); + } +} +/* +C:\lamport\tla\pluscal>java -mx1000m -cp "c:/lamport/tla/newtools/tla2-inria-workspace/tla2-inria/tlatools/class" tlc2.TLC -cleanup Quicksort2Procs.tla +TLC2 Version 2.05 of 18 May 2012 +Running in Model-Checking mode. +Parsing file Quicksort2Procs.tla +Parsing file C:\lamport\tla\newtools\tla2-inria-workspace\tla2-inria\tlatools\class\tla2sany\StandardModules\Naturals.tla +Parsing file C:\lamport\tla\newtools\tla2-inria-workspace\tla2-inria\tlatools\class\tla2sany\StandardModules\Sequences.tla +Semantic processing of module Naturals +Semantic processing of module Sequences +Semantic processing of module Quicksort2Procs +Starting... (2012-08-10 17:38:29) +Implied-temporal checking--satisfiability problem has 1 branches. +Computing initial states... +Finished computing initial states: 27 distinct states generated. +Checking temporal properties for the complete state space... +Model checking completed. No error has been found. + Estimates of the probability that TLC did not check all reachable states + because two distinct states had the same fingerprint: + calculated (optimistic): val = 1.6E-15 + based on the actual fingerprints: val = 1.1E-14 +445 states generated, 361 distinct states found, 0 states left on queue. +The depth of the complete state graph search is 15. +Finished. (2012-08-10 17:38:29) +*/ \ No newline at end of file diff --git a/tlatools/test/pcal/QuicksortMacroTest.java b/tlatools/test/pcal/QuicksortMacroTest.java new file mode 100644 index 0000000000000000000000000000000000000000..19876442cab166a454bbd210ecf71fc9836da533 --- /dev/null +++ b/tlatools/test/pcal/QuicksortMacroTest.java @@ -0,0 +1,76 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package pcal; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.output.EC; + +public class QuicksortMacroTest extends PCalModelCheckerTestCase { + + public QuicksortMacroTest() { + super("QuicksortMacro", "pcal", new String[] {"-wf", "-termination"}); + } + + @Test + public void testSpec() { + assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "27")); + assertTrue(recorder.recordedWithStringValues(EC.TLC_CHECKING_TEMPORAL_PROPS, "complete", "306")); + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertFalse(recorder.recorded(EC.GENERAL)); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "390", "306", "0")); + assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "13")); + + assertZeroUncovered(); + } +} +/* +C:\lamport\tla\pluscal>java -mx1000m -cp "c:/lamport/tla/newtools/tla2-inria-workspace/tla2-inria/tlatools/class" tlc2.TLC -cleanup QuicksortMacro.tla +TLC2 Version 2.05 of 18 May 2012 +Running in Model-Checking mode. +Parsing file QuicksortMacro.tla +Parsing file C:\lamport\tla\newtools\tla2-inria-workspace\tla2-inria\tlatools\class\tla2sany\StandardModules\Naturals.tla +Parsing file C:\lamport\tla\newtools\tla2-inria-workspace\tla2-inria\tlatools\class\tla2sany\StandardModules\Sequences.tla +Semantic processing of module Naturals +Semantic processing of module Sequences +Semantic processing of module QuicksortMacro +Starting... (2012-08-10 17:38:27) +Implied-temporal checking--satisfiability problem has 1 branches. +Computing initial states... +Finished computing initial states: 27 distinct states generated. +Checking temporal properties for the complete state space... +Model checking completed. No error has been found. + Estimates of the probability that TLC did not check all reachable states + because two distinct states had the same fingerprint: + calculated (optimistic): val = 1.4E-15 + based on the actual fingerprints: val = 4.6E-15 +390 states generated, 306 distinct states found, 0 states left on queue. +The depth of the complete state graph search is 13. +Finished. (2012-08-10 17:38:28) +*/ \ No newline at end of file diff --git a/tlatools/test/pcal/QuicksortTest.java b/tlatools/test/pcal/QuicksortTest.java new file mode 100644 index 0000000000000000000000000000000000000000..bc8a6bb67e257c743723346e35dc0d99f3fb7db4 --- /dev/null +++ b/tlatools/test/pcal/QuicksortTest.java @@ -0,0 +1,78 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package pcal; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.output.EC; + +public class QuicksortTest extends PCalModelCheckerTestCase { + + public QuicksortTest() { + super("Quicksort", "pcal", new String[] {"-wf", "-termination"}); + } + + @Test + public void testSpec() { + assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "27")); + assertTrue(recorder.recordedWithStringValues(EC.TLC_CHECKING_TEMPORAL_PROPS, "complete", "933")); + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertFalse(recorder.recorded(EC.GENERAL)); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "1017", "933", "0")); + assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "16")); + + assertZeroUncovered(); + } +} +/* +C:\lamport\tla\pluscal>java -mx1000m -cp "c:/lamport/tla/newtools/tla2-inria-workspace/tla2-inria/tlatools/class" tlc2.TLC -cleanup Quicksort.tla +TLC2 Version 2.05 of 18 May 2012 +Running in Model-Checking mode. +Parsing file Quicksort.tla +Parsing file C:\lamport\tla\newtools\tla2-inria-workspace\tla2-inria\tlatools\class\tla2sany\StandardModules\Naturals.tla +Parsing file C:\lamport\tla\newtools\tla2-inria-workspace\tla2-inria\tlatools\class\tla2sany\StandardModules\Sequences.tla +Parsing file C:\lamport\tla\newtools\tla2-inria-workspace\tla2-inria\tlatools\class\tla2sany\StandardModules\TLC.tla +Semantic processing of module Naturals +Semantic processing of module Sequences +Semantic processing of module TLC +Semantic processing of module Quicksort +Starting... (2012-08-10 17:38:26) +Implied-temporal checking--satisfiability problem has 1 branches. +Computing initial states... +Finished computing initial states: 27 distinct states generated. +Checking temporal properties for the complete state space... +Model checking completed. No error has been found. + Estimates of the probability that TLC did not check all reachable states + because two distinct states had the same fingerprint: + calculated (optimistic): val = 4.2E-15 + based on the actual fingerprints: val = 2.3E-14 +1017 states generated, 933 distinct states found, 0 states left on queue. +The depth of the complete state graph search is 16. +Finished. (2012-08-10 17:38:26) +*/ \ No newline at end of file diff --git a/tlatools/test/pcal/RABTest.java b/tlatools/test/pcal/RABTest.java new file mode 100644 index 0000000000000000000000000000000000000000..a3099d1f12eaaf2365c6ddb6be7c6a5d666e74f3 --- /dev/null +++ b/tlatools/test/pcal/RABTest.java @@ -0,0 +1,248 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package pcal; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.Test; + +import tlc2.output.EC; + +public class RABTest extends PCalModelCheckerTestCase { + + public RABTest() { + super("RAB", "pcal", EC.ExitStatus.VIOLATION_SAFETY); + } + + @Test + public void testSpec() { + assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "4")); + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertFalse(recorder.recorded(EC.GENERAL)); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "551", "350", "130")); + assertEquals(7, recorder.getRecordAsInt(EC.TLC_SEARCH_DEPTH)); + + assertTrue(recorder.recorded(EC.TLC_STATE_PRINT2)); + final List<String> expectedTrace = new ArrayList<String>(); + expectedTrace.add("/\\ myattr = (p0 :> \"A\" @@ p1 :> \"A\")\n" + + "/\\ temp = ( p0 :>\n" + + " [ A |-> [valid |-> FALSE, value |-> FALSE],\n" + + " B |-> [valid |-> FALSE, value |-> FALSE] ] @@\n" + + " p1 :>\n" + + " [ A |-> [valid |-> FALSE, value |-> FALSE],\n" + + " B |-> [valid |-> FALSE, value |-> FALSE] ] )\n" + + "/\\ calc = [A |-> FALSE, B |-> TRUE]\n" + + "/\\ pc = (p0 :> \"Loop\" @@ p1 :> \"Loop\")\n" + + "/\\ flags = [ A |-> [valid |-> FALSE, value |-> FALSE],\n" + + " B |-> [valid |-> FALSE, value |-> FALSE] ]"); + expectedTrace.add("/\\ myattr = (p0 :> \"A\" @@ p1 :> \"A\")\n" + + "/\\ temp = ( p0 :>\n" + + " [ A |-> [valid |-> TRUE, value |-> FALSE],\n" + + " B |-> [valid |-> FALSE, value |-> FALSE] ] @@\n" + + " p1 :>\n" + + " [ A |-> [valid |-> FALSE, value |-> FALSE],\n" + + " B |-> [valid |-> FALSE, value |-> FALSE] ] )\n" + + "/\\ calc = [A |-> FALSE, B |-> TRUE]\n" + + "/\\ pc = (p0 :> \"FetchFlags\" @@ p1 :> \"Loop\")\n" + + "/\\ flags = [ A |-> [valid |-> FALSE, value |-> FALSE],\n" + + " B |-> [valid |-> FALSE, value |-> FALSE] ]"); + expectedTrace.add("/\\ myattr = (p0 :> \"A\" @@ p1 :> \"A\")\n" + + "/\\ temp = ( p0 :>\n" + + " [ A |-> [valid |-> TRUE, value |-> FALSE],\n" + + " B |-> [valid |-> FALSE, value |-> FALSE] ] @@\n" + + " p1 :>\n" + + " [ A |-> [valid |-> FALSE, value |-> FALSE],\n" + + " B |-> [valid |-> FALSE, value |-> FALSE] ] )\n" + + "/\\ calc = [A |-> FALSE, B |-> TRUE]\n" + + "/\\ pc = (p0 :> \"StoreFlags\" @@ p1 :> \"Loop\")\n" + + "/\\ flags = [ A |-> [valid |-> FALSE, value |-> FALSE],\n" + + " B |-> [valid |-> FALSE, value |-> FALSE] ]"); + expectedTrace.add("/\\ myattr = (p0 :> \"A\" @@ p1 :> \"B\")\n" + + "/\\ temp = ( p0 :>\n" + + " [ A |-> [valid |-> TRUE, value |-> FALSE],\n" + + " B |-> [valid |-> FALSE, value |-> FALSE] ] @@\n" + + " p1 :>\n" + + " [ A |-> [valid |-> FALSE, value |-> FALSE],\n" + + " B |-> [valid |-> TRUE, value |-> TRUE] ] )\n" + + "/\\ calc = [A |-> FALSE, B |-> TRUE]\n" + + "/\\ pc = (p0 :> \"StoreFlags\" @@ p1 :> \"FetchFlags\")\n" + + "/\\ flags = [ A |-> [valid |-> FALSE, value |-> FALSE],\n" + + " B |-> [valid |-> FALSE, value |-> FALSE] ]"); + expectedTrace.add("/\\ myattr = (p0 :> \"A\" @@ p1 :> \"B\")\n" + + "/\\ temp = ( p0 :>\n" + + " [ A |-> [valid |-> TRUE, value |-> FALSE],\n" + + " B |-> [valid |-> FALSE, value |-> FALSE] ] @@\n" + + " p1 :>\n" + + " [ A |-> [valid |-> FALSE, value |-> FALSE],\n" + + " B |-> [valid |-> TRUE, value |-> TRUE] ] )\n" + + "/\\ calc = [A |-> FALSE, B |-> TRUE]\n" + + "/\\ pc = (p0 :> \"StoreFlags\" @@ p1 :> \"StoreFlags\")\n" + + "/\\ flags = [ A |-> [valid |-> FALSE, value |-> FALSE],\n" + + " B |-> [valid |-> FALSE, value |-> FALSE] ]"); + expectedTrace.add("/\\ myattr = (p0 :> \"A\" @@ p1 :> \"B\")\n" + + "/\\ temp = ( p0 :>\n" + + " [ A |-> [valid |-> TRUE, value |-> FALSE],\n" + + " B |-> [valid |-> FALSE, value |-> FALSE] ] @@\n" + + " p1 :>\n" + + " [ A |-> [valid |-> FALSE, value |-> FALSE],\n" + + " B |-> [valid |-> TRUE, value |-> TRUE] ] )\n" + + "/\\ calc = [A |-> FALSE, B |-> TRUE]\n" + + "/\\ pc = (p0 :> \"StoreFlags\" @@ p1 :> \"ReadFlags\")\n" + + "/\\ flags = [ A |-> [valid |-> FALSE, value |-> FALSE],\n" + + " B |-> [valid |-> TRUE, value |-> TRUE] ]"); + expectedTrace.add("/\\ myattr = (p0 :> \"A\" @@ p1 :> \"B\")\n" + + "/\\ temp = ( p0 :>\n" + + " [ A |-> [valid |-> TRUE, value |-> FALSE],\n" + + " B |-> [valid |-> FALSE, value |-> FALSE] ] @@\n" + + " p1 :>\n" + + " [ A |-> [valid |-> FALSE, value |-> FALSE],\n" + + " B |-> [valid |-> TRUE, value |-> TRUE] ] )\n" + + "/\\ calc = [A |-> FALSE, B |-> TRUE]\n" + + "/\\ pc = (p0 :> \"ReadFlags\" @@ p1 :> \"ReadFlags\")\n" + + "/\\ flags = [ A |-> [valid |-> TRUE, value |-> FALSE],\n" + + " B |-> [valid |-> FALSE, value |-> FALSE] ]"); + assertTraceWith(recorder.getRecords(EC.TLC_STATE_PRINT2), expectedTrace); + + assertZeroUncovered(); + } +} +/* +C:\lamport\tla\pluscal>java -mx1000m -cp "c:/lamport/tla/newtools/tla2-inria-workspace/tla2-inria/tlatools/class" tlc2.TLC -cleanup RAB.tla +TLC2 Version 2.05 of 18 May 2012 +Running in Model-Checking mode. +Parsing file RAB.tla +Parsing file C:\lamport\tla\newtools\tla2-inria-workspace\tla2-inria\tlatools\class\tla2sany\StandardModules\Naturals.tla +Parsing file C:\lamport\tla\newtools\tla2-inria-workspace\tla2-inria\tlatools\class\tla2sany\StandardModules\Sequences.tla +Parsing file C:\lamport\tla\newtools\tla2-inria-workspace\tla2-inria\tlatools\class\tla2sany\StandardModules\TLC.tla +Semantic processing of module Naturals +Semantic processing of module Sequences +Semantic processing of module TLC +Semantic processing of module RAB +Starting... (2012-08-10 17:38:30) +Computing initial states... +Finished computing initial states: 4 distinct states generated. +Error: Invariant Consistency is violated. +Error: The behavior up to this point is: +State 1: <Initial predicate> +/\ myattr = (p0 :> "A" @@ p1 :> "A") +/\ temp = ( p0 :> + [ A |-> [valid |-> FALSE, value |-> FALSE], + B |-> [valid |-> FALSE, value |-> FALSE] ] @@ + p1 :> + [ A |-> [valid |-> FALSE, value |-> FALSE], + B |-> [valid |-> FALSE, value |-> FALSE] ] ) +/\ calc = [A |-> FALSE, B |-> TRUE] +/\ pc = (p0 :> "Loop" @@ p1 :> "Loop") +/\ flags = [ A |-> [valid |-> FALSE, value |-> FALSE], + B |-> [valid |-> FALSE, value |-> FALSE] ] + +State 2: <Action line 163, col 15 to line 175, col 44 of module RAB> +/\ myattr = (p0 :> "A" @@ p1 :> "A") +/\ temp = ( p0 :> + [ A |-> [valid |-> TRUE, value |-> FALSE], + B |-> [valid |-> FALSE, value |-> FALSE] ] @@ + p1 :> + [ A |-> [valid |-> FALSE, value |-> FALSE], + B |-> [valid |-> FALSE, value |-> FALSE] ] ) +/\ calc = [A |-> FALSE, B |-> TRUE] +/\ pc = (p0 :> "FetchFlags" @@ p1 :> "Loop") +/\ flags = [ A |-> [valid |-> FALSE, value |-> FALSE], + B |-> [valid |-> FALSE, value |-> FALSE] ] + +State 3: <Action line 182, col 21 to line 185, col 58 of module RAB> +/\ myattr = (p0 :> "A" @@ p1 :> "A") +/\ temp = ( p0 :> + [ A |-> [valid |-> TRUE, value |-> FALSE], + B |-> [valid |-> FALSE, value |-> FALSE] ] @@ + p1 :> + [ A |-> [valid |-> FALSE, value |-> FALSE], + B |-> [valid |-> FALSE, value |-> FALSE] ] ) +/\ calc = [A |-> FALSE, B |-> TRUE] +/\ pc = (p0 :> "StoreFlags" @@ p1 :> "Loop") +/\ flags = [ A |-> [valid |-> FALSE, value |-> FALSE], + B |-> [valid |-> FALSE, value |-> FALSE] ] + +State 4: <Action line 163, col 15 to line 175, col 44 of module RAB> +/\ myattr = (p0 :> "A" @@ p1 :> "B") +/\ temp = ( p0 :> + [ A |-> [valid |-> TRUE, value |-> FALSE], + B |-> [valid |-> FALSE, value |-> FALSE] ] @@ + p1 :> + [ A |-> [valid |-> FALSE, value |-> FALSE], + B |-> [valid |-> TRUE, value |-> TRUE] ] ) +/\ calc = [A |-> FALSE, B |-> TRUE] +/\ pc = (p0 :> "StoreFlags" @@ p1 :> "FetchFlags") +/\ flags = [ A |-> [valid |-> FALSE, value |-> FALSE], + B |-> [valid |-> FALSE, value |-> FALSE] ] + +State 5: <Action line 182, col 21 to line 185, col 58 of module RAB> +/\ myattr = (p0 :> "A" @@ p1 :> "B") +/\ temp = ( p0 :> + [ A |-> [valid |-> TRUE, value |-> FALSE], + B |-> [valid |-> FALSE, value |-> FALSE] ] @@ + p1 :> + [ A |-> [valid |-> FALSE, value |-> FALSE], + B |-> [valid |-> TRUE, value |-> TRUE] ] ) +/\ calc = [A |-> FALSE, B |-> TRUE] +/\ pc = (p0 :> "StoreFlags" @@ p1 :> "StoreFlags") +/\ flags = [ A |-> [valid |-> FALSE, value |-> FALSE], + B |-> [valid |-> FALSE, value |-> FALSE] ] + +State 6: <Action line 187, col 21 to line 190, col 57 of module RAB> +/\ myattr = (p0 :> "A" @@ p1 :> "B") +/\ temp = ( p0 :> + [ A |-> [valid |-> TRUE, value |-> FALSE], + B |-> [valid |-> FALSE, value |-> FALSE] ] @@ + p1 :> + [ A |-> [valid |-> FALSE, value |-> FALSE], + B |-> [valid |-> TRUE, value |-> TRUE] ] ) +/\ calc = [A |-> FALSE, B |-> TRUE] +/\ pc = (p0 :> "StoreFlags" @@ p1 :> "ReadFlags") +/\ flags = [ A |-> [valid |-> FALSE, value |-> FALSE], + B |-> [valid |-> TRUE, value |-> TRUE] ] + +State 7: <Action line 187, col 21 to line 190, col 57 of module RAB> +/\ myattr = (p0 :> "A" @@ p1 :> "B") +/\ temp = ( p0 :> + [ A |-> [valid |-> TRUE, value |-> FALSE], + B |-> [valid |-> FALSE, value |-> FALSE] ] @@ + p1 :> + [ A |-> [valid |-> FALSE, value |-> FALSE], + B |-> [valid |-> TRUE, value |-> TRUE] ] ) +/\ calc = [A |-> FALSE, B |-> TRUE] +/\ pc = (p0 :> "ReadFlags" @@ p1 :> "ReadFlags") +/\ flags = [ A |-> [valid |-> TRUE, value |-> FALSE], + B |-> [valid |-> FALSE, value |-> FALSE] ] + +551 states generated, 350 distinct states found, 131 states left on queue. +The depth of the complete state graph search is 7. +Finished. (2012-08-10 17:38:30) +*/ \ No newline at end of file diff --git a/tlatools/test/pcal/RealQuicksort2Test.java b/tlatools/test/pcal/RealQuicksort2Test.java new file mode 100644 index 0000000000000000000000000000000000000000..ea808b8e35609fc52d7ac42321572d515bda2e42 --- /dev/null +++ b/tlatools/test/pcal/RealQuicksort2Test.java @@ -0,0 +1,78 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package pcal; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.output.EC; + +public class RealQuicksort2Test extends PCalModelCheckerTestCase { + + public RealQuicksort2Test() { + super("RealQuicksort2", "pcal", new String[] {"-wf", "-termination"}); + } + + @Test + public void testSpec() { + assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "33")); + assertTrue(recorder.recordedWithStringValues(EC.TLC_CHECKING_TEMPORAL_PROPS, "complete", "612")); + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertFalse(recorder.recorded(EC.GENERAL)); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "853", "612", "0")); + assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "11")); + + assertZeroUncovered(); + } +} +/* +C:\lamport\tla\pluscal>java -mx1000m -cp "c:/lamport/tla/newtools/tla2-inria-workspace/tla2-inria/tlatools/class" tlc2.TLC -cleanup RealQuicksort2.tla +TLC2 Version 2.05 of 18 May 2012 +Running in Model-Checking mode. +Parsing file RealQuicksort2.tla +Parsing file C:\lamport\tla\newtools\tla2-inria-workspace\tla2-inria\tlatools\class\tla2sany\StandardModules\Naturals.tla +Parsing file C:\lamport\tla\newtools\tla2-inria-workspace\tla2-inria\tlatools\class\tla2sany\StandardModules\Sequences.tla +Parsing file C:\lamport\tla\newtools\tla2-inria-workspace\tla2-inria\tlatools\class\tla2sany\StandardModules\FiniteSets.tla +Semantic processing of module Naturals +Semantic processing of module Sequences +Semantic processing of module FiniteSets +Semantic processing of module RealQuicksort2 +Starting... (2012-08-10 17:38:33) +Implied-temporal checking--satisfiability problem has 1 branches. +Computing initial states... +Finished computing initial states: 33 distinct states generated. +Checking temporal properties for the complete state space... +Model checking completed. No error has been found. + Estimates of the probability that TLC did not check all reachable states + because two distinct states had the same fingerprint: + calculated (optimistic): val = 8.0E-15 + based on the actual fingerprints: val = 2.2E-14 +853 states generated, 612 distinct states found, 0 states left on queue. +The depth of the complete state graph search is 11. +Finished. (2012-08-10 17:38:33) +*/ \ No newline at end of file diff --git a/tlatools/test/pcal/RealQuicksortTest.java b/tlatools/test/pcal/RealQuicksortTest.java new file mode 100644 index 0000000000000000000000000000000000000000..c7d0548c4dffd809d214b431802a93dc25d5f1c3 --- /dev/null +++ b/tlatools/test/pcal/RealQuicksortTest.java @@ -0,0 +1,78 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package pcal; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.output.EC; + +public class RealQuicksortTest extends PCalModelCheckerTestCase { + + public RealQuicksortTest() { + super("RealQuicksort", "pcal", new String[] {"-wf", "-termination"}); + } + + @Test + public void testSpec() { + assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "33")); + assertTrue(recorder.recordedWithStringValues(EC.TLC_CHECKING_TEMPORAL_PROPS, "complete", "495")); + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertFalse(recorder.recorded(EC.GENERAL)); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "706", "495", "0")); + assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "14")); + + assertZeroUncovered(); + } +} +/* +C:\lamport\tla\pluscal>java -mx1000m -cp "c:/lamport/tla/newtools/tla2-inria-workspace/tla2-inria/tlatools/class" tlc2.TLC -cleanup RealQuicksort.tla +TLC2 Version 2.05 of 18 May 2012 +Running in Model-Checking mode. +Parsing file RealQuicksort.tla +Parsing file C:\lamport\tla\newtools\tla2-inria-workspace\tla2-inria\tlatools\class\tla2sany\StandardModules\Naturals.tla +Parsing file C:\lamport\tla\newtools\tla2-inria-workspace\tla2-inria\tlatools\class\tla2sany\StandardModules\Sequences.tla +Parsing file C:\lamport\tla\newtools\tla2-inria-workspace\tla2-inria\tlatools\class\tla2sany\StandardModules\FiniteSets.tla +Semantic processing of module Naturals +Semantic processing of module Sequences +Semantic processing of module FiniteSets +Semantic processing of module RealQuicksort +Starting... (2012-08-10 17:38:31) +Implied-temporal checking--satisfiability problem has 1 branches. +Computing initial states... +Finished computing initial states: 33 distinct states generated. +Checking temporal properties for the complete state space... +Model checking completed. No error has been found. + Estimates of the probability that TLC did not check all reachable states + because two distinct states had the same fingerprint: + calculated (optimistic): val = 5.7E-15 + based on the actual fingerprints: val = 2.7E-15 +706 states generated, 495 distinct states found, 0 states left on queue. +The depth of the complete state graph search is 14. +Finished. (2012-08-10 17:38:32) +*/ \ No newline at end of file diff --git a/tlatools/test/pcal/ReallySimpleMultiProcTest.java b/tlatools/test/pcal/ReallySimpleMultiProcTest.java new file mode 100644 index 0000000000000000000000000000000000000000..2c2ed58b2f4cfb07b427cb5d97fab2d8f44e5a43 --- /dev/null +++ b/tlatools/test/pcal/ReallySimpleMultiProcTest.java @@ -0,0 +1,52 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package pcal; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.output.EC; + +public class ReallySimpleMultiProcTest extends PCalModelCheckerTestCase { + + public ReallySimpleMultiProcTest() { + super("ReallySimpleMultiProc", "pcal", new String[] {"-wf", "-termination"}); + } + + @Test + public void testSpec() { + assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "4")); + assertTrue(recorder.recordedWithStringValues(EC.TLC_CHECKING_TEMPORAL_PROPS, "complete", "76")); + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertFalse(recorder.recorded(EC.GENERAL)); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "144", "76", "0")); + assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "7")); + + assertZeroUncovered(); + } +} diff --git a/tlatools/test/pcal/SBBTest.java b/tlatools/test/pcal/SBBTest.java new file mode 100644 index 0000000000000000000000000000000000000000..f9c4074439642a999e0bf7aa127e9cfb9ca4a78b --- /dev/null +++ b/tlatools/test/pcal/SBBTest.java @@ -0,0 +1,150 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package pcal; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.Test; + +import tlc2.output.EC; +import tlc2.output.EC.ExitStatus; + +public class SBBTest extends PCalModelCheckerTestCase { + + public SBBTest() { + super("SBB", "pcal", ExitStatus.VIOLATION_SAFETY); + } + + @Test + public void testSpec() { + assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "1")); + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertFalse(recorder.recorded(EC.GENERAL)); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "3126", "1617", "328")); + assertEquals(15, recorder.getRecordAsInt(EC.TLC_SEARCH_DEPTH)); + + assertTrue(recorder.recorded(EC.TLC_STATE_PRINT2)); + final List<String> expectedTrace = new ArrayList<String>(); + expectedTrace.add("/\\ availablebuffers = {b1, b2, b3}\n" + + "/\\ publishedbuffers = {}\n" + + "/\\ sb = [buf |-> b0, owner |-> NoPid]\n" + + "/\\ buf = (p0 :> NoBuf @@ p1 :> NoBuf)\n" + + "/\\ op = (p0 :> {} @@ p1 :> {})\n" + + "/\\ pc = (p0 :> \"Loop\" @@ p1 :> \"Loop\")"); + expectedTrace.add("/\\ availablebuffers = {b1, b2, b3}\n" + + "/\\ publishedbuffers = {}\n" + + "/\\ sb = [buf |-> b0, owner |-> NoPid]\n" + + "/\\ buf = (p0 :> b0 @@ p1 :> NoBuf)\n" + + "/\\ op = (p0 :> \"Publish\" @@ p1 :> {})\n" + + "/\\ pc = (p0 :> \"Publish1\" @@ p1 :> \"Loop\")"); + expectedTrace.add("/\\ availablebuffers = {b1, b2, b3}\n" + + "/\\ publishedbuffers = {}\n" + + "/\\ sb = [buf |-> b0, owner |-> NoPid]\n" + + "/\\ buf = (p0 :> b0 @@ p1 :> NoBuf)\n" + + "/\\ op = (p0 :> \"Publish\" @@ p1 :> {})\n" + + "/\\ pc = (p0 :> \"Publish2\" @@ p1 :> \"Loop\")"); + expectedTrace.add("/\\ availablebuffers = {b1, b2, b3}\n" + + "/\\ publishedbuffers = {}\n" + + "/\\ sb = [buf |-> b0, owner |-> NoPid]\n" + + "/\\ buf = (p0 :> b0 @@ p1 :> b0)\n" + + "/\\ op = (p0 :> \"Publish\" @@ p1 :> \"Modify\")\n" + + "/\\ pc = (p0 :> \"Publish2\" @@ p1 :> \"Modify1\")"); + expectedTrace.add("/\\ availablebuffers = {b2, b3}\n" + + "/\\ publishedbuffers = {}\n" + + "/\\ sb = [buf |-> b0, owner |-> NoPid]\n" + + "/\\ buf = (p0 :> b0 @@ p1 :> b1)\n" + + "/\\ op = (p0 :> \"Publish\" @@ p1 :> \"Modify\")\n" + + "/\\ pc = (p0 :> \"Publish2\" @@ p1 :> \"Modify2\")"); + expectedTrace.add("/\\ availablebuffers = {b2, b3}\n" + + "/\\ publishedbuffers = {}\n" + + "/\\ sb = [buf |-> b0, owner |-> p1]\n" + + "/\\ buf = (p0 :> b0 @@ p1 :> b1)\n" + + "/\\ op = (p0 :> \"Publish\" @@ p1 :> \"Modify\")\n" + + "/\\ pc = (p0 :> \"Publish2\" @@ p1 :> \"Modify3\")"); + expectedTrace.add("/\\ availablebuffers = {b2, b3}\n" + + "/\\ publishedbuffers = {}\n" + + "/\\ sb = [buf |-> b1, owner |-> p1]\n" + + "/\\ buf = (p0 :> b0 @@ p1 :> b1)\n" + + "/\\ op = (p0 :> \"Publish\" @@ p1 :> \"Modify\")\n" + + "/\\ pc = (p0 :> \"Publish2\" @@ p1 :> \"Loop\")"); + expectedTrace.add("/\\ availablebuffers = {b2, b3}\n" + + "/\\ publishedbuffers = {}\n" + + "/\\ sb = [buf |-> b1, owner |-> p1]\n" + + "/\\ buf = (p0 :> b0 @@ p1 :> b1)\n" + + "/\\ op = (p0 :> \"Publish\" @@ p1 :> \"Modify\")\n" + + "/\\ pc = (p0 :> \"Publish2\" @@ p1 :> \"Modify1\")"); + expectedTrace.add("/\\ availablebuffers = {b2, b3}\n" + + "/\\ publishedbuffers = {}\n" + + "/\\ sb = [buf |-> b1, owner |-> p1]\n" + + "/\\ buf = (p0 :> b0 @@ p1 :> b1)\n" + + "/\\ op = (p0 :> \"Publish\" @@ p1 :> \"Modify\")\n" + + "/\\ pc = (p0 :> \"Publish2\" @@ p1 :> \"Modify2\")"); + expectedTrace.add("/\\ availablebuffers = {b2, b3}\n" + + "/\\ publishedbuffers = {}\n" + + "/\\ sb = [buf |-> b1, owner |-> NoPid]\n" + + "/\\ buf = (p0 :> b0 @@ p1 :> b1)\n" + + "/\\ op = (p0 :> \"Publish\" @@ p1 :> \"Modify\")\n" + + "/\\ pc = (p0 :> \"Publish3\" @@ p1 :> \"Modify2\")"); + expectedTrace.add("/\\ availablebuffers = {b2, b3}\n" + + "/\\ publishedbuffers = {b0}\n" + + "/\\ sb = [buf |-> b1, owner |-> NoPid]\n" + + "/\\ buf = (p0 :> b0 @@ p1 :> b1)\n" + + "/\\ op = (p0 :> \"Publish\" @@ p1 :> \"Modify\")\n" + + "/\\ pc = (p0 :> \"Loop\" @@ p1 :> \"Modify2\")"); + expectedTrace.add("/\\ availablebuffers = {b2, b3}\n" + + "/\\ publishedbuffers = {b0}\n" + + "/\\ sb = [buf |-> b1, owner |-> NoPid]\n" + + "/\\ buf = (p0 :> b1 @@ p1 :> b1)\n" + + "/\\ op = (p0 :> \"Publish\" @@ p1 :> \"Modify\")\n" + + "/\\ pc = (p0 :> \"Publish1\" @@ p1 :> \"Modify2\")"); + expectedTrace.add("/\\ availablebuffers = {b2, b3}\n" + + "/\\ publishedbuffers = {b0}\n" + + "/\\ sb = [buf |-> b1, owner |-> NoPid]\n" + + "/\\ buf = (p0 :> b1 @@ p1 :> b1)\n" + + "/\\ op = (p0 :> \"Publish\" @@ p1 :> \"Modify\")\n" + + "/\\ pc = (p0 :> \"Publish2\" @@ p1 :> \"Modify2\")"); + expectedTrace.add("/\\ availablebuffers = {b2, b3}\n" + + "/\\ publishedbuffers = {b0}\n" + + "/\\ sb = [buf |-> b1, owner |-> NoPid]\n" + + "/\\ buf = (p0 :> b1 @@ p1 :> b1)\n" + + "/\\ op = (p0 :> \"Publish\" @@ p1 :> \"Modify\")\n" + + "/\\ pc = (p0 :> \"Publish3\" @@ p1 :> \"Modify2\")"); + expectedTrace.add("/\\ availablebuffers = {b2, b3}\n" + + "/\\ publishedbuffers = {b0, b1}\n" + + "/\\ sb = [buf |-> b1, owner |-> NoPid]\n" + + "/\\ buf = (p0 :> b1 @@ p1 :> b1)\n" + + "/\\ op = (p0 :> \"Publish\" @@ p1 :> \"Modify\")\n" + + "/\\ pc = (p0 :> \"Loop\" @@ p1 :> \"Modify2\")"); + assertTraceWith(recorder.getRecords(EC.TLC_STATE_PRINT2), expectedTrace); + + assertZeroUncovered(); + } +} diff --git a/tlatools/test/pcal/SemaphoreMutexTest.java b/tlatools/test/pcal/SemaphoreMutexTest.java new file mode 100644 index 0000000000000000000000000000000000000000..bab0bb2def3a75de5850115ebb55ea8d55cf95c3 --- /dev/null +++ b/tlatools/test/pcal/SemaphoreMutexTest.java @@ -0,0 +1,74 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package pcal; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.output.EC; + +public class SemaphoreMutexTest extends PCalModelCheckerTestCase { + + public SemaphoreMutexTest() { + super("SemaphoreMutex", "pcal", new String[] {"-sf"}); + } + + @Test + public void testSpec() { + assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "1")); + assertTrue(recorder.recordedWithStringValues(EC.TLC_CHECKING_TEMPORAL_PROPS, "complete", "32")); + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertFalse(recorder.recorded(EC.GENERAL)); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "73", "32", "0")); + assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "6")); + + assertZeroUncovered(); + } +} +/* +C:\lamport\tla\pluscal>java -mx1000m -cp "c:/lamport/tla/newtools/tla2-inria-workspace/tla2-inria/tlatools/class" tlc2.TLC -cleanup SemaphoreMutex.tla +TLC2 Version 2.05 of 18 May 2012 +Running in Model-Checking mode. +Parsing file SemaphoreMutex.tla +Parsing file C:\lamport\tla\newtools\tla2-inria-workspace\tla2-inria\tlatools\class\tla2sany\StandardModules\Naturals.tla +Semantic processing of module Naturals +Semantic processing of module SemaphoreMutex +Starting... (2012-08-10 17:38:36) +Implied-temporal checking--satisfiability problem has 1 branches. +Computing initial states... +Finished computing initial states: 1 distinct state generated. +Checking temporal properties for the complete state space... +Model checking completed. No error has been found. + Estimates of the probability that TLC did not check all reachable states + because two distinct states had the same fingerprint: + calculated (optimistic): val = 7.1E-17 + based on the actual fingerprints: val = 2.9E-15 +73 states generated, 32 distinct states found, 0 states left on queue. +The depth of the complete state graph search is 6. +Finished. (2012-08-10 17:38:36) +*/ \ No newline at end of file diff --git a/tlatools/test/pcal/SimpleLoopTest.java b/tlatools/test/pcal/SimpleLoopTest.java new file mode 100644 index 0000000000000000000000000000000000000000..81e655475b07724106015170b4d7add0bd0f217e --- /dev/null +++ b/tlatools/test/pcal/SimpleLoopTest.java @@ -0,0 +1,78 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package pcal; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.output.EC; + +public class SimpleLoopTest extends PCalModelCheckerTestCase { + + public SimpleLoopTest() { + super("SimpleLoop", "pcal", new String[] {"-wf", "-termination"}); + } + + @Test + public void testSpec() { + assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "1")); + assertTrue(recorder.recordedWithStringValues(EC.TLC_CHECKING_TEMPORAL_PROPS, "complete", "12")); + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertFalse(recorder.recorded(EC.GENERAL)); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "13", "12", "0")); + assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "12")); + + assertZeroUncovered(); + } +} +/* +C:\lamport\tla\pluscal>java -mx1000m -cp "c:/lamport/tla/newtools/tla2-inria-workspace/tla2-inria/tlatools/class" tlc2.TLC -cleanup SimpleLoop.tla +TLC2 Version 2.05 of 18 May 2012 +Running in Model-Checking mode. +Parsing file SimpleLoop.tla +Parsing file C:\lamport\tla\newtools\tla2-inria-workspace\tla2-inria\tlatools\class\tla2sany\StandardModules\Naturals.tla +Parsing file C:\lamport\tla\newtools\tla2-inria-workspace\tla2-inria\tlatools\class\tla2sany\StandardModules\TLC.tla +Parsing file C:\lamport\tla\newtools\tla2-inria-workspace\tla2-inria\tlatools\class\tla2sany\StandardModules\Sequences.tla +Semantic processing of module Naturals +Semantic processing of module Sequences +Semantic processing of module TLC +Semantic processing of module SimpleLoop +Starting... (2012-08-10 17:38:37) +Implied-temporal checking--satisfiability problem has 1 branches. +Computing initial states... +Finished computing initial states: 1 distinct state generated. +Checking temporal properties for the complete state space... +Model checking completed. No error has been found. + Estimates of the probability that TLC did not check all reachable states + because two distinct states had the same fingerprint: + calculated (optimistic): val = 6.5E-19 + based on the actual fingerprints: val = 2.1E-18 +13 states generated, 12 distinct states found, 0 states left on queue. +The depth of the complete state graph search is 12. +Finished. (2012-08-10 17:38:37) +*/ \ No newline at end of file diff --git a/tlatools/test/pcal/SimpleLoopWithProcedureTest.java b/tlatools/test/pcal/SimpleLoopWithProcedureTest.java new file mode 100644 index 0000000000000000000000000000000000000000..6d03a43224c5eb7484d02fa39dd0ea93d8e83aa5 --- /dev/null +++ b/tlatools/test/pcal/SimpleLoopWithProcedureTest.java @@ -0,0 +1,158 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package pcal; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.output.EC; + +public class SimpleLoopWithProcedureTest extends PCalModelCheckerTestCase { + + public SimpleLoopWithProcedureTest() { + super("SimpleLoopWithProcedure", "pcal", new String[] {"-wf", "-termination"}); + } + + @Test + public void testSpec() { + assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "2")); + assertTrue(recorder.recordedWithStringValues(EC.TLC_CHECKING_TEMPORAL_PROPS, "complete", "64")); + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertFalse(recorder.recorded(EC.GENERAL)); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "66", "64", "0")); + assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "32")); + + assertZeroUncovered(); + } +} +/* +C:\lamport\tla\pluscal>java -mx1000m -cp "c:/lamport/tla/newtools/tla2-inria-workspace/tla2-inria/tlatools/class" tlc2.TLC -cleanup SimpleLoopWithProcedure.tla +TLC2 Version 2.05 of 18 May 2012 +Running in Model-Checking mode. +Parsing file SimpleLoopWithProcedure.tla +Parsing file C:\lamport\tla\newtools\tla2-inria-workspace\tla2-inria\tlatools\class\tla2sany\StandardModules\Naturals.tla +Parsing file C:\lamport\tla\newtools\tla2-inria-workspace\tla2-inria\tlatools\class\tla2sany\StandardModules\Sequences.tla +Parsing file C:\lamport\tla\newtools\tla2-inria-workspace\tla2-inria\tlatools\class\tla2sany\StandardModules\TLC.tla +Semantic processing of module Naturals +Semantic processing of module Sequences +Semantic processing of module TLC +Semantic processing of module SimpleLoopWithProcedure +Starting... (2012-08-10 17:38:38) +Implied-temporal checking--satisfiability problem has 1 branches. +Computing initial states... +Finished computing initial states: 2 distinct states generated. +0 TRUE +0 TRUE +0 TRUE +0 TRUE +0 TRUE +0 TRUE +0 TRUE +0 TRUE +3 TRUE +3 TRUE +3 TRUE +3 TRUE +4 TRUE +4 TRUE +4 TRUE +4 TRUE +6 TRUE +6 TRUE +6 TRUE +6 TRUE +8 TRUE +8 TRUE +8 TRUE +8 TRUE +9 TRUE +9 TRUE +9 TRUE +9 TRUE +12 TRUE +12 TRUE +12 TRUE +12 TRUE +12 TRUE +12 TRUE +12 TRUE +12 TRUE +16 TRUE +16 TRUE +16 TRUE +16 TRUE +15 TRUE +15 TRUE +15 TRUE +15 TRUE +20 TRUE +20 TRUE +20 TRUE +20 TRUE +18 TRUE +18 TRUE +18 TRUE +18 TRUE +24 TRUE +24 TRUE +24 TRUE +24 TRUE +21 TRUE +21 TRUE +21 TRUE +21 TRUE +28 TRUE +28 TRUE +28 TRUE +28 TRUE +24 TRUE +24 TRUE +24 TRUE +24 TRUE +32 TRUE +32 TRUE +32 TRUE +32 TRUE +27 TRUE +27 TRUE +27 TRUE +27 TRUE +36 TRUE +36 TRUE +36 TRUE +36 TRUE +Checking temporal properties for the complete state space... +Model checking completed. No error has been found. + Estimates of the probability that TLC did not check all reachable states + because two distinct states had the same fingerprint: + calculated (optimistic): val = 6.9E-18 + based on the actual fingerprints: val = 8.3E-16 +66 states generated, 64 distinct states found, 0 states left on queue. +The depth of the complete state graph search is 32. +Finished. (2012-08-10 17:38:39) +*/ \ No newline at end of file diff --git a/tlatools/test/pcal/SimpleMultiProcTest.java b/tlatools/test/pcal/SimpleMultiProcTest.java new file mode 100644 index 0000000000000000000000000000000000000000..597ed04223c45cc6dfb1b3952c57b23850505524 --- /dev/null +++ b/tlatools/test/pcal/SimpleMultiProcTest.java @@ -0,0 +1,77 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package pcal; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.output.EC; + +public class SimpleMultiProcTest extends PCalModelCheckerTestCase { + + public SimpleMultiProcTest() { + super("SimpleMultiProc", "pcal", new String[] {"-wf", "-termination"}); + } + + @Test + public void testSpec() { + assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "16")); + assertTrue(recorder.recordedWithStringValues(EC.TLC_CHECKING_TEMPORAL_PROPS, "complete", "103944")); + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertFalse(recorder.recorded(EC.GENERAL)); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "369680", "103944", "0")); + assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "17")); + } +} +/* +C:\lamport\tla\pluscal>java -mx1000m -cp "c:/lamport/tla/newtools/tla2-inria-workspace/tla2-inria/tlatools/class" tlc2.TLC -cleanup SimpleMultiProc.tla +TLC2 Version 2.05 of 18 May 2012 +Running in Model-Checking mode. +Parsing file SimpleMultiProc.tla +Parsing file C:\lamport\tla\newtools\tla2-inria-workspace\tla2-inria\tlatools\class\tla2sany\StandardModules\Naturals.tla +Parsing file C:\lamport\tla\newtools\tla2-inria-workspace\tla2-inria\tlatools\class\tla2sany\StandardModules\Sequences.tla +Parsing file C:\lamport\tla\newtools\tla2-inria-workspace\tla2-inria\tlatools\class\tla2sany\StandardModules\TLC.tla +Semantic processing of module Naturals +Semantic processing of module Sequences +Semantic processing of module TLC +Semantic processing of module SimpleMultiProc +Starting... (2012-08-10 17:38:40) +Implied-temporal checking--satisfiability problem has 1 branches. +Computing initial states... +Finished computing initial states: 16 distinct states generated. +Progress(7) at 2012-08-10 17:38:43: 27922 states generated (27922 s/min), 12586 distinct states found (12586 ds/min), 6768 states left on queue. +Checking temporal properties for the complete state space... +Model checking completed. No error has been found. + Estimates of the probability that TLC did not check all reachable states + because two distinct states had the same fingerprint: + calculated (optimistic): val = 1.5E-9 + based on the actual fingerprints: val = 4.7E-10 +369680 states generated, 103944 distinct states found, 0 states left on queue. +The depth of the complete state graph search is 17. +Finished. (2012-08-10 17:39:23) +*/ \ No newline at end of file diff --git a/tlatools/test/pcal/StackTestTest.java b/tlatools/test/pcal/StackTestTest.java new file mode 100644 index 0000000000000000000000000000000000000000..752db590030723b19edf95465c80dd5af1161e41 --- /dev/null +++ b/tlatools/test/pcal/StackTestTest.java @@ -0,0 +1,98 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package pcal; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.output.EC; + +public class StackTestTest extends PCalModelCheckerTestCase { + + public StackTestTest() { + super("StackTest", "pcal"); + } + + @Test + public void testSpec() { + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertFalse(recorder.recorded(EC.GENERAL)); + } +} +/* +TLC2 Version 2.12 of 29 January 2018 +Running breadth-first search Model-Checking with 1 worker on 4 cores with 5303MB heap and 64MB offheap memory (Linux 4.15.0-22-generic amd64, Oracle Corporation 1.8.0_171 x86_64). +Parsing file /home/markus/Desktop/LesliePCalTest/alltests/StackTest.tla +Parsing file /home/markus/src/TLA/tla/tlatools/class/tla2sany/StandardModules/Sequences.tla +Parsing file /home/markus/src/TLA/tla/tlatools/class/tla2sany/StandardModules/Naturals.tla +Parsing file /home/markus/src/TLA/tla/tlatools/class/tla2sany/StandardModules/TLC.tla +Semantic processing of module Naturals +Semantic processing of module Sequences +Semantic processing of module TLC +Semantic processing of module StackTest +Starting... (2018-05-23 12:30:31) +Computing initial states... +Finished computing initial states: 1 distinct state generated. +Warning: The EXCEPT was applied to non-existing fields of the value at +line 36, col 26 to line 36, col 30 of module StackTest +(Use the -nowarning option to disable this warning.) +Error: TLC threw an unexpected exception. +This was probably caused by an error in the spec or model. +See the User Output or TLC Console for clues to what happened. +The exception was a tlc2.tool.EvalException +: Attempted to apply the operator overridden by the Java method +public static tlc2.value.Value tlc2.module.TLC.Assert(tlc2.value.Value,tlc2.value.Value), +but it produced the following error: +The first argument of Assert evaluated to FALSE; the second argument was: +"Failure of assertion at line 13, column 17." +Error: The behavior up to this point is: +State 1: <Initial predicate> +/\ stack = (0 :> <<>> @@ 1 :> <<>> @@ 2 :> <<>>) +/\ a = (0 :> 42 @@ 1 :> 42 @@ 2 :> 42) +/\ pc = (0 :> "Q1" @@ 1 :> "Q1" @@ 2 :> "Q1") + +State 2: <Q1 line 51, col 13 to line 59, col 47 of module StackTest> +/\ stack = ( 0 :> <<[pc |-> "Done", a |-> 42, procedure |-> "P"]>> @@ + 1 :> <<>> @@ + 2 :> <<>> ) +/\ a = (0 :> 22 @@ 1 :> 42 @@ 2 :> 42) +/\ pc = (0 :> "P1" @@ 1 :> "Q1" @@ 2 :> "Q1") + +Error: The error occurred when TLC was evaluating the nested +expressions at the following positions: +0. Line 35, column 13 to line 42, column 21 in StackTest +1. Line 35, column 16 to line 35, column 30 in StackTest +2. Line 36, column 16 to line 36, column 80 in StackTest +3. Line 37, column 16 to line 38, column 68 in StackTest + + +4 states generated, 4 distinct states found, 2 states left on queue. +The depth of the complete state graph search is 2. +The average outdegree of the complete state graph is 3 (minimum is 3, the maximum 3 and the 95th percentile is 3). +Finished in 00s at (2018-05-23 12:30:31) +*/ \ No newline at end of file diff --git a/tlatools/test/pcal/StarkMutexTest.java b/tlatools/test/pcal/StarkMutexTest.java new file mode 100644 index 0000000000000000000000000000000000000000..5e7f49fd994922090e50ece44a34444bb561a2e2 --- /dev/null +++ b/tlatools/test/pcal/StarkMutexTest.java @@ -0,0 +1,75 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package pcal; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.output.EC; + +public class StarkMutexTest extends PCalModelCheckerTestCase { + + public StarkMutexTest() { + super("StarkMutex", "pcal"); + } + + @Test + public void testSpec() { + assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "1")); + assertTrue(recorder.recordedWithStringValues(EC.TLC_CHECKING_TEMPORAL_PROPS, "complete", "69504", "3 branches of ")); + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertFalse(recorder.recorded(EC.GENERAL)); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "54809", "23168", "0")); + assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "92")); + } +} +/* +C:\lamport\tla\pluscal>java -mx1000m -cp "c:/lamport/tla/newtools/tla2-inria-workspace/tla2-inria/tlatools/class" tlc2.TLC -cleanup StarkMutex.tla +TLC2 Version 2.05 of 18 May 2012 +Running in Model-Checking mode. +Parsing file StarkMutex.tla +Parsing file C:\lamport\tla\newtools\tla2-inria-workspace\tla2-inria\tlatools\class\tla2sany\StandardModules\Naturals.tla +Parsing file C:\lamport\tla\newtools\tla2-inria-workspace\tla2-inria\tlatools\class\tla2sany\StandardModules\Sequences.tla +Semantic processing of module Naturals +Semantic processing of module Sequences +Semantic processing of module StarkMutex +Starting... (2012-08-10 17:39:24) +Implied-temporal checking--satisfiability problem has 3 branches. +Computing initial states... +Finished computing initial states: 1 distinct state generated. +Progress(34) at 2012-08-10 17:39:27: 6899 states generated (6899 s/min), 3423 distinct states found (3423 ds/min), 265 states left on queue. +Checking temporal properties for the complete state space... +Model checking completed. No error has been found. + Estimates of the probability that TLC did not check all reachable states + because two distinct states had the same fingerprint: + calculated (optimistic): val = 4.0E-11 + based on the actual fingerprints: val = 1.6E-10 +54809 states generated, 23168 distinct states found, 0 states left on queue. +The depth of the complete state graph search is 92. +Finished. (2012-08-10 17:39:49) +*/ \ No newline at end of file diff --git a/tlatools/test/tlc2/tool/liveness/Test055.java b/tlatools/test/pcal/SubSubTest.java similarity index 77% rename from tlatools/test/tlc2/tool/liveness/Test055.java rename to tlatools/test/pcal/SubSubTest.java index 4a35ec0c1b282e0a8d30cc14a02b830cc6c134ec..dedd8655e10c8d52b4aeff0f3530be41bcb05c96 100644 --- a/tlatools/test/tlc2/tool/liveness/Test055.java +++ b/tlatools/test/pcal/SubSubTest.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2015 Microsoft Research. All rights reserved. + * Copyright (c) 2018 Microsoft Research. All rights reserved. * * The MIT License (MIT) * @@ -23,24 +23,29 @@ * Contributors: * Markus Alexander Kuppe - initial API and implementation ******************************************************************************/ +package pcal; -package tlc2.tool.liveness; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; import tlc2.output.EC; -public class Test055 extends ModelCheckerTestCase { +public class SubSubTest extends PCalModelCheckerTestCase { - public Test055() { - super("test55"); + public SubSubTest() { + super("SubSub", "pcal"); } + @Test public void testSpec() { - // ModelChecker has finished and generated the expected amount of states - assertTrue(recorder.recorded(EC.TLC_MODE_MC)); - assertTrue(recorder.recorded(EC.TLC_FINISHED)); - assertTrue(recorder.recorded(EC.TLC_COMPUTING_INIT)); - assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "2")); assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "1")); - assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "3", "2", "0")); + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertFalse(recorder.recorded(EC.GENERAL)); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "14", "8", "0")); + assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "4")); + + assertZeroUncovered(); } } diff --git a/tlatools/test/pcal/SyncConsTest.java b/tlatools/test/pcal/SyncConsTest.java new file mode 100644 index 0000000000000000000000000000000000000000..43b92e126ba523f65ee5116d0d95e74b270c251c --- /dev/null +++ b/tlatools/test/pcal/SyncConsTest.java @@ -0,0 +1,77 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package pcal; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.output.EC; + +public class SyncConsTest extends PCalModelCheckerTestCase { + + public SyncConsTest() { + super("SyncCons", "pcal"); + } + + @Test + public void testSpec() { + assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "2")); + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertFalse(recorder.recorded(EC.GENERAL)); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "22580", "8754", "0")); + assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "48")); + + assertZeroUncovered(); + } +} +/* +C:\lamport\tla\pluscal>java -mx1000m -cp "c:/lamport/tla/newtools/tla2-inria-workspace/tla2-inria/tlatools/class" tlc2.TLC -cleanup SyncCons.tla +TLC2 Version 2.05 of 18 May 2012 +Running in Model-Checking mode. +Parsing file SyncCons.tla +Parsing file C:\lamport\tla\newtools\tla2-inria-workspace\tla2-inria\tlatools\class\tla2sany\StandardModules\Naturals.tla +Parsing file C:\lamport\tla\newtools\tla2-inria-workspace\tla2-inria\tlatools\class\tla2sany\StandardModules\FiniteSets.tla +Parsing file C:\lamport\tla\newtools\tla2-inria-workspace\tla2-inria\tlatools\class\tla2sany\StandardModules\TLC.tla +Parsing file C:\lamport\tla\newtools\tla2-inria-workspace\tla2-inria\tlatools\class\tla2sany\StandardModules\Sequences.tla +Semantic processing of module Naturals +Semantic processing of module Sequences +Semantic processing of module FiniteSets +Semantic processing of module TLC +Semantic processing of module SyncCons +Starting... (2012-08-10 17:39:56) +Computing initial states... +Finished computing initial states: 2 distinct states generated. +Model checking completed. No error has been found. + Estimates of the probability that TLC did not check all reachable states + because two distinct states had the same fingerprint: + calculated (optimistic): val = 6.6E-12 + based on the actual fingerprints: val = 4.4E-12 +22580 states generated, 8754 distinct states found, 0 states left on queue. +The depth of the complete state graph search is 48. +Finished. (2012-08-10 17:39:57) +*/ \ No newline at end of file diff --git a/tlatools/test/pcal/TestPCandStackTest.java b/tlatools/test/pcal/TestPCandStackTest.java new file mode 100644 index 0000000000000000000000000000000000000000..239597e7744cdfe42aa229decbc76e13fb526eff --- /dev/null +++ b/tlatools/test/pcal/TestPCandStackTest.java @@ -0,0 +1,95 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package pcal; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.output.EC; + +public class TestPCandStackTest extends PCalModelCheckerTestCase { + + public TestPCandStackTest() { + super("TestPCandStack", "pcal"); + } + + @Test + public void testSpec() { + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertFalse(recorder.recorded(EC.GENERAL)); + } +} +/* +TLC2 Version 2.12 of 29 January 2018 +Running breadth-first search Model-Checking with 1 worker on 4 cores with 5303MB heap and 64MB offheap memory (Linux 4.15.0-22-generic amd64, Oracle Corporation 1.8.0_171 x86_64). +Parsing file /home/markus/Desktop/LesliePCalTest/alltests/TestPCandStack.tla +Parsing file /home/markus/src/TLA/tla/tlatools/class/tla2sany/StandardModules/Naturals.tla +Parsing file /home/markus/src/TLA/tla/tlatools/class/tla2sany/StandardModules/Sequences.tla +Parsing file /home/markus/src/TLA/tla/tlatools/class/tla2sany/StandardModules/TLC.tla +Semantic processing of module Naturals +Semantic processing of module Sequences +Semantic processing of module TLC +Semantic processing of module TestPCandStack +Starting... (2018-05-23 12:30:30) +Computing initial states... +Finished computing initial states: 1 distinct state generated. +Warning: The EXCEPT was applied to non-existing fields of the value at +line 46, col 26 to line 46, col 30 of module TestPCandStack +(Use the -nowarning option to disable this warning.) +Error: TLC threw an unexpected exception. +This was probably caused by an error in the spec or model. +See the User Output or TLC Console for clues to what happened. +The exception was a tlc2.tool.EvalException +: Attempted to apply the operator overridden by the Java method +public static tlc2.value.Value tlc2.module.TLC.Assert(tlc2.value.Value,tlc2.value.Value), +but it produced the following error: +The first argument of Assert evaluated to FALSE; the second argument was: +"Failure of assertion at line 16, column 26." +Error: The behavior up to this point is: +State 1: <Initial predicate> +/\ stack = <<<<>>, <<>>, <<>>>> +/\ pc = <<"M1", "M1", "M1">> + +State 2: <M1 line 43, col 13 to line 47, col 47 of module TestPCandStack> +/\ stack = <<<<>>, <<>>, <<>>>> +/\ pc = <<"M2", "M1", "M1">> + +Error: The error occurred when TLC was evaluating the nested +expressions at the following positions: +0. Line 49, column 13 to line 56, column 29 in TestPCandStack +1. Line 49, column 16 to line 49, column 30 in TestPCandStack +2. Line 50, column 16 to line 54, column 79 in TestPCandStack +3. Line 51, column 24 to line 52, column 79 in TestPCandStack +4. Line 51, column 27 to line 52, column 79 in TestPCandStack + + +4 states generated, 4 distinct states found, 2 states left on queue. +The depth of the complete state graph search is 2. +The average outdegree of the complete state graph is 3 (minimum is 3, the maximum 3 and the 95th percentile is 3). +Finished in 00s at (2018-05-23 12:30:30) +*/ \ No newline at end of file diff --git a/tlatools/test/pcal/TestReplaceTest.java b/tlatools/test/pcal/TestReplaceTest.java new file mode 100644 index 0000000000000000000000000000000000000000..9d498528eb300167cbe4c0197ea05d64079741ec --- /dev/null +++ b/tlatools/test/pcal/TestReplaceTest.java @@ -0,0 +1,52 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package pcal; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.output.EC; + +public class TestReplaceTest extends PCalModelCheckerTestCase { + + public TestReplaceTest() { + super("TestReplace", "pcal"); + } + + @Test + public void testSpec() { + assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "1")); + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertFalse(recorder.recorded(EC.GENERAL)); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "18", "17", "0")); + assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "17")); + + assertUncovered("line 124, col 32 to line 124, col 41 of module TestReplace: 0\n" + + "line 175, col 33 to line 175, col 41 of module TestReplace: 0"); + } +} diff --git a/tlatools/test/pcal/TestTabsTest.java b/tlatools/test/pcal/TestTabsTest.java new file mode 100644 index 0000000000000000000000000000000000000000..ceb4f580356c4e591874fa11552cf94ec13a77dd --- /dev/null +++ b/tlatools/test/pcal/TestTabsTest.java @@ -0,0 +1,75 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package pcal; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.output.EC; + +public class TestTabsTest extends PCalModelCheckerTestCase { + + public TestTabsTest() { + super("TestTabs", "pcal"); + } + + @Test + public void testSpec() { + assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "1")); + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertFalse(recorder.recorded(EC.GENERAL)); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "3", "2", "0")); + assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "2")); + + assertZeroUncovered(); + } +} +/* +C:\lamport\tla\pluscal>java -mx1000m -cp "c:/lamport/tla/newtools/tla2-inria-workspace/tla2-inria/tlatools/class" tlc2.TLC -cleanup TestTabs.tla +TLC2 Version 2.05 of 18 May 2012 +Running in Model-Checking mode. +Parsing file TestTabs.tla +Parsing file C:\lamport\tla\newtools\tla2-inria-workspace\tla2-inria\tlatools\class\tla2sany\StandardModules\Naturals.tla +Parsing file C:\lamport\tla\newtools\tla2-inria-workspace\tla2-inria\tlatools\class\tla2sany\StandardModules\TLC.tla +Parsing file C:\lamport\tla\newtools\tla2-inria-workspace\tla2-inria\tlatools\class\tla2sany\StandardModules\Sequences.tla +Semantic processing of module Naturals +Semantic processing of module Sequences +Semantic processing of module TLC +Semantic processing of module TestTabs +Starting... (2012-08-10 17:40:01) +Computing initial states... +Finished computing initial states: 1 distinct state generated. +Model checking completed. No error has been found. + Estimates of the probability that TLC did not check all reachable states + because two distinct states had the same fingerprint: + calculated (optimistic): val = 1.1E-19 + based on the actual fingerprints: val = 1.1E-19 +3 states generated, 2 distinct states found, 0 states left on queue. +The depth of the complete state graph search is 2. +Finished. (2012-08-10 17:40:01) +*/ \ No newline at end of file diff --git a/tlatools/test/pcal/TestTest.java b/tlatools/test/pcal/TestTest.java new file mode 100644 index 0000000000000000000000000000000000000000..3385dd16518e10f3274ca807df8eed09ec630166 --- /dev/null +++ b/tlatools/test/pcal/TestTest.java @@ -0,0 +1,63 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package pcal; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.output.EC; + +public class TestTest extends PCalModelCheckerTestCase { + + public TestTest() { + super("Test", "pcal", new String[] {"-wf", "-termination"}); + } + + @Test + public void testSpec() { + assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "1")); + assertTrue(recorder.recordedWithStringValues(EC.TLC_CHECKING_TEMPORAL_PROPS, "complete", "4")); + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertFalse(recorder.recorded(EC.GENERAL)); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "5", "4", "0")); + assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "4")); + + assertUncovered("line 47, col 31 to line 47, col 37 of module Test: 0\n" + + "line 48, col 31 to line 48, col 37 of module Test: 0\n" + + "line 51, col 24 to line 51, col 30 of module Test: 0\n" + + "line 52, col 9 to line 52, col 28 of module Test: 0\n" + + "line 53, col 9 to line 53, col 28 of module Test: 0\n" + + "line 54, col 9 to line 54, col 14 of module Test: 0\n" + + "line 68, col 43 to line 68, col 49 of module Test: 0\n" + + "line 69, col 43 to line 69, col 49 of module Test: 0\n" + + "line 72, col 36 to line 72, col 42 of module Test: 0\n" + + "line 73, col 21 to line 73, col 26 of module Test: 0\n" + + "line 84, col 32 to line 84, col 38 of module Test: 0\n" + + "line 87, col 25 to line 87, col 31 of module Test: 0"); + } +} diff --git a/tlatools/test/pcal/TreeBarrierTest.java b/tlatools/test/pcal/TreeBarrierTest.java new file mode 100644 index 0000000000000000000000000000000000000000..8822ae03f635310f0d1504afe54292ac280b7a84 --- /dev/null +++ b/tlatools/test/pcal/TreeBarrierTest.java @@ -0,0 +1,76 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package pcal; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.output.EC; + +public class TreeBarrierTest extends PCalModelCheckerTestCase { + + public TreeBarrierTest() { + super("TreeBarrier", "pcal", new String[] {"-wfNext"}); + } + + @Test + public void testSpec() { + assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "1")); + assertTrue(recorder.recordedWithStringValues(EC.TLC_CHECKING_TEMPORAL_PROPS, "complete", "12570", "6 branches of ")); // 6 * 2095 = 12570 + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertFalse(recorder.recorded(EC.GENERAL)); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "5414", "2095", "0")); + assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "106")); + + assertUncovered("line 120, col 19 to line 120, col 32 of module TreeBarrier: 0"); + } +} +/* +C:\lamport\tla\pluscal>java -mx1000m -cp "c:/lamport/tla/newtools/tla2-inria-workspace/tla2-inria/tlatools/class" tlc2.TLC -cleanup TreeBarrier.tla +TLC2 Version 2.05 of 18 May 2012 +Running in Model-Checking mode. +Parsing file TreeBarrier.tla +Parsing file C:\lamport\tla\newtools\tla2-inria-workspace\tla2-inria\tlatools\class\tla2sany\StandardModules\Naturals.tla +Parsing file C:\lamport\tla\newtools\tla2-inria-workspace\tla2-inria\tlatools\class\tla2sany\StandardModules\Sequences.tla +Semantic processing of module Naturals +Semantic processing of module Sequences +Semantic processing of module TreeBarrier +Starting... (2012-08-10 17:39:50) +Implied-temporal checking--satisfiability problem has 6 branches. +Computing initial states... +Finished computing initial states: 1 distinct state generated. +Checking temporal properties for the complete state space... +Model checking completed. No error has been found. + Estimates of the probability that TLC did not check all reachable states + because two distinct states had the same fingerprint: + calculated (optimistic): val = 3.8E-13 + based on the actual fingerprints: val = 6.4E-14 +5414 states generated, 2095 distinct states found, 0 states left on queue. +The depth of the complete state graph search is 106. +Finished. (2012-08-10 17:39:53) +*/ \ No newline at end of file diff --git a/tlatools/test/pcal/ULCallReturn1Test.java b/tlatools/test/pcal/ULCallReturn1Test.java new file mode 100644 index 0000000000000000000000000000000000000000..89275814ba9f7e0567180f8eb7bc07251354e74e --- /dev/null +++ b/tlatools/test/pcal/ULCallReturn1Test.java @@ -0,0 +1,52 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package pcal; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.output.EC; + +public class ULCallReturn1Test extends PCalModelCheckerTestCase { + + public ULCallReturn1Test() { + super("ULCallReturn1", "pcal", new String[] {"-wf", "-termination"}); + } + + @Test + public void testSpec() { + assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "1")); + assertTrue(recorder.recordedWithStringValues(EC.TLC_CHECKING_TEMPORAL_PROPS, "complete", "8")); + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertFalse(recorder.recorded(EC.GENERAL)); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "9", "8", "0")); + assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "8")); + + assertZeroUncovered(); + } +} diff --git a/tlatools/test/pcal/ULEuclidTest.java b/tlatools/test/pcal/ULEuclidTest.java new file mode 100644 index 0000000000000000000000000000000000000000..f6a144210ca0a39eda9a94c95a9edacadfc4d569 --- /dev/null +++ b/tlatools/test/pcal/ULEuclidTest.java @@ -0,0 +1,52 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package pcal; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.output.EC; + +public class ULEuclidTest extends PCalModelCheckerTestCase { + + public ULEuclidTest() { + super("ULEuclid", "pcal", new String[] {"-wf", "-termination"}); + } + + @Test + public void testSpec() { + assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "400")); + assertTrue(recorder.recordedWithStringValues(EC.TLC_CHECKING_TEMPORAL_PROPS, "complete", "6352")); + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertFalse(recorder.recorded(EC.GENERAL)); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "6752", "6352", "0")); + assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "42")); + + assertZeroUncovered(); + } +} diff --git a/tlatools/test/pcal/ULEvenOddTest.java b/tlatools/test/pcal/ULEvenOddTest.java new file mode 100644 index 0000000000000000000000000000000000000000..534f8af2c4001b1faca9e048b0f254469ee364a4 --- /dev/null +++ b/tlatools/test/pcal/ULEvenOddTest.java @@ -0,0 +1,54 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package pcal; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.output.EC; + +public class ULEvenOddTest extends PCalModelCheckerTestCase { + + public ULEvenOddTest() { + super("ULEvenOdd", "pcal", new String[] {"-wf", "-termination"}); + } + + @Test + public void testSpec() { + assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "1")); + assertTrue(recorder.recordedWithStringValues(EC.TLC_CHECKING_TEMPORAL_PROPS, "complete", "13")); + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertFalse(recorder.recorded(EC.GENERAL)); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "14", "13", "0")); + assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "13")); + + assertUncovered("line 67, col 24 to line 67, col 38 of module ULEvenOdd: 0\n" + + "line 68, col 24 to line 68, col 36 of module ULEvenOdd: 0\n" + + "line 69, col 24 to line 69, col 51 of module ULEvenOdd: 0"); + } +} diff --git a/tlatools/test/pcal/ULFactorial2Test.java b/tlatools/test/pcal/ULFactorial2Test.java new file mode 100644 index 0000000000000000000000000000000000000000..facd2dd7b9170b111acc7856ab3f8aadea9db34e --- /dev/null +++ b/tlatools/test/pcal/ULFactorial2Test.java @@ -0,0 +1,56 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package pcal; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.output.EC; + +public class ULFactorial2Test extends PCalModelCheckerTestCase { + + public ULFactorial2Test() { + super("ULFactorial2", "pcal", new String[] {"-wf", "-termination"}); + } + + @Test + public void testSpec() { + assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "1")); + assertTrue(recorder.recordedWithStringValues(EC.TLC_CHECKING_TEMPORAL_PROPS, "complete", "9")); + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertFalse(recorder.recorded(EC.GENERAL)); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "10", "9", "0")); + assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "9")); + + assertUncovered("line 53, col 24 to line 53, col 43 of module ULFactorial2: 0\n" + + "line 54, col 24 to line 54, col 41 of module ULFactorial2: 0\n" + + "line 55, col 24 to line 55, col 47 of module ULFactorial2: 0\n" + + "line 56, col 24 to line 56, col 43 of module ULFactorial2: 0\n" + + "line 57, col 24 to line 57, col 55 of module ULFactorial2: 0"); + } +} diff --git a/tlatools/test/pcal/ULQuicksortMacroTest.java b/tlatools/test/pcal/ULQuicksortMacroTest.java new file mode 100644 index 0000000000000000000000000000000000000000..2d256c3c1085c8457c67a791b957828e494ef4b7 --- /dev/null +++ b/tlatools/test/pcal/ULQuicksortMacroTest.java @@ -0,0 +1,52 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package pcal; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.output.EC; + +public class ULQuicksortMacroTest extends PCalModelCheckerTestCase { + + public ULQuicksortMacroTest() { + super("ULQuicksortMacro", "pcal", new String[] {"-wf", "-termination"}); + } + + @Test + public void testSpec() { + assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "27")); + assertTrue(recorder.recordedWithStringValues(EC.TLC_CHECKING_TEMPORAL_PROPS, "complete", "258")); + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertFalse(recorder.recorded(EC.GENERAL)); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "342", "258", "0")); + assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "11")); + + assertZeroUncovered(); + } +} diff --git a/tlatools/test/pcal/UnhandledInvalidSyntaxTest.java b/tlatools/test/pcal/UnhandledInvalidSyntaxTest.java new file mode 100644 index 0000000000000000000000000000000000000000..32442af1b6c8bd84890e3948f16cd72e66145bbf --- /dev/null +++ b/tlatools/test/pcal/UnhandledInvalidSyntaxTest.java @@ -0,0 +1,80 @@ +/******************************************************************************* + * Copyright (c) 2019 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package pcal; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import java.io.IOException; +import java.util.Arrays; + +import org.junit.Test; + +import util.ToolIO; + +public class UnhandledInvalidSyntaxTest extends PCalTest { + + @Test + public void test1() throws IOException { + assertEquals(trans.STATUS_EXIT_WITH_ERRORS, trans.runMe(new String[] {"-nocfg", + writeTempFile("MissingSemicolonTest1", + "---- MODULE algo ----\n" + + "(*\n" + + " --algorithm algo\n" + + " begin\n" + + " await;\n" + + " end algorithm;\n" + + "*)\n" + + "====" + )})); + + assertTrue(Arrays.toString(ToolIO.getAllMessages()), + Arrays.asList(ToolIO.getAllMessages()).contains("\nUnrecoverable error:\n" + + " -- Unknown error at or before\n" + + " line 5, column 2.\n")); + } + + @Test + public void test2() throws IOException { + assertEquals(trans.STATUS_EXIT_WITH_ERRORS, trans.runMe(new String[] {"-nocfg", + writeTempFile("MissingSemicolonTest2", + "---- MODULE algo ----\n" + + "(*\n" + + " --algorithm algo\n" + + " begin\n" + + " if TRUE then\n" + // missing semicolon + " end if;\n" + + " end algorithm;\n" + + "*)\n" + + "====" + )})); + + assertTrue(Arrays.toString(ToolIO.getAllMessages()), + Arrays.asList(ToolIO.getAllMessages()).contains("\nUnrecoverable error:\n" + + " -- Unknown error at or before\n" + + " line 6, column 8.\n")); + } +} \ No newline at end of file diff --git a/tlatools/test/pcal/UniprocDefineTest.java b/tlatools/test/pcal/UniprocDefineTest.java new file mode 100644 index 0000000000000000000000000000000000000000..eb85e1c2689b8e4a2ec3c75e6ef327a8bd324e3b --- /dev/null +++ b/tlatools/test/pcal/UniprocDefineTest.java @@ -0,0 +1,75 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package pcal; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.output.EC; + +public class UniprocDefineTest extends PCalModelCheckerTestCase { + + public UniprocDefineTest() { + super("UniprocDefine", "pcal"); + } + + @Test + public void testSpec() { + assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "1")); + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertFalse(recorder.recorded(EC.GENERAL)); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "5", "4", "0")); + assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "4")); + + assertZeroUncovered(); + } +} +/* +C:\lamport\tla\pluscal>java -mx1000m -cp "c:/lamport/tla/newtools/tla2-inria-workspace/tla2-inria/tlatools/class" tlc2.TLC -cleanup UniprocDefine.tla +TLC2 Version 2.05 of 18 May 2012 +Running in Model-Checking mode. +Parsing file UniprocDefine.tla +Parsing file C:\lamport\tla\newtools\tla2-inria-workspace\tla2-inria\tlatools\class\tla2sany\StandardModules\Naturals.tla +Parsing file C:\lamport\tla\newtools\tla2-inria-workspace\tla2-inria\tlatools\class\tla2sany\StandardModules\Sequences.tla +Parsing file C:\lamport\tla\newtools\tla2-inria-workspace\tla2-inria\tlatools\class\tla2sany\StandardModules\TLC.tla +Semantic processing of module Naturals +Semantic processing of module Sequences +Semantic processing of module TLC +Semantic processing of module UniprocDefine +Starting... (2012-08-10 17:39:58) +Computing initial states... +Finished computing initial states: 1 distinct state generated. +Model checking completed. No error has been found. + Estimates of the probability that TLC did not check all reachable states + because two distinct states had the same fingerprint: + calculated (optimistic): val = 2.2E-19 + based on the actual fingerprints: val = 6.0E-19 +5 states generated, 4 distinct states found, 0 states left on queue. +The depth of the complete state graph search is 4. +Finished. (2012-08-10 17:39:59) +*/ \ No newline at end of file diff --git a/tlatools/test/tla2sany/drivers/Bug156TEStackOverflowTest.java b/tlatools/test/tla2sany/drivers/Bug156TEStackOverflowTest.java index 7b396d3817216425315992910904bbea80d3e2a8..02f935a051e68e4f3e00fae06ce8e5ca6bb0517e 100644 --- a/tlatools/test/tla2sany/drivers/Bug156TEStackOverflowTest.java +++ b/tlatools/test/tla2sany/drivers/Bug156TEStackOverflowTest.java @@ -1,21 +1,24 @@ package tla2sany.drivers; -import junit.framework.TestCase; +import org.junit.Before; +import org.junit.Test; + import tla2sany.modanalyzer.SpecObj; import tla2sany.parser.ParseException; import util.SimpleFilenameToStream; import util.ToolIO; /** - * @see http://bugzilla.tlaplus.net/show_bug.cgi?id=156 + * @see Bug #156 in general/bugzilla/index.html */ -public class Bug156TEStackOverflowTest extends TestCase { +public class Bug156TEStackOverflowTest { private SpecObj moduleSpec; /** * @throws java.lang.Exception */ + @Before public void setUp() throws Exception { // create a model and initialize moduleSpec = new SpecObj("test-model/Bug156/TE.tla", new SimpleFilenameToStream()); @@ -26,6 +29,7 @@ public class Bug156TEStackOverflowTest extends TestCase { * Test method for {@link tla2sany.drivers.SANY#frontEndParse(tla2sany.modanalyzer.SpecObj, java.io.PrintStream)}. * @throws ParseException */ + @Test public void testFrontEndParse() throws ParseException { // uncomment if bug 156 has been fixed // try { diff --git a/tlatools/test/tla2sany/drivers/Github429Test.java b/tlatools/test/tla2sany/drivers/Github429Test.java new file mode 100644 index 0000000000000000000000000000000000000000..11d70a05f04fa397c890d70709fb7ee827080aaf --- /dev/null +++ b/tlatools/test/tla2sany/drivers/Github429Test.java @@ -0,0 +1,33 @@ +package tla2sany.drivers; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import tla2sany.modanalyzer.SpecObj; +import tlc2.tool.CommonTestCase; +import util.SimpleFilenameToStream; +import util.ToolIO; + +public class Github429Test { + + private SpecObj moduleSpec; + + @Before + public void setUp() throws Exception { + // create a model and initialize + moduleSpec = new SpecObj(CommonTestCase.BASE_PATH + "Github429.tla", new SimpleFilenameToStream()); + SANY.frontEndInitialize(moduleSpec, ToolIO.out); + } + + @Test + public void testForFailedParse() { + try { + SANY.frontEndParse(moduleSpec, ToolIO.out); + SANY.frontEndSemanticAnalysis(moduleSpec, ToolIO.out, false); + } catch (final Exception e) { + Assert.fail("No exception should occur during parse. Instead encountered [" + e.getClass() + + "] with message: " + e.getMessage()); + } + } +} diff --git a/tlatools/test/tla2sany/drivers/IllegalOperatorTest.java b/tlatools/test/tla2sany/drivers/IllegalOperatorTest.java new file mode 100644 index 0000000000000000000000000000000000000000..2cec33db7440ac436d9ded5a8760feac439187f3 --- /dev/null +++ b/tlatools/test/tla2sany/drivers/IllegalOperatorTest.java @@ -0,0 +1,51 @@ +/******************************************************************************* + * Copyright (c) 2017 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tla2sany.drivers; + +import org.junit.Test; + +import tlc2.tool.CommonTestCase; +import util.TestPrintStream; +import util.ToolIO; + +public class IllegalOperatorTest { + + @Test + public void test() { + final TestPrintStream testPrintStream = new TestPrintStream(); + ToolIO.out = testPrintStream; + + SANY.SANYmain( + new String[] { CommonTestCase.BASE_PATH + "IllegalOperatorTest" }); + + testPrintStream.assertSubstring("*** Errors: 1\n" + + "\n" + + "line 3, col 8 to line 3, col 11 of module IllegalOperatorTest\n" + + "\n" + + "Argument number 1 to operator 'D' \n" + + "should be a 1-parameter operator."); + } +} diff --git a/tlatools/test/tla2sany/st/LocationTest.java b/tlatools/test/tla2sany/st/LocationTest.java new file mode 100644 index 0000000000000000000000000000000000000000..214250a399ff4b078885f644d24115e6a5347a20 --- /dev/null +++ b/tlatools/test/tla2sany/st/LocationTest.java @@ -0,0 +1,111 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tla2sany.st; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.util.Arrays; +import java.util.Iterator; +import java.util.TreeSet; + +import org.junit.Test; + +public class LocationTest { + + @Test + public void testContains() { + assertTrue(new Location(0, 0, 10, 10).includes(new Location(0, 0, 10, 10))); + assertFalse(new Location(1, 0, 10, 9).includes(new Location(0, 0, 10, 10))); + + assertTrue(new Location(0, 0, 10, 10).includes(new Location(1, 0, 10, 9))); + assertFalse(new Location(1, 0, 10, 9).includes(new Location(0, 0, 10, 10))); + + + Location[] parsedLocations = Location + .getParsedLocations("line 781, col 31 to line 784, col 68 of module OpenAddressing\n" + + "line 783, col 38 to line 784, col 68 of module OpenAddressing\n" + + "line 784, col 41 to line 784, col 68 of module OpenAddressing\n" + + + "line 786, col 30 to line 786, col 69 of module OpenAddressing\n" + + "line 793, col 29 to line 793, col 63 of module OpenAddressing"); + + assertTrue(parsedLocations[0].includes(parsedLocations[0])); + + assertTrue(parsedLocations[0].includes(parsedLocations[1])); + assertFalse(parsedLocations[1].includes(parsedLocations[0])); + + assertTrue(parsedLocations[0].includes(parsedLocations[2])); + assertFalse(parsedLocations[2].includes(parsedLocations[0])); + + assertFalse(parsedLocations[0].includes(parsedLocations[3])); + assertFalse(parsedLocations[3].includes(parsedLocations[0])); + assertFalse(parsedLocations[4].includes(parsedLocations[0])); + } + + @Test + public void testComparator() { + final Location[] parsedLocations = Location.getParsedLocations( + "line 15, col 9 to line 15, col 9 of module CostMetrics\n" + + "line 15, col 9 to line 15, col 17 of module CostMetrics\n" + + "line 8, col 11 to line 8, col 11 of module CostMetrics\n" + + "line 8, col 13 to line 8, col 13 of module CostMetrics\n" + + "line 8, col 9 to line 8, col 15 of module CostMetrics\n" + + "line 14, col 15 to line 14, col 17 of module CostMetrics\n" + + "line 15, col 15 to line 15, col 17 of module CostMetrics\n" + + "line 16, col 34 to line 16, col 52 of module CostMetrics\n" + + "line 8, col 9 to line 8, col 15 of module CostMetrics\n" + + "line 16, col 42 to line 16, col 51 of module CostMetrics\n" + + "line 16, col 42 to line 16, col 50 of module CostMetrics\n" + + "line 16, col 46 to line 16, col 46 of module CostMetrics\n" + + "line 16, col 46 to line 16, col 50 of module CostMetrics\n" + + "line 16, col 46 to line 16, col 50 of module CostMetrics\n" + + "line 23, col 6 to line 25, col 18 of module CostMetrics\n" + + "line 18, col 9 to line 18, col 9 of module CostMetrics"); + assertEquals(16, parsedLocations.length); + + final TreeSet<Location> locations = new TreeSet<>(Arrays.asList(parsedLocations)); + assertEquals(14, locations.size()); + + final Iterator<Location> iterator = locations.iterator(); + Location l = iterator.next(); + for (int i = 1; i < locations.size(); i++) { + final Location next = iterator.next(); + assertTrue(l.bLine <= next.bLine); + if (l.bLine == next.bLine) { + assertTrue(l.bColumn <= next.bColumn); + if (l.bColumn == next.bColumn) { + assertTrue(l.eLine <= next.eLine); + if (l.eLine == next.eLine) { + assertTrue(l.eColumn < next.eColumn); + } + } + } + l = next; + } + } +} diff --git a/tlatools/test/tlc2/TLCTest.java b/tlatools/test/tlc2/TLCTest.java index ad8fc027cac3ccda08375a892a9d79e0d4358134..2cdc42cde9681233627b535c3491e95745d6fd76 100644 --- a/tlatools/test/tlc2/TLCTest.java +++ b/tlatools/test/tlc2/TLCTest.java @@ -1,96 +1,135 @@ package tlc2; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.junit.Assume.assumeTrue; + +import org.junit.Test; + +import tlc2.tool.fp.FPSetConfiguration; +import tlc2.tool.fp.FPSetFactory; +import util.TLAConstants; import util.TLCRuntime; -import junit.framework.TestCase; -public class TLCTest extends TestCase { +public class TLCTest { + @Test public void testHandleParametersAbsoluteInvalid() { final TLC tlc = new TLC(); - assertFalse(tlc.handleParameters(new String[] {"-fpmem", "-1", "MC"})); + assertFalse(tlc.handleParameters(new String[] {"-fpmem", "-1", TLAConstants.Files.MODEL_CHECK_FILE_BASENAME})); } + @Test public void testHandleParametersAbsoluteValid() { final TLC tlc = new TLC(); - assertTrue(tlc.handleParameters(new String[] {"-fpmem", "101", "MC"})); + assertTrue(tlc.handleParameters(new String[] {"-fpmem", "101", TLAConstants.Files.MODEL_CHECK_FILE_BASENAME})); } + @Test public void testHandleParametersFractionInvalid() { final TLC tlc = new TLC(); - assertFalse(tlc.handleParameters(new String[] {"-fpmem", "-0.5", "MC"})); + assertFalse(tlc.handleParameters(new String[] {"-fpmem", "-0.5", TLAConstants.Files.MODEL_CHECK_FILE_BASENAME})); } /** * Allocating to little should result in min default */ + @Test public void testHandleParametersAllocateLowerBound() { final TLC tlc = new TLC(); - assertTrue(tlc.handleParameters(new String[] {"-fpmem", "0", "MC"})); + assertTrue(tlc.handleParameters(new String[] {"-fpmem", "0", TLAConstants.Files.MODEL_CHECK_FILE_BASENAME})); + final FPSetConfiguration fpSetConfiguration = tlc.getFPSetConfiguration(); + assumeTrue(FPSetFactory.allocatesOnHeap(fpSetConfiguration.getImplementation())); assertEquals("Allocating to little should result in min default", - TLCRuntime.MinFpMemSize, tlc.getFPSetConfiguration() + TLCRuntime.MinFpMemSize, fpSetConfiguration .getMemoryInBytes()); } /** * Overallocating should result in max default */ + @Test public void testHandleParametersAllocateUpperBound() { final TLC tlc = new TLC(); - assertTrue(tlc.handleParameters(new String[] {"-fpmem", Long.toString(Long.MAX_VALUE), "MC"})); + assertTrue(tlc.handleParameters(new String[] {"-fpmem", Long.toString(Long.MAX_VALUE), + TLAConstants.Files.MODEL_CHECK_FILE_BASENAME})); final long maxMemory = (long) (Runtime.getRuntime().maxMemory() * 0.75d); + final FPSetConfiguration fpSetConfiguration = tlc.getFPSetConfiguration(); + assumeTrue(FPSetFactory.allocatesOnHeap(fpSetConfiguration.getImplementation())); assertEquals("Overallocating should result in max default (75%)", - maxMemory, tlc.getFPSetConfiguration().getMemoryInBytes()); + maxMemory, fpSetConfiguration.getMemoryInBytes()); } /** * .5 is valid */ + @Test public void testHandleParametersAllocateHalf() { final TLC tlc = new TLC(); - assertTrue(tlc.handleParameters(new String[] {"-fpmem", ".5", "MC"})); + assertTrue(tlc.handleParameters(new String[] {"-fpmem", ".5", TLAConstants.Files.MODEL_CHECK_FILE_BASENAME})); final long maxMemory = (long) (Runtime.getRuntime().maxMemory() * 0.50d); + final FPSetConfiguration fpSetConfiguration = tlc.getFPSetConfiguration(); + assumeTrue(FPSetFactory.allocatesOnHeap(fpSetConfiguration.getImplementation())); assertEquals("Overallocating should result in max default (50%)", - maxMemory, tlc.getFPSetConfiguration().getMemoryInBytes()); + maxMemory, fpSetConfiguration.getMemoryInBytes()); } /** * .99 is valid */ + @Test public void testHandleParametersAllocate90() { final TLC tlc = new TLC(); - assertTrue(tlc.handleParameters(new String[] {"-fpmem", ".99", "MC"})); + assertTrue(tlc.handleParameters(new String[] {"-fpmem", ".99", TLAConstants.Files.MODEL_CHECK_FILE_BASENAME})); final long maxMemory = (long) (Runtime.getRuntime().maxMemory() * 0.99d); + final FPSetConfiguration fpSetConfiguration = tlc.getFPSetConfiguration(); + assumeTrue(FPSetFactory.allocatesOnHeap(fpSetConfiguration.getImplementation())); assertEquals("Overallocating should result in max default (99%)", - maxMemory, tlc.getFPSetConfiguration().getMemoryInBytes()); + maxMemory, fpSetConfiguration.getMemoryInBytes()); } /** * is valid */ + @Test public void testHandleParametersMaxSetSize() { final int progDefault = TLCGlobals.setBound; TLC tlc = new TLC(); - assertFalse(tlc.handleParameters(new String[] {"-maxSetSize", "NaN", "MC"})); + assertFalse(tlc.handleParameters(new String[] {"-maxSetSize", "NaN", TLAConstants.Files.MODEL_CHECK_FILE_BASENAME})); tlc = new TLC(); - assertFalse(tlc.handleParameters(new String[] {"-maxSetSize", "0", "MC"})); + assertFalse(tlc.handleParameters(new String[] {"-maxSetSize", "0", TLAConstants.Files.MODEL_CHECK_FILE_BASENAME})); tlc = new TLC(); - assertFalse(tlc.handleParameters(new String[] {"-maxSetSize", "-1", "MC"})); + assertFalse(tlc.handleParameters(new String[] {"-maxSetSize", "-1", TLAConstants.Files.MODEL_CHECK_FILE_BASENAME})); tlc = new TLC(); - assertFalse(tlc.handleParameters(new String[] {"-maxSetSize", Integer.toString(Integer.MIN_VALUE), "MC"})); + assertFalse(tlc.handleParameters(new String[] { "-maxSetSize", Integer.toString(Integer.MIN_VALUE), + TLAConstants.Files.MODEL_CHECK_FILE_BASENAME })); tlc = new TLC(); - assertTrue(tlc.handleParameters(new String[] {"-maxSetSize", "1", "MC"})); + assertTrue(tlc.handleParameters(new String[] {"-maxSetSize", "1", TLAConstants.Files.MODEL_CHECK_FILE_BASENAME})); assertTrue(TLCGlobals.setBound == 1); tlc = new TLC(); - assertTrue(tlc.handleParameters(new String[] {"-maxSetSize", Integer.toString(progDefault), "MC"})); + assertTrue(tlc.handleParameters( + new String[] { "-maxSetSize", Integer.toString(progDefault), TLAConstants.Files.MODEL_CHECK_FILE_BASENAME })); assertTrue(TLCGlobals.setBound == progDefault); tlc = new TLC(); - assertTrue(tlc.handleParameters(new String[] {"-maxSetSize", Integer.toString(Integer.MAX_VALUE), "MC"})); + assertTrue(tlc.handleParameters(new String[] { "-maxSetSize", Integer.toString(Integer.MAX_VALUE), + TLAConstants.Files.MODEL_CHECK_FILE_BASENAME })); assertTrue(TLCGlobals.setBound == Integer.MAX_VALUE); } + + @Test + public void testRuntimeConversion() { + assertEquals("59s", TLC.convertRuntimeToHumanReadable(59000L)); + assertEquals("59min 59s", TLC.convertRuntimeToHumanReadable(3599000L)); + assertEquals("23h 59min", TLC.convertRuntimeToHumanReadable(86340000L)); + assertEquals("1d 23h", TLC.convertRuntimeToHumanReadable(169200000L)); + assertEquals("2d 23h", TLC.convertRuntimeToHumanReadable(255600000L)); + assertEquals("99d 23h", TLC.convertRuntimeToHumanReadable(8636400000L)); + } } diff --git a/tlatools/test/tlc2/TestDriver.java b/tlatools/test/tlc2/TestDriver.java index 2ec9ba768cc08a13572b099149c6ce7072d2f659..9bbd8bb4786c5d8a630d0b85e59f3c475e599993 100644 --- a/tlatools/test/tlc2/TestDriver.java +++ b/tlatools/test/tlc2/TestDriver.java @@ -8,7 +8,6 @@ public class TestDriver private static final int COUNT = 3; private static final long TIMEOUT = 1000 * 5; - private static final int STEP = 30; private static int reported; private static TLCThread tlcThread; diff --git a/tlatools/test/tlc2/TestDriver2.java b/tlatools/test/tlc2/TestDriver2.java deleted file mode 100644 index 6f28d608a860f4f80e0130089a7165c1f4732890..0000000000000000000000000000000000000000 --- a/tlatools/test/tlc2/TestDriver2.java +++ /dev/null @@ -1,232 +0,0 @@ -package tlc2; - -import java.io.File; - -import util.SimpleFilenameToStream; -import util.ToolIO; - -public class TestDriver2 -{ - - /** - * @author Simon Zambrovski - * @version $Id: TLCJob.java 638 2009-04-10 04:08:14Z simonzam $ - */ - private static final long TIMEOUT = 1000 * 5; - private static final int REPEATS = 10; - - private String rootModule; - private String cfgFile; - private String projectDir; - - private TLCThread tlcThread; - private int workers = 2; - - private int reported; - - - public static void main(String[] args) - { - if (args.length < 6 ) - { - // -workers 2 -config AtomicBakery_MC_1.cfg -metadir C:\org.zambrovski\download\AtomicBakery_MC_1.toolbox AtomicBakery_MC_1 - System.out.println("Call with: -workers <num> -config <name-of-config.cfg> -metadir <dir-to-put-states> <name-of-module>"); - } - TestDriver2 testDriver2 = new TestDriver2(args[6], args[3], args[5]); - testDriver2.setWorkers(Integer.parseInt(args[1])); - - for (int i = 0 ; i < REPEATS; i++) - { - testDriver2.reported = 0; - testDriver2.run(); - } - System.exit(0); - } - /** - * @param name - */ - public TestDriver2(String rootModule, String cfgFile, String projectDir) - { - - this.rootModule = rootModule; - this.cfgFile = cfgFile; - this.projectDir = projectDir; - - // initialize the progress reporting variable - this.reported = 0; - - } - - /** - * Sets the number of workers - * @param workers number of threads to be run in parallel - */ - public void setWorkers(int workers) - { - this.workers = workers; - } - - protected int run() - { - report("entering run"); - - // setup tool IO - // Reset the tool output messages. - ToolIO.reset(); - ToolIO.setMode(ToolIO.TOOL); - ToolIO.setUserDir(new File(rootModule).getParent()); - - // create a TLC instance - TLC tlc = new TLC(); - report("tlc created " + tlc.toString()); - - // setup name resolver - // in RCP FS model use: - // tlc.setResolver(new RCPNameToFileIStream(null)); - - // for simple FS model: - tlc.setResolver(new SimpleFilenameToStream()); - - // setup SpecObj from parser - // SpecObj specObj = ToolboxHandle.getSpecObj(); - // tlc.setSpecObject(specObj); - - // handle parameters - String[] params = new String[] { - "-config", cfgFile, - // "-coverage", "0.1", - "-workers", "" + workers, - "-metadir", projectDir, - rootModule }; - boolean status = tlc.handleParameters(params); - - // report errors in parameters - if (!status) - { - return -1; - } - - // create thread for TLC running - tlcThread = new TLCThread(tlc); - tlcThread.setName("TLC Thread"); - report("tlcthread created " + tlcThread.getId()); - - // Start the TLC thread - tlcThread.start(); - report("tlcthread started"); - - while (this.checkAndSleep()) - { - // report the messages created since last reporting - // check the cancellation status - if (isCancelled()) - { - // cancel the TLC - tlc.setCanceledFlag(true); - - // report the messages created since last reporting - reportProgress(); - - // abnormal termination - return -1; - } - } - // report progress - reportProgress(); - - // successful termination - return 1; - } - - /** - * Method for external cancellation - */ - private boolean isCancelled() - { - return false; - } - - - // check if TLC is ready and sleep - private boolean checkAndSleep() - { - report("entering checkAndSleep()"); - try - { - report("Go sleep \t" + System.currentTimeMillis()); - // go sleep - Thread.sleep(TIMEOUT); - - report("Wake up \t" + System.currentTimeMillis()); - - } catch (InterruptedException e) - { - // nothing to do - e.printStackTrace(); - } - - // return true if the tlc is still calculating - boolean result = tlcThread.isRunning(); - report("leaving checkAndSleep() with " + result); - return result; - } - - /** - * Report progress to the monitor - */ - protected void reportProgress() - { - // report progress - String[] messages = ToolIO.getAllMessages(); - for (; reported < messages.length; reported++) - { - System.out.println(messages[reported]); - } - } - - /** - * Thread to run TLC in - */ - class TLCThread extends Thread - { - private boolean isRunning = false; - private TLC tlc; - - public TLCThread(TLC tlc) - { - this.tlc = tlc; - } - - public void run() - { - synchronized (this) - { - report("TLC Thread: {STARTED}"); - isRunning = true; - } - // start TLC - this.tlc.process(); - - synchronized (this) - { - isRunning = false; - report("TLC Thread: {FINISHED}"); - } - } - - /** - * - * @return - */ - public synchronized boolean isRunning() - { - return isRunning; - } - } - - public void report(String message) - { - // System.err.println("" + Thread.currentThread().getId() + "\t" + message); - } - -} diff --git a/tlatools/test/tlc2/TestMPRecorder.java b/tlatools/test/tlc2/TestMPRecorder.java index a8061ea16380f7007513937179863828ebbde70a..54dc1c6be99d516b45b8cfac45eac2c0fcdbedb5 100644 --- a/tlatools/test/tlc2/TestMPRecorder.java +++ b/tlatools/test/tlc2/TestMPRecorder.java @@ -30,6 +30,10 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.function.Predicate; +import java.util.stream.Collectors; + +import tlc2.output.EC; public class TestMPRecorder extends tlc2.output.MPRecorder { private final Map<Integer, List<Object>> records = new HashMap<Integer, List<Object>>(); @@ -48,7 +52,25 @@ public class TestMPRecorder extends tlc2.output.MPRecorder { public List<Object> getRecords(int code) { return records.get(code); } + + private List<Object> getRecordsOrDefault(final int code, final List<Object> defaultValue) { + return records.getOrDefault(code, defaultValue); + } + + public int getRecordAsInt(int code) { + return Integer.parseInt(((String[]) records.get(code).get(0))[0]); + } + public List<String[]> getRecordAsStringArray(int code) { + final List<Object> l = records.get(code); + + final List<String[]> strs = new ArrayList<>(l.size()); + for (Object o : l) { + strs.add((String[]) o); + } + return strs; + } + // This is a best effort implementation that only checks the first // elements of the nested records and contained arrays public boolean recordedWithStringValue(int code, String str) { @@ -59,6 +81,30 @@ public class TestMPRecorder extends tlc2.output.MPRecorder { } } + public boolean recordedWithSubStringValue(int code, String substring) { + return recordedWithSubStringValue(code, substring, 0); + } + + public boolean recordedWithSubStringValue(int code, String substring, int idx) { + try { + Object object = records.get(code).get(0); + if (object instanceof String[]) { + String[] strs = (String[]) object; + for (String string : strs) { + if (string.contains(substring)) { + return true; + } + } + return false; + } else if (object instanceof String) { + return ((String) object).contains(substring); + } + return false; + } catch (Exception e) { + return false; + } + } + public boolean recordedWithStringValueAt(int code, String str, int idx) { try { Object object = records.get(code).get(0); @@ -80,8 +126,167 @@ public class TestMPRecorder extends tlc2.output.MPRecorder { if (!recordedWithStringValueAt(code, string, i++)) { return false; } + } + return true; + } + + public String getCoverageRecords() { + final List<Object> coverages = getRecords(EC.TLC_COVERAGE_VALUE); + String out = ""; + if (coverages == null) { + return out; + } + for (final Object o : coverages) { + final String[] coverage = (String[]) o; + out += coverage[0] + ": " + Integer.parseInt(coverage[1]) + "\n"; + } + return out; + } + + public List<Coverage> getActionCoverage() { + final List<Object> init = getRecordsOrDefault(EC.TLC_COVERAGE_INIT, new ArrayList<>(0)); + final List<Object> next = getRecordsOrDefault(EC.TLC_COVERAGE_NEXT, new ArrayList<>(0)); + final List<Object> prop = getRecordsOrDefault(EC.TLC_COVERAGE_PROPERTY, new ArrayList<>(0)); + init.addAll(next); + init.addAll(prop); + + return init.stream().map(o -> (String[]) o).map(a -> new Coverage(a)).filter(Coverage::isAction) + .collect(Collectors.toList()); + } + + public List<Coverage> getZeroCoverage() { + return getCoverage(EC.TLC_COVERAGE_VALUE, (Predicate<? super Coverage>) o -> o.isZero()); + } + + public List<Coverage> getNonZeroCoverage() { + return getCoverage(EC.TLC_COVERAGE_VALUE, (Predicate<? super Coverage>) o -> !o.isZero()); + } + + public List<Coverage> getCostCoverage() { + return getCoverage(EC.TLC_COVERAGE_VALUE_COST, (Predicate<? super Coverage>) o -> !o.isZero()); + } + + private List<Coverage> getCoverage(final int code, Predicate<? super Coverage> p) { + final List<Object> coverages = getRecordsOrDefault(code, new ArrayList<>(0)); + return coverages.stream().map(o -> (String[]) o).map(a -> new Coverage(a)).filter(p) + .collect(Collectors.toList()); + } + + public static class Coverage { + private final String line; + private final long count; + private final long cost; + //TODO Take level into account in comparison! + private final int level; + private final boolean isAction; + + public Coverage(String[] line) { + this.isAction = line[0].startsWith("<"); + this.line = line[0].replace("|", "").trim(); + this.level = line[0].length() - this.line.length(); + if (line.length == 1) { + this.count = -1; + this.cost = -1; + } else if (line.length == 2) { + this.count = Long.valueOf(line[1].trim()); + this.cost = -1; + } else if (line.length == 3) { + this.count = Long.valueOf(line[1].trim()); + this.cost = Long.valueOf(line[2].trim()); + } else { + throw new IllegalArgumentException(); + } + } + + public String getLine() { + return line; + } + + public long getCount() { + return count; + } + + public int getLevel() { + return level; + } + + public boolean isZero() { + return count == 0L; + } + + public boolean isCoverage() { + return !isAction; + } + + public boolean isCost() { + return cost >= 0; + } + + public boolean isAction() { + return isAction; + } + + @Override + public String toString() { + return "Coverage [line=" + line + ", count=" + count + ", cost=" + cost + "]"; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + (int) (count ^ (count >>> 32)); + result = prime * result + (int) (cost ^ (cost >>> 32)); + result = prime * result + ((line == null) ? 0 : line.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + Coverage other = (Coverage) obj; + if (count != other.count) + return false; + if (cost != other.cost) + return false; + if (line == null) { + if (other.line != null) + return false; + } else if (!line.equals(other.line)) + return false; return true; } - return false; + } + + /* (non-Javadoc) + * @see java.lang.Object#toString() + */ + public String toString() { + final StringBuffer buf = new StringBuffer(records.size()); + for(Integer key : records.keySet()) { + final List<Object> list = records.get(key); + for (Object elem : list) { + if (elem instanceof String[]) { + String[] strs = (String[]) elem; + for (String s : strs) { + buf.append(key); + buf.append(" -> "); + buf.append(s); + buf.append("\n"); + } + } else if (elem instanceof String) { + buf.append(key); + buf.append(" -> "); + buf.append(elem); + buf.append("\n"); + } + } + } + return buf.toString(); } } diff --git a/tlatools/test/tlc2/model/AssignmentTest.java b/tlatools/test/tlc2/model/AssignmentTest.java new file mode 100644 index 0000000000000000000000000000000000000000..c3918f4d40ca19a9fb665caf10d0bc6c8a3fed40 --- /dev/null +++ b/tlatools/test/tlc2/model/AssignmentTest.java @@ -0,0 +1,55 @@ +/******************************************************************************* + * Copyright (c) 2015 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ + +package tlc2.model; + +import static org.junit.Assert.assertEquals; + +import org.junit.Test; + +import tlc2.model.Assignment; + +public class AssignmentTest { + + @Test + public void test() { + final Assignment a = new Assignment("X", new String[0], "X"); + a.setModelValue(true); + assertEquals("X", a.prettyPrint()); + + final Assignment b = new Assignment("Y", new String[0], "{a1, b1}"); + b.setModelValue(true); + assertEquals("Y" + Assignment.ASSIGNMENT_SIGN + "{a1, b1}", b.prettyPrint()); + + final Assignment c = new Assignment("Z", new String[0], "{s1, s2}"); + c.setModelValue(true); + c.setSymmetric(true); + assertEquals("Z" + Assignment.ASSIGNMENT_SIGN + "s{s1, s2}", c.prettyPrint()); + + final Assignment d = new Assignment("W", new String[0], "1"); + assertEquals("W" + Assignment.ASSIGNMENT_SIGN + "1", d.prettyPrint()); + } +} diff --git a/tlatools/test/tlc2/model/FormulaTest.java b/tlatools/test/tlc2/model/FormulaTest.java new file mode 100644 index 0000000000000000000000000000000000000000..cf4d97f1394db69ae6e56b2bbe61ac9b19d7b38d --- /dev/null +++ b/tlatools/test/tlc2/model/FormulaTest.java @@ -0,0 +1,52 @@ +package tlc2.model; + +import static org.junit.Assert.*; + +import org.junit.Test; + +import tlc2.model.Formula; + +public class FormulaTest { + + @Test + public void testUnnamed() { + Formula formula = new Formula("TRUE"); + assertFalse(formula.isNamed()); + assertEquals("TRUE", formula.getRightHandSide()); + + formula = new Formula("LET clock[i \\in 1..(__trace_var_state)] ==\n" + + " IF i = 1\n" + + " THEN [ p \\in DOMAIN pc |-> 0 ]\n" + + " ELSE clock[i - 1]\n" + + "IN clock[__trace_var_state]"); + assertFalse(formula.isNamed()); + assertEquals("LET clock[i \\in 1..(__trace_var_state)] ==\n" + + " IF i = 1\n" + + " THEN [ p \\in DOMAIN pc |-> 0 ]\n" + + " ELSE clock[i - 1]\n" + + "IN clock[__trace_var_state]", formula.getRightHandSide()); + } + + @Test + public void testNamed() { + Formula formula = new Formula("foo == TRUE"); + assertEquals("foo", formula.getLeftHandSide()); + assertEquals("TRUE", formula.getRightHandSide()); + + formula = new Formula("foo == LET bar == TRUE IN bar"); + assertEquals("foo", formula.getLeftHandSide()); + assertEquals("LET bar == TRUE IN bar", formula.getRightHandSide()); + + formula = new Formula("bar == LET clock[i \\in 1..(__trace_var_state)] ==\n" + + " IF i = 1\n" + + " THEN [ p \\in DOMAIN pc |-> 0 ]\n" + + " ELSE clock[i - 1]\n" + + "IN clock[__trace_var_state]"); + assertEquals("bar", formula.getLeftHandSide()); + assertEquals("LET clock[i \\in 1..(__trace_var_state)] ==\n" + + " IF i = 1\n" + + " THEN [ p \\in DOMAIN pc |-> 0 ]\n" + + " ELSE clock[i - 1]\n" + + "IN clock[__trace_var_state]", formula.getRightHandSide()); + } +} diff --git a/tlatools/test/tlc2/model/TypedSetTest.java b/tlatools/test/tlc2/model/TypedSetTest.java new file mode 100644 index 0000000000000000000000000000000000000000..2388d72e75e1de1c0f529036b0172c18a9c3061c --- /dev/null +++ b/tlatools/test/tlc2/model/TypedSetTest.java @@ -0,0 +1,93 @@ +package tlc2.model; + +import junit.framework.TestCase; +import tlc2.model.TypedSet; + +/** + * Test of the typed set factory + * @author Simon Zambrovski + * @version $Id$ + */ +public class TypedSetTest extends TestCase +{ + + /** + * Test method for {@link tlc2.model.TypedSet#parseSet(java.lang.String)}. + */ + public void testParseSet1() + { + + TypedSet reference = new TypedSet(); + reference.setValues(new String[]{"a", "b", "c", "d", "dsfdf"}); + assertEquals(reference, TypedSet.parseSet("a, b, c, d, dsfdf")); + } + + /** + * Test method for {@link tlc2.model.TypedSet#parseSet(java.lang.String)}. + */ + public void testParseSet2() + { + TypedSet reference = new TypedSet(); + reference.setValues(new String[]{"1", "2", "p", "h!@#$%^&*()_", "dsfdf"}); + assertEquals(reference, TypedSet.parseSet("1, 2, p, h!@#$%^&*()_, dsfdf")); + } + + /** + * Test method for {@link tlc2.model.TypedSet#parseSet(java.lang.String)}. + */ + public void testParseSet3() + { + // positive test + TypedSet reference = new TypedSet(); + reference.setValues(new String[]{"1", "2", "3", "4", "5"}); + reference.setType("p"); + TypedSet sample = TypedSet.parseSet("p_1, p_2, p_3, \n p_4, p_5"); + assertEquals(reference, sample); + } + + + /** + * Test method for {@link tlc2.model.TypedSet#parseSet(java.lang.String)}. + */ + public void testParseSet4() + { + TypedSet reference = new TypedSet(); + reference.setValues(new String[]{"p_1", "i_2", "p_3", "p_4", "p_5"}); + TypedSet sample = TypedSet.parseSet("p_1, i_2, p_3, p_4, p_5"); + assertEquals(reference, sample); + } + + + /** + * Test method for {@link tlc2.model.TypedSet#parseSet(java.lang.String)}. + */ + public void testParseSet5() + { + TypedSet reference = new TypedSet(); + reference.setValues(new String[]{"p_", "p_2", "p_3", "p_4", "p_5"}); + TypedSet sample = TypedSet.parseSet("p_, p_2, p_3, p_4, p_5"); + assertEquals(reference, sample); + } + + /** + * Test method for {@link tlc2.model.TypedSet#parseSet(java.lang.String)}. + */ + public void testParseSet6() + { + // null set + TypedSet reference = new TypedSet(); + TypedSet sample = TypedSet.parseSet(""); + assertEquals(reference, sample); + + sample = TypedSet.parseSet(null); + assertEquals(reference, sample); + + sample = TypedSet.parseSet(", , , , "); + assertEquals(reference, sample); + + sample = TypedSet.parseSet("{, , , ,}"); + assertEquals(reference, sample); + + } + +} diff --git a/tlatools/test/tlc2/module/RandomizationTest.java b/tlatools/test/tlc2/module/RandomizationTest.java new file mode 100644 index 0000000000000000000000000000000000000000..75132a85135dcc9099a558fbf46cdfe7ca6f7f9c --- /dev/null +++ b/tlatools/test/tlc2/module/RandomizationTest.java @@ -0,0 +1,209 @@ +/******************************************************************************* + * Copyright (c) 20178 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.module; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import org.junit.BeforeClass; +import org.junit.Test; + +import tlc2.tool.EvalException; +import tlc2.util.FP64; +import tlc2.value.RandomEnumerableValues; +import tlc2.value.impl.Enumerable; +import tlc2.value.impl.IntValue; +import tlc2.value.impl.IntervalValue; +import tlc2.value.impl.SetEnumValue; +import tlc2.value.impl.StringValue; +import tlc2.value.impl.Value; + +public class RandomizationTest { + + @BeforeClass + public static void setup() { + // Make test repeatable by setting random seed always to same value. + RandomEnumerableValues.setSeed(15041980L); + + // Initialize FP64 to prevent NPE in hashCode (which relies on Value#fingerprint). + FP64.Init(); + } + + /* RandomSubsetSet */ + + @Test + public void testV1Valid() { + final Enumerable randomSubset = (Enumerable) Randomization.RandomSubsetSet(IntValue.gen(42), new StringValue("0.1"), + new IntervalValue(1, 42)); + + assertNotNull(randomSubset); + assertEquals(42, randomSubset.size()); + } + + @Test + public void testV2Larger1() { + try { + Randomization.RandomSubsetSet(IntValue.gen(23), new StringValue("1.1"), new IntervalValue(1, 42)); + } catch (final EvalException ee) { + assertTrue(ee.getMessage().contains("1.1")); + return; + } + fail(); + } + + /* RandomSetOfSubsets */ + + @Test + public void testV1Negative() { + final Value v1 = IntValue.gen(-42); + try { + Randomization.RandomSetOfSubsets(v1, IntValue.gen(42), new IntervalValue(1, 42)); + } catch (final EvalException ee) { + assertTrue(ee.getMessage().contains("The first argument of RandomSetOfSubsets should be a nonnegative integer, but instead it is:\n-42")); + return; + } + fail(); + } + + @Test + public void testV1NoIntValue() { + final Value v1 = new StringValue("52"); + try { + Randomization.RandomSetOfSubsets(v1, IntValue.gen(42), new IntervalValue(1, 42)); + } catch (final EvalException ee) { + assertTrue(ee.getMessage().contains("The first argument of RandomSetOfSubsets should be a nonnegative integer, but instead it is:\n\"52\"")); + return; + } + fail(); + } + + @Test + public void testV1Zero() { + final Value v1 = IntValue.gen(0); + final Enumerable randomSubset = (Enumerable) Randomization.RandomSetOfSubsets(v1, IntValue.gen(42), + new IntervalValue(1, 42)); + + assertNotNull(randomSubset); + assertEquals(0, randomSubset.size()); + } + + @Test + public void testV2Zero() { + final Enumerable randomSubset = (Enumerable) Randomization.RandomSetOfSubsets(IntValue.gen(23), IntValue.gen(0), + new IntervalValue(1, 42)); + assertEquals(1, randomSubset.size()); + // empty set is only member + assertTrue(randomSubset.member(new SetEnumValue())); + } + + @Test + public void testV2Negative() { + try { + Randomization.RandomSetOfSubsets(IntValue.gen(23), IntValue.gen(-1), new IntervalValue(1, 42)); + } catch (final EvalException ee) { + assertTrue(ee.getMessage().contains("The second argument of RandomSetOfSubsets should be a nonnegative integer, but instead it is:\n-1")); + return; + } + fail(); + } + + @Test + public void testV3Empty() { + try { + Randomization.RandomSetOfSubsets(IntValue.gen(42), IntValue.gen(42), new SetEnumValue()); + } catch (final EvalException ee) { + assertTrue(ee.getMessage().contains( + "The first argument of RandomSetOfSubsets should be a nonnegative integer that is smaller than the subset's size of 2^0, but instead it is:\n42")); + return; + } + fail(); + } + + @Test + public void testV3AstronomicallyLarge() { + final Enumerable randomSubset = (Enumerable) Randomization.RandomSetOfSubsets(IntValue.gen(42), IntValue.gen(42), + new IntervalValue(1, 256)); + + assertNotNull(randomSubset); + assertEquals(42, randomSubset.size()); + } + + @Test + public void testV3isInfinite() { + try { + Randomization.RandomSetOfSubsets(IntValue.gen(42), IntValue.gen(42), Naturals.Nat()); + } catch (final EvalException ee) { + assertTrue(ee.getMessage().contains( + "The third argument of RandomSetOfSubsets should be a finite set, but instead it is:\nNat")); + return; + } + fail(); + } + + @Test + public void testRSSV2Zero() { + final Enumerable randomSubset = (Enumerable) Randomization.RandomSetOfSubsets(IntValue.gen(23), IntValue.gen(0), + new IntervalValue(1, 42)); + assertEquals(1, randomSubset.size()); + // empty set is only member + assertTrue(randomSubset.member(new SetEnumValue())); + } + + @Test + public void testRSSV2Negative() { + try { + Randomization.RandomSetOfSubsets(IntValue.gen(23), IntValue.gen(-1), new IntervalValue(1, 42)); + } catch (final EvalException ee) { + assertTrue(ee.getMessage().contains("The second argument of RandomSetOfSubsets should be a nonnegative integer, but instead it is:\n-1")); + return; + } + fail(); + } + + @Test + public void testRSSV2Cardinality() { + final Enumerable randomSubset = (Enumerable) Randomization.RandomSetOfSubsets(IntValue.gen(32), IntValue.gen(5), + new IntervalValue(1, 5)); + assertEquals(1, randomSubset.size()); + // With probability 1 (n = 5), the operator - due to collisions - only generates + // a single subset which is the input set. + assertTrue(randomSubset.member(new IntervalValue(1, 5))); + } + + @Test + public void testRSSV2TwiceCardinality() { + try { + Randomization.RandomSetOfSubsets(IntValue.gen(23), IntValue.gen(10), new IntervalValue(1, 5)); + } catch (final EvalException ee) { + assertTrue(ee.getMessage().contains( + "The second argument of RandomSetOfSubsets should be a nonnegative integer in range 0..Cardinality(S), but instead it is:\n10")); + return; + } + fail(); + } +} diff --git a/tlatools/test/tlc2/module/SequencesTest.java b/tlatools/test/tlc2/module/SequencesTest.java new file mode 100644 index 0000000000000000000000000000000000000000..b4c315bc2e41986aa61fb6b46139f92366520521 --- /dev/null +++ b/tlatools/test/tlc2/module/SequencesTest.java @@ -0,0 +1,85 @@ +/******************************************************************************* + * Copyright (c) 2019 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.module; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import org.junit.Test; + +import tlc2.output.EC; +import tlc2.tool.EvalException; +import tlc2.value.impl.IntValue; +import tlc2.value.impl.StringValue; +import tlc2.value.impl.Value; + +public class SequencesTest { + + @Test + public void testHeadString() { + Value a = Sequences.Head(new StringValue("a")); + assertTrue(a instanceof StringValue); + assertEquals("a", ((StringValue) a).val.toString()); + + a = Sequences.Head(new StringValue("abc")); + assertTrue(a instanceof StringValue); + assertEquals("a", ((StringValue) a).val.toString()); + } + + @Test + public void testHeadStringEmpty() { + try { + Sequences.Head(new StringValue("")); + } catch (EvalException e) { + assertEquals(EC.TLC_MODULE_APPLY_EMPTY_SEQ, e.getErrorCode()); + return; + } + fail(); + } + + @Test + public void testAppendString() { + Value a = Sequences.Append(new StringValue(""), new StringValue("a")); + assertTrue(a instanceof StringValue); + assertEquals("a", ((StringValue) a).val.toString()); + + a = Sequences.Append(new StringValue("abc"), new StringValue("d")); + assertTrue(a instanceof StringValue); + assertEquals("abcd", ((StringValue) a).val.toString()); + } + + @Test + public void testAppendStringNonString() { + try { + Sequences.Append(new StringValue(""), IntValue.ValZero); + } catch (EvalException e) { + assertEquals(EC.TLC_MODULE_EVALUATING, e.getErrorCode()); + return; + } + fail(); + } +} diff --git a/tlatools/test/tlc2/module/TLCTest.java b/tlatools/test/tlc2/module/TLCTest.java new file mode 100644 index 0000000000000000000000000000000000000000..78a310b6d080cf42be789617ed2b32de7ec3479f --- /dev/null +++ b/tlatools/test/tlc2/module/TLCTest.java @@ -0,0 +1,123 @@ +/******************************************************************************* + * Copyright (c) 2016 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ + +package tlc2.module; + +import java.util.HashSet; +import java.util.Set; + +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; + +import tlc2.util.FP64; +import tlc2.value.impl.Enumerable; +import tlc2.value.impl.FcnRcdValue; +import tlc2.value.impl.IntValue; +import tlc2.value.impl.IntervalValue; +import tlc2.value.impl.SetEnumValue; +import tlc2.value.impl.TupleValue; +import tlc2.value.impl.Value; +import tlc2.value.impl.ValueEnumeration; + +public class TLCTest { + + @BeforeClass + public static void setup() { + FP64.Init(); + } + + /** + * func2 == <<1,2,3>> + * ASSUME (3 :> 11 @@ func2) = <<1,2,11>> + */ + @Test + public void testA() { + final Value f = new FcnRcdValue(new Value [] { IntValue.gen(3) }, new Value [] { IntValue.gen(11) }, true); + final Value g = new TupleValue(new Value [] { IntValue.gen(1), IntValue.gen(2), IntValue.gen(3) }); + + final Value combined = TLC.CombineFcn(f, g); + Assert.assertTrue(combined instanceof FcnRcdValue); + final FcnRcdValue rcdVal = (FcnRcdValue) combined; + // Have to normalize to bring values/domain into natural order expected by assertions below + rcdVal.normalize(); + + // domain + Assert.assertEquals(3, rcdVal.domain.length); + Assert.assertArrayEquals(new Value [] { IntValue.gen(1), IntValue.gen(2), IntValue.gen(3) }, rcdVal.domain); + + // values + Assert.assertEquals(3, rcdVal.values.length); + Assert.assertArrayEquals(new Value [] { IntValue.gen(1), IntValue.gen(2), IntValue.gen(11) }, rcdVal.values); + } + + /** + * func2 == <<1,2,3>> + * ASSUME (4 :> 11 @@ func2) = <<1,2,3,11>> + */ + @Test + public void testB() { + final Value f = new FcnRcdValue(new Value [] { IntValue.gen(4) }, new Value [] { IntValue.gen(11) }, true); + final Value g = new TupleValue(new Value [] { IntValue.gen(1), IntValue.gen(2), IntValue.gen(3), IntValue.gen(11) }); + + final Value combined = TLC.CombineFcn(f, g); + Assert.assertTrue(combined instanceof FcnRcdValue); + final FcnRcdValue rcdVal = (FcnRcdValue) combined; + // Have to normalize to bring values/domain into natural order expected by assertions below + rcdVal.normalize(); + + // domain + Assert.assertEquals(4, rcdVal.domain.length); + Assert.assertArrayEquals(new Value [] { IntValue.gen(1), IntValue.gen(2), IntValue.gen(3), IntValue.gen(4) }, + rcdVal.domain); + + // values + Assert.assertEquals(4, rcdVal.values.length); + Assert.assertArrayEquals(new Value [] { IntValue.gen(1), IntValue.gen(2), IntValue.gen(3), IntValue.gen(11) }, + rcdVal.values); + } + + @Test + public void testPermutations() { + final SetEnumValue in = (SetEnumValue) new IntervalValue(1, 5).toSetEnum(); + Assert.assertEquals(5, in.size()); + + final Value permutations = TLC.Permutations(in); + Assert.assertTrue(permutations instanceof Enumerable); + Assert.assertEquals(120, permutations.size()); + + final Set<Value > values = new HashSet<>(permutations.size()); + + final ValueEnumeration elements = ((Enumerable) permutations).elements(); + Value val = null; + while ((val = elements.nextElement()) != null) { + Assert.assertEquals(in.size(), val.size()); + values.add(val); + } + + Assert.assertEquals(120, values.size()); + } +} diff --git a/tlatools/test/tlc2/output/CFGCopierTest.java b/tlatools/test/tlc2/output/CFGCopierTest.java new file mode 100644 index 0000000000000000000000000000000000000000..eadeefc735e19b792bb827f07d2f838927b23cc3 --- /dev/null +++ b/tlatools/test/tlc2/output/CFGCopierTest.java @@ -0,0 +1,64 @@ +package tlc2.output; + +import java.io.IOException; +import java.io.StringReader; +import java.io.StringWriter; + +import org.junit.Assert; +import org.junit.Test; + +public class CFGCopierTest { + private static final String INIT_NEXT_CFG = "\\* INIT definition\n" + + "INIT\n" + + "init_15767838108312000\n" + + "\\* NEXT definition\n" + + "NEXT\n" + + "next_15767838108313000\n" + + "\\* Action Constraint definition\n" + + "ACTION_CONSTRAINT\n" + + "action_constr_15767838108314000\n"; + // Yes, this is an illegal CFG as it specifies both SPECIFICATION and INIT/NEXT + private static final String ORIGINAL_CFG = "\\* CONSTANT definitions\n" + + "CONSTANT\n" + + "N <- const_157376354642853000\n" + + "\\* SPECIFICATION definition\n" + + "SPECIFICATION\n" + + "Spec\n" + + "\\* INIT definition\n" + + "INIT\n" + + "BigInit\n" + + "\\* NEXT definition\n" + + "NEXT\n" + + "SmallNext\n" + + "\\* INVARIANT definition\n" + + "INVARIANT\n" + + "TypeInvariant\n" + + "Invariant\n" + + "\\* PROPERTY definition\n" + + "PROPERTY\n" + + "Termination\n" + + "\\* Generated on Thu Nov 14 12:32:26 PST 2019\n"; + private static final String NEW_CFG = "\\* CONSTANT definitions\n" + + "CONSTANT\n" + + "N <- const_157376354642853000\n" + + "\\* INVARIANT definition\n" + + "INVARIANT\n" + + "TypeInvariant\n" + + "Invariant\n" + + "\\* PROPERTY definition\n" + + "PROPERTY\n" + + "Termination\n" + + INIT_NEXT_CFG + + "\n"; + + @Test + public void testCopy() throws IOException { + final CFGCopier copier = new CFGCopier("doesn't", "matter", null, INIT_NEXT_CFG); + final StringReader sr = new StringReader(ORIGINAL_CFG); + final StringWriter sw = new StringWriter(); + + copier.copy(sr, sw); + // We compare the substring because there is a new generation time stamp at the end of the newly created config + Assert.assertEquals(NEW_CFG, sw.getBuffer().toString().substring(0, NEW_CFG.length())); + } +} diff --git a/tlatools/test/tlc2/output/ErrorPrinterTest.java b/tlatools/test/tlc2/output/MPTest.java similarity index 50% rename from tlatools/test/tlc2/output/ErrorPrinterTest.java rename to tlatools/test/tlc2/output/MPTest.java index 2b603cd30a70d420efad233268f50fa4a0caabcb..43c199632945a8ccc640441b620e75451a849fcd 100644 --- a/tlatools/test/tlc2/output/ErrorPrinterTest.java +++ b/tlatools/test/tlc2/output/MPTest.java @@ -1,21 +1,22 @@ package tlc2.output; -import junit.framework.TestCase; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import org.junit.Before; +import org.junit.Test; + import util.ToolIO; -/** - * @author Simon Zambrovski - * @version $Id$ - */ -public class ErrorPrinterTest extends TestCase +public class MPTest { /* (non-Javadoc) * @see junit.framework.TestCase#setUp() */ - protected void setUp() throws Exception + @Before + public void setUp() throws Exception { - super.setUp(); ToolIO.setMode(ToolIO.TOOL); ToolIO.reset(); } @@ -23,7 +24,8 @@ public class ErrorPrinterTest extends TestCase /** * Test method for {@link tlc2.output.MP#printError(int)}. */ - public void testPrintErrorInt() + @Test + public void testPrintErrorInt() { MP.printError(EC.UNIT_TEST); String[] allMessages = ToolIO.getAllMessages(); @@ -34,7 +36,8 @@ public class ErrorPrinterTest extends TestCase /** * Test method for {@link tlc2.output.MP#printError(int, java.lang.String)}. */ - public void testPrintErrorIntString() + @Test + public void testPrintErrorIntString() { String parameter = "EXPECTED"; MP.printError(EC.UNIT_TEST, parameter); @@ -46,7 +49,8 @@ public class ErrorPrinterTest extends TestCase /** * Test method for {@link tlc2.output.MP#printError(int, java.lang.String[])}. */ - public void testPrintErrorIntStringArray() + @Test + public void testPrintErrorIntStringArray() { String[] parameters = new String[] { "EXPECTED", "EXPECTED2", "TOO MANY" }; MP.printError(EC.UNIT_TEST, parameters); @@ -55,5 +59,22 @@ public class ErrorPrinterTest extends TestCase assertEquals("Error: [" + parameters[0] + "][" + parameters[1] + "]", allMessages[0]); } - + @Test + public void testPrintProgressStats() { + String[] parameters = new String[] { + "this.trace.getLevelForReporting()", + MP.format(3000000), + MP.format(5000), + MP.format(1222333444), + MP.format(10000), + MP.format(1234) + }; + MP.printMessage(EC.TLC_PROGRESS_STATS, parameters); + String[] allMessages = ToolIO.getAllMessages(); + assertEquals(1, allMessages.length); + assertTrue(allMessages[0], allMessages[0].contains( + "3,000,000 states generated (10,000 s/min), 5,000 distinct states found (1,234 ds/min), 1,222,333,444 states left on queue.") + || allMessages[0].contains( + "3.000.000 states generated (10.000 s/min), 5.000 distinct states found (1.234 ds/min), 1.222.333.444 states left on queue.")); + } } diff --git a/tlatools/test/tlc2/output/SpecTraceExpressionWriterTest.java b/tlatools/test/tlc2/output/SpecTraceExpressionWriterTest.java new file mode 100644 index 0000000000000000000000000000000000000000..1f2fb1ddb96c6de2f82b6e0a8c5b0ae79d0a3949 --- /dev/null +++ b/tlatools/test/tlc2/output/SpecTraceExpressionWriterTest.java @@ -0,0 +1,160 @@ +package tlc2.output; + +import static org.junit.Assert.assertTrue; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import org.junit.Before; +import org.junit.Test; + +import tla2sany.drivers.FrontEndException; +import tla2sany.drivers.SANY; +import tla2sany.modanalyzer.SpecObj; +import tlc2.model.Formula; +import tlc2.model.MCState; +import tlc2.model.TraceExpressionInformationHolder; +import util.TLAConstants; +import util.TestPrintStream; + +/** + * The genesis for these tests is regressions that were introduced by beautification changes made as part of #393. + * + * As future spec-generation methods are touched, something implementing them should be added below. + */ +public class SpecTraceExpressionWriterTest { + static private final String TRIVIAL_TWO_STATE_DEADLOCK_PREAMBLE + = "VARIABLE x, y\n" + + "XIncr == (x' = x * 2)\n" + + " /\\ (x < 8)\n" + + " /\\ UNCHANGED y\n" + + "YIncr == (y' = x + y)\n" + + " /\\ (y < 15)\n" + + " /\\ UNCHANGED x\n"; + static private final String[] TRIVIAL_TWO_STATE_DEADLOCK_INIT + = new String[] { + "TestInit", + "TestInit == x \\in 1 .. 10 /\\ y \\in 1 .. 10\n" + }; + static private final String[] TRIVIAL_TWO_STATE_DEADLOCK_NEXT + = new String[] { + "TestNext", + "TestNext == YIncr \\/ XIncr\n" + }; + static private final String ERROR_STATE_IP + = "1: <Initial predicate>\n" + + "/\\ x = 8\n" + + "/\\ y = 7\n"; + static private final String ERROR_STATE_1 + = "2: <YIncr line 8, col 10 to line 10, col 26 of module Bla>\n" + + "/\\ x = 8\n" + +"/\\ y = 15\n"; + + + private SpecTraceExpressionWriter writer; + private File tlaFile; + private File cfgFile; + + @Before + public void setUp() throws IOException { + tlaFile = File.createTempFile("sptewt_", ".tla"); + tlaFile.deleteOnExit(); + cfgFile = File.createTempFile("sptewt_", ".cfg"); + cfgFile.deleteOnExit(); + + final String tlaFilename = tlaFile.getName(); + final int baseNameLength = tlaFilename.length() - TLAConstants.Files.TLA_EXTENSION.length(); + final String specName = tlaFilename.substring(0, baseNameLength); + writer = new SpecTraceExpressionWriter(); + writer.addPrimer(specName, "Naturals"); + writer.appendContentToBuffers(TRIVIAL_TWO_STATE_DEADLOCK_PREAMBLE, null); + } + + private void concludeTest() throws FrontEndException, IOException { + writer.writeFiles(tlaFile, cfgFile); + + final SpecObj so = new SpecObj(tlaFile.getAbsolutePath(), null); + final TestPrintStream printStream = new TestPrintStream(); + + final int result = SANY.frontEndMain(so, tlaFile.getAbsolutePath(), printStream); + if (result != 0) { + throw new FrontEndException("Parsing returned a non-zero success code (" + result + ")"); + } + } + + private List<MCState> generateStatesForDeadlockCondition() { + final List<MCState> states = new ArrayList<>(); + + states.add(MCState.parseState(ERROR_STATE_IP)); + states.add(MCState.parseState(ERROR_STATE_1)); + + return states; + } + + @Test + public void testInitNextWithNoError() throws Exception { + writer.addInitNextDefinitions(TRIVIAL_TWO_STATE_DEADLOCK_INIT, TRIVIAL_TWO_STATE_DEADLOCK_NEXT, + "writerTestInit", "writerTextNext"); + + concludeTest(); + } + + @Test + public void testInitNextWithError() throws Exception { + final List<MCState> trace = generateStatesForDeadlockCondition(); + final StringBuilder tempCFGBuffer = new StringBuilder(); + final StringBuilder[] tlaBuffers + = SpecTraceExpressionWriter.addInitNextToBuffers(tempCFGBuffer, trace, null, "STEWInit", "STEWNext", + "STEWAC", TRIVIAL_TWO_STATE_DEADLOCK_NEXT[0], true); + + writer.appendContentToBuffers(tlaBuffers[0].toString(), tempCFGBuffer.toString()); + writer.addTraceFunction(trace); + writer.appendContentToBuffers(tlaBuffers[1].toString(), null); + + concludeTest(); + } + + @Test + public void testInitNextWithErrorAndTraceExpression() throws Exception { + final List<MCState> trace = generateStatesForDeadlockCondition(); + writer.addTraceFunction(trace); + + final List<Formula> expressions = new ArrayList<>(); + expressions.add(new Formula("ENABLED XIncr")); + expressions.add(new Formula("y # 7")); + final TraceExpressionInformationHolder[] traceExpressions + = writer.createAndAddVariablesAndDefinitions(expressions, "writerTestTraceExpressions"); + writer.addInitNext(trace, traceExpressions, "STEWInit", "STEWNext", "STEWAC", TRIVIAL_TWO_STATE_DEADLOCK_NEXT[0]); + + concludeTest(); + } + + + @Test + public void testMultilineTraceExpression() throws Exception { + final List<MCState> trace = generateStatesForDeadlockCondition(); + writer.addTraceFunction(trace); + + final List<Formula> expressions = new ArrayList<>(); + expressions.add(new Formula("\n" + + "/\\ y # 7\n" + + "/\\ \\/ TRUE\n" + + " \\* Comment" + + " \\/ FALSE")); + // Named expression + final Formula e = new Formula("namedExpression == \n" + + " (* A commend \n over two lines*)" + + " /\\ \\/ TRUE\n" + + " \\* Comment" + + " \\/ FALSE"); + assertTrue(e.isNamed()); + expressions.add(e); + final TraceExpressionInformationHolder[] traceExpressions + = writer.createAndAddVariablesAndDefinitions(expressions, "writerTestTraceExpressions"); + writer.addInitNext(trace, traceExpressions, "STEWInit", "STEWNext", "STEWAC", TRIVIAL_TWO_STATE_DEADLOCK_NEXT[0]); + + concludeTest(); + } +} diff --git a/tlatools/test/tlc2/output/TLACopierTest.java b/tlatools/test/tlc2/output/TLACopierTest.java new file mode 100644 index 0000000000000000000000000000000000000000..654553dd4004c333fce14915b71eb9fa8028ac1c --- /dev/null +++ b/tlatools/test/tlc2/output/TLACopierTest.java @@ -0,0 +1,72 @@ +package tlc2.output; + +import java.io.IOException; +import java.io.StringReader; +import java.io.StringWriter; + +import org.junit.Assert; +import org.junit.Test; + +public class TLACopierTest { + private static final String INIT_NEXT_DEFINITIONS = "init_ldq ==\n" + + " TRUE\n" + + " \\/ FALSE\n" + + " \n" + + "next_ldq ==\n" + + " FALSE\n"; + private static final String ORIGINAL_SPEC = "---- MODULE MC ----\n" + + "EXTENDS Queens, TLC\n" + + "\n" + + "\\* CONSTANT definitions @modelParameterConstants:0N\n" + + "const_157376354642853000 == \n" + + "3\n" + + "----\n" + + "\n" + + "=============================================================================\n" + + "\\* Modification History\n" + + "\\* Created Thu Nov 14 12:32:26 PST 2019 by loki\n"; + private static final String NEW_SPEC_EXTENDED = "---- MODULE Spectacle ----\n" + + "EXTENDS Queens, TLC\n" + + "\n" + + "\\* CONSTANT definitions @modelParameterConstants:0N\n" + + "const_157376354642853000 == \n" + + "3\n" + + "----\n" + + "\n" + + INIT_NEXT_DEFINITIONS + "\n" + + "=============================================================================\n" + + "\\* Modification History\n" + + "\\* Created Thu Nov 14 12:32:26 PST 2019 by loki\n"; + private static final String NEW_SPEC_NONEXTENDED = "---- MODULE Spectacle ----\n" + + "EXTENDS Queens, TLC, TLC, Toolbox\n" + + "\n" + + "\\* CONSTANT definitions @modelParameterConstants:0N\n" + + "const_157376354642853000 == \n" + + "3\n" + + "----\n" + + "\n" + + INIT_NEXT_DEFINITIONS + "\n" + + "=============================================================================\n" + + "\\* Modification History\n" + + "\\* Created Thu Nov 14 12:32:26 PST 2019 by loki\n"; + + @Test + public void testNonExtending() throws IOException { + final TLACopier copier = new TLACopier("MC", "Spectacle", null, INIT_NEXT_DEFINITIONS, false, false); + final StringReader sr = new StringReader(ORIGINAL_SPEC); + final StringWriter sw = new StringWriter(); + + copier.copy(sr, sw); + Assert.assertEquals(NEW_SPEC_NONEXTENDED, sw.getBuffer().toString()); + } + + @Test + public void testExtending() throws IOException { + final TLACopier copier = new TLACopier("MC", "Spectacle", null, INIT_NEXT_DEFINITIONS, true, true); + final StringReader sr = new StringReader(ORIGINAL_SPEC); + final StringWriter sw = new StringWriter(); + + copier.copy(sr, sw); + Assert.assertEquals(NEW_SPEC_EXTENDED, sw.getBuffer().toString()); + } +} diff --git a/tlatools/test/tlc2/tool/.gitattributes b/tlatools/test/tlc2/tool/.gitattributes new file mode 100644 index 0000000000000000000000000000000000000000..918458c130d4d970d54568d475c092205fce9ebc --- /dev/null +++ b/tlatools/test/tlc2/tool/.gitattributes @@ -0,0 +1,3 @@ +## Declare DumpAsDotTest.dot to keep unix line endings on checkout even on Windows. +## With "crlf" DumpAsDotTest fails on Windows. +DumpAsDotTest.dot text eol=lf \ No newline at end of file diff --git a/tlatools/test/tlc2/tool/ASTest.java b/tlatools/test/tlc2/tool/ASTest.java new file mode 100644 index 0000000000000000000000000000000000000000..1d8bcc269b8acb1f254d326df625d91749813b17 --- /dev/null +++ b/tlatools/test/tlc2/tool/ASTest.java @@ -0,0 +1,47 @@ +/******************************************************************************* + * Copyright (c) 2015 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ + +package tlc2.tool; + +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.output.EC; +import tlc2.output.EC.ExitStatus; +import tlc2.tool.liveness.ModelCheckerTestCase; + +public class ASTest extends ModelCheckerTestCase { + + public ASTest() { + super("AS", "AS", ExitStatus.FAILURE_SPEC_EVAL); + } + + @Test + public void testSpec() { + assertTrue(recorder.recorded(EC.TLC_STATES_AND_NO_NEXT_ACTION)); + } +} diff --git a/tlatools/test/tlc2/tool/AbsoluteSpecPathTest.java b/tlatools/test/tlc2/tool/AbsoluteSpecPathTest.java new file mode 100644 index 0000000000000000000000000000000000000000..62b4d921aab72a793326020738dbf9bbc72721dd --- /dev/null +++ b/tlatools/test/tlc2/tool/AbsoluteSpecPathTest.java @@ -0,0 +1,42 @@ +package tlc2.tool; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.junit.Assume.assumeFalse; + +import org.junit.Test; + +import tlc2.TLC; +import tlc2.TestMPRecorder; +import tlc2.output.EC; +import tlc2.output.MP; + +public class AbsoluteSpecPathTest extends CommonTestCase { + + public AbsoluteSpecPathTest() { + super(new TestMPRecorder()); + } + + @Test + public void test() throws Exception { + // Check that BASE_DIR is actually set to make sure we have an absolute path to + // work with. If this test gets executed from within the Eclipse IDE, manually + // set -Dbasedir=/path/to/tlatools/ + assumeFalse(BASE_DIR.equals("")); + + MP.setRecorder(recorder); + + // Do not call TLC#main because we won't get control back (system.exit) to check + // assertions below. + final TLC tlc = new TLC(); + tlc.handleParameters(new String[] {BASE_PATH + "Test2"}); + tlc.process(); + + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "5")); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "6", "5", "0")); + + // No 'general' errors recorded + assertFalse(recorder.recorded(EC.GENERAL)); + } +} diff --git a/tlatools/test/tlc2/tool/AssertExpressionStack.java b/tlatools/test/tlc2/tool/AssertExpressionStack.java new file mode 100644 index 0000000000000000000000000000000000000000..11c2624a4eb1dd6ad17e2dd60c8bb36125b4c898 --- /dev/null +++ b/tlatools/test/tlc2/tool/AssertExpressionStack.java @@ -0,0 +1,60 @@ +/******************************************************************************* + * Copyright (c) 2017 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.Test; + +import tlc2.output.EC; +import tlc2.tool.liveness.ModelCheckerTestCase; + +public class AssertExpressionStack extends ModelCheckerTestCase { + + public AssertExpressionStack() { + super("AssertExpressionStack"); + } + + @Test + public void testSpec() { + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertFalse(recorder.recorded(EC.TLC_BUG)); + + assertTrue(recorder.recorded(EC.TLC_BEHAVIOR_UP_TO_THIS_POINT)); + + final List<String> expectedTrace = new ArrayList<String>(2); + expectedTrace.add("x = 0"); + expectedTrace.add("x = 1"); + assertTraceWith(recorder.getRecords(EC.TLC_STATE_PRINT2), expectedTrace); + + // Assert a proper nested expression has been recorded which represents the call stack. + assertFalse(recorder.recordedWithStringValue(EC.TLC_NESTED_EXPRESSION, " The error call stack is empty.\n")); + } +} diff --git a/tlatools/test/tlc2/tool/AssignmentInitExpensiveTest.java b/tlatools/test/tlc2/tool/AssignmentInitExpensiveTest.java new file mode 100644 index 0000000000000000000000000000000000000000..1e43faa4135164bad5ea49a35ae441b6745cdda8 --- /dev/null +++ b/tlatools/test/tlc2/tool/AssignmentInitExpensiveTest.java @@ -0,0 +1,50 @@ +/******************************************************************************* + * Copyright (c) 2017 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.output.EC; +import tlc2.tool.liveness.ModelCheckerTestCase; + +public class AssignmentInitExpensiveTest extends ModelCheckerTestCase { + + public AssignmentInitExpensiveTest() { + super("AssignmentInitExpensive"); + } + + @Test + public void test() { + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertFalse(recorder.recorded(EC.GENERAL)); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "10002", "1", "0")); + + assertZeroUncovered(); + } +} diff --git a/tlatools/test/tlc2/tool/AssignmentInitNegTest.java b/tlatools/test/tlc2/tool/AssignmentInitNegTest.java new file mode 100644 index 0000000000000000000000000000000000000000..cec19a9cca35c3ee1441d07e96dbc0f347885a83 --- /dev/null +++ b/tlatools/test/tlc2/tool/AssignmentInitNegTest.java @@ -0,0 +1,50 @@ +/******************************************************************************* + * Copyright (c) 2017 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.output.EC; +import tlc2.tool.liveness.ModelCheckerTestCase; + +public class AssignmentInitNegTest extends ModelCheckerTestCase { + + public AssignmentInitNegTest() { + super("AssignmentInitNeg"); + } + + @Test + public void test() { + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertFalse(recorder.recorded(EC.GENERAL)); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "2", "1", "0")); + + assertZeroUncovered(); + } +} diff --git a/tlatools/test/tlc2/tool/AssignmentInitTest.java b/tlatools/test/tlc2/tool/AssignmentInitTest.java new file mode 100644 index 0000000000000000000000000000000000000000..2e18f1fc888032751ec9740ee314675f05bc1ead --- /dev/null +++ b/tlatools/test/tlc2/tool/AssignmentInitTest.java @@ -0,0 +1,50 @@ +/******************************************************************************* + * Copyright (c) 2017 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.output.EC; +import tlc2.tool.liveness.ModelCheckerTestCase; + +public class AssignmentInitTest extends ModelCheckerTestCase { + + public AssignmentInitTest() { + super("AssignmentInit"); + } + + @Test + public void test() { + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertFalse(recorder.recorded(EC.GENERAL)); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "6", "5", "0")); + + assertZeroUncovered(); + } +} diff --git a/tlatools/test/tlc2/tool/AssignmentNext2Test.java b/tlatools/test/tlc2/tool/AssignmentNext2Test.java new file mode 100644 index 0000000000000000000000000000000000000000..e3bdff754b77e1eaa537d5540f1f74e54b748741 --- /dev/null +++ b/tlatools/test/tlc2/tool/AssignmentNext2Test.java @@ -0,0 +1,49 @@ +/******************************************************************************* + * Copyright (c) 2017 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.output.EC; +import tlc2.tool.liveness.ModelCheckerTestCase; + +public class AssignmentNext2Test extends ModelCheckerTestCase { + + public AssignmentNext2Test() { + super("AssignmentNext2"); + } + + @Test + public void test() { + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertFalse(recorder.recorded(EC.GENERAL)); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "3", "2", "0")); + assertZeroUncovered(); + } +} diff --git a/tlatools/test/tlc2/tool/AssignmentNext3Test.java b/tlatools/test/tlc2/tool/AssignmentNext3Test.java new file mode 100644 index 0000000000000000000000000000000000000000..a02f8a42855ceb747e807a31dbf3805888c2a5dc --- /dev/null +++ b/tlatools/test/tlc2/tool/AssignmentNext3Test.java @@ -0,0 +1,49 @@ +/******************************************************************************* + * Copyright (c) 2017 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.output.EC; +import tlc2.tool.liveness.ModelCheckerTestCase; + +public class AssignmentNext3Test extends ModelCheckerTestCase { + + public AssignmentNext3Test() { + super("AssignmentNext3"); + } + + @Test + public void test() { + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertFalse(recorder.recorded(EC.GENERAL)); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "26", "5", "0")); + assertZeroUncovered(); + } +} diff --git a/tlatools/test/tlc2/tool/AssignmentNextTest.java b/tlatools/test/tlc2/tool/AssignmentNextTest.java new file mode 100644 index 0000000000000000000000000000000000000000..accf6f366173fceeb5f25f6d16dad799b065d9c5 --- /dev/null +++ b/tlatools/test/tlc2/tool/AssignmentNextTest.java @@ -0,0 +1,51 @@ +/******************************************************************************* + * Copyright (c) 2017 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.output.EC; +import tlc2.tool.liveness.ModelCheckerTestCase; + +public class AssignmentNextTest extends ModelCheckerTestCase { + + public AssignmentNextTest() { + super("AssignmentNext"); + } + + @Test + public void test() { + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertFalse(recorder.recorded(EC.GENERAL)); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "3", "2", "0")); + assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "2")); + + assertZeroUncovered(); + } +} diff --git a/tlatools/test/tlc2/tool/BagsTest.java b/tlatools/test/tlc2/tool/BagsTest.java new file mode 100644 index 0000000000000000000000000000000000000000..dedf5edf806bbf04ba263fb71816964ac19ddd34 --- /dev/null +++ b/tlatools/test/tlc2/tool/BagsTest.java @@ -0,0 +1,50 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ + +package tlc2.tool; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.output.EC; +import tlc2.tool.liveness.ModelCheckerTestCase; + +public class BagsTest extends ModelCheckerTestCase { + + public BagsTest() { + super("BagsTest"); + } + + @Test + public void testSpec() { + // ModelChecker has finished and generated the expected amount of states + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertFalse(recorder.recorded(EC.GENERAL)); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "0", "0", "0")); + } +} diff --git a/tlatools/test/tlc2/tool/BugzillaBug279Test.java b/tlatools/test/tlc2/tool/BugzillaBug279Test.java new file mode 100644 index 0000000000000000000000000000000000000000..600d376390bebc53a9c620d61f390d81c8b4d0ab --- /dev/null +++ b/tlatools/test/tlc2/tool/BugzillaBug279Test.java @@ -0,0 +1,116 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ + +package tlc2.tool; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.Test; + +import tlc2.output.EC; +import tlc2.output.EC.ExitStatus; +import tlc2.tool.liveness.ModelCheckerTestCase; +import tlc2.value.Values; +import tlc2.value.impl.IntervalValue; +import tlc2.value.impl.SubsetValue; + +/** + * TLC bug caused by TLC's not preserving the semantics of CHOOSE + * + * Leslie Lamport 2012-03-08 00:26:08 UTC + * + * Running TLC on the spec written by Tom + * Rodeheffer that I will attach finds a deadlock (as it should) and then + * produces the following error when trying to generate the error trace: + * + * Failed to recover the initial state from its fingerprint. + * This is probably a TLC bug(2). + * + * The problem occurs because the value that TLC computes for a CHOOSE + * depends on TLC's internal representation of its argument. To compute + * the 2nd state of the trace, TLC sets the variables 'set' and 'fun' to S1 + * and Ch(S1), respectively, where Ch(S1) equals CHOOSE of an expression + * containing S1. Fingerprinting that state causes TLC to canonicalize + * the representation of the value of 'set', changing its representation of + * the value of S1. When TLC constructs the error trace, it must + * re-execute the spec to get the states in the trace. When it tries to + * compute the 2nd state of the trace, it uses the canonicalized + * representation of S1, causing it to compute a different value of + * variable 'set' than it did the first time, so it doesn't find a state + * with the correct fingerprint as the next state of the trace. (Note that + * the error message is misleading, since it's not the intiial state that + * TLC fails to recover.) + * + * There seem to be two possible fixes. The first is to canonicalize the + * argument of CHOOSE whenever it is evaluated. The second is to create a + * separate deep copy of the state before fingerprinting it. The first + * seems like the best solution, since I can't think of any practical case + * in which this would cause performance problems. However, I will consult + * with Yuan Yu before doing anything about this. + */ +public class BugzillaBug279Test extends ModelCheckerTestCase { + + public BugzillaBug279Test() { + super("InitStateBug", "Bug279", ExitStatus.VIOLATION_DEADLOCK); + } + + @Override + protected boolean checkDeadLock() { + return true; + } + + @Override + protected boolean doDump() { + // For this test, dumping means enumerating the SUBSETs which creates too much + // overhead. The test is also about avoiding enumerating SUBSET. + return false; + } + + @Test + public void testSpec() { + // ModelChecker has finished and generated the expected amount of states + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "3", "3", "0")); + assertFalse(recorder.recorded(EC.GENERAL)); + + // Assert the error trace + assertTrue(recorder.recorded(EC.TLC_DEADLOCK_REACHED)); + assertTrue(recorder.recorded(EC.TLC_STATE_PRINT2)); + final List<String> expectedTrace = new ArrayList<String>(6); + expectedTrace.add("/\\ set = {}\n/\\ pc = 0\n/\\ fun = {}"); + expectedTrace.add("/\\ set = SUBSET 1..20\n/\\ pc = 1\n/\\ fun = {5}"); + expectedTrace.add( + "/\\ set = " + Values.ppr(new SubsetValue(new IntervalValue(1, 8)).toSetEnum().normalize()) + + "\n/\\ pc = 2\n/\\ fun = {5}"); + assertTraceWith(recorder.getRecords(EC.TLC_STATE_PRINT2), expectedTrace); + + assertZeroUncovered(); + } +} diff --git a/tlatools/test/tlc2/tool/liveness/Test056.java b/tlatools/test/tlc2/tool/CodePlexBug21Test.java similarity index 76% rename from tlatools/test/tlc2/tool/liveness/Test056.java rename to tlatools/test/tlc2/tool/CodePlexBug21Test.java index f54c2737f82b4c44fae92447563a631a09e05726..6c10a5580fe9955bb2d1507abcffccdc71db74a5 100644 --- a/tlatools/test/tlc2/tool/liveness/Test056.java +++ b/tlatools/test/tlc2/tool/CodePlexBug21Test.java @@ -24,23 +24,30 @@ * Markus Alexander Kuppe - initial API and implementation ******************************************************************************/ -package tlc2.tool.liveness; +package tlc2.tool; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; import tlc2.output.EC; +import tlc2.tool.liveness.ModelCheckerTestCase; -public class Test056 extends ModelCheckerTestCase { +/** + * see http://tlaplus.codeplex.com/workitem/21 + */ +public class CodePlexBug21Test extends ModelCheckerTestCase { - public Test056() { - super("test56"); + public CodePlexBug21Test() { + super("CodePlexBug21"); } - + + @Test public void testSpec() { // ModelChecker has finished and generated the expected amount of states - assertTrue(recorder.recorded(EC.TLC_MODE_MC)); assertTrue(recorder.recorded(EC.TLC_FINISHED)); - assertTrue(recorder.recorded(EC.TLC_COMPUTING_INIT)); - assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "2")); - assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "3")); - assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "9", "6", "0")); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "0", "0", "0")); + assertFalse(recorder.recorded(EC.GENERAL)); } } diff --git a/tlatools/test/tlc2/tool/CommonTestCase.java b/tlatools/test/tlc2/tool/CommonTestCase.java new file mode 100644 index 0000000000000000000000000000000000000000..ef430379929639a5718c2f5c711f07094fd935d0 --- /dev/null +++ b/tlatools/test/tlc2/tool/CommonTestCase.java @@ -0,0 +1,238 @@ +/******************************************************************************* + * Copyright (c) 2016 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ + +package tlc2.tool; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import java.io.File; +import java.util.Arrays; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +import org.junit.runner.RunWith; + +import tlc2.TLCGlobals; +import tlc2.TestMPRecorder; +import tlc2.TestMPRecorder.Coverage; +import tlc2.output.EC; +import tlc2.output.MPRecorder; +import tlc2.tool.liveness.GraphNode; +import tlc2.util.BitVector; +import tlc2.util.BufferedRandomAccessFile; +import util.IsolatedTestCaseRunner; + +@RunWith(IsolatedTestCaseRunner.class) +public abstract class CommonTestCase { + + protected static final String BASE_DIR = System.getProperty("basedir", ""); + protected static final String TEST_MODEL = "test-model" + File.separator; + public static final String BASE_PATH = BASE_DIR + TEST_MODEL; + + protected final TestMPRecorder recorder; + + public CommonTestCase() { + this(new TestMPRecorder()); + } + + public CommonTestCase(final TestMPRecorder testMPRecorder) { + recorder = testMPRecorder; + } + + /** + * Asserts that the actual trace and the expected error trace are equal. + * + * @param actual + * The actual trace as recorded by {@link MPRecorder}. + * @param expectedTrace + * The expected trace. + */ + protected void assertTraceWith(final List<Object> actual, final List<String> expectedTrace) { + assertEquals(expectedTrace.size(), actual.size()); + for (int i = 0; i < expectedTrace.size(); i++) { + final Object[] objs = (Object[]) actual.get(i); + final TLCStateInfo stateInfo = (TLCStateInfo) objs[0]; + final String info = (String) stateInfo.info; + if (i == 0) { + // The first state has to be an initial state. + "<Initial predicate>".equals(info); + } else { + // ... all others are reachable via an action. + info.startsWith("<Action"); + } + assertEquals(expectedTrace.get(i), + stateInfo.toString().trim()); // trimmed to remove any newlines or whitespace + assertEquals(i+1, objs[1]); + } + } + + /** + * Asserts that the error trace ends in stuttering at the given number. + * + * @param stateNum + * The number of the stuttering state + */ + protected void assertStuttering(int stateNum) { + assertTrue(recorder.recorded(EC.TLC_STATE_PRINT3)); + List<Object> stutter = recorder.getRecords(EC.TLC_STATE_PRINT3); + assertTrue(stutter.size() > 0); + Object[] object = (Object[]) stutter.get(0); + assertEquals(stateNum, object[1]); + } + + /** + * Asserts that the error trace loops back to the state with the given + * number. + * + * @param i The loop back state number. + */ + protected void assertBackToState(int stateNum) { + assertTrue(recorder.recorded(EC.TLC_BACK_TO_STATE)); + List<Object> loop = recorder.getRecords(EC.TLC_BACK_TO_STATE); + assertTrue(loop.size() > 0); + Object[] object = (Object[]) loop.get(0); + assertEquals(Integer.toString(stateNum), object[0]); + } + + /** + * Asserts that the error trace loops back to the state with the given + * number. + * + * @param i The loop back state number. + * @param action The action label associated with the loop back marker + */ + protected void assertBackToState(int stateNum, final String action) { + assertTrue(recorder.recorded(EC.TLC_BACK_TO_STATE)); + List<Object> loop = recorder.getRecords(EC.TLC_BACK_TO_STATE); + assertTrue(loop.size() > 0); + Object[] object = (Object[]) loop.get(0); + assertTrue(object.length > 1); + assertEquals(Integer.toString(stateNum), object[0]); + assertEquals(action, object[1]); + } + + /** + * Check the file size of the AbstractDiskGraph files to assert that the + * expected amount of ptrs and nodes (outgoing arcs) have been written to + * disk. + * <p> + * CAUTION: The order in which the transitions are inserted into the + * {@link GraphNode} determines the size of the {@link BitVector}. I.e. if + * the truth values of the first N nodes inserted are true, and the + * remainder is false, the BitVector's size will correspond to N. However, + * if the first N truth values are false, followed by M trues, the + * BitVector's size is N + M. + * <p> + * See {@link GraphNode}'s constructor: it initializes {@link BitVector} + * with capacity zero and subsequently grows BV when bits are set to true. + * <p> + * + * @see BitVector#read(BufferedRandomAccessFile) + * @see BitVector#write(BufferedRandomAccessFile) + * @see GraphNode#read(BufferedRandomAccessFile) + * @see GraphNode#write(BufferedRandomAccessFile) + * + * @param nodesSize + * @param ptrsSize + */ + protected void assertNodeAndPtrSizes(final long nodesSize, final long ptrsSize) { + final String metadir = TLCGlobals.mainChecker.metadir; + assertNotNull(metadir); + + final File nodes = new File(metadir + File.separator + "nodes_0"); + assertTrue(nodes.exists()); + assertEquals(nodesSize, nodes.length()); + + final File ptrs = new File(metadir + File.separator + "ptrs_0"); + assertTrue(ptrs.exists()); + assertEquals(ptrsSize, ptrs.length()); + } + + // Checks if all uncovered (zero) lines are found and no more (don't care if the invocation and costs match). + protected void assertUncovered(final String expectedUncovered) { + final List<Coverage> expected = Arrays.asList(expectedUncovered.trim().split("\n")).stream() + .map(o -> new Coverage(o.split(":"))).collect(Collectors.toList()); + + final Set<Coverage> expectedZero = expected.stream().filter(Coverage::isZero).collect(Collectors.toSet()); + final Set<Coverage> actualZeroCoverage = recorder.getZeroCoverage().stream().collect(Collectors.toSet()); + assertEquals(expectedZero, actualZeroCoverage); + } + + protected void assertZeroUncovered() { + assertTrue(recorder.getZeroCoverage().isEmpty()); + } + + protected void assertCoverage(final String expectedCoverage) { + // Lines can be reported multiple times if invoked from different actions!!! + + final List<Coverage> expected = Arrays.asList(expectedCoverage.split("\n")).stream() + .map(o -> new Coverage(o.split(":"))).collect(Collectors.toList()); + + // Step A: + // Validation of coverage results is split into two steps. Step A checks if all + // uncovered (zero) lines are found, step B checks if non-zero lines exist. + final Set<Coverage> expectedZero = expected.stream().filter(Coverage::isZero) + .filter(Coverage::isCoverage).collect(Collectors.toSet()); + final Set<Coverage> actualZeroCoverage = recorder.getZeroCoverage().stream().collect(Collectors.toSet()); + assertEquals(expectedZero, actualZeroCoverage); + + // Step B1 (coverage): + final List<Coverage> actualNonZeroCoverage = recorder.getNonZeroCoverage(); + final List<Coverage> expectedNonZeroCoverage = expected.stream().filter(Coverage::isCoverage). + filter(c -> !c.isCost()).collect(Collectors.toList()); + expectedNonZeroCoverage.removeAll(actualZeroCoverage); + for (int i = 0; i < actualNonZeroCoverage.size(); i++) { + final Coverage a = actualNonZeroCoverage.get(i); + final Coverage e = expectedNonZeroCoverage.get(i); + assertEquals(e, a); + } + assertTrue(expectedNonZeroCoverage.size() == actualNonZeroCoverage.size()); + + // Step B2 (coverage with cost): + final List<Coverage> actualCostCoverage = recorder.getCostCoverage(); + final List<Coverage> expectedCostCoverage = expected.stream().filter(Coverage::isCoverage) + .filter(Coverage::isCost).collect(Collectors.toList()); + for (int i = 0; i < actualCostCoverage.size(); i++) { + final Coverage a = actualCostCoverage.get(i); + final Coverage e = expectedCostCoverage.get(i); + assertEquals(e, a); + } + assertTrue(expectedCostCoverage.size() == actualCostCoverage.size()); + + // Step C (actions): + final List<Coverage> actualActions = recorder.getActionCoverage(); + final List<Coverage> expectedActions = expected.stream().filter(Coverage::isAction).collect(Collectors.toList()); + for (int i = 0; i < actualActions.size(); i++) { + final Coverage a = actualActions.get(i); + final Coverage e = expectedActions.get(i); + assertEquals(e, a); + } + assertTrue(expectedActions.size() == actualActions.size()); + } +} diff --git a/tlatools/test/tlc2/tool/ContinueTest.java b/tlatools/test/tlc2/tool/ContinueTest.java new file mode 100644 index 0000000000000000000000000000000000000000..9129418a3313b93f77481eca055085f71418ac26 --- /dev/null +++ b/tlatools/test/tlc2/tool/ContinueTest.java @@ -0,0 +1,105 @@ +/******************************************************************************* + * Copyright (c) 2020 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import java.io.FileNotFoundException; +import java.io.IOException; + +import org.junit.Test; + +import tlc2.output.EC; +import tlc2.output.EC.ExitStatus; +import tlc2.tool.liveness.ModelCheckerTestCase; + +public class ContinueTest extends ModelCheckerTestCase { + + public ContinueTest() { + super("Continue", new String[] { "-continue" }, ExitStatus.SUCCESS); + } + + @Test + public void testSpec() throws FileNotFoundException, IOException { + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "32", "29", "0")); + assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "29")); + + assertTrue(recorder.recorded(EC.TLC_STATE_PRINT2)); + + // With -continue, TLC simply prints two or more consecutive traces in no given + // order (determined by concurrent BFS) to stdout. This means that the + // MPRecorder just concatenates the traces and it is difficult to check them. + // For now we check that the concatenated trace has the expected number of states. + assertEquals(32, recorder.getRecords(EC.TLC_STATE_PRINT2).size()); +// // Trace 1 +// final List<String> expectedTrace = new ArrayList<String>(2); +// expectedTrace.add("/\\ x = 1\n/\\ y = 1"); +// expectedTrace.add("/\\ x = 2\n/\\ y = 2"); +// assertTraceWith(recorder.getRecords(EC.TLC_STATE_PRINT2), expectedTrace); +// // Trace 2 +// final List<String> expectedTrace = new ArrayList<String>(30); +// expectedTrace.add("/\\ x = 1\n/\\ y = 1"); +// expectedTrace.add("/\\ x = 2\n/\\ y = 1"); +// expectedTrace.add("/\\ x = 3\n/\\ y = 1"); +// expectedTrace.add("/\\ x = 4\n/\\ y = 1"); +// expectedTrace.add("/\\ x = 5\n/\\ y = 1"); +// expectedTrace.add("/\\ x = 6\n/\\ y = 1"); +// expectedTrace.add("/\\ x = 7\n/\\ y = 1"); +// expectedTrace.add("/\\ x = 8\n/\\ y = 1"); +// expectedTrace.add("/\\ x = 9\n/\\ y = 1"); +// expectedTrace.add("/\\ x = 10\n/\\ y = 1"); +// expectedTrace.add("/\\ x = 11\n/\\ y = 1"); +// expectedTrace.add("/\\ x = 12\n/\\ y = 1"); +// expectedTrace.add("/\\ x = 13\n/\\ y = 1"); +// expectedTrace.add("/\\ x = 14\n/\\ y = 1"); +// expectedTrace.add("/\\ x = 15\n/\\ y = 1"); +// expectedTrace.add("/\\ x = 16\n/\\ y = 1"); +// expectedTrace.add("/\\ x = 17\n/\\ y = 1"); +// expectedTrace.add("/\\ x = 18\n/\\ y = 1"); +// expectedTrace.add("/\\ x = 19\n/\\ y = 1"); +// expectedTrace.add("/\\ x = 20\n/\\ y = 1"); +// expectedTrace.add("/\\ x = 21\n/\\ y = 1"); +// expectedTrace.add("/\\ x = 22\n/\\ y = 1"); +// expectedTrace.add("/\\ x = 23\n/\\ y = 1"); +// expectedTrace.add("/\\ x = 24\n/\\ y = 1"); +// expectedTrace.add("/\\ x = 25\n/\\ y = 1"); +// expectedTrace.add("/\\ x = 26\n/\\ y = 1"); +// expectedTrace.add("/\\ x = 27\n/\\ y = 1"); +// expectedTrace.add("/\\ x = 28\n/\\ y = 1"); +// expectedTrace.add("/\\ x = 29\n/\\ y = 1"); +// expectedTrace.add("/\\ x = 30\n/\\ y = 1"); +// assertTraceWith(recorder.getRecords(EC.TLC_STATE_PRINT2), expectedTrace); + + assertZeroUncovered(); + } + + @Override + protected int getNumberOfThreads() { + return 3; + } +} diff --git a/tlatools/test/tlc2/tool/DepthFirstDieHardTest.java b/tlatools/test/tlc2/tool/DepthFirstDieHardTest.java new file mode 100644 index 0000000000000000000000000000000000000000..75997ec73551443a41d04fe7e57bcf6787f936c9 --- /dev/null +++ b/tlatools/test/tlc2/tool/DepthFirstDieHardTest.java @@ -0,0 +1,68 @@ +/******************************************************************************* + * Copyright (c) 2015 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ + +package tlc2.tool; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.Test; + +import tlc2.output.EC; +import tlc2.output.EC.ExitStatus; +import tlc2.tool.liveness.ModelCheckerTestCase; + +public class DepthFirstDieHardTest extends ModelCheckerTestCase { + + public DepthFirstDieHardTest() { + super("DieHard", "", new String[] {"-dfid", "7"}, ExitStatus.VIOLATION_SAFETY); + } + + @Test + public void testSpec() { + // ModelChecker has finished and generated the expected amount of states + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertFalse(recorder.recorded(EC.GENERAL)); + + // Assert the error trace + assertTrue(recorder.recorded(EC.TLC_STATE_PRINT1)); + final List<String> expectedTrace = new ArrayList<String>(7); + expectedTrace.add("/\\ action = \"nondet\"\n/\\ smallBucket = 0\n/\\ bigBucket = 0\n/\\ water_to_pour = 0"); + expectedTrace.add("/\\ action = \"fill big\"\n/\\ smallBucket = 0\n/\\ bigBucket = 5\n/\\ water_to_pour = 0"); + expectedTrace.add("/\\ action = \"pour big to small\"\n/\\ smallBucket = 3\n/\\ bigBucket = 2\n/\\ water_to_pour = 3"); + expectedTrace.add("/\\ action = \"empty small\"\n/\\ smallBucket = 0\n/\\ bigBucket = 2\n/\\ water_to_pour = 3"); + expectedTrace.add("/\\ action = \"pour big to small\"\n/\\ smallBucket = 2\n/\\ bigBucket = 0\n/\\ water_to_pour = 2"); + + expectedTrace.add("/\\ action = \"fill big\"\n/\\ smallBucket = 2\n/\\ bigBucket = 5\n/\\ water_to_pour = 2"); + + expectedTrace.add("/\\ action = \"pour big to small\"\n/\\ smallBucket = 3\n/\\ bigBucket = 4\n/\\ water_to_pour = 1"); + assertTraceWith(recorder.getRecords(EC.TLC_STATE_PRINT1), expectedTrace); + assertZeroUncovered(); + } +} diff --git a/tlatools/test/tlc2/tool/DepthFirstErrorTraceTest.java b/tlatools/test/tlc2/tool/DepthFirstErrorTraceTest.java new file mode 100644 index 0000000000000000000000000000000000000000..c6249216383b02c3cc08eab681dd481ff9f50212 --- /dev/null +++ b/tlatools/test/tlc2/tool/DepthFirstErrorTraceTest.java @@ -0,0 +1,67 @@ +/******************************************************************************* + * Copyright (c) 2015 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ + +package tlc2.tool; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.Test; + +import tlc2.output.EC; +import tlc2.output.EC.ExitStatus; +import tlc2.tool.liveness.ModelCheckerTestCase; + +public class DepthFirstErrorTraceTest extends ModelCheckerTestCase { + + public DepthFirstErrorTraceTest() { + super("DepthFirstErrorTrace", "", new String[] {"-dfid", "9"}, ExitStatus.VIOLATION_SAFETY); + } + + @Test + public void testSpec() { + // ModelChecker has finished and generated the expected amount of states + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertFalse(recorder.recorded(EC.GENERAL)); + + // Assert the error trace + assertTrue(recorder.recorded(EC.TLC_STATE_PRINT1)); + final List<String> expectedTrace = new ArrayList<String>(5); + expectedTrace.add("x = 0"); + expectedTrace.add("x = 1"); + expectedTrace.add("x = 2"); + expectedTrace.add("x = 3"); + expectedTrace.add("x = 4"); + expectedTrace.add("x = 5"); + expectedTrace.add("x = 6"); + expectedTrace.add("x = 7"); + assertTraceWith(recorder.getRecords(EC.TLC_STATE_PRINT1), expectedTrace); + assertZeroUncovered(); + } +} diff --git a/tlatools/test/tlc2/tool/DepthFirstTerminate.java b/tlatools/test/tlc2/tool/DepthFirstTerminate.java new file mode 100644 index 0000000000000000000000000000000000000000..25f832a9970327e88e14b993779f263ea5a74cfd --- /dev/null +++ b/tlatools/test/tlc2/tool/DepthFirstTerminate.java @@ -0,0 +1,57 @@ +/******************************************************************************* + * Copyright (c) 2017 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ + +package tlc2.tool; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.output.EC; +import tlc2.tool.liveness.ModelCheckerTestCase; + +public class DepthFirstTerminate extends ModelCheckerTestCase { + + public DepthFirstTerminate() { + super("DepthFirstTerminate", "", new String[] { "-dfid", "50" }); + } + + @Test + public void testSpec() { + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertFalse(recorder.recorded(EC.GENERAL)); + } + + /* (non-Javadoc) + * @see tlc2.tool.liveness.ModelCheckerTestCase#getNumberOfThreads() + */ + @Override + protected int getNumberOfThreads() { + // Run this test with as many threads possible to hopefully spot concurrency issues. + return Runtime.getRuntime().availableProcessors(); + } +} diff --git a/tlatools/test/tlc2/tool/DiameterTest.java b/tlatools/test/tlc2/tool/DiameterTest.java new file mode 100644 index 0000000000000000000000000000000000000000..34d28c0f2d626a9971bad3e4351bbdb7dbbae00f --- /dev/null +++ b/tlatools/test/tlc2/tool/DiameterTest.java @@ -0,0 +1,66 @@ +/******************************************************************************* + * Copyright (c) 2017 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.output.EC; +import tlc2.tool.liveness.ModelCheckerTestCase; + +public class DiameterTest extends ModelCheckerTestCase { + + public DiameterTest() { + super("DieHardTLA"); + } + + @Test + public void testSpec() { + // ModelChecker has finished without errors and generated the expected + // amount of states + assertFalse(recorder.recorded(EC.GENERAL)); + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "97", "16", "0")); + + // The diameter is known to be 8 as reported by TLC running with a + // single worker. With multiple workers, it's possible to get a higher + // or a lower number. + final int level = recorder.getRecordAsInt(EC.TLC_SEARCH_DEPTH); + assertTrue(String.format("Level below threshold: %s", level), level >= 8); + + assertZeroUncovered(); + } + + /* (non-Javadoc) + * @see tlc2.tool.liveness.ModelCheckerTestCase#getNumberOfThreads() + */ + @Override + protected int getNumberOfThreads() { + return 4; + } +} diff --git a/tlatools/test/tlc2/tool/DistributedTrace.java b/tlatools/test/tlc2/tool/DistributedTrace.java new file mode 100644 index 0000000000000000000000000000000000000000..5fe2b89677972dc12fc8cfbb59134dfb76326864 --- /dev/null +++ b/tlatools/test/tlc2/tool/DistributedTrace.java @@ -0,0 +1,73 @@ +/******************************************************************************* + * Copyright (c) 2017 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.Test; + +import tlc2.output.EC; +import tlc2.tool.liveness.ModelCheckerTestCase; + +public class DistributedTrace extends ModelCheckerTestCase { + + public DistributedTrace() { + super("DistributedTrace"); + } + + @Test + public void testSpec() { + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertFalse(recorder.recorded(EC.GENERAL)); + + // Assert the error trace + assertTrue(recorder.recorded(EC.TLC_STATE_PRINT2)); + final List<String> expectedTrace = new ArrayList<String>(5); + expectedTrace.add("x = 10"); + expectedTrace.add("x = 11"); + expectedTrace.add("x = 12"); + expectedTrace.add("x = 13"); + expectedTrace.add("x = 14"); + expectedTrace.add("x = 15"); + expectedTrace.add("x = 16"); + expectedTrace.add("x = 17"); + expectedTrace.add("x = 18"); + expectedTrace.add("x = 19"); + expectedTrace.add("x = 20"); + assertTraceWith(recorder.getRecords(EC.TLC_STATE_PRINT2), expectedTrace); + } + + /* (non-Javadoc) + * @see tlc2.tool.liveness.ModelCheckerTestCase#getNumberOfThreads() + */ + protected int getNumberOfThreads() { + return 4; + } +} diff --git a/tlatools/test/tlc2/tool/DumpAsDotTest.dot b/tlatools/test/tlc2/tool/DumpAsDotTest.dot new file mode 100644 index 0000000000000000000000000000000000000000..09c7b56bab8a6a27c66ec21a90730e0a67b5c251 --- /dev/null +++ b/tlatools/test/tlc2/tool/DumpAsDotTest.dot @@ -0,0 +1,47 @@ +strict digraph DiskGraph { +edge [colorscheme="paired12"] +nodesep=0.35; +subgraph cluster_graph { +color="white"; +609737673425276830 [label="/\\ b = FALSE\n/\\ x = 0",style = filled] +6816998822487979083 [label="/\\ b = TRUE\n/\\ x = 0",style = filled] +3365478001808954030 [label="/\\ b = FALSE\n/\\ x = 1",style = filled] +8671809759910816123 [label="/\\ b = TRUE\n/\\ x = 1",style = filled] +5040481953810085374 [label="/\\ b = FALSE\n/\\ x = 2",style = filled] +1377963776297717291 [label="/\\ b = TRUE\n/\\ x = 2",style = filled] +7147721571019581646 [label="/\\ b = FALSE\n/\\ x = 3",style = filled] +3881310712274735899 [label="/\\ b = TRUE\n/\\ x = 3",style = filled] +609737673425276830 -> 8671809759910816123 [label="B",color="2",fontcolor="2"]; +609737673425276830 -> 609737673425276830 [style="dashed"]; +6816998822487979083 -> 609737673425276830 [label="A",color="3",fontcolor="3"]; +6816998822487979083 -> 6816998822487979083 [style="dashed"]; +3365478001808954030 -> 1377963776297717291 [label="B",color="2",fontcolor="2"]; +3365478001808954030 -> 3365478001808954030 [style="dashed"]; +8671809759910816123 -> 3365478001808954030 [label="A",color="3",fontcolor="3"]; +8671809759910816123 -> 8671809759910816123 [style="dashed"]; +5040481953810085374 -> 3881310712274735899 [label="B",color="2",fontcolor="2"]; +5040481953810085374 -> 5040481953810085374 [style="dashed"]; +1377963776297717291 -> 5040481953810085374 [label="A",color="3",fontcolor="3"]; +1377963776297717291 -> 1377963776297717291 [style="dashed"]; +7147721571019581646 -> -4210745456684007285 [label="B",color="2",fontcolor="2"]; +-4210745456684007285 [label="/\\ b = TRUE\n/\\ x = 4"]; +7147721571019581646 -> 7147721571019581646 [style="dashed"]; +3881310712274735899 -> 7147721571019581646 [label="A",color="3",fontcolor="3"]; +3881310712274735899 -> 3881310712274735899 [style="dashed"]; +-4210745456684007285 -> -7819220713745958050 [label="A",color="3",fontcolor="3"]; +-7819220713745958050 [label="/\\ b = FALSE\n/\\ x = 4"]; +-4210745456684007285 -> -4210745456684007285 [style="dashed"]; +-7819220713745958050 -> -2066378075513578053 [label="B",color="2",fontcolor="2"]; +-2066378075513578053 [label="/\\ b = TRUE\n/\\ x = 5"]; +-7819220713745958050 -> -7819220713745958050 [style="dashed"]; +-2066378075513578053 -> -2066378075513578053 [style="dashed"]; +{rank = same; 1377963776297717291;5040481953810085374;8671809759910816123;3365478001808954030;6816998822487979083;609737673425276830;3881310712274735899;7147721571019581646;} +{rank = same; -4210745456684007285;} +{rank = same; -7819220713745958050;} +{rank = same; -2066378075513578053;} +} +subgraph cluster_legend {graph[style=bold];label = "Next State Actions" style="solid" +node [ labeljust="l",colorscheme="paired12",style=filled,shape=record ] +A [label="A",fillcolor=3] +B [label="B",fillcolor=2] +}} \ No newline at end of file diff --git a/tlatools/test/tlc2/tool/DumpAsDotTest.java b/tlatools/test/tlc2/tool/DumpAsDotTest.java new file mode 100644 index 0000000000000000000000000000000000000000..51400037d040a5367066f979d874470a63b65e57 --- /dev/null +++ b/tlatools/test/tlc2/tool/DumpAsDotTest.java @@ -0,0 +1,83 @@ +/******************************************************************************* + * Copyright (c) 2015 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.Arrays; + +import org.junit.Test; + +import tlc2.output.EC; +import tlc2.output.EC.ExitStatus; +import tlc2.tool.liveness.ModelCheckerTestCase; + +public class DumpAsDotTest extends ModelCheckerTestCase { + + public DumpAsDotTest() { + super("MCa", "CodePlexBug08", new String[] { "-dump", "dot,colorize,actionlabels", + System.getProperty("java.io.tmpdir") + File.separator + "DumpAsDotTest" }, ExitStatus.VIOLATION_LIVENESS); + } + + @Test + public void testSpec() throws IOException { + // ModelChecker has finished and generated the expected amount of states + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertFalse(recorder.recorded(EC.GENERAL)); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "18", "11", "0")); + + // -dump appends the ".dump" extension to the file name + final File dumpFile = new File(System.getProperty("java.io.tmpdir") + File.separator + "DumpAsDotTest.dot"); + assertTrue(dumpFile.exists()); + + // If the file exist, simply compare it to a correct and manually checked version. + final InputStream master = getClass().getResourceAsStream("DumpAsDotTest.dot"); + assertTrue(Arrays.equals(getBytes(master), getBytes(new FileInputStream(dumpFile)))); + + assertZeroUncovered(); + } + + // http://stackoverflow.com/a/17861016 + public static byte[] getBytes(InputStream is) throws IOException { + final ByteArrayOutputStream os = new ByteArrayOutputStream(); + try { + byte[] buffer = new byte[0xFFFF]; + for (int len; (len = is.read(buffer)) != -1;) { + os.write(buffer, 0, len); + } + os.flush(); + return os.toByteArray(); + } finally { + os.close(); + } + } +} diff --git a/tlatools/test/tlc2/tool/EmptySubsetEqTest.java b/tlatools/test/tlc2/tool/EmptySubsetEqTest.java new file mode 100644 index 0000000000000000000000000000000000000000..17faf52b0cf9ad97022804ff229d9a736ba2607f --- /dev/null +++ b/tlatools/test/tlc2/tool/EmptySubsetEqTest.java @@ -0,0 +1,68 @@ +/******************************************************************************* + * Copyright (c) 2017 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool; + +import static org.junit.Assert.assertTrue; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.Test; + +import tlc2.output.EC; +import tlc2.output.EC.ExitStatus; +import tlc2.tool.liveness.ModelCheckerTestCase; + +public class EmptySubsetEqTest extends ModelCheckerTestCase { + + // This the supplements SubsetEqTest. It checks that TLC does not + // incorrectly reduce the expression SUBSET (1..3) \subseteq (1..4). The + // empty subset {} is not a subset of (1..4). + public EmptySubsetEqTest() { + super("EmptySubsetEq", ExitStatus.FAILURE_SPEC_EVAL); + } + + @Test + public void testSpec() { + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "1", "1", "0")); + + assertTrue(recorder.recordedWithStringValue(EC.GENERAL, + "TLC threw an unexpected exception.\nThis was probably caused by an " + + "error in the spec or model.\nSee the User Output or TLC Console " + + "for clues to what happened.\nThe exception was a " + + "java.lang.RuntimeException\n: Attempted to check if the value:\n" + + "{}\nis in the integer interval 1..4")); + + // Expect an error trace consisting of a single state. + assertTrue(recorder.recorded(EC.TLC_STATE_PRINT2)); + final List<String> expectedTrace = new ArrayList<String>(4); + expectedTrace.add("b = TRUE"); + assertTraceWith(recorder.getRecords(EC.TLC_STATE_PRINT2), expectedTrace); + + assertUncovered("line 8, col 9 to line 8, col 48 of module EmptySubsetEq: 0\n"); + } +} diff --git a/tlatools/test/tlc2/tool/liveness/Test063.java b/tlatools/test/tlc2/tool/EmptyTest.java similarity index 78% rename from tlatools/test/tlc2/tool/liveness/Test063.java rename to tlatools/test/tlc2/tool/EmptyTest.java index f08e0ef08c4358511c84d14e79274f6d1db76c80..2e5f9b0d2e11b9f6016a3d4d952f2a2884ca422e 100644 --- a/tlatools/test/tlc2/tool/liveness/Test063.java +++ b/tlatools/test/tlc2/tool/EmptyTest.java @@ -24,21 +24,27 @@ * Markus Alexander Kuppe - initial API and implementation ******************************************************************************/ -package tlc2.tool.liveness; +package tlc2.tool; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; import tlc2.output.EC; +import tlc2.tool.liveness.ModelCheckerTestCase; -public class Test063 extends ModelCheckerTestCase { +public class EmptyTest extends ModelCheckerTestCase { - public Test063() { - super("test63"); + public EmptyTest() { + super("Empty", "", new String[] {"-coverage", "1"}); } + @Test public void testSpec() { // ModelChecker has finished and generated the expected amount of states assertTrue(recorder.recorded(EC.TLC_FINISHED)); - assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "2")); - assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "72")); - assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "696", "216", "0")); + assertFalse(recorder.recorded(EC.GENERAL)); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "0", "0", "0")); } } diff --git a/tlatools/test/tlc2/tool/EvalControlTest.java b/tlatools/test/tlc2/tool/EvalControlTest.java new file mode 100644 index 0000000000000000000000000000000000000000..6eac3c251b203ee7c69a04d9d9c97ca1af8d7e49 --- /dev/null +++ b/tlatools/test/tlc2/tool/EvalControlTest.java @@ -0,0 +1,44 @@ +package tlc2.tool; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +public class EvalControlTest { + + @Test + public void test() { + int control = EvalControl.Clear; + + assertFalse(EvalControl.isEnabled(control)); + assertFalse(EvalControl.isKeepLazy(control)); + assertFalse(EvalControl.isPrimed(control)); + + control = EvalControl.setEnabled(control); + assertTrue(EvalControl.isEnabled(control)); + assertFalse(EvalControl.isKeepLazy(control)); + assertFalse(EvalControl.isPrimed(control)); + + control = EvalControl.setKeepLazy(control); + assertTrue(EvalControl.isEnabled(control)); + assertTrue(EvalControl.isKeepLazy(control)); + assertFalse(EvalControl.isPrimed(control)); + + control = EvalControl.setPrimed(control); + assertTrue(EvalControl.isEnabled(control)); + assertTrue(EvalControl.isKeepLazy(control)); + assertTrue(EvalControl.isPrimed(control)); + } + + @Test + public void testIfEnabled() { + int control = EvalControl.Clear; + + assertFalse(EvalControl.isPrimed(EvalControl.setPrimedIfEnabled(control))); + + control = EvalControl.setEnabled(control); + assertTrue(EvalControl.isEnabled(control)); + assertTrue(EvalControl.isPrimed(EvalControl.setPrimedIfEnabled(control))); + } +} diff --git a/tlatools/test/tlc2/tool/EvaluatingValueTest.java b/tlatools/test/tlc2/tool/EvaluatingValueTest.java new file mode 100644 index 0000000000000000000000000000000000000000..0fb42252d63e2ba8ab40582ed2bcae44d0e5bf17 --- /dev/null +++ b/tlatools/test/tlc2/tool/EvaluatingValueTest.java @@ -0,0 +1,75 @@ +/******************************************************************************* + * Copyright (c) 2020 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool; + +import static org.junit.Assert.*; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.BlockJUnit4ClassRunner; + +import tla2sany.semantic.ExprOrOpArgNode; +import tlc2.output.EC; +import tlc2.overrides.Evaluation; +import tlc2.tool.coverage.CostModel; +import tlc2.tool.impl.Tool; +import tlc2.tool.liveness.ModelCheckerTestCase; +import tlc2.util.Context; +import tlc2.value.impl.BoolValue; +import tlc2.value.impl.IntValue; +import tlc2.value.impl.Value; +import util.UniqueString; + +@RunWith(BlockJUnit4ClassRunner.class) +public class EvaluatingValueTest extends ModelCheckerTestCase { + + public EvaluatingValueTest() { + super("EvaluatingValueTest"); + } + + @Test + public void testSpec() { + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + // Two states: x = 1 and x = 42 (see action method below). + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "3", "2", "0")); + assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "2")); + + assertFalse(recorder.recorded(EC.GENERAL)); + + assertFalse(recorder.recorded(EC.TLC_TEMPORAL_PROPERTY_VIOLATED)); + assertFalse(recorder.recorded(EC.TLC_COUNTER_EXAMPLE)); + } + + @Evaluation(definition = "A", module = "EvaluatingValueTest") + public synchronized static Value action(final Tool tool, final ExprOrOpArgNode[] args, final Context c, + final TLCState s0, final TLCState s1, final int control, final CostModel cm) { + + // Set value of x variable of successor state to 42. + s1.bind(UniqueString.of("x"), IntValue.gen(42)); + + return BoolValue.ValTrue; + } +} diff --git a/tlatools/test/tlc2/tool/FingerprintExceptionInitTest.java b/tlatools/test/tlc2/tool/FingerprintExceptionInitTest.java new file mode 100644 index 0000000000000000000000000000000000000000..ee6e6474e1580c3de9e0e54a7819757a995ee2be --- /dev/null +++ b/tlatools/test/tlc2/tool/FingerprintExceptionInitTest.java @@ -0,0 +1,58 @@ +/******************************************************************************* + * Copyright (c) 2017 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Ian Morris Nieves - initial design and implementation + ******************************************************************************/ + +package tlc2.tool; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.output.EC; +import tlc2.output.EC.ExitStatus; +import tlc2.tool.liveness.ModelCheckerTestCase; + +public class FingerprintExceptionInitTest extends ModelCheckerTestCase { + + public FingerprintExceptionInitTest() { + super("FingerprintExceptionInit", ExitStatus.FAILURE_SPEC_EVAL); + } + + @Test + public void testSpec() { + // ModelChecker has finished with a fingerprint exception and underlying overflow exception + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertFalse(recorder.recorded(EC.GENERAL)); + String arg1 = "1) line 7, col 20 to line 7, col 32 of module FingerprintExceptionInit\n" + + "0) line 7, col 13 to line 7, col 33 of module FingerprintExceptionInit\n"; + String arg2 = "Overflow when computing the number of elements in:\n" + + "SUBSET 1..36"; + assertTrue(recorder.recordedWithStringValues(EC.TLC_FINGERPRINT_EXCEPTION, arg1, arg2)); + + assertUncovered("line 8, col 39 to line 8, col 64 of module FingerprintExceptionInit: 0\n" + + "line 8, col 71 to line 8, col 76 of module FingerprintExceptionInit: 0\n"); + } +} diff --git a/tlatools/test/tlc2/tool/FingerprintExceptionNextTest.java b/tlatools/test/tlc2/tool/FingerprintExceptionNextTest.java new file mode 100644 index 0000000000000000000000000000000000000000..e78f039cb40b5e6a25ab05789a3fc8b67b3c4098 --- /dev/null +++ b/tlatools/test/tlc2/tool/FingerprintExceptionNextTest.java @@ -0,0 +1,58 @@ +/******************************************************************************* + * Copyright (c) 2017 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Ian Morris Nieves - initial design and implementation + ******************************************************************************/ + +package tlc2.tool; + +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.output.EC; +import tlc2.output.EC.ExitStatus; +import tlc2.tool.liveness.ModelCheckerTestCase; + +public class FingerprintExceptionNextTest extends ModelCheckerTestCase { + + public FingerprintExceptionNextTest() { + super("FingerprintExceptionNext", ExitStatus.FAILURE_SPEC_EVAL); + } + + @Test + public void testSpec() { + // ModelChecker has finished with a general exception, a fingerprint exception and underlying overflow exception + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "2", "1", "0")); + assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "1")); + assertTrue(recorder.recorded(EC.GENERAL)); + String arg1 = "1) line 8, col 51 to line 8, col 63 of module FingerprintExceptionNext\n" + + "0) line 8, col 44 to line 8, col 64 of module FingerprintExceptionNext\n"; + String arg2 = "Overflow when computing the number of elements in:\n" + + "SUBSET 1..32"; + assertTrue(recorder.recordedWithStringValues(EC.TLC_FINGERPRINT_EXCEPTION, arg1, arg2)); + + assertUncovered("line 8, col 71 to line 8, col 76 of module FingerprintExceptionNext: 0\n"); + } +} diff --git a/tlatools/test/tlc2/tool/Github179aTest.java b/tlatools/test/tlc2/tool/Github179aTest.java new file mode 100644 index 0000000000000000000000000000000000000000..659032fa67c77990324af1ff6efcb7f94a7be84a --- /dev/null +++ b/tlatools/test/tlc2/tool/Github179aTest.java @@ -0,0 +1,51 @@ +/******************************************************************************* + * Copyright (c) 2019 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool; + +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.output.EC; +import tlc2.output.EC.ExitStatus; +import tlc2.tool.liveness.ModelCheckerTestCase; + +public class Github179aTest extends ModelCheckerTestCase { + + public Github179aTest() { + super("Github179a", ExitStatus.VIOLATION_ASSUMPTION); + } + + @Test + public void testSpec() { + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + + assertTrue(recorder.recordedWithStringValues(EC.TLC_MODULE_VALUE_JAVA_METHOD_OVERRIDE, + "public static tlc2.value.impl.Value tlc2.module.TLC.PrintT(tlc2.value.impl.Value)", + "Attempted to check equality of integer 1 with non-integer:\n" + + "{1}")); + } +} diff --git a/tlatools/test/tlc2/tool/Github179bTest.java b/tlatools/test/tlc2/tool/Github179bTest.java new file mode 100644 index 0000000000000000000000000000000000000000..48cff9f0cf00ba0f6ce57d3746efd90770c943a9 --- /dev/null +++ b/tlatools/test/tlc2/tool/Github179bTest.java @@ -0,0 +1,58 @@ +/******************************************************************************* + * Copyright (c) 2019 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool; + +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.output.EC; +import tlc2.output.EC.ExitStatus; +import tlc2.tool.liveness.ModelCheckerTestCase; + +public class Github179bTest extends ModelCheckerTestCase { + + public Github179bTest() { + super("Github179b", ExitStatus.FAILURE_SPEC_EVAL); + } + + @Test + public void testSpec() { + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + + assertTrue(recorder.recordedWithStringValues(EC.TLC_MODULE_VALUE_JAVA_METHOD_OVERRIDE, + "public static tlc2.value.impl.Value tlc2.module.TLC.Print(tlc2.value.impl.Value,tlc2.value.impl.Value)", + "Attempted to check equality of integer 1 with non-integer:\n" + + "{1}")); + assertTrue(recorder.recordedWithStringValues(EC.TLC_NESTED_EXPRESSION, + "0. Line 13, column 1 to line 13, column 30 in Github179b\n" + + "1. Line 13, column 1 to line 13, column 23 in Github179b\n" + + "2. Line 9, column 27 to line 9, column 79 in Github179b\n" + + "3. Line 9, column 40 to line 9, column 79 in Github179b\n" + + "4. Line 9, column 43 to line 9, column 52 in Github179b\n" + + "\n")); + } +} diff --git a/tlatools/test/tlc2/tool/Github179cTest.java b/tlatools/test/tlc2/tool/Github179cTest.java new file mode 100644 index 0000000000000000000000000000000000000000..12425d933c63950d600378b58fd7cd61835acae7 --- /dev/null +++ b/tlatools/test/tlc2/tool/Github179cTest.java @@ -0,0 +1,59 @@ +/******************************************************************************* + * Copyright (c) 2019 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool; + +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.output.EC; +import tlc2.output.EC.ExitStatus; +import tlc2.tool.liveness.ModelCheckerTestCase; + +public class Github179cTest extends ModelCheckerTestCase { + + public Github179cTest() { + super("Github179c", ExitStatus.FAILURE_SPEC_EVAL); + } + + @Test + public void testSpec() { + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + + assertTrue(recorder.recordedWithStringValues(EC.TLC_MODULE_VALUE_JAVA_METHOD_OVERRIDE, + "public static tlc2.value.impl.Value tlc2.module.TLC.PrintT(tlc2.value.impl.Value)", + "Attempted to check equality of integer 1 with non-integer:\n" + + "{1}")); + assertTrue(recorder.recordedWithStringValues(EC.TLC_NESTED_EXPRESSION, + "0. Line 16, column 9 to line 16, column 42 in Github179c\n" + + "1. Line 16, column 9 to line 16, column 33 in Github179c\n" + + "2. Line 16, column 9 to line 16, column 25 in Github179c\n" + + "3. Line 10, column 11 to line 12, column 39 in Github179c\n" + + "4. Line 10, column 24 to line 12, column 39 in Github179c\n" + + "5. Line 10, column 27 to line 10, column 36 in Github179c\n" + + "\n")); + } +} diff --git a/tlatools/test/tlc2/tool/Github361Test.java b/tlatools/test/tlc2/tool/Github361Test.java new file mode 100644 index 0000000000000000000000000000000000000000..0124a26dd6b0630dc28d6de35223b51820ea0071 --- /dev/null +++ b/tlatools/test/tlc2/tool/Github361Test.java @@ -0,0 +1,59 @@ +/******************************************************************************* + * Copyright (c) 2019 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool; + +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.output.EC; +import tlc2.output.EC.ExitStatus; +import tlc2.tool.liveness.ModelCheckerTestCase; + +public class Github361Test extends ModelCheckerTestCase { + + public Github361Test() { + super("Github361", ExitStatus.SUCCESS); + } + + @Test + public void testSpec() { + // This implicitly tests SpecProcessor#processConstantDefns(ModuleNode), which + // must not fully initialize (fingerprint) values because it is for some + // definitions (such as Partitions in Github361.tla) too expensive. This is + // the reason why it runs with multiple threads to make sure optimizations for + // single-threaded TLC hide bugs. + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "2", "1", "0")); + assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "1")); + } + + @Override + protected int getNumberOfThreads() { + // See comment above. + return 2; + } +} diff --git a/tlatools/test/tlc2/tool/Github362Test.java b/tlatools/test/tlc2/tool/Github362Test.java new file mode 100644 index 0000000000000000000000000000000000000000..2e9d564ff70c41c98ee531f86305e8c8986d5c16 --- /dev/null +++ b/tlatools/test/tlc2/tool/Github362Test.java @@ -0,0 +1,66 @@ +/******************************************************************************* + * Copyright (c) 2019 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool; + +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.output.EC; +import tlc2.output.EC.ExitStatus; +import tlc2.tool.liveness.ModelCheckerTestCase; +import util.TestPrintStream; +import util.ToolIO; + +public class Github362Test extends ModelCheckerTestCase { + + private TestPrintStream testPrintStream; + + public Github362Test() { + super("Github362", ExitStatus.SUCCESS); + } + + @Override + public void beforeSetUp() { + testPrintStream = new TestPrintStream(); + ToolIO.out = testPrintStream; + } + + @Test + public void testSpec() { + testPrintStream.assertSubstring("<<\"Evaluated initial state in A; overloadedName is: \", \"fizzbuzz\">>"); + testPrintStream.assertSubstring("<<\"From A's perspective, B's overloadedName is: \", \"x\">>"); + testPrintStream.assertSubstring("<<\"Evaluating initial state in B; overloadedName is \", \"x\">>"); + + testPrintStream.assertSubstring("<<\"Evaluated initial state in A; overloadedConst is: \", 4711>>"); + testPrintStream.assertSubstring("<<\"From A's perspective, B's overloadedConst is: \", 42>>"); + testPrintStream.assertSubstring("<<\"Evaluating initial state in B; overloadedConst is \", 42>>"); + + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertTrue(recorder.recorded(EC.TLC_SUCCESS)); + } + +} diff --git a/tlatools/test/tlc2/tool/Github391Test.java b/tlatools/test/tlc2/tool/Github391Test.java new file mode 100644 index 0000000000000000000000000000000000000000..d699b05247c25739931d5dbae64ee6ff01d08811 --- /dev/null +++ b/tlatools/test/tlc2/tool/Github391Test.java @@ -0,0 +1,48 @@ +/******************************************************************************* + * Copyright (c) 2019 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool; + +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.output.EC; +import tlc2.output.EC.ExitStatus; +import tlc2.tool.liveness.ModelCheckerTestCase; + +public class Github391Test extends ModelCheckerTestCase { + + public Github391Test() { + super("Github391", ExitStatus.SUCCESS); + } + + @Test + public void testSpec() { + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "0", "0", "0")); + assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "1")); + } +} diff --git a/tlatools/test/tlc2/tool/Github407.dump b/tlatools/test/tlc2/tool/Github407.dump new file mode 100644 index 0000000000000000000000000000000000000000..c1dd5569b6d74802ca058a19b2fabae520f94548 --- /dev/null +++ b/tlatools/test/tlc2/tool/Github407.dump @@ -0,0 +1,12 @@ +State 1: +state = {} + +State 2: +state = {"d1"} + +State 3: +state = {"d2"} + +State 4: +state = {"d1", "d2"} + diff --git a/tlatools/test/tlc2/tool/Github407Test.java b/tlatools/test/tlc2/tool/Github407Test.java new file mode 100644 index 0000000000000000000000000000000000000000..65a842dd80f9fc82ba84733fa6db427d7de07835 --- /dev/null +++ b/tlatools/test/tlc2/tool/Github407Test.java @@ -0,0 +1,73 @@ +/******************************************************************************* + * Copyright (c) 2019 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool; + +import static org.junit.Assert.assertTrue; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.util.Arrays; + +import org.junit.Test; + +import tlc2.output.EC; +import tlc2.output.EC.ExitStatus; +import tlc2.tool.liveness.ModelCheckerTestCase; + +public class Github407Test extends ModelCheckerTestCase { + + public Github407Test() { + super("Github407", + new String[] { "-dump", System.getProperty("java.io.tmpdir") + File.separator + "Github407" }, + ExitStatus.SUCCESS); + } + + @Test + public void testSpec() throws FileNotFoundException, IOException { + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "9", "4", "0")); + assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "3")); + + final File dumpFile = new File(System.getProperty("java.io.tmpdir") + File.separator + "Github407.dump"); + assertTrue(dumpFile.exists()); + + // If the file exist, simply compare it to a correct and manually checked version. + final InputStream master = getClass().getResourceAsStream("Github407.dump"); + assertTrue(Arrays.equals(DumpAsDotTest.getBytes(master), DumpAsDotTest.getBytes(new FileInputStream(dumpFile)))); + + assertZeroUncovered(); + + } + + @Override + protected boolean doDump() { + // Create the non-dot dump explicitly through constructor above. + return false; + } +} diff --git a/tlatools/test/tlc2/tool/Github432Test.java b/tlatools/test/tlc2/tool/Github432Test.java new file mode 100644 index 0000000000000000000000000000000000000000..fc871cff6da493c86e21a2d258af29ca7a1dd327 --- /dev/null +++ b/tlatools/test/tlc2/tool/Github432Test.java @@ -0,0 +1,222 @@ +/******************************************************************************* + * Copyright (c) 2019 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.StandardCopyOption; +import java.util.concurrent.atomic.AtomicInteger; + +import org.junit.Assert; +import org.junit.Test; + +import tlc2.output.EC; +import tlc2.tool.liveness.ModelCheckerTestCase; + +public class Github432Test extends ModelCheckerTestCase { + private static final AtomicInteger TEST_COUNTER = new AtomicInteger(0); + + private static final String CONFIG_FILE = "Github432.cfg"; + private static final String CONFIG_FILE_BACKUP = "Github432.cfg_bak"; + + private static final String HUMANS_TOKEN = "%1%"; + private static final String OTHERS_TOKEN = "%2%"; + + + private String[] expectedWarnings; + + public Github432Test() { + super("Github432"); + } + + @Override + protected void beforeSetUp() { + final int testNumber = TEST_COUNTER.getAndIncrement(); + final String humans; + final String others; + + switch (testNumber) { + case 0: + humans = "Alice"; + others = "Cat, Dog"; + expectedWarnings = new String[] {"", "Humans", "has", "s"}; + break; + case 1: + humans = ""; + others = "Emu"; + expectedWarnings = new String[] {"s", "Humans, and Others", "have", ""}; + break; + case 2: + humans = "Frank, Glenda"; + others = ""; + expectedWarnings = new String[] {"", "Others", "has", "s"}; + break; + default: + humans = "Hauser, Ignatio"; + others = "Jackal, Kangaroo"; + expectedWarnings = null; + break; + } + + try { + createConfigFile(humans, others); + } catch (final Exception e) { + revertConfigFile(); + + Assert.fail(e.getMessage()); + } + } + + @Override + protected void beforeTearDown() { + revertConfigFile(); + } + + @Override + protected void assertExitStatus() { + // We don't care - and the spec actually fails for test-b + } + + private void compareToExpectedResults(final String[] array) { + Assert.assertNotNull("Reported parameters should not be null.", array); + Assert.assertEquals("Expected warnings should be the same cardinality of the reported parameters encountered.", + expectedWarnings.length, array.length); + for (int i = 0; i < array.length; i++) { + Assert.assertEquals(expectedWarnings[i], array[i]); + } + } + + @Test + public void testA() throws FileNotFoundException, IOException { + if (expectedWarnings != null) { + compareToExpectedResults((String[])recorder.getRecords(EC.TLC_SYMMETRY_SET_TOO_SMALL).get(0)); + } else { + Assert.assertFalse(recorder.recorded(EC.TLC_SYMMETRY_SET_TOO_SMALL)); + } + } + + @Test + public void testB() throws FileNotFoundException, IOException { + if (expectedWarnings != null) { + compareToExpectedResults((String[])recorder.getRecords(EC.TLC_SYMMETRY_SET_TOO_SMALL).get(0)); + } else { + Assert.assertFalse(recorder.recorded(EC.TLC_SYMMETRY_SET_TOO_SMALL)); + } + } + + @Test + public void testC() throws FileNotFoundException, IOException { + if (expectedWarnings != null) { + compareToExpectedResults((String[])recorder.getRecords(EC.TLC_SYMMETRY_SET_TOO_SMALL).get(0)); + } else { + Assert.assertFalse(recorder.recorded(EC.TLC_SYMMETRY_SET_TOO_SMALL)); + } + } + + @Test + public void testD() throws FileNotFoundException, IOException { + if (expectedWarnings != null) { + compareToExpectedResults((String[])recorder.getRecords(EC.TLC_SYMMETRY_SET_TOO_SMALL).get(0)); + } else { + Assert.assertFalse(recorder.recorded(EC.TLC_SYMMETRY_SET_TOO_SMALL)); + } + } + + private void createConfigFile(final String humans, final String others) throws IOException { + final File configFile = new File(BASE_DIR + TEST_MODEL + CONFIG_FILE); + final File backup = new File(BASE_DIR + TEST_MODEL + CONFIG_FILE_BACKUP); + + if (backup.exists()) { + Assert.fail("Github432 test state is incoherent: the backup file already exists at " + + backup.getAbsolutePath()); + } + + try { + Files.move(configFile.toPath(), backup.toPath(), StandardCopyOption.ATOMIC_MOVE); + } catch (final IOException e) { + Assert.fail(e.getMessage()); + } + + try { + try (final BufferedWriter bw = new BufferedWriter(new FileWriter(configFile))) { + try (final BufferedReader br = new BufferedReader(new FileReader(backup))) { + boolean humansFound = false; + boolean othersFound = false; + String line; + while ((line = br.readLine()) != null) { + int index = humansFound ? -1 : line.indexOf(HUMANS_TOKEN); + if (index == -1) { + index = othersFound ? -1 : line.indexOf(OTHERS_TOKEN); + + if (index == -1) { + bw.write(line); + } else { + final int secondStart = index + OTHERS_TOKEN.length(); + final String convertedLine = line.substring(0, index) + others + + line.substring(secondStart); + bw.write(convertedLine); + othersFound = true; + } + } else { + final int secondStart = index + HUMANS_TOKEN.length(); + final String convertedLine = line.substring(0, index) + humans + + line.substring(secondStart); + bw.write(convertedLine); + humansFound = true; + } + + bw.newLine(); + } + } + } + } catch (final IOException e) { + + revertConfigFile(); + + Assert.fail(e.getMessage()); + } + } + + private void revertConfigFile() { + final File backup = new File(BASE_DIR + TEST_MODEL + CONFIG_FILE_BACKUP); + + if (backup.exists()) { + final File configFile = new File(BASE_DIR + TEST_MODEL + CONFIG_FILE); + + try { + Files.move(backup.toPath(), configFile.toPath(), StandardCopyOption.REPLACE_EXISTING); + } catch (final IOException e) { + Assert.fail(e.getMessage()); + } + } + } +} diff --git a/tlatools/test/tlc2/tool/Github461Test.java b/tlatools/test/tlc2/tool/Github461Test.java new file mode 100644 index 0000000000000000000000000000000000000000..1e05070fb0c52ddc02cc9418d49339f7baffd695 --- /dev/null +++ b/tlatools/test/tlc2/tool/Github461Test.java @@ -0,0 +1,72 @@ +/******************************************************************************* + * Copyright (c) 2020 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool; + +import static org.junit.Assert.assertTrue; + +import java.io.FileNotFoundException; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import org.junit.Test; + +import tlc2.output.EC; +import tlc2.tool.liveness.ModelCheckerTestCase; + +public class Github461Test extends ModelCheckerTestCase { + + public Github461Test() { + super("Github461", EC.ExitStatus.VIOLATION_ASSERT); + } + + @Test + public void testSpec() throws FileNotFoundException, IOException { + // Assert evaluation error. + assertTrue(recorder.recordedWithStringValue(EC.TLC_VALUE_ASSERT_FAILED, + "\"Failure of assertion at line 8, column 4.\"")); + + // Assert an error trace. + assertTrue(recorder.recorded(EC.TLC_STATE_PRINT2)); + + // Assert the correct trace. + final List<String> expectedTrace = new ArrayList<String>(4); + expectedTrace.add("x = 0"); + expectedTrace.add("x = 1"); + expectedTrace.add("x = 2"); + expectedTrace.add("x = 3"); + expectedTrace.add("x = 4"); + assertTraceWith(recorder.getRecords(EC.TLC_STATE_PRINT2), expectedTrace); + + // Assert the underlying error message with stack trace. + assertTrue(recorder.recordedWithStringValues(EC.TLC_NESTED_EXPRESSION, + "0. Line 9, column 5 to line 10, column 17 in Github461\n" + + "1. Line 9, column 8 to line 9, column 65 in Github461\n" + + "\n")); + + assertZeroUncovered(); + } +} diff --git a/tlatools/test/tlc2/tool/Github798ITest.java b/tlatools/test/tlc2/tool/Github798ITest.java new file mode 100644 index 0000000000000000000000000000000000000000..6dad1288fd45581996b2ffb0d18cdf2500080d7d --- /dev/null +++ b/tlatools/test/tlc2/tool/Github798ITest.java @@ -0,0 +1,60 @@ +/******************************************************************************* + * Copyright (c) 2023 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.io.IOException; + +import org.junit.Test; + +import tlc2.output.EC; +import tlc2.tool.liveness.ModelCheckerTestCase; + +public class Github798ITest extends ModelCheckerTestCase { + + public Github798ITest() { + super("Github798I", new String[] { "-config", "Github798I.tla" }, EC.ExitStatus.SUCCESS); + } + + protected boolean noGenerateSpec() { + return true; + } + + protected boolean doDumpTrace() { + return false; + } + + @Test + public void testSpec() throws IOException { + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertFalse(recorder.recorded(EC.GENERAL)); + + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "2", "1", "0")); + assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "1")); + } +} diff --git a/tlatools/test/tlc2/tool/Github798NTest.java b/tlatools/test/tlc2/tool/Github798NTest.java new file mode 100644 index 0000000000000000000000000000000000000000..2cb00946cc9329cd4975877b47ecd6bc3ecbd94b --- /dev/null +++ b/tlatools/test/tlc2/tool/Github798NTest.java @@ -0,0 +1,60 @@ +/******************************************************************************* + * Copyright (c) 2023 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.io.IOException; + +import org.junit.Test; + +import tlc2.output.EC; +import tlc2.tool.liveness.ModelCheckerTestCase; + +public class Github798NTest extends ModelCheckerTestCase { + + public Github798NTest() { + super("Github798N", new String[] { "-config", "Github798N.tla" }, EC.ExitStatus.SUCCESS); + } + + protected boolean noGenerateSpec() { + return true; + } + + protected boolean doDumpTrace() { + return false; + } + + @Test + public void testSpec() throws IOException { + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertFalse(recorder.recorded(EC.GENERAL)); + + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "3", "2", "0")); + assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "2")); + } +} diff --git a/tlatools/test/tlc2/tool/IncompleteNextMultipleActionsTest.java b/tlatools/test/tlc2/tool/IncompleteNextMultipleActionsTest.java new file mode 100644 index 0000000000000000000000000000000000000000..e7bd8a3107b02283aef55e64d5eb484b751ec282 --- /dev/null +++ b/tlatools/test/tlc2/tool/IncompleteNextMultipleActionsTest.java @@ -0,0 +1,74 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ + +package tlc2.tool; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.Test; + +import tlc2.output.EC; +import tlc2.output.EC.ExitStatus; +import tlc2.tool.liveness.ModelCheckerTestCase; + +public class IncompleteNextMultipleActionsTest extends ModelCheckerTestCase { + + public IncompleteNextMultipleActionsTest() { + super("IncompleteNextMultipleActions", ExitStatus.FAILURE_SPEC_EVAL); + } + + @Test + public void testSpec() { + // ModelChecker has finished and generated the expected amount of states + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertFalse(recorder.recorded(EC.GENERAL)); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "2", "1", "0")); + assertFalse(recorder.recorded(EC.GENERAL)); + + // Assert the error trace + assertTrue(recorder.recorded(EC.TLC_STATE_PRINT2)); + final List<String> expectedTrace = new ArrayList<String>(4); + expectedTrace.add("/\\ x = 0\n/\\ y = 0\n/\\ z = 0"); + expectedTrace.add("/\\ x = 1\n/\\ y = null\n/\\ z = null"); + assertTraceWith(recorder.getRecords(EC.TLC_STATE_PRINT2), expectedTrace); + + // Assert TLC indicates unassigned variable + assertTrue(recorder.recorded(EC.TLC_STATE_NOT_COMPLETELY_SPECIFIED_NEXT)); + final List<Object> records = recorder.getRecords(EC.TLC_STATE_NOT_COMPLETELY_SPECIFIED_NEXT); + assertEquals("A1", ((String[]) records.get(0))[0]); + assertEquals("s are", ((String[]) records.get(0))[1]); + assertEquals("y, z", ((String[]) records.get(0))[2]); + + assertUncovered("line 8, col 16 to line 8, col 21 of module IncompleteNextMultipleActions: 0\n" + + "line 8, col 26 to line 8, col 31 of module IncompleteNextMultipleActions: 0\n" + + "line 8, col 7 to line 8, col 11 of module IncompleteNextMultipleActions: 0\n"); + } +} diff --git a/tlatools/test/tlc2/tool/IncompleteNextTest.java b/tlatools/test/tlc2/tool/IncompleteNextTest.java new file mode 100644 index 0000000000000000000000000000000000000000..cc16dcacfd17877197f599dbd63b05b131f447e1 --- /dev/null +++ b/tlatools/test/tlc2/tool/IncompleteNextTest.java @@ -0,0 +1,71 @@ +/******************************************************************************* + * Copyright (c) 2015 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ + +package tlc2.tool; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.Test; + +import tlc2.output.EC; +import tlc2.output.EC.ExitStatus; +import tlc2.tool.liveness.ModelCheckerTestCase; + +public class IncompleteNextTest extends ModelCheckerTestCase { + + public IncompleteNextTest() { + super("IncompleteNext", ExitStatus.FAILURE_SPEC_EVAL); + } + + @Test + public void testSpec() { + // ModelChecker has finished and generated the expected amount of states + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertFalse(recorder.recorded(EC.GENERAL)); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "2", "1", "0")); + assertFalse(recorder.recorded(EC.GENERAL)); + + // Assert the error trace + assertTrue(recorder.recorded(EC.TLC_STATE_PRINT2)); + final List<String> expectedTrace = new ArrayList<String>(4); + expectedTrace.add("/\\ x = 0\n/\\ y = 0"); + expectedTrace.add("/\\ x = 1\n/\\ y = null"); + assertTraceWith(recorder.getRecords(EC.TLC_STATE_PRINT2), expectedTrace); + + // Assert TLC indicates unassigned variable + assertTrue(recorder.recorded(EC.TLC_STATE_NOT_COMPLETELY_SPECIFIED_NEXT)); + final List<Object> records = recorder.getRecords(EC.TLC_STATE_NOT_COMPLETELY_SPECIFIED_NEXT); + assertEquals(" is", ((String[]) records.get(0))[0]); + assertEquals("y", ((String[]) records.get(0))[1]); + + assertZeroUncovered(); + } +} diff --git a/tlatools/test/tlc2/tool/InliningTest.java b/tlatools/test/tlc2/tool/InliningTest.java new file mode 100644 index 0000000000000000000000000000000000000000..e980ade6d7da40cadea6fa526ee9bcb872e2ee6b --- /dev/null +++ b/tlatools/test/tlc2/tool/InliningTest.java @@ -0,0 +1,161 @@ +/******************************************************************************* + * Copyright (c) 2019 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.io.IOException; +import java.lang.reflect.Method; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +import org.junit.Test; + +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import jdk.jfr.consumer.RecordedObject; +import jdk.jfr.consumer.RecordingFile; +import tlc2.output.EC; +import tlc2.output.EC.ExitStatus; +import tlc2.tool.impl.FastTool; +import tlc2.tool.impl.Tool; +import tlc2.tool.liveness.ModelCheckerTestCase; +import tlc2.util.ExpectInlined; + +@SuppressWarnings("restriction") +public class InliningTest extends ModelCheckerTestCase { + + /* + * The high-level idea is that we record the JVM's CompilerInlining while the + * JVM executes the test case. Afterwards, we check that a bunch of methods that + * are annotated with a marker have been correctly inlined. For this to work, + * the test has to run long enough for the JVM to warm up. + * Thanks to https://twitter.com/ErikGahlin/status/1207018011674185728 for showing + * how to use the JFR API. + */ + + private final Recording r = new Recording(); + + public InliningTest() { + super("InlineMC", "CodePlexBug08", ExitStatus.SUCCESS); + } + + @Override + protected void beforeSetUp() { + r.enable("jdk.CompilerInlining"); + r.start(); + } + + // testSpec runs after model-checking. + @Test + public void testSpec() throws IOException { + // ModelChecker has finished at this point. + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertFalse(recorder.recorded(EC.GENERAL)); + assertZeroUncovered(); + + // stop recording and read the jfr from from disk. Close the recording + // afterwards. + r.stop(); + Path p = Paths.get("test.jfr"); // test.jfr is the default file name. + r.dump(p); + final List<RecordedEvent> recordedEvents = RecordingFile.readAllEvents(p); + r.close(); + + // "hot method too big" and not for "callee is too large": + // https://www.lmax.com/blog/staff-blogs/2016/03/30/notes-hotspot-compiler-flags/ + final Set<RecordedObject> notInlined = recordedEvents.stream() + .filter(ev -> ev.hasField("message")) + .filter(ev -> "hot method too big".equals(ev.getString("message"))) + .map(ev -> (RecordedObject) ev.getValue("callee")) + .filter(ro -> ro.getString("type").startsWith("tlc2/tool/impl/Tool") + || ro.getString("type").startsWith("tlc2/tool/impl/FastTool")) + .collect(Collectors.toSet()); + + // Make sure the test ran long enough for compilation to detect methods as hot. + assertFalse(notInlined.isEmpty()); + + // For now we only care that methods in Tool get correctly inlined + // because its methods are guaranteed to be on the hot path. + Method[] dm = Tool.class.getDeclaredMethods(); + for (int i = 0; i < dm.length; i++) { + if (dm[i].getAnnotation(ExpectInlined.class) != null) { + notIn(dm[i], notInlined); + } + } + dm = FastTool.class.getDeclaredMethods(); + for (int i = 0; i < dm.length; i++) { + if (dm[i].getAnnotation(ExpectInlined.class) != null) { + notIn(dm[i], notInlined); + } + } + } + + // This matching is likely brittle and will fail in ways that everybody will + // agree should have been accounted for. When it does, please check if + // RecordedObject has finally been changed to RecordedMethod + // (https://twitter.com/ErikGahlin/status/1207536016858505217). + private void notIn(final Method method, final Set<RecordedObject> notInlined) { + final List<RecordedObject> methodNameMatches = notInlined.stream() + .filter(ro -> method.getName().equals(ro.getString("name"))).collect(Collectors.toList()); + for (RecordedObject methodNameMatch : methodNameMatches) { + assertTrue(isNoMatch(methodNameMatch, method)); + } + } + + // I warned you that this doesn't work. + private boolean isNoMatch(RecordedObject methodNameMatch, Method method) { + final String desc = methodNameMatch.getString("descriptor"); + final String[] params = desc.substring(1, desc.indexOf(")")).split(";"); + if (method.getParameterCount() == params.length) { + Class<?>[] parameters = method.getParameterTypes(); + for (int j = 0; j < params.length; j++) { + final String paramType = parameters[j].toString().replace(".", "/").replaceFirst("^(class|interface) ", + "L"); + if (!params[j].equals(paramType)) { + return true; + } + } + System.out.println(methodNameMatch); +// return false; + } + return true; + } + + @Override + protected boolean doCoverage() { + return false; + } + + @Override + protected boolean doDump() { + return false; + } +} diff --git a/tlatools/test/tlc2/tool/MinimalSetOfInitStatesTest.java b/tlatools/test/tlc2/tool/MinimalSetOfInitStatesTest.java new file mode 100644 index 0000000000000000000000000000000000000000..e454c39c3a9ff169540dd3f692d1b525c26baa0c --- /dev/null +++ b/tlatools/test/tlc2/tool/MinimalSetOfInitStatesTest.java @@ -0,0 +1,57 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.output.EC; +import tlc2.tool.liveness.ModelCheckerTestCase; + +public class MinimalSetOfInitStatesTest extends ModelCheckerTestCase { + + public MinimalSetOfInitStatesTest() { + super("MinimalSetOfInitStates"); + } + + @Test + public void testSpec() { + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertFalse(recorder.recorded(EC.GENERAL)); + + // Without the fix to tlc2.tool.Tool.getInitStates(ActionItemList, TLCState, + // IStateFunctor), the number of generated initial states would be 6 with + // 4 being distinct. The fix in getInitStates causes TLC to more efficiently + // evaluate the init predicate and avoid generating the two duplicates. + assertTrue(recorder.recordedWithStringValues(EC.TLC_INIT_GENERATED2, "8", "s", "6")); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "14", "6", "0")); + assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "1")); + + assertZeroUncovered(); + } +} diff --git a/tlatools/test/tlc2/tool/MinimalSetOfNextStatesTest.java b/tlatools/test/tlc2/tool/MinimalSetOfNextStatesTest.java new file mode 100644 index 0000000000000000000000000000000000000000..c8297a265bd9418fa8c523fae735811dc4f31fd5 --- /dev/null +++ b/tlatools/test/tlc2/tool/MinimalSetOfNextStatesTest.java @@ -0,0 +1,54 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.output.EC; +import tlc2.tool.liveness.ModelCheckerTestCase; + +public class MinimalSetOfNextStatesTest extends ModelCheckerTestCase { + + public MinimalSetOfNextStatesTest() { + super("MinimalSetOfNextStates"); + } + + @Test + public void testSpec() { + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertFalse(recorder.recorded(EC.GENERAL)); + + assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "1")); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "57", "7", "0")); // 57 instead of 71. + assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "2")); + + assertUncovered("line 33, col 10 to line 33, col 15 of module MinimalSetOfNextStates: 0\n" + + "line 41, col 10 to line 41, col 15 of module MinimalSetOfNextStates: 0\n"); + } +} diff --git a/tlatools/test/tlc2/tool/MinimumDiameterTest.java b/tlatools/test/tlc2/tool/MinimumDiameterTest.java new file mode 100644 index 0000000000000000000000000000000000000000..c1f96240de0bc70c0bba6260f745c58de797ff3f --- /dev/null +++ b/tlatools/test/tlc2/tool/MinimumDiameterTest.java @@ -0,0 +1,58 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.output.EC; +import tlc2.tool.liveness.ModelCheckerTestCase; + +public class MinimumDiameterTest extends ModelCheckerTestCase { + + public MinimumDiameterTest() { + super("MinimumDiameter"); + } + + @Test + public void testSpec() { + assertFalse(recorder.recorded(EC.GENERAL)); + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "2", "1", "0")); + + // Minimum diameter with a single state is 1 (not zero)! + assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "1")); + + assertZeroUncovered(); + } + + @Override + protected boolean doDump() { + return false; + } +} diff --git a/tlatools/test/tlc2/tool/PrintTraceRaceTest.java b/tlatools/test/tlc2/tool/PrintTraceRaceTest.java new file mode 100644 index 0000000000000000000000000000000000000000..c602aa8b19f37fea26169bd943aea1185864b67e --- /dev/null +++ b/tlatools/test/tlc2/tool/PrintTraceRaceTest.java @@ -0,0 +1,80 @@ +/******************************************************************************* + * Copyright (c) 2015 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ + +package tlc2.tool; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.util.List; + +import org.junit.Test; + +import tlc2.output.EC; +import tlc2.output.EC.ExitStatus; +import tlc2.tool.liveness.ModelCheckerTestCase; +import util.TLAConstants; + +public class PrintTraceRaceTest extends ModelCheckerTestCase { + + public PrintTraceRaceTest() { + super(TLAConstants.Files.MODEL_CHECK_FILE_BASENAME, "PrintTraceRace", ExitStatus.FAILURE_SAFETY_EVAL); + } + + @Test + public void testSpec() { + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "2", "2", "0")); + assertFalse(recorder.recorded(EC.GENERAL)); + + assertTrue(recorder.recorded(EC.TLC_BEHAVIOR_UP_TO_THIS_POINT)); + + final List<Object> records = recorder.getRecords(EC.TLC_STATE_PRINT2); + + int i = 0; // State's position in records + Object[] objs = (Object[]) records.get(i++); + TLCStateInfo stateInfo = (TLCStateInfo) objs[0]; + assertEquals("S = [q |-> <<>>, i |-> 1]", + stateInfo.toString().trim()); // trimmed to remove any newlines or whitespace + assertEquals(i, objs[1]); + + objs = (Object[]) records.get(i++); + stateInfo = (TLCStateInfo) objs[0]; + assertEquals("S = [q |-> <<1>>, i |-> 2]", + stateInfo.toString().trim()); // trimmed to remove any newlines or whitespace + assertEquals(i, objs[1]); + + assertEquals(2, objs.length); + + assertUncovered("line 15, col 12 to line 15, col 28 of module PrintTraceRace: 0"); + } + + protected int getNumberOfThreads() { + // This bug only shows up with multiple threads. + return 4; + } +} diff --git a/tlatools/test/tlc2/tool/RandomElementT4Test.java b/tlatools/test/tlc2/tool/RandomElementT4Test.java new file mode 100644 index 0000000000000000000000000000000000000000..c45a2181a320b3b3e9d23260d8f7d721f83ba1e8 --- /dev/null +++ b/tlatools/test/tlc2/tool/RandomElementT4Test.java @@ -0,0 +1,82 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.util.List; +import java.util.Map; + +import org.junit.Test; + +import tlc2.output.EC; +import tlc2.output.EC.ExitStatus; +import tlc2.tool.liveness.ModelCheckerTestCase; +import tlc2.value.IValue; +import tlc2.value.impl.IntValue; +import util.UniqueString; + +public class RandomElementT4Test extends ModelCheckerTestCase { + + public RandomElementT4Test() { + super("RandomElement", new String[] {"-seed", Long.toString(15041980L)}, ExitStatus.VIOLATION_SAFETY); + } + + @Test + public void test() { + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertFalse(recorder.recorded(EC.TLC_BUG)); + + assertTrue(recorder.recorded(EC.TLC_BEHAVIOR_UP_TO_THIS_POINT)); + + final List<Object> records = recorder.getRecords(EC.TLC_STATE_PRINT2); + assertEquals(11, records.size()); + + int cnt = 0; + for (Object r : records) { + final Object[] objs = (Object[]) r; + final TLCStateInfo info = (TLCStateInfo) objs[0]; + final Map<UniqueString, IValue> vals = info.state.getVals(); + + final IValue y = vals.get(UniqueString.uniqueStringOf("y")); + assertEquals(cnt++, ((IntValue) y).val); + + final IValue x = info.state.getVals().get(UniqueString.uniqueStringOf("x")); + assertTrue(1 <= ((IntValue) x).val && ((IntValue) x).val <= 1000); + + final int statenum = (int) objs[1]; + assertEquals(cnt, statenum); + } + } + + protected int getNumberOfThreads() { + // With 4 threads the counter-examples is not predictable anymore because it + // depends on thread scheduling. + return 4; + } +} diff --git a/tlatools/test/tlc2/tool/RandomElementTest.java b/tlatools/test/tlc2/tool/RandomElementTest.java new file mode 100644 index 0000000000000000000000000000000000000000..0066f0134a4f9f857f151b2c3aa17ebc44e5a9af --- /dev/null +++ b/tlatools/test/tlc2/tool/RandomElementTest.java @@ -0,0 +1,70 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.Test; + +import tlc2.output.EC; +import tlc2.output.EC.ExitStatus; +import tlc2.tool.liveness.ModelCheckerTestCase; + +public class RandomElementTest extends ModelCheckerTestCase { + + public RandomElementTest() { + super("RandomElement", new String[] {"-seed", Long.toString(8006803340504660123L)}, ExitStatus.VIOLATION_SAFETY); + } + + @Test + public void test() { + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertFalse(recorder.recorded(EC.TLC_BUG)); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "932", "855", "388")); + + assertTrue(recorder.recorded(EC.TLC_BEHAVIOR_UP_TO_THIS_POINT)); + + final List<String> expectedTrace = new ArrayList<String>(11); + expectedTrace.add("/\\ x = 843\n/\\ y = 0"); + expectedTrace.add("/\\ x = 920\n/\\ y = 1"); + expectedTrace.add("/\\ x = 483\n/\\ y = 2"); + expectedTrace.add("/\\ x = 173\n/\\ y = 3"); + expectedTrace.add("/\\ x = 590\n/\\ y = 4"); + expectedTrace.add("/\\ x = 104\n/\\ y = 5"); + expectedTrace.add("/\\ x = 785\n/\\ y = 6"); + expectedTrace.add("/\\ x = 463\n/\\ y = 7"); + expectedTrace.add("/\\ x = 443\n/\\ y = 8"); + expectedTrace.add("/\\ x = 151\n/\\ y = 9"); + expectedTrace.add("/\\ x = 767\n/\\ y = 10"); + assertTraceWith(recorder.getRecords(EC.TLC_STATE_PRINT2), expectedTrace); + + assertZeroUncovered(); + } +} diff --git a/tlatools/test/tlc2/tool/RandomElementXandYTest.java b/tlatools/test/tlc2/tool/RandomElementXandYTest.java new file mode 100644 index 0000000000000000000000000000000000000000..15effbe5bd21dddc8f3aeb0b38f0739273cbd722 --- /dev/null +++ b/tlatools/test/tlc2/tool/RandomElementXandYTest.java @@ -0,0 +1,64 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.Test; + +import tlc2.output.EC; +import tlc2.output.EC.ExitStatus; +import tlc2.tool.liveness.ModelCheckerTestCase; + +public class RandomElementXandYTest extends ModelCheckerTestCase { + + public RandomElementXandYTest() { + super("RandomElementXandY", new String[] {"-seed", Long.toString(8006642976694192746L)}, ExitStatus.VIOLATION_SAFETY); + // 8006642976694192746L produces a trace of three states. + // 8006642976346685430L and 8006642974998076619L results in no violation of an invariant + // 8006642972812024640L a trace with two states + } + + @Test + public void test() { + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertFalse(recorder.recorded(EC.TLC_BUG)); + + assertTrue(recorder.recorded(EC.TLC_BEHAVIOR_UP_TO_THIS_POINT)); + + final List<String> expectedTrace = new ArrayList<String>(11); + expectedTrace.add("/\\ x = 0\n/\\ y = 0"); + expectedTrace.add("/\\ x = 1\n/\\ y = 1"); + expectedTrace.add("/\\ x = 0\n/\\ y = 1"); + assertTraceWith(recorder.getRecords(EC.TLC_STATE_PRINT2), expectedTrace); + + assertZeroUncovered(); + } +} diff --git a/tlatools/test/tlc2/tool/RandomSubset.java b/tlatools/test/tlc2/tool/RandomSubset.java new file mode 100644 index 0000000000000000000000000000000000000000..539455fc48d1fbe64e249111df0d224522a8bd63 --- /dev/null +++ b/tlatools/test/tlc2/tool/RandomSubset.java @@ -0,0 +1,71 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.Test; + +import tlc2.output.EC; +import tlc2.output.EC.ExitStatus; +import tlc2.tool.liveness.ModelCheckerTestCase; + +public abstract class RandomSubset extends ModelCheckerTestCase { + + private final int x; + private final int y; + + public RandomSubset(final long seed, final int x, final int y) { + // Initial seed with a randomly chosen but fixed value for x and y to be + // predictable. The two subclasses chose different values to test that different + // seeds result in different values. + super("RandomSubset", new String[] {"-seed", Long.toString(seed)}, ExitStatus.VIOLATION_SAFETY); + this.x = x; + this.y = y; + } + + @Test + public void testSpec() { + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertFalse(recorder.recorded(EC.GENERAL)); + + assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "2002")); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "2003", "2003", "2001")); + assertEquals(2, recorder.getRecordAsInt(EC.TLC_SEARCH_DEPTH)); + + assertTrue(recorder.recorded(EC.TLC_STATE_PRINT2)); + final List<String> expectedTrace = new ArrayList<String>(); + expectedTrace.add("/\\ x = " + x + "\n" + "/\\ y = " + y + "\n" + "/\\ z = TRUE"); + expectedTrace.add("/\\ x = " + x + "\n" + "/\\ y = " + y + "\n" + "/\\ z = FALSE"); + assertTraceWith(recorder.getRecords(EC.TLC_STATE_PRINT2), expectedTrace); + } + +} diff --git a/tlatools/test/tlc2/tool/RandomSubsetATest.java b/tlatools/test/tlc2/tool/RandomSubsetATest.java new file mode 100644 index 0000000000000000000000000000000000000000..44e4429427dad340128b7d396f2ea5cadbe72346 --- /dev/null +++ b/tlatools/test/tlc2/tool/RandomSubsetATest.java @@ -0,0 +1,33 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool; + +public class RandomSubsetATest extends RandomSubset { + + public RandomSubsetATest() { + super(15041980L, 47567, 100000003); + } +} diff --git a/tlatools/test/tlc2/tool/RandomSubsetBTest.java b/tlatools/test/tlc2/tool/RandomSubsetBTest.java new file mode 100644 index 0000000000000000000000000000000000000000..05edf09ddb478a1615b94b94c538077650934e79 --- /dev/null +++ b/tlatools/test/tlc2/tool/RandomSubsetBTest.java @@ -0,0 +1,33 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool; + +public class RandomSubsetBTest extends RandomSubset { + + public RandomSubsetBTest() { + super(918347981374L, 66058, 100000003); + } +} diff --git a/tlatools/test/tlc2/tool/RandomSubsetNextT4Test.java b/tlatools/test/tlc2/tool/RandomSubsetNextT4Test.java new file mode 100644 index 0000000000000000000000000000000000000000..04d2708f57481898553119367d864bacbf167efd --- /dev/null +++ b/tlatools/test/tlc2/tool/RandomSubsetNextT4Test.java @@ -0,0 +1,82 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.util.List; +import java.util.Map; + +import org.junit.Test; + +import tlc2.output.EC; +import tlc2.output.EC.ExitStatus; +import tlc2.tool.liveness.ModelCheckerTestCase; +import tlc2.value.IValue; +import tlc2.value.impl.IntValue; +import util.UniqueString; + +public class RandomSubsetNextT4Test extends ModelCheckerTestCase { + + public RandomSubsetNextT4Test() { + super("RandomSubsetNext", ExitStatus.VIOLATION_SAFETY); + } + + @Test + public void testSpec() { + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertFalse(recorder.recorded(EC.TLC_BUG)); + + assertTrue(recorder.recorded(EC.TLC_BEHAVIOR_UP_TO_THIS_POINT)); + + final List<Object> records = recorder.getRecords(EC.TLC_STATE_PRINT2); + assertEquals(11, records.size()); + + int cnt = 0; + for (Object r : records) { + final Object[] objs = (Object[]) r; + final TLCStateInfo info = (TLCStateInfo) objs[0]; + final Map<UniqueString, IValue> vals = info.state.getVals(); + + final IValue y = vals.get(UniqueString.uniqueStringOf("y")); + assertEquals(cnt++, ((IntValue) y).val); + + final IValue x = info.state.getVals().get(UniqueString.uniqueStringOf("x")); + assertTrue(1 <= ((IntValue) x).val && ((IntValue) x).val <= 1000); + + final int statenum = (int) objs[1]; + assertEquals(cnt, statenum); + } + } + + protected int getNumberOfThreads() { + // With 4 threads the counter-examples is not predictable anymore because it + // depends on thread scheduling. + return 4; + } +} diff --git a/tlatools/test/tlc2/tool/RandomSubsetNextTest.java b/tlatools/test/tlc2/tool/RandomSubsetNextTest.java new file mode 100644 index 0000000000000000000000000000000000000000..81618cee0ff1b46affe9c6748f18bd8db05f2fe5 --- /dev/null +++ b/tlatools/test/tlc2/tool/RandomSubsetNextTest.java @@ -0,0 +1,70 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.Test; + +import tlc2.output.EC; +import tlc2.output.EC.ExitStatus; +import tlc2.tool.liveness.ModelCheckerTestCase; + +public class RandomSubsetNextTest extends ModelCheckerTestCase { + + public RandomSubsetNextTest() { + super("RandomSubsetNext", new String[] {"-seed", Long.toString(15041980L)}, ExitStatus.VIOLATION_SAFETY); + } + + @Test + public void testSpec() { + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertFalse(recorder.recorded(EC.TLC_BUG)); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "67321", "7732", "999")); + + assertTrue(recorder.recorded(EC.TLC_BEHAVIOR_UP_TO_THIS_POINT)); + + final List<String> expectedTrace = new ArrayList<String>(11); + expectedTrace.add("/\\ x = 43\n/\\ y = 0"); + expectedTrace.add("/\\ x = 2\n/\\ y = 1"); + expectedTrace.add("/\\ x = 95\n/\\ y = 2"); + expectedTrace.add("/\\ x = 40\n/\\ y = 3"); + expectedTrace.add("/\\ x = 6\n/\\ y = 4"); + expectedTrace.add("/\\ x = 168\n/\\ y = 5"); + expectedTrace.add("/\\ x = 225\n/\\ y = 6"); + expectedTrace.add("/\\ x = 93\n/\\ y = 7"); + expectedTrace.add("/\\ x = 42\n/\\ y = 8"); + expectedTrace.add("/\\ x = 8\n/\\ y = 9"); + expectedTrace.add("/\\ x = 30\n/\\ y = 10"); + assertTraceWith(recorder.getRecords(EC.TLC_STATE_PRINT2), expectedTrace); + + assertZeroUncovered(); + } +} diff --git a/tlatools/test/tlc2/tool/RandomSubsetSetOfFcnsTest.java b/tlatools/test/tlc2/tool/RandomSubsetSetOfFcnsTest.java new file mode 100644 index 0000000000000000000000000000000000000000..8140b06efc02d0024b5418d3701c5dc92384cfb2 --- /dev/null +++ b/tlatools/test/tlc2/tool/RandomSubsetSetOfFcnsTest.java @@ -0,0 +1,82 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.output.EC; +import tlc2.tool.liveness.ModelCheckerTestCase; + +public class RandomSubsetSetOfFcnsTest extends ModelCheckerTestCase { + + public RandomSubsetSetOfFcnsTest() { + super("RandomSubsetSetOfFcns"); + } + + @Test + public void testSpec() { + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertFalse(recorder.recorded(EC.GENERAL)); + + assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "1000")); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "2000", "1000", "0")); + assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "1")); +// +// assertTrue(recorder.recorded(EC.TLC_STATE_PRINT2)); +// +// final List<Object> actual = recorder.getRecords(EC.TLC_STATE_PRINT2); +// assertEquals(2, actual.size()); +// +// final TLCStateInfo first = (TLCStateInfo) ((Object[]) actual.get(0))[0]; +// assertTrue(((String) first.info).startsWith("<Initial predicate>")); +// final Map<UniqueString, Value> firstState = first.state.getVals(); +// assertEquals(3, firstState.size()); +// +// // Check x and y values are within defined ranges. +// final IntValue firstX = (IntValue) firstState.get(UniqueString.uniqueStringOf("x")); +// assertTrue(1 <= firstX.val && firstX.val <= 100000000); +// final IntValue firstY = (IntValue) firstState.get(UniqueString.uniqueStringOf("y")); +// assertTrue(100000000 <= firstY.val && firstX.val <= 100000010); +// +// // Check z is true +// assertEquals(BoolValue.ValTrue, (BoolValue) firstState.get(UniqueString.uniqueStringOf("z"))); +// +// final TLCStateInfo second = (TLCStateInfo) ((Object[]) actual.get(1))[0]; +// assertTrue(((String) second.info).startsWith("<Next line 10, col 9 to line 11, col 21 of module RandomSubset>")); +// final Map<UniqueString, Value> secondState = second.state.getVals(); +// assertEquals(3, secondState.size()); +// // UNCHANGED x,y +// assertEquals(firstX.val, ((IntValue) secondState.get(UniqueString.uniqueStringOf("x"))).val); +// assertEquals(firstY.val, ((IntValue) secondState.get(UniqueString.uniqueStringOf("y"))).val); +// // Check z is false +// assertEquals(BoolValue.ValFalse, (BoolValue) secondState.get(UniqueString.uniqueStringOf("z"))); + + assertZeroUncovered(); + } +} diff --git a/tlatools/test/tlc2/tool/RandomSubsetTest.java b/tlatools/test/tlc2/tool/RandomSubsetTest.java new file mode 100644 index 0000000000000000000000000000000000000000..fb562980cefd954f72780e821a444c5935089220 --- /dev/null +++ b/tlatools/test/tlc2/tool/RandomSubsetTest.java @@ -0,0 +1,92 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.util.List; +import java.util.Map; + +import org.junit.Test; + +import tlc2.output.EC; +import tlc2.output.EC.ExitStatus; +import tlc2.tool.liveness.ModelCheckerTestCase; +import tlc2.value.IBoolValue; +import tlc2.value.IValue; +import tlc2.value.impl.BoolValue; +import tlc2.value.impl.IntValue; +import util.UniqueString; + +public class RandomSubsetTest extends ModelCheckerTestCase { + + public RandomSubsetTest() { + super("RandomSubset", ExitStatus.VIOLATION_SAFETY); + } + + @Test + public void testSpec() { + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertFalse(recorder.recorded(EC.GENERAL)); + + assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "2002")); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "2003", "2003", "2001")); + assertEquals(2, recorder.getRecordAsInt(EC.TLC_SEARCH_DEPTH)); + + assertTrue(recorder.recorded(EC.TLC_STATE_PRINT2)); + + final List<Object> actual = recorder.getRecords(EC.TLC_STATE_PRINT2); + assertEquals(2, actual.size()); + + final TLCStateInfo first = (TLCStateInfo) ((Object[]) actual.get(0))[0]; + assertTrue(((String) first.info).startsWith("<Initial predicate>")); + final Map<UniqueString, IValue> firstState = first.state.getVals(); + assertEquals(3, firstState.size()); + + // Check x and y values are within defined ranges. + final IntValue firstX = (IntValue) firstState.get(UniqueString.uniqueStringOf("x")); + assertTrue(1 <= firstX.val && firstX.val <= 100000000); + final IntValue firstY = (IntValue) firstState.get(UniqueString.uniqueStringOf("y")); + assertTrue(100000000 <= firstY.val && firstX.val <= 100000010); + + // Check z is true + assertEquals(BoolValue.ValTrue, (IBoolValue) firstState.get(UniqueString.uniqueStringOf("z"))); + + final TLCStateInfo second = (TLCStateInfo) ((Object[]) actual.get(1))[0]; + assertTrue(((String) second.info).startsWith("<Next line 10, col 9 to line 11, col 21 of module RandomSubset>")); + final Map<UniqueString, IValue> secondState = second.state.getVals(); + assertEquals(3, secondState.size()); + // UNCHANGED x,y + assertEquals(firstX.val, ((IntValue) secondState.get(UniqueString.uniqueStringOf("x"))).val); + assertEquals(firstY.val, ((IntValue) secondState.get(UniqueString.uniqueStringOf("y"))).val); + // Check z is false + assertEquals(BoolValue.ValFalse, (IBoolValue) secondState.get(UniqueString.uniqueStringOf("z"))); + + assertZeroUncovered(); + } +} diff --git a/tlatools/test/tlc2/tool/SetOfStatesTest.java b/tlatools/test/tlc2/tool/SetOfStatesTest.java new file mode 100644 index 0000000000000000000000000000000000000000..68171cc4627bc428aafc2fe26647d586423a1876 --- /dev/null +++ b/tlatools/test/tlc2/tool/SetOfStatesTest.java @@ -0,0 +1,173 @@ +/******************************************************************************* + * Copyright (c) 2015 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ + +package tlc2.tool; + +import java.util.HashSet; +import java.util.Set; + +import junit.framework.TestCase; +import tlc2.tool.queue.DummyTLCState; +import tlc2.util.SetOfStates; + +public class SetOfStatesTest extends TestCase { + + public void testSizeEmpty() { + final SetOfStates s = new SetOfStates(16); + + assertEquals(16, s.capacity()); + assertEquals(0, s.size()); + } + + public void testSize() { + final SetOfStates s = new SetOfStates(16); + s.put(new DummyTLCState(1L)); + + assertEquals(16, s.capacity()); + assertEquals(1, s.size()); + } + + public void testGrow() { + final SetOfStates s = new SetOfStates(1); + + for(int i = 0; i < 32; i++) { + s.put(new DummyTLCState(i)); + } + + assertTrue(s.capacity() > 32); + assertEquals(32, s.size()); + } + + public void testIterate() { + final SetOfStates s = new SetOfStates(1); + + for(int i = 1; i <= 32; i++) { + assertFalse(s.put(new DummyTLCState(i))); + } + assertEquals(32, s.size()); + + // successor is not equal to predecessor + TLCState predecessor = null; + for (int i = 0; i < s.size(); i++) { + TLCState state = s.next(); + assertNotSame(predecessor, state); + predecessor = state; + } + s.resetNext(); + + // The combined sum of elements is correct + long sum = 0L; + for (int i = 0; i < s.size(); i++) { + final TLCState elem = s.next(); + sum += elem.fingerPrint(); + } + assertEquals((32 / 2) * (1 + 32), sum); + } + + public void testDuplicates() { + final SetOfStates s = new SetOfStates(1); + + for(int i = 1; i <= 32; i++) { + assertFalse(s.put(new DummyTLCState(i))); + } + assertEquals(32, s.size()); + + // Adding the same elements again fails + final Set<TLCState> states = new HashSet<TLCState>(s.size()); + for (int i = 0; i < s.size(); i++) { + states.add(s.next()); + } + for (TLCState aState : states) { + assertTrue(s.put(aState)); + } + assertEquals(32, states.size()); + } + + public void testDuplicatesButNotEqual() { + // duplicates in terms of fingerprints, but not in terms of equality + // (symmetry). + final SetOfStates s = new SetOfStates(1); + + int id = 1; + for(int i = 1; i <= 32; i++) { + assertFalse(s.put(new EqualityDummyTLCState(i, id++))); + } + assertEquals(32, s.size()); + + // Add 32 more elements with identical fp but different ids + for(int i = 1; i <= 32; i++) { + assertFalse(s.put(new EqualityDummyTLCState(i, id++))); + } + assertEquals(64, s.size()); + + // Add 32 more elements with identical fp and ids + id = 1; + for(int i = 1; i <= 32; i++) { + assertTrue(s.put(new EqualityDummyTLCState(i, id++))); + } + assertEquals(64, s.size()); + } + + @SuppressWarnings("serial") + private static class EqualityDummyTLCState extends DummyTLCState { + + private final int id; + + public EqualityDummyTLCState(final int fp, final int id) { + super(fp); + this.id = id; + } + + /* (non-Javadoc) + * @see java.lang.Object#hashCode() + */ + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + id; + result = (int) (prime * result + fingerPrint()); + return result; + } + + /* (non-Javadoc) + * @see java.lang.Object#equals(java.lang.Object) + */ + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + EqualityDummyTLCState other = (EqualityDummyTLCState) obj; + if (fingerPrint() != other.fingerPrint()) + return false; + if (id != other.id) + return false; + return true; + } + } +} diff --git a/tlatools/test/tlc2/tool/SetPredValueTest.java b/tlatools/test/tlc2/tool/SetPredValueTest.java new file mode 100644 index 0000000000000000000000000000000000000000..8e38a9fb931b9c848a9d3df6701ac4f634390eb0 --- /dev/null +++ b/tlatools/test/tlc2/tool/SetPredValueTest.java @@ -0,0 +1,48 @@ +/******************************************************************************* + * Copyright (c) 2019 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.output.EC; +import tlc2.tool.liveness.ModelCheckerTestCase; + +public class SetPredValueTest extends ModelCheckerTestCase { + + public SetPredValueTest() { + super("SetPredValue"); + } + + @Test + public void testSpec() { + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertFalse(recorder.recorded(EC.GENERAL)); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "0", "0", "0")); + } +} diff --git a/tlatools/test/tlc2/tool/StandardModulesTest.java b/tlatools/test/tlc2/tool/StandardModulesTest.java new file mode 100644 index 0000000000000000000000000000000000000000..c3f766e33b51db43f061ac9f5c1c2b68216c098a --- /dev/null +++ b/tlatools/test/tlc2/tool/StandardModulesTest.java @@ -0,0 +1,49 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.output.EC; +import tlc2.tool.liveness.ModelCheckerTestCase; + +public class StandardModulesTest extends ModelCheckerTestCase { + + public StandardModulesTest() { + super("StandardModules"); + } + + @Test + public void testSpec() { + // Check that all standard modules parse (StandardModules.tla extends all of them). + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertFalse(recorder.recorded(EC.GENERAL)); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "0", "0", "0")); + } +} diff --git a/tlatools/test/tlc2/tool/SubsetEqTest.java b/tlatools/test/tlc2/tool/SubsetEqTest.java new file mode 100644 index 0000000000000000000000000000000000000000..b1c81b690e525a9b3fb841209724e42ee505328f --- /dev/null +++ b/tlatools/test/tlc2/tool/SubsetEqTest.java @@ -0,0 +1,57 @@ +/******************************************************************************* + * Copyright (c) 2017 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.output.EC; +import tlc2.tool.liveness.ModelCheckerTestCase; + +public class SubsetEqTest extends ModelCheckerTestCase { + + public SubsetEqTest() { + super("SubsetEq"); + } + + @Test + public void testSpec() { + // This is a performance test. With the original implementation - where + // TLC does not reduce (SUBSET A \subseteq SUBSET B) - the test takes + // forever to generate the successor state. Reduced to (A \subseteq B), + // the test completes within seconds. + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertFalse(recorder.recorded(EC.GENERAL)); + + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "2", "1", "0")); + // No error trace! + assertFalse(recorder.recorded(EC.TLC_STATE_PRINT2)); + + assertZeroUncovered(); + } +} diff --git a/tlatools/test/tlc2/tool/TLCGetLevelTest.java b/tlatools/test/tlc2/tool/TLCGetLevelTest.java new file mode 100644 index 0000000000000000000000000000000000000000..c807d3064a9a9520a8c930920fb26c597c969abf --- /dev/null +++ b/tlatools/test/tlc2/tool/TLCGetLevelTest.java @@ -0,0 +1,71 @@ +/******************************************************************************* + * Copyright (c) 2019 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.Test; + +import tlc2.output.EC; +import tlc2.output.EC.ExitStatus; +import tlc2.tool.liveness.ModelCheckerTestCase; + +public class TLCGetLevelTest extends ModelCheckerTestCase { + + public TLCGetLevelTest() { + super("TLCGetLevel", ExitStatus.VIOLATION_LIVENESS); + } + + @Test + public void testSpec() { + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "4", "4", "0")); + assertFalse(recorder.recorded(EC.GENERAL)); + + // Assert TLC has found a temporal violation and a counter example + assertTrue(recorder.recorded(EC.TLC_TEMPORAL_PROPERTY_VIOLATED)); + assertTrue(recorder.recorded(EC.TLC_COUNTER_EXAMPLE)); + + // Assert the error trace + assertTrue(recorder.recorded(EC.TLC_STATE_PRINT2)); + final List<String> expectedTrace = new ArrayList<String>(4); + expectedTrace.add("/\\ x = 0\n" + + "/\\ y = 0"); + expectedTrace.add("/\\ x = 1\n" + + "/\\ y = 1"); + expectedTrace.add("/\\ x = 2\n" + + "/\\ y = 2"); + expectedTrace.add("/\\ x = 3\n" + + "/\\ y = 3"); + assertTraceWith(recorder.getRecords(EC.TLC_STATE_PRINT2), expectedTrace); + + assertStuttering(5); + } +} diff --git a/tlatools/test/tlc2/tool/TLCGetNamedUndefinedTest.java b/tlatools/test/tlc2/tool/TLCGetNamedUndefinedTest.java new file mode 100644 index 0000000000000000000000000000000000000000..cd47a068673481c6e170bb4fe1cb4d4a338bc3bb --- /dev/null +++ b/tlatools/test/tlc2/tool/TLCGetNamedUndefinedTest.java @@ -0,0 +1,48 @@ +/******************************************************************************* + * Copyright (c) 2019 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool; + +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.output.EC; +import tlc2.output.EC.ExitStatus; +import tlc2.tool.liveness.ModelCheckerTestCase; + +public class TLCGetNamedUndefinedTest extends ModelCheckerTestCase { + + public TLCGetNamedUndefinedTest() { + super("TLCGetNamedUndefined", ExitStatus.ERROR_CONFIG_PARSE); + } + + @Test + public void testSpec() { + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertTrue(recorder.recorded(EC.TLC_MODULE_TLCGET_UNDEFINED)); + assertTrue(recorder.recorded(EC.TLC_CONFIG_SUBSTITUTION_NON_CONSTANT)); + } +} diff --git a/tlatools/test/tlc2/tool/TLCGetNonDeterminismTest.java b/tlatools/test/tlc2/tool/TLCGetNonDeterminismTest.java new file mode 100644 index 0000000000000000000000000000000000000000..87ebb1020f26ed16f06c1ecdb559eaa5698d1ee6 --- /dev/null +++ b/tlatools/test/tlc2/tool/TLCGetNonDeterminismTest.java @@ -0,0 +1,77 @@ +/******************************************************************************* + * Copyright (c) 2019 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.Ignore; +import org.junit.Test; + +import tlc2.output.EC; +import tlc2.tool.liveness.ModelCheckerTestCase; + +public class TLCGetNonDeterminismTest extends ModelCheckerTestCase { + + public TLCGetNonDeterminismTest() { + super("TLCGetNonDeterminism"); + } + + @Test + @Ignore("No known fix/By design") + public void testSpec() { + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertFalse(recorder.recorded(EC.GENERAL)); + + // Assert TLC has found a temporal violation and a counter example + assertTrue(recorder.recorded(EC.TLC_TEMPORAL_PROPERTY_VIOLATED)); + assertTrue(recorder.recorded(EC.TLC_COUNTER_EXAMPLE)); + + // Assert the error trace + assertTrue(recorder.recorded(EC.TLC_STATE_PRINT2)); + final List<String> expectedTrace = new ArrayList<String>(4); + expectedTrace.add("/\\ x = 0\n" + + "/\\ y = 0"); + expectedTrace.add("/\\ x = 1\n" + + "/\\ y = 1"); + expectedTrace.add("/\\ x = 2\n" + + "/\\ y = 2"); + expectedTrace.add("/\\ x = 3\n" + + "/\\ y = 3"); + assertTraceWith(recorder.getRecords(EC.TLC_STATE_PRINT2), expectedTrace); + + assertStuttering(5); + } + + @Override + protected int getNumberOfThreads() { + // This test passes with a single worker! There is a slim chance it passes with more workers. + return 4; + } +} diff --git a/tlatools/test/tlc2/tool/TLCSetInitTest.java b/tlatools/test/tlc2/tool/TLCSetInitTest.java new file mode 100644 index 0000000000000000000000000000000000000000..478ef7a4d004a6dd97da92cf6e6be43db2db6afa --- /dev/null +++ b/tlatools/test/tlc2/tool/TLCSetInitTest.java @@ -0,0 +1,48 @@ +/******************************************************************************* + * Copyright (c) 2016 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ + +package tlc2.tool; + +import static org.junit.Assert.assertFalse; + +import org.junit.Test; + +import tlc2.output.EC; +import tlc2.tool.liveness.ModelCheckerTestCase; + +public class TLCSetInitTest extends ModelCheckerTestCase { + + public TLCSetInitTest() { + super("TLCSetInit"); + } + + @Test + public void testSpec() { + assertFalse(recorder.recorded(EC.GENERAL)); + + assertZeroUncovered(); + } +} diff --git a/tlatools/test/tlc2/tool/TLCSetTest.java b/tlatools/test/tlc2/tool/TLCSetTest.java new file mode 100644 index 0000000000000000000000000000000000000000..69a2c87e501fc88e5d581593de8d5ca757e29bc5 --- /dev/null +++ b/tlatools/test/tlc2/tool/TLCSetTest.java @@ -0,0 +1,53 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.output.EC; +import tlc2.tool.liveness.ModelCheckerTestCase; + +public class TLCSetTest extends ModelCheckerTestCase { + + public TLCSetTest() { + super("TLCSet"); + } + + @Test + public void testSpec() { + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertFalse(recorder.recorded(EC.GENERAL)); + assertZeroUncovered(); + } + + @Override + protected boolean doDump() { + return false; + } +} diff --git a/tlatools/test/tlc2/tool/TLCStates.java b/tlatools/test/tlc2/tool/TLCStates.java new file mode 100644 index 0000000000000000000000000000000000000000..c05339e98b228baf2dbf441465ca5cc55a05e94a --- /dev/null +++ b/tlatools/test/tlc2/tool/TLCStates.java @@ -0,0 +1,63 @@ +/******************************************************************************* + * Copyright (c) 2019 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool; + +import tla2sany.semantic.ASTConstants; +import tla2sany.semantic.OpDeclNode; +import tlc2.value.impl.IntValue; +import util.UniqueString; + +public abstract class TLCStates { + + public static TLCState createDummyState() { + return createDummyState(1); + } + + public static TLCState createDummyState(final int numVars) { + UniqueString.setVariableCount(numVars); + + // Create variable declarations (no values yet). + final OpDeclNode[] variables = new OpDeclNode[numVars]; + for (int i = 0; i < numVars; i++) { + UniqueString us = UniqueString.uniqueStringOf("v" + Integer.toString(i)); + us.setLoc(i); + variables[i] = new OpDeclNode(us, ASTConstants.VariableDeclKind, 1, 0, null, null, null); + } + + // Initialize the empty state (variable declarations are static/final per TLC + // run). + TLCStateMut.setVariables(variables); + final TLCState state = TLCState.Empty.createEmpty(); + state.uid = 0; + + // Assign values to variables. + for (int i = 0; i < numVars; i++) { + state.bind(variables[i].getName(), IntValue.gen(i)); + } + + return state; + } +} diff --git a/tlatools/test/tlc2/tool/TSnapShotTest.java b/tlatools/test/tlc2/tool/TSnapShotTest.java new file mode 100644 index 0000000000000000000000000000000000000000..55bf3cde2d2ee7a976f926c2bfb0e8f114dd9b9c --- /dev/null +++ b/tlatools/test/tlc2/tool/TSnapShotTest.java @@ -0,0 +1,58 @@ +/******************************************************************************* + * Copyright (c) 2015 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ + +package tlc2.tool; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.output.EC; +import tlc2.output.EC.ExitStatus; +import tlc2.tool.liveness.ModelCheckerTestCase; +import util.TLAConstants; + +public class TSnapShotTest extends ModelCheckerTestCase { + + public TSnapShotTest() { + super(TLAConstants.Files.MODEL_CHECK_FILE_BASENAME, "TSnapShot", ExitStatus.FAILURE_SAFETY_EVAL); + } + + @Test + public void testSpec() { + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertFalse(recorder.recorded(EC.GENERAL)); + assertFalse(recorder.recorded(EC.TLC_BUG)); + + assertTrue(recorder.recorded(EC.TLC_BEHAVIOR_UP_TO_THIS_POINT)); + } + + protected int getNumberOfThreads() { + // This bug only shows up with multiple threads. + return 4; + } +} diff --git a/tlatools/test/tlc2/tool/TraceWithLargeSetOfInitialStatesTest.java b/tlatools/test/tlc2/tool/TraceWithLargeSetOfInitialStatesTest.java new file mode 100644 index 0000000000000000000000000000000000000000..dfb79ba450383a06a7adb9c2d9e67e6f6ca50e1c --- /dev/null +++ b/tlatools/test/tlc2/tool/TraceWithLargeSetOfInitialStatesTest.java @@ -0,0 +1,61 @@ +/******************************************************************************* + * Copyright (c) 2017 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.Test; + +import tlc2.output.EC; +import tlc2.output.EC.ExitStatus; +import tlc2.tool.liveness.ModelCheckerTestCase; + +public class TraceWithLargeSetOfInitialStatesTest extends ModelCheckerTestCase { + + public TraceWithLargeSetOfInitialStatesTest() { + super("TraceWithLargeSetOfInitialStatesTest", new String[] { "-maxSetSize", "10" }, ExitStatus.VIOLATION_SAFETY); + } + + @Test + public void testSpec() { + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertFalse(recorder.recorded(EC.GENERAL)); + assertFalse(recorder.recorded(EC.TLC_BUG)); + + assertTrue(recorder.recorded(EC.TLC_BEHAVIOR_UP_TO_THIS_POINT)); + + final List<String> expectedTrace = new ArrayList<String>(2); + expectedTrace.add("/\\ x = 1\n/\\ y = FALSE"); + expectedTrace.add("/\\ x = 1\n/\\ y = TRUE"); + assertTraceWith(recorder.getRecords(EC.TLC_STATE_PRINT2), expectedTrace); + + assertZeroUncovered(); + } +} diff --git a/tlatools/test/tlc2/tool/UserModuleOverrideAnnotationTest.java b/tlatools/test/tlc2/tool/UserModuleOverrideAnnotationTest.java new file mode 100644 index 0000000000000000000000000000000000000000..d2239121bb4b23fbe306b134677f1a8a26c267e0 --- /dev/null +++ b/tlatools/test/tlc2/tool/UserModuleOverrideAnnotationTest.java @@ -0,0 +1,58 @@ +/******************************************************************************* + * Copyright (c) 2019 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.BlockJUnit4ClassRunner; + +import tlc2.output.EC; +import tlc2.tool.liveness.ModelCheckerTestCase; + +@RunWith(BlockJUnit4ClassRunner.class) +public class UserModuleOverrideAnnotationTest extends ModelCheckerTestCase { + + public UserModuleOverrideAnnotationTest() { + super("UserModuleOverrideAnnotation"); + } + + @Test + public void testSpec() { + recorder.recorded(EC.TLC_MODULE_VALUE_JAVA_METHOD_OVERRIDE_MODULE_MISMATCH); + recorder.recorded(EC.TLC_MODULE_VALUE_JAVA_METHOD_OVERRIDE_IDENTIFIER_MISMATCH); + recorder.recorded(EC.TLC_MODULE_VALUE_JAVA_METHOD_OVERRIDE_MISMATCH); + + // ModelChecker has finished and generated the expected amount of states + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "2", "1", "0")); + assertFalse(recorder.recorded(EC.GENERAL)); + + assertZeroUncovered(); + } +} diff --git a/tlatools/test/tlc2/tool/UserModuleOverrideFromJarTest.java b/tlatools/test/tlc2/tool/UserModuleOverrideFromJarTest.java new file mode 100644 index 0000000000000000000000000000000000000000..48122f345fb66c16c8c9616988169d84c4f3c43f --- /dev/null +++ b/tlatools/test/tlc2/tool/UserModuleOverrideFromJarTest.java @@ -0,0 +1,81 @@ +/******************************************************************************* + * Copyright (c) 2016 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.util.Collections; +import java.util.Comparator; +import java.util.List; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.BlockJUnit4ClassRunner; + +import tlc2.output.EC; +import tlc2.tool.liveness.ModelCheckerTestCase; + +@RunWith(BlockJUnit4ClassRunner.class) +public class UserModuleOverrideFromJarTest extends ModelCheckerTestCase { + + public UserModuleOverrideFromJarTest() { + // This test only works when executed from customBuild.xml because it requires an extra classpath setting. + super("UserModuleOverrideBase"); + } + + @Test + public void testSpec() { + final List<String[]> mismatches = recorder.getRecordAsStringArray(EC.TLC_MODULE_VALUE_JAVA_METHOD_OVERRIDE_MISMATCH); + assertEquals(2, mismatches.size()); + Collections.sort(mismatches, new Comparator<String[]>() { + @Override + public int compare(String[] o1, String[] o2) { + return o1[0].compareTo(o2[0]); + } + }); + + // arity mismatch + String[] strs = mismatches.get(0); + assertEquals("Get2", strs[0]); + assertTrue(strs[1].endsWith("UserModuleOverrideFromJar.class")); // This contains a system dependent path. + assertEquals("<Java Method: public static tlc2.value.impl.Value UserModuleOverrideFromJar.Get2(tlc2.value.impl.Value)>", strs[2]); + + // name mismatch (no such operator) + strs = mismatches.get(1); + assertEquals("Get3", strs[0]); + assertTrue(strs[1].endsWith("UserModuleOverrideFromJar.class")); + assertEquals("<Java Method: public static tlc2.value.impl.Value UserModuleOverrideFromJar.Get3()>", strs[2]); + + // ModelChecker has finished and generated the expected amount of states + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "2", "1", "0")); + assertFalse(recorder.recorded(EC.GENERAL)); + + assertZeroUncovered(); + } +} diff --git a/tlatools/test/tlc2/tool/UserModuleOverrideTest.java b/tlatools/test/tlc2/tool/UserModuleOverrideTest.java new file mode 100644 index 0000000000000000000000000000000000000000..63c672bf1d6d1b575085507c5be85b9dc98b32f7 --- /dev/null +++ b/tlatools/test/tlc2/tool/UserModuleOverrideTest.java @@ -0,0 +1,80 @@ +/******************************************************************************* + * Copyright (c) 2016 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.util.Collections; +import java.util.Comparator; +import java.util.List; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.BlockJUnit4ClassRunner; + +import tlc2.output.EC; +import tlc2.tool.liveness.ModelCheckerTestCase; + +@RunWith(BlockJUnit4ClassRunner.class) +public class UserModuleOverrideTest extends ModelCheckerTestCase { + + public UserModuleOverrideTest() { + super("UserModuleOverride"); + } + + @Test + public void testSpec() { + final List<String[]> mismatches = recorder.getRecordAsStringArray(EC.TLC_MODULE_VALUE_JAVA_METHOD_OVERRIDE_MISMATCH); + assertEquals(2, mismatches.size()); + Collections.sort(mismatches, new Comparator<String[]>() { + @Override + public int compare(String[] o1, String[] o2) { + return o1[0].compareTo(o2[0]); + } + }); + + // arity mismatch + String[] strs = mismatches.get(0); + assertEquals("Get2", strs[0]); + assertTrue(strs[1].endsWith("UserModuleOverride.class")); // This contains a system dependent path. + assertEquals("<Java Method: public static tlc2.value.impl.Value UserModuleOverride.Get2(tlc2.value.impl.Value)>", strs[2]); + + // name mismatch (no such operator) + strs = mismatches.get(1); + assertEquals("Get3", strs[0]); + assertTrue(strs[1].endsWith("UserModuleOverride.class")); + assertEquals("<Java Method: public static tlc2.value.impl.Value UserModuleOverride.Get3()>", strs[2]); + + // ModelChecker has finished and generated the expected amount of states + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "2", "1", "0")); + assertFalse(recorder.recorded(EC.GENERAL)); + + assertZeroUncovered(); + } +} diff --git a/tlatools/test/tlc2/tool/ViewMapTest.java b/tlatools/test/tlc2/tool/ViewMapTest.java new file mode 100644 index 0000000000000000000000000000000000000000..c2cc3934953326351cdb319b778e3becacfd5e21 --- /dev/null +++ b/tlatools/test/tlc2/tool/ViewMapTest.java @@ -0,0 +1,68 @@ +/******************************************************************************* + * Copyright (c) 2017 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.Test; + +import tlc2.output.EC; +import tlc2.output.EC.ExitStatus; +import tlc2.tool.liveness.ModelCheckerTestCase; + +public class ViewMapTest extends ModelCheckerTestCase { + + public ViewMapTest() { + super("ViewMap", new String[] { "-view" }, ExitStatus.VIOLATION_SAFETY); + } + + @Test + public void testSpec() { + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertFalse(recorder.recorded(EC.GENERAL)); + assertFalse(recorder.recorded(EC.TLC_BUG)); + + assertTrue(recorder.recorded(EC.TLC_BEHAVIOR_UP_TO_THIS_POINT)); + + final List<String> expectedTrace = new ArrayList<String>(8); + expectedTrace.add("/\\ buffer = <<>>\n/\\ waitset = {}"); + expectedTrace.add("/\\ buffer = <<>>\n/\\ waitset = {c1}"); + expectedTrace.add("/\\ buffer = <<>>\n/\\ waitset = {c1, c2}"); + expectedTrace.add("/\\ buffer = <<\"d\">>\n/\\ waitset = {c2}"); + + expectedTrace.add("/\\ buffer = <<\"d\">>\n/\\ waitset = {c2, p1}"); + expectedTrace.add("/\\ buffer = << >>\n/\\ waitset = {p1}"); + expectedTrace.add("/\\ buffer = << >>\n/\\ waitset = {c1, p1}"); + expectedTrace.add("/\\ buffer = << >>\n/\\ waitset = {c1, c2, p1}"); + assertTraceWith(recorder.getRecords(EC.TLC_STATE_PRINT2), expectedTrace); + + assertUncovered("line 91, col 60 to line 91, col 73 of module ViewMap: 0"); + } +} diff --git a/tlatools/test/tlc2/tool/checkpoint/CheckpointOnViolationTest.java b/tlatools/test/tlc2/tool/checkpoint/CheckpointOnViolationTest.java new file mode 100644 index 0000000000000000000000000000000000000000..0415bab18a7e6c34f45f78e98395ee84a30f49c2 --- /dev/null +++ b/tlatools/test/tlc2/tool/checkpoint/CheckpointOnViolationTest.java @@ -0,0 +1,70 @@ +/******************************************************************************* + * Copyright (c) 2019 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.checkpoint; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.output.EC; +import tlc2.output.EC.ExitStatus; +import tlc2.tool.liveness.ModelCheckerTestCase; + +public class CheckpointOnViolationTest extends ModelCheckerTestCase { + + public CheckpointOnViolationTest() { + super("DieHard", ExitStatus.VIOLATION_SAFETY); + } + + @Test + public void testSpec() { + // ModelChecker has finished and generated the expected amount of states. + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertFalse(recorder.recorded(EC.GENERAL)); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "252", "54", "11")); + + // Check the violation + assertTrue(recorder.recorded(EC.TLC_STATE_PRINT2)); + assertEquals(7, recorder.getRecords(EC.TLC_STATE_PRINT2).size()); + + // Check that a checkpoint has been taken. + assertTrue(recorder.recorded(EC.TLC_CHECKPOINT_START)); + assertTrue(recorder.recorded(EC.TLC_CHECKPOINT_END)); + + assertZeroUncovered(); + } + + @Override + protected int doCheckpoint() { + // Request checkpoints but make it highly unlike for the test to ever create a + // checkpoint because the checkpoint interval was exceeded. We want to test + // TLC's functionality to take a checkpoint when time-bound model-checking is on + // and/or a violation has been found. + return (Integer.MAX_VALUE / 60000); + } +} diff --git a/tlatools/test/tlc2/tool/checkpoint/CheckpointWhenTimeBoundTest.java b/tlatools/test/tlc2/tool/checkpoint/CheckpointWhenTimeBoundTest.java new file mode 100644 index 0000000000000000000000000000000000000000..a9142f70501d65c41c51a499dfbd259b6b051843 --- /dev/null +++ b/tlatools/test/tlc2/tool/checkpoint/CheckpointWhenTimeBoundTest.java @@ -0,0 +1,73 @@ +/******************************************************************************* + * Copyright (c) 2019 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.checkpoint; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.TLC; +import tlc2.output.EC; +import tlc2.tool.liveness.ModelCheckerTestCase; + +public class CheckpointWhenTimeBoundTest extends ModelCheckerTestCase { + + public CheckpointWhenTimeBoundTest() { + super("InfiniteStateSpace", "checkpoint"); + } + + @Override + public void setUp() { + System.setProperty(TLC.class.getName() + ".stopAfter", "5"); // five seconds + super.setUp(); + } + + @Test + public void testSpec() { + // ModelChecker has finished and generated the expected amount of states. + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertFalse(recorder.recorded(EC.GENERAL)); + + assertFalse(recorder.recorded(EC.TLC_STATE_PRINT1)); + assertFalse(recorder.recorded(EC.TLC_STATE_PRINT2)); + + // Check that a checkpoint has been taken. + assertTrue(recorder.recorded(EC.TLC_CHECKPOINT_START)); + assertTrue(recorder.recorded(EC.TLC_CHECKPOINT_END)); + + assertZeroUncovered(); + } + + @Override + protected int doCheckpoint() { + // Request checkpoints but make it highly unlike for the test to ever create a + // checkpoint because the checkpoint interval was exceeded. We want to test + // TLC's functionality to take a checkpoint when time-bound model-checking is on + // and/or a violation has been found. + return (Integer.MAX_VALUE / 60000); + } +} diff --git a/tlatools/test/tlc2/tool/coverage/ACoverageTest.java b/tlatools/test/tlc2/tool/coverage/ACoverageTest.java new file mode 100644 index 0000000000000000000000000000000000000000..1c09fcec324f1e276e1ea62c2c63c8c9ed072466 --- /dev/null +++ b/tlatools/test/tlc2/tool/coverage/ACoverageTest.java @@ -0,0 +1,79 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * loki der quaeler - initial API and implementation + * Markus Alexander Kuppe + ******************************************************************************/ +package tlc2.tool.coverage; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.output.EC; + +public class ACoverageTest extends AbstractCoverageTest { + + public ACoverageTest () { + super("A"); + } + + @Test + public void testSpec () { + // ModelChecker has finished and generated the expected amount of states + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "2")); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "7", "3", "0")); + + // No 'general' errors recorded + assertFalse(recorder.recorded(EC.GENERAL)); + + assertFalse(recorder.recorded(EC.TLC_COVERAGE_MISMATCH)); + assertCoverage("<Init line 7, col 1 to line 7, col 4 of module A>: 1:1\n" + + " line 7, col 12 to line 7, col 16 of module A: 1\n" + + " line 8, col 12 to line 8, col 21 of module A: 1\n" + + " |line 5, col 11 to line 5, col 49 of module A: 1\n" + + " ||line 5, col 31 to line 5, col 49 of module A: 131072\n" + + " ||line 5, col 20 to line 5, col 27 of module A: 1\n" + // This used to be a level deeper and value 131072 + " |line 8, col 16 to line 8, col 20 of module A: 1\n" + + "<A line 13, col 1 to line 13, col 4 of module A>: 1:3\n" + + " line 13, col 9 to line 13, col 19 of module A: 3\n" + + " |line 13, col 14 to line 13, col 19 of module A: 3\n" + + " ||line 11, col 11 to line 11, col 61 of module A: 3\n" + + " |||line 11, col 31 to line 11, col 61 of module A: 30\n" + + " ||||line 11, col 57 to line 11, col 61 of module A: 162\n" + + " ||||line 11, col 41 to line 11, col 52 of module A: 30\n" + + " |||line 11, col 24 to line 11, col 27 of module A: 3\n" + + " ||line 13, col 18 to line 13, col 18 of module A: 3\n" + + "<B line 15, col 1 to line 15, col 4 of module A>: 1:3\n" + + " line 15, col 9 to line 15, col 19 of module A: 3\n" + + " |line 15, col 14 to line 15, col 19 of module A: 3\n" + + " ||line 11, col 11 to line 11, col 61 of module A: 3\n" + + " |||line 11, col 31 to line 11, col 61 of module A: 9\n" + + " ||||line 11, col 57 to line 11, col 61 of module A: 15\n" + + " ||||line 11, col 41 to line 11, col 52 of module A: 9\n" + + " |||line 11, col 24 to line 11, col 27 of module A: 3\n" + + " ||line 15, col 18 to line 15, col 18 of module A: 3"); + } +} diff --git a/tlatools/test/tlc2/tool/coverage/AbstractCoverageTest.java b/tlatools/test/tlc2/tool/coverage/AbstractCoverageTest.java new file mode 100644 index 0000000000000000000000000000000000000000..1d733ef19520f486bef631d553a12ec2d6546732 --- /dev/null +++ b/tlatools/test/tlc2/tool/coverage/AbstractCoverageTest.java @@ -0,0 +1,36 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * loki der quaeler - initial API and implementation + * Markus Alexander Kuppe + ******************************************************************************/ +package tlc2.tool.coverage; + +import tlc2.tool.liveness.ModelCheckerTestCase; + +public abstract class AbstractCoverageTest extends ModelCheckerTestCase { + + public AbstractCoverageTest(String spec) { + super(spec, "coverage", new String[] {"-coverage", "9999"}); // To not interfere with testing, 9999 to make sure only final coverage is reported. + } +} diff --git a/tlatools/test/tlc2/tool/coverage/BCoverageTest.java b/tlatools/test/tlc2/tool/coverage/BCoverageTest.java new file mode 100644 index 0000000000000000000000000000000000000000..eb1386c47d105527daee3494b0911e3716f49556 --- /dev/null +++ b/tlatools/test/tlc2/tool/coverage/BCoverageTest.java @@ -0,0 +1,60 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * loki der quaeler - initial API and implementation + * Markus Alexander Kuppe + ******************************************************************************/ +package tlc2.tool.coverage; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.output.EC; + +public class BCoverageTest extends AbstractCoverageTest { + + public BCoverageTest () { + super("B"); + } + + @Test + public void testSpec () { + // ModelChecker has finished and generated the expected amount of states + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "2")); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "5", "2", "0")); + + // No 'general' errors recorded + assertFalse(recorder.recorded(EC.GENERAL)); + + assertFalse(recorder.recorded(EC.TLC_COVERAGE_MISMATCH)); + assertCoverage("<Init line 4, col 1 to line 4, col 4 of module B>: 1:1\n" + + " line 4, col 9 to line 4, col 17 of module B: 1\n" + + "<A line 8, col 1 to line 8, col 1 of module B>: 1:2\n" + + " line 8, col 6 to line 8, col 19 of module B: 2\n" + + "<B line 10, col 1 to line 10, col 1 of module B>: 0:2\n" + + " line 10, col 6 to line 10, col 19 of module B: 2"); + } +} diff --git a/tlatools/test/tlc2/tool/coverage/CCoverageTest.java b/tlatools/test/tlc2/tool/coverage/CCoverageTest.java new file mode 100644 index 0000000000000000000000000000000000000000..b5a40b16da90067cca0c51b1666fbbb5e01d9c1a --- /dev/null +++ b/tlatools/test/tlc2/tool/coverage/CCoverageTest.java @@ -0,0 +1,125 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * loki der quaeler - initial API and implementation + * Markus Alexander Kuppe + ******************************************************************************/ +package tlc2.tool.coverage; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.output.EC; + +public class CCoverageTest extends AbstractCoverageTest { + + public CCoverageTest () { + super("C"); + } + + @Test + public void testSpec () { + // ModelChecker has finished and generated the expected amount of states + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "17")); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "253", "20", "0")); + + // No 'general' errors recorded + assertFalse(recorder.recorded(EC.GENERAL)); + + assertFalse(recorder.recorded(EC.TLC_COVERAGE_MISMATCH)); + assertCoverage("<Init line 14, col 1 to line 14, col 4 of module C>: 3:3\n" + + " line 14, col 12 to line 14, col 21 of module C: 1\n" + + " line 15, col 12 to line 15, col 16 of module C: 3\n" + + "<A line 17, col 1 to line 17, col 1 of module C>: 16:20\n" + + " line 17, col 9 to line 17, col 17 of module C: 40\n" + + " |line 17, col 9 to line 17, col 9 of module C: 20\n" + + " |line 17, col 15 to line 17, col 17 of module C: 20\n" + + " line 18, col 9 to line 18, col 17 of module C: 40\n" + + " |line 18, col 9 to line 18, col 9 of module C: 20\n" + + " |line 18, col 15 to line 18, col 17 of module C: 20\n" + + " line 19, col 9 to line 19, col 17 of module C: 20\n" + + " line 20, col 9 to line 20, col 19 of module C: 20\n" + + "<B line 22, col 1 to line 22, col 1 of module C>: 0:20\n" + + " line 22, col 9 to line 22, col 17 of module C: 40\n" + + " |line 22, col 9 to line 22, col 9 of module C: 20\n" + + " |line 22, col 15 to line 22, col 17 of module C: 20\n" + + " line 12, col 30 to line 12, col 48 of module C: 640\n" + + " line 12, col 19 to line 12, col 26 of module C: 20\n" + + " line 23, col 12 to line 23, col 15 of module C: 20\n" + + " line 24, col 9 to line 24, col 25 of module C: 20\n" + + "<C line 26, col 1 to line 26, col 1 of module C>: 0:0\n" + + " line 26, col 9 to line 26, col 14 of module C: 20\n" + + " line 27, col 9 to line 27, col 17 of module C: 0\n" + + " line 28, col 9 to line 28, col 18 of module C: 0\n" + + "<D line 30, col 1 to line 30, col 1 of module C>: 1:210\n" + + " line 30, col 6 to line 30, col 16 of module C: 210\n" + + " |line 30, col 13 to line 30, col 16 of module C: 20\n" + + " line 30, col 21 to line 30, col 31 of module C: 210\n" + + "<U1 line 32, col 1 to line 32, col 2 of module C>: 0:0\n" + + " line 32, col 7 to line 32, col 11 of module C: 20\n" + + " line 32, col 16 to line 32, col 29 of module C: 0\n" + + "<U2 line 34, col 1 to line 34, col 2 of module C>: 0:0\n" + + " line 34, col 7 to line 34, col 11 of module C: 20\n" + + " line 34, col 16 to line 34, col 32 of module C: 0\n" + + "<U3 line 36, col 1 to line 36, col 2 of module C>: 0:0\n" + + " line 36, col 7 to line 36, col 11 of module C: 20\n" + + " line 36, col 16 to line 36, col 26 of module C: 0\n" + + " line 36, col 31 to line 36, col 41 of module C: 0\n" + + "<Inv line 48, col 1 to line 48, col 3 of module C>\n" + + " line 48, col 8 to line 54, col 26 of module C: 21\n" + + " |line 48, col 11 to line 48, col 19 of module C: 21\n" + + " |line 49, col 11 to line 49, col 19 of module C: 21\n" + + " |line 50, col 11 to line 50, col 22 of module C: 21\n" + + " ||line 46, col 17 to line 46, col 64 of module C: 21\n" + + " |||line 46, col 42 to line 46, col 64 of module C: 5376\n" + + " ||||line 46, col 55 to line 46, col 64 of module C: 21504\n" + + " ||||line 46, col 51 to line 46, col 51 of module C: 5376\n" + + " |||line 46, col 26 to line 46, col 38 of module C: 21:26880\n" + + " |line 51, col 23 to line 51, col 26 of module C: 21\n" + + " |line 52, col 14 to line 54, col 26 of module C: 21\n" + + " ||line 52, col 17 to line 52, col 27 of module C: 21\n" + + " ||line 53, col 17 to line 53, col 40 of module C: 21\n" + + " |||line 53, col 32 to line 53, col 40 of module C: 105\n" + + " |||line 53, col 26 to line 53, col 29 of module C: 21\n" + + " ||line 54, col 17 to line 54, col 26 of module C: 21\n" + + "<Inv2 line 56, col 1 to line 56, col 4 of module C>\n" + + " line 56, col 9 to line 62, col 27 of module C: 21\n" + + " |line 56, col 12 to line 56, col 20 of module C: 21\n" + + " |line 57, col 12 to line 57, col 20 of module C: 21\n" + + " |line 58, col 12 to line 58, col 23 of module C: 21\n" + + " ||line 46, col 17 to line 46, col 64 of module C: 21\n" + + " |||line 46, col 42 to line 46, col 64 of module C: 10752\n" + + " ||||line 46, col 55 to line 46, col 64 of module C: 48384\n" + + " ||||line 46, col 51 to line 46, col 51 of module C: 10752\n" + + " |||line 46, col 26 to line 46, col 38 of module C: 21:59136\n" + + " |line 59, col 24 to line 59, col 27 of module C: 21\n" + + " |line 60, col 15 to line 62, col 27 of module C: 21\n" + + " ||line 60, col 15 to line 60, col 28 of module C: 21\n" + + " ||line 61, col 17 to line 62, col 27 of module C: 21\n" + + " |||line 61, col 32 to line 62, col 27 of module C: 105\n" + + " |||line 61, col 26 to line 61, col 29 of module C: 21"); + } +} diff --git a/tlatools/test/tlc2/tool/coverage/CoverageStatisticsTest.java b/tlatools/test/tlc2/tool/coverage/CoverageStatisticsTest.java new file mode 100644 index 0000000000000000000000000000000000000000..b6926887f6f2f03166f1f29e5d83150c4bbc6ac5 --- /dev/null +++ b/tlatools/test/tlc2/tool/coverage/CoverageStatisticsTest.java @@ -0,0 +1,104 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * loki der quaeler - initial API and implementation + * Markus Alexander Kuppe + ******************************************************************************/ +package tlc2.tool.coverage; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.output.EC; + +public class CoverageStatisticsTest extends AbstractCoverageTest { + + public CoverageStatisticsTest () { + super("CoverageStatistics"); + } + + @Test + public void testSpec () { + // ModelChecker has finished and generated the expected amount of states + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "17")); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "98", "19", "0")); + + // No 'general' errors recorded + assertFalse(recorder.recorded(EC.GENERAL)); + + assertCoverage("<Init line 12, col 1 to line 12, col 4 of module CoverageStatistics>: 3:3\n" + + " line 12, col 12 to line 12, col 21 of module CoverageStatistics: 1\n" + + " line 13, col 12 to line 13, col 16 of module CoverageStatistics: 3\n" + + "<A line 15, col 1 to line 15, col 1 of module CoverageStatistics>: 16:19\n" + + " line 15, col 9 to line 15, col 17 of module CoverageStatistics: 38\n" + + " |line 15, col 9 to line 15, col 9 of module CoverageStatistics: 19\n" + + " |line 15, col 15 to line 15, col 17 of module CoverageStatistics: 19\n" + + " line 16, col 9 to line 16, col 17 of module CoverageStatistics: 38\n" + + " |line 16, col 9 to line 16, col 9 of module CoverageStatistics: 19\n" + + " |line 16, col 15 to line 16, col 17 of module CoverageStatistics: 19\n" + + " line 17, col 9 to line 17, col 17 of module CoverageStatistics: 19\n" + + " line 18, col 9 to line 18, col 19 of module CoverageStatistics: 19\n" + + "<B line 20, col 1 to line 20, col 1 of module CoverageStatistics>: 0:19\n" + + " line 20, col 9 to line 20, col 17 of module CoverageStatistics: 38\n" + + " |line 20, col 9 to line 20, col 9 of module CoverageStatistics: 19\n" + + " |line 20, col 15 to line 20, col 17 of module CoverageStatistics: 19\n" + + " line 21, col 9 to line 21, col 25 of module CoverageStatistics: 19\n" + + "<C line 26, col 1 to line 26, col 1 of module CoverageStatistics>: 0:0\n" + + " line 26, col 9 to line 26, col 14 of module CoverageStatistics: 19\n" + + " line 27, col 9 to line 27, col 17 of module CoverageStatistics: 0\n" + + " line 28, col 9 to line 28, col 18 of module CoverageStatistics: 0\n" + + "<U1 line 30, col 1 to line 30, col 2 of module CoverageStatistics>: 0:0\n" + + " line 30, col 7 to line 30, col 11 of module CoverageStatistics: 19\n" + + " line 30, col 16 to line 30, col 29 of module CoverageStatistics: 0\n" + + "<U2 line 32, col 1 to line 32, col 2 of module CoverageStatistics>: 0:0\n" + + " line 32, col 7 to line 32, col 11 of module CoverageStatistics: 19\n" + + " line 32, col 16 to line 32, col 32 of module CoverageStatistics: 0\n" + + "<U3 line 34, col 1 to line 34, col 2 of module CoverageStatistics>: 0:0\n" + + " line 34, col 7 to line 34, col 11 of module CoverageStatistics: 19\n" + + " line 34, col 16 to line 34, col 26 of module CoverageStatistics: 0\n" + + " line 34, col 31 to line 34, col 41 of module CoverageStatistics: 0\n" + + "<U4 line 36, col 1 to line 36, col 2 of module CoverageStatistics>: 0:0\n" + + " line 36, col 7 to line 36, col 11 of module CoverageStatistics: 19\n" + + " line 36, col 16 to line 36, col 26 of module CoverageStatistics: 0\n" + + " line 36, col 31 to line 36, col 41 of module CoverageStatistics: 0\n" + + "<UC1 line 38, col 1 to line 38, col 3 of module CoverageStatistics>: 0:19\n" + + " line 38, col 8 to line 38, col 16 of module CoverageStatistics: 38\n" + + " |line 38, col 8 to line 38, col 8 of module CoverageStatistics: 19\n" + + " |line 38, col 14 to line 38, col 16 of module CoverageStatistics: 19\n" + + " line 38, col 21 to line 38, col 37 of module CoverageStatistics: 19\n" + + "<UC2 line 40, col 1 to line 40, col 3 of module CoverageStatistics>: 0:19\n" + + " line 40, col 8 to line 40, col 16 of module CoverageStatistics: 38\n" + + " |line 40, col 8 to line 40, col 8 of module CoverageStatistics: 19\n" + + " |line 40, col 14 to line 40, col 16 of module CoverageStatistics: 19\n" + + " line 40, col 21 to line 40, col 31 of module CoverageStatistics: 19\n" + + " line 40, col 36 to line 40, col 46 of module CoverageStatistics: 19\n" + + "<UC3 line 42, col 1 to line 42, col 3 of module CoverageStatistics>: 0:19\n" + + " line 42, col 8 to line 42, col 16 of module CoverageStatistics: 38\n" + + " |line 42, col 8 to line 42, col 8 of module CoverageStatistics: 19\n" + + " |line 42, col 14 to line 42, col 16 of module CoverageStatistics: 19\n" + + " line 42, col 21 to line 42, col 34 of module CoverageStatistics: 19"); + } +} diff --git a/tlatools/test/tlc2/tool/coverage/DCoverageTest.java b/tlatools/test/tlc2/tool/coverage/DCoverageTest.java new file mode 100644 index 0000000000000000000000000000000000000000..205fde171dfa1e4de2fedf655c8fe90e9b1752eb --- /dev/null +++ b/tlatools/test/tlc2/tool/coverage/DCoverageTest.java @@ -0,0 +1,68 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * loki der quaeler - initial API and implementation + * Markus Alexander Kuppe + ******************************************************************************/ +package tlc2.tool.coverage; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.output.EC; + +public class DCoverageTest extends AbstractCoverageTest { + + public DCoverageTest () { + super("D"); + } + + @Test + public void testSpec () { + // ModelChecker has finished and generated the expected amount of states + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "2")); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "7", "3", "0")); + + // No 'general' errors recorded + assertFalse(recorder.recorded(EC.GENERAL)); + + assertFalse(recorder.recorded(EC.TLC_COVERAGE_MISMATCH)); + assertCoverage("<Init line 5, col 1 to line 5, col 4 of module D>: 1:1\n" + + " line 5, col 9 to line 5, col 13 of module D: 1\n" + + "<A line 11, col 1 to line 11, col 1 of module D>: 1:3\n" + + " line 11, col 6 to line 11, col 17 of module D: 3\n" + + " |line 11, col 11 to line 11, col 17 of module D: 3\n" + + " ||line 9, col 12 to line 9, col 47 of module D: 9\n" + + " |||line 9, col 15 to line 9, col 19 of module D: 9\n" + + " |||line 9, col 33 to line 9, col 47 of module D: 6\n" + + "<B line 13, col 1 to line 13, col 1 of module D>: 1:3\n" + + " line 13, col 6 to line 13, col 17 of module D: 3\n" + + " |line 13, col 11 to line 13, col 17 of module D: 3\n" + + " ||line 9, col 12 to line 9, col 47 of module D: 27\n" + + " |||line 9, col 15 to line 9, col 19 of module D: 27\n" + + " |||line 9, col 33 to line 9, col 47 of module D: 24"); + } +} diff --git a/tlatools/test/tlc2/tool/coverage/ECoverageTest.java b/tlatools/test/tlc2/tool/coverage/ECoverageTest.java new file mode 100644 index 0000000000000000000000000000000000000000..944dbaa32237592ee8cd9a49cabfdf9095adeaae --- /dev/null +++ b/tlatools/test/tlc2/tool/coverage/ECoverageTest.java @@ -0,0 +1,59 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * loki der quaeler - initial API and implementation + * Markus Alexander Kuppe + ******************************************************************************/ +package tlc2.tool.coverage; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.output.EC; + +public class ECoverageTest extends AbstractCoverageTest { + + public ECoverageTest () { + super("E"); + } + + @Test + public void testSpec () { + // ModelChecker has finished and generated the expected amount of states + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "2")); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "73", "9", "0")); + + // No 'general' errors recorded + assertFalse(recorder.recorded(EC.GENERAL)); + + assertFalse(recorder.recorded(EC.TLC_COVERAGE_MISMATCH)); + assertCoverage("<Init line 10, col 1 to line 10, col 4 of module E>: 1:1\n" + + " line 10, col 9 to line 10, col 13 of module E: 1\n" + + "<Next line 12, col 1 to line 12, col 4 of module E>: 8:72\n" + + " line 12, col 9 to line 12, col 20 of module E: 72\n" + + " |line 12, col 16 to line 12, col 20 of module E: 9"); + } +} diff --git a/tlatools/test/tlc2/tool/coverage/FCoverageTest.java b/tlatools/test/tlc2/tool/coverage/FCoverageTest.java new file mode 100644 index 0000000000000000000000000000000000000000..25c48c58cd10d58693b7f525d27222fbaaeccd6a --- /dev/null +++ b/tlatools/test/tlc2/tool/coverage/FCoverageTest.java @@ -0,0 +1,74 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * loki der quaeler - initial API and implementation + * Markus Alexander Kuppe + ******************************************************************************/ +package tlc2.tool.coverage; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.output.EC; + +public class FCoverageTest extends AbstractCoverageTest { + + public FCoverageTest () { + super("F"); + } + + @Test + public void testSpec () { + // ModelChecker has finished and generated the expected amount of states + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "1")); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "4", "2", "0")); + + // No 'general' errors recorded + assertFalse(recorder.recorded(EC.GENERAL)); + + assertFalse(recorder.recorded(EC.TLC_COVERAGE_MISMATCH)); + assertCoverage("<Init line 8, col 1 to line 8, col 4 of module F>: 2:2\n" + + " line 8, col 9 to line 8, col 79 of module F: 2\n" + + " |line 8, col 15 to line 8, col 79 of module F: 1\n" + + " ||line 6, col 25 to line 6, col 56 of module F: 1:3\n" + + " |||line 6, col 37 to line 6, col 54 of module F: 5\n" + + " ||||line 6, col 37 to line 6, col 40 of module F: 5\n" + + " ||||line 6, col 45 to line 6, col 54 of module F: 4\n" + + " |||||line 6, col 47 to line 6, col 47 of module F: 4\n" + + " |||||line 6, col 50 to line 6, col 53 of module F: 2\n" + + " |||||line 8, col 63 to line 8, col 78 of module F: 4\n" + + " ||||||line 8, col 63 to line 8, col 73 of module F: 4\n" + + " ||||||line 8, col 78 to line 8, col 78 of module F: 2\n" + + " |||line 6, col 33 to line 6, col 33 of module F: 1\n" + + " ||line 8, col 18 to line 8, col 28 of module F: 1:6\n" + + " ||line 8, col 41 to line 8, col 45 of module F: 5\n" + + " ||line 8, col 63 to line 8, col 78 of module F: 4\n" + + " |||line 8, col 63 to line 8, col 73 of module F: 4\n" + + " |||line 8, col 78 to line 8, col 78 of module F: 2\n" + + "<Next line 10, col 1 to line 10, col 4 of module F>: 0:2\n" + + " line 10, col 9 to line 10, col 19 of module F: 2"); + } +} diff --git a/tlatools/test/tlc2/tool/coverage/GCoverageTest.java b/tlatools/test/tlc2/tool/coverage/GCoverageTest.java new file mode 100644 index 0000000000000000000000000000000000000000..340eeb8a17b325aa7df3b6934aedbab06d04fc95 --- /dev/null +++ b/tlatools/test/tlc2/tool/coverage/GCoverageTest.java @@ -0,0 +1,64 @@ +/******************************************************************************* + * Copyright (c) 2019 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.coverage; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.output.EC; + +public class GCoverageTest extends AbstractCoverageTest { + + public GCoverageTest () { + super("G"); + } + + @Test + public void testSpec () { + // ModelChecker has finished and generated the expected amount of states + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "1")); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "2", "1", "0")); + + // No 'general' errors recorded + assertFalse(recorder.recorded(EC.GENERAL)); + + assertCoverage("<Init line 6, col 1 to line 6, col 4 of module G>: 1:1\n" + + " line 6, col 9 to line 6, col 20 of module G: 1\n" + + "<Next line 8, col 1 to line 8, col 4 of module G>: 0:1\n" + + " line 8, col 12 to line 8, col 25 of module G: 1\n" + + " line 9, col 12 to line 9, col 27 of module G: 2\n" + + " line 10, col 12 to line 10, col 23 of module G: 2\n" + + "<Prop line 12, col 1 to line 12, col 4 of module G>\n" + + " line 12, col 9 to line 12, col 20 of module G: 1\n" + + " |line 8, col 12 to line 8, col 25 of module G: 1\n" + + " |line 9, col 12 to line 9, col 27 of module G: 1\n" + + " |line 10, col 12 to line 10, col 23 of module G: 1"); + assertFalse(recorder.recorded(EC.TLC_COVERAGE_MISMATCH)); + } +} diff --git a/tlatools/test/tlc2/tool/coverage/Github314CoverageTest.java b/tlatools/test/tlc2/tool/coverage/Github314CoverageTest.java new file mode 100644 index 0000000000000000000000000000000000000000..4f8f86f0c87b0e705ae6d82147af14c1d2c7d1d7 --- /dev/null +++ b/tlatools/test/tlc2/tool/coverage/Github314CoverageTest.java @@ -0,0 +1,53 @@ +/******************************************************************************* + * Copyright (c) 2019 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.coverage; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.output.EC; + +public class Github314CoverageTest extends AbstractCoverageTest { + + public Github314CoverageTest () { + super("Github314"); + } + + @Test + public void testSpec () { + // ModelChecker has finished and generated the expected amount of states + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "3")); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "3", "3", "0")); + + // No 'general' errors recorded + assertFalse(recorder.recorded(EC.GENERAL)); + + assertFalse(recorder.recorded(EC.TLC_COVERAGE_MISMATCH)); + } +} diff --git a/tlatools/test/tlc2/tool/coverage/Github377CoverageTest.java b/tlatools/test/tlc2/tool/coverage/Github377CoverageTest.java new file mode 100644 index 0000000000000000000000000000000000000000..d23707671092dd8bc8d71cf08f9c27c65dddc5e9 --- /dev/null +++ b/tlatools/test/tlc2/tool/coverage/Github377CoverageTest.java @@ -0,0 +1,83 @@ +/******************************************************************************* + * Copyright (c) 2019 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.coverage; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.output.EC; + +public class Github377CoverageTest extends AbstractCoverageTest { + + public Github377CoverageTest () { + super("Github377"); + } + + @Test + public void testSpec () { + // ModelChecker has finished and generated the expected amount of states + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "1")); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "2", "1", "0")); + + // No 'general' errors recorded + assertFalse(recorder.recorded(EC.GENERAL)); + + assertCoverage("<Action line 5, col 9 to line 5, col 16 of module Github377>: 1:1\n" + + " line 5, col 9 to line 5, col 16 of module Github377: 1\n" + + "<Action line 5, col 24 to line 5, col 34 of module Github377>: 0:1\n" + + " line 5, col 24 to line 5, col 34 of module Github377: 1\n" + + "<1Inv line 9, col 1 to line 9, col 4 of module Github377>\n" + + " line 10, col 6 to line 10, col 69 of module Github377: 1\n" + + " line 11, col 5 to line 11, col 13 of module Github377: 1\n" + + "<1InvNonRec line 15, col 1 to line 15, col 10 of module Github377>\n" + + " line 16, col 6 to line 16, col 40 of module Github377: 1\n" + + " line 17, col 5 to line 17, col 13 of module Github377: 1\n" + + "<2Inv line 21, col 1 to line 21, col 4 of module Github377>\n" + + " line 25, col 10 to line 25, col 47 of module Github377: 1\n" + + " line 26, col 7 to line 26, col 11 of module Github377: 1\n" + + "<2aInv line 28, col 1 to line 28, col 5 of module Github377>\n" + + " line 32, col 10 to line 33, col 21 of module Github377: 1\n" + + " line 34, col 7 to line 34, col 11 of module Github377: 1\n" + + "<2bInv line 36, col 1 to line 36, col 5 of module Github377>\n" + + " line 40, col 10 to line 40, col 31 of module Github377: 1\n" + + " line 41, col 7 to line 41, col 11 of module Github377: 1\n" + + "<3aInv line 44, col 1 to line 44, col 5 of module Github377>\n" + + " line 48, col 10 to line 50, col 21 of module Github377: 1\n" + + " line 51, col 7 to line 51, col 11 of module Github377: 1\n" + + "<3bInv line 55, col 1 to line 55, col 5 of module Github377>\n" + + " line 58, col 11 to line 58, col 42 of module Github377: 1\n" + + " line 57, col 11 to line 57, col 70 of module Github377: 1\n" + + " line 60, col 13 to line 60, col 31 of module Github377: 1\n" + + " line 61, col 7 to line 61, col 11 of module Github377: 1\n" + + "<4Inv line 66, col 1 to line 66, col 4 of module Github377>\n" + + " line 71, col 11 to line 71, col 18 of module Github377: 1\n" + + " line 72, col 7 to line 72, col 11 of module Github377: 1"); + assertFalse(recorder.recorded(EC.TLC_COVERAGE_MISMATCH)); + } +} diff --git a/tlatools/test/tlc2/tool/coverage/HCoverageTest.java b/tlatools/test/tlc2/tool/coverage/HCoverageTest.java new file mode 100644 index 0000000000000000000000000000000000000000..7dcb347651e6f5779b06e2003bd0c1f940543739 --- /dev/null +++ b/tlatools/test/tlc2/tool/coverage/HCoverageTest.java @@ -0,0 +1,75 @@ +/******************************************************************************* + * Copyright (c) 2019 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.coverage; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.output.EC; + +public class HCoverageTest extends AbstractCoverageTest { + + public HCoverageTest () { + super("H"); + } + + @Test + public void testSpec () { + // ModelChecker has finished and generated the expected amount of states + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "3")); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "26", "6", "0")); + + // No 'general' errors recorded + assertFalse(recorder.recorded(EC.GENERAL)); + + assertCoverage("<Inv line 22, col 1 to line 22, col 3 of module H>: 1:31\n" + + " line 22, col 11 to line 22, col 16 of module H: 1\n" + + " line 23, col 11 to line 23, col 15 of module H: 31\n" + + "<A line 11, col 1 to line 11, col 1 of module H>: 1:6\n" + + " line 11, col 6 to line 11, col 12 of module H: 6\n" + + "<BandC line 13, col 1 to line 13, col 5 of module H (13 13 14 28)>: 2:15\n" + + " line 13, col 16 to line 13, col 26 of module H: 11\n" + + " |line 13, col 16 to line 13, col 16 of module H: 6\n" + + " |line 13, col 22 to line 13, col 26 of module H: 6\n" + + " line 14, col 16 to line 14, col 28 of module H: 15\n" + + " |line 14, col 23 to line 14, col 28 of module H: 5\n" + + "<BandC line 13, col 1 to line 13, col 5 of module H (15 13 16 22)>: 1:3\n" + + " line 15, col 16 to line 15, col 21 of module H: 3\n" + + " |line 15, col 16 to line 15, col 16 of module H: 6\n" + + " line 16, col 16 to line 16, col 22 of module H: 3\n" + + "<DandE line 18, col 1 to line 18, col 5 of module H (18 11 18 27)>: 1:1\n" + + " line 18, col 11 to line 18, col 16 of module H: 7\n" + + " |line 18, col 11 to line 18, col 11 of module H: 6\n" + + " line 18, col 21 to line 18, col 27 of module H: 1\n" + + "<DandE line 18, col 1 to line 18, col 5 of module H (18 34 18 53)>: 0:0\n" + + " line 18, col 34 to line 18, col 40 of module H: 6\n" + + " line 18, col 45 to line 18, col 53 of module H: 0"); + assertFalse(recorder.recorded(EC.TLC_COVERAGE_MISMATCH)); + } +} diff --git a/tlatools/test/tlc2/tool/coverage/ICoverageTest.java b/tlatools/test/tlc2/tool/coverage/ICoverageTest.java new file mode 100644 index 0000000000000000000000000000000000000000..a8dcaf43a062a01d78883fda0614012686635518 --- /dev/null +++ b/tlatools/test/tlc2/tool/coverage/ICoverageTest.java @@ -0,0 +1,66 @@ +/******************************************************************************* + * Copyright (c) 2019 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.coverage; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.output.EC; + +public class ICoverageTest extends AbstractCoverageTest { + + public ICoverageTest () { + super("I"); + } + + @Test + public void testSpec () { + // ModelChecker has finished and generated the expected amount of states + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "1")); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "20", "5", "0")); + + // No 'general' errors recorded + assertFalse(recorder.recorded(EC.GENERAL)); + + assertCoverage("<Action line 11, col 9 to line 11, col 25 of module I>: 5:5\n" + + " line 11, col 9 to line 11, col 25 of module I: 5\n" + + " |line 11, col 15 to line 11, col 25 of module I: 1:6\n" + + "<Action line 11, col 52 to line 11, col 55 of module I>: 0:15\n" + + " line 11, col 52 to line 11, col 52 of module I: 15\n" + + " |line 8, col 1 to line 9, col 22 of module I: 15\n" + + " ||line 9, col 8 to line 9, col 22 of module I: 15\n" + + " ||line 8, col 9 to line 8, col 15 of module I: 15\n" + + " line 11, col 54 to line 11, col 54 of module I: 15\n" + + "<Inv line 13, col 1 to line 13, col 3 of module I>\n" + + " line 13, col 8 to line 13, col 34 of module I: 5\n" + + " |line 13, col 27 to line 13, col 34 of module I: 15\n" + + " |line 13, col 17 to line 13, col 24 of module I: 5"); + assertFalse(recorder.recorded(EC.TLC_COVERAGE_MISMATCH)); + } +} diff --git a/tlatools/test/tlc2/tool/coverage/JCoverageTest.java b/tlatools/test/tlc2/tool/coverage/JCoverageTest.java new file mode 100644 index 0000000000000000000000000000000000000000..0a1bfde26c4517186850d89adf0f48ebba4d46fe --- /dev/null +++ b/tlatools/test/tlc2/tool/coverage/JCoverageTest.java @@ -0,0 +1,65 @@ +/******************************************************************************* + * Copyright (c) 2019 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.coverage; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.output.EC; + +public class JCoverageTest extends AbstractCoverageTest { + + public JCoverageTest () { + super("J"); + } + + @Test + public void testSpec () { + // ModelChecker has finished and generated the expected amount of states + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "2")); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "11", "6", "0")); + + // No 'general' errors recorded + assertFalse(recorder.recorded(EC.GENERAL)); + + assertCoverage("<Init line 5, col 1 to line 5, col 4 of module J>: 5:5\n" + + " line 5, col 12 to line 5, col 28 of module J: 1\n" + + " line 6, col 12 to line 6, col 40 of module J: 5\n" + + "<Next line 12, col 1 to line 12, col 4 of module J>: 1:6\n" + + " line 12, col 12 to line 12, col 22 of module J: 6\n" + + " |line 12, col 17 to line 12, col 22 of module J: 6\n" + + " ||line 9, col 2 to line 10, col 19 of module J: 6\n" + + " |||line 9, col 7 to line 9, col 11 of module J: 6\n" + + " |||line 9, col 16 to line 9, col 40 of module J: 2\n" + + " |||line 10, col 7 to line 10, col 19 of module J: 4\n" + + " ||line 12, col 21 to line 12, col 21 of module J: 6\n" + + " line 13, col 12 to line 13, col 22 of module J: 6"); + assertFalse(recorder.recorded(EC.TLC_COVERAGE_MISMATCH)); + } +} diff --git a/tlatools/test/tlc2/tool/coverage/KCoverageTest.java b/tlatools/test/tlc2/tool/coverage/KCoverageTest.java new file mode 100644 index 0000000000000000000000000000000000000000..1a10a275bbfb7ff32abb2e2c2d2fbe07485c6539 --- /dev/null +++ b/tlatools/test/tlc2/tool/coverage/KCoverageTest.java @@ -0,0 +1,53 @@ +/******************************************************************************* + * Copyright (c) 2019 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.coverage; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.output.EC; + +public class KCoverageTest extends AbstractCoverageTest { + + public KCoverageTest () { + super("K"); + } + + @Test + public void testSpec () { + // ModelChecker has finished and generated the expected amount of states + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "1")); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "2", "1", "0")); + + // No 'general' errors recorded + assertFalse(recorder.recorded(EC.GENERAL)); + + assertFalse(recorder.recorded(EC.TLC_COVERAGE_MISMATCH)); + } +} diff --git a/tlatools/test/tlc2/tool/coverage/LCoverageTest.java b/tlatools/test/tlc2/tool/coverage/LCoverageTest.java new file mode 100644 index 0000000000000000000000000000000000000000..16cb8569cb2aad46741f67b155a8debd747d60d2 --- /dev/null +++ b/tlatools/test/tlc2/tool/coverage/LCoverageTest.java @@ -0,0 +1,53 @@ +/******************************************************************************* + * Copyright (c) 2020 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.coverage; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.output.EC; + +public class LCoverageTest extends AbstractCoverageTest { + + public LCoverageTest () { + super("L"); + } + + @Test + public void testSpec () { + // ModelChecker has finished and generated the expected amount of states + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "1")); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "2", "1", "0")); + + // No 'general' errors recorded + assertFalse(recorder.recorded(EC.GENERAL)); + + assertFalse(recorder.recorded(EC.TLC_COVERAGE_MISMATCH)); + } +} diff --git a/tlatools/test/tlc2/tool/coverage/OpApplNodeWrapperTest.java b/tlatools/test/tlc2/tool/coverage/OpApplNodeWrapperTest.java new file mode 100644 index 0000000000000000000000000000000000000000..29b6f3ff8fe6b785bb704c4b64e341a3ca27b956 --- /dev/null +++ b/tlatools/test/tlc2/tool/coverage/OpApplNodeWrapperTest.java @@ -0,0 +1,187 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.coverage; + +import org.junit.Before; +import org.junit.Test; +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +import tla2sany.parser.SyntaxTreeNode; +import tla2sany.semantic.AbortException; +import tla2sany.semantic.ModuleNode; +import tla2sany.semantic.OpApplNode; +import tla2sany.semantic.SymbolNode; +import tla2sany.xml.SymbolContext; +import tlc2.TLCGlobals; +import util.TestPrintStream; +import util.ToolIO; +import util.UniqueString; + +public class OpApplNodeWrapperTest { + + @Before + public void setup() { + TLCGlobals.coverageInterval = 1; + ToolIO.out = new TestPrintStream(); + } + + @Test + public void testReportCoverage01() { + final OpApplNodeWrapper root = new OpApplNodeWrapper(); + root.report(); + ((TestPrintStream) ToolIO.out).assertEmpty(); + + root.addChild(new OpApplNodeWrapper()); + } + + @Test + public void testReportCoverage02() { + final OpApplNodeWrapper root = new OpApplNodeWrapper(); + root.incInvocations(42); + + root.addChild(getNode(23)); + root.addChild(getNode(24)); + root.addChild(getNode(0)); // Not reported + + root.report(); + ((TestPrintStream) ToolIO.out) + .assertContains(" Unknown location: 42\n" + + " |In module --TLA+ BUILTINS--: 23\n" + + " |In module --TLA+ BUILTINS--: 24"); + } + + @Test + public void testReportCoverage03() { + final OpApplNodeWrapper root = new OpApplNodeWrapper(); + root.incInvocations(42); + + OpApplNodeWrapper childA = getNode(23); + childA.addChild(getNode(546)); + root.addChild(childA); + + OpApplNodeWrapper childB = getNode(24); + root.addChild(childB); + childB.addChild(getNode(0)); // Not reported because 0 + + OpApplNodeWrapper childC = getNode(0); + root.addChild(childC); // Not reported + + childC.addChild(getNode(17)); // Must be reported despite C being 0 + + root.report(); + ((TestPrintStream) ToolIO.out) + .assertContains(" Unknown location: 42\n" + + " |In module --TLA+ BUILTINS--: 23\n" + + " ||In module --TLA+ BUILTINS--: 546\n" + + " |In module --TLA+ BUILTINS--: 24\n" + + " |In module --TLA+ BUILTINS--: 17"); + } + + /* + line 8, col 12 to line 8, col 21 of module A: 1 + |line 5, col 11 to line 5, col 49 of module A: 1 + ||line 5, col 31 to line 5, col 49 of module A: 131072 + ||line 5, col 20 to line 5, col 27 of module A: 131072 + |||line 5, col 27 to line 5, col 27 of module A: 1 + |line 8, col 16 to line 8, col 20 of module A: 1 + */ + @Test + public void testReportCoverage04() { + final OpApplNodeWrapper root = new OpApplNodeWrapper(); + root.incInvocations(1); + + OpApplNodeWrapper childA = getNode(1); + root.addChild(childA); + + childA.addChild(getNode(131072)); + + OpApplNodeWrapper cChildA = getNode(131072); + childA.addChild(cChildA); + + cChildA.addChild(getNode(1)); + + OpApplNodeWrapper childB = getNode(1); + root.addChild(childB); + + root.report(); + ((TestPrintStream) ToolIO.out) + .assertContains(" Unknown location: 1\n" + + " |In module --TLA+ BUILTINS--: 1\n" + + " ||In module --TLA+ BUILTINS--: 131072\n" + + " ||In module --TLA+ BUILTINS--: 131072\n" + + " |||In module --TLA+ BUILTINS--: 1\n" + + " |In module --TLA+ BUILTINS--: 1"); + } + + // It is dummies all the way down... + + private OpApplNodeWrapper getNode(long count) { + final SymbolNode sn = new DummySymbolNode(Long.toString(count)); + final OpApplNode node = new DummyOpApplNode(sn); + return new OpApplNodeWrapper(node, count); + } + + private static class DummySymbolNode extends SymbolNode { + + protected DummySymbolNode(String name) { + super(1, SyntaxTreeNode.nullSTN, UniqueString.uniqueStringOf(name)); + } + + @Override + public int getArity() { + throw new UnsupportedOperationException("not implemented"); + } + + @Override + public boolean isLocal() { + throw new UnsupportedOperationException("not implemented"); + } + + @Override + public boolean match(OpApplNode test, ModuleNode mn) throws AbortException { + throw new UnsupportedOperationException("not implemented"); + } + + @Override + protected Element getSymbolElement(Document doc, SymbolContext context) { + throw new UnsupportedOperationException("not implemented"); + } + + @Override + protected String getNodeRef() { + throw new UnsupportedOperationException("not implemented"); + } + + } + + private static class DummyOpApplNode extends OpApplNode { + + public DummyOpApplNode(SymbolNode sn) { + super(sn); + } + } +} diff --git a/tlatools/test/tlc2/tool/distributed/DieHardDistributedTLCTest.java b/tlatools/test/tlc2/tool/distributed/DieHardDistributedTLCTest.java new file mode 100644 index 0000000000000000000000000000000000000000..19685ccf7866ffb68f52ae3476c8abadbcf9a20a --- /dev/null +++ b/tlatools/test/tlc2/tool/distributed/DieHardDistributedTLCTest.java @@ -0,0 +1,66 @@ +/******************************************************************************* + * Copyright (c) 2015 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ + +package tlc2.tool.distributed; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.Test; + +import tlc2.output.EC; + +public class DieHardDistributedTLCTest extends DistributedTLCTestCase { + + public DieHardDistributedTLCTest() { + super("DieHard", BASE_PATH, new String[] {"-deadlock"}); + } + + @Test + public void testSpec() { + // ModelChecker has finished and generated the expected amount of states + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertFalse(recorder.recorded(EC.GENERAL)); + + // Assert the error trace + assertTrue(recorder.recorded(EC.TLC_BEHAVIOR_UP_TO_THIS_POINT)); + assertTrue(recorder.recorded(EC.TLC_STATE_PRINT2)); + final List<String> expectedTrace = new ArrayList<String>(7); + expectedTrace.add("/\\ action = \"nondet\"\n/\\ smallBucket = 0\n/\\ bigBucket = 0\n/\\ water_to_pour = 0"); + expectedTrace.add("/\\ action = \"fill big\"\n/\\ smallBucket = 0\n/\\ bigBucket = 5\n/\\ water_to_pour = 0"); + expectedTrace.add("/\\ action = \"pour big to small\"\n/\\ smallBucket = 3\n/\\ bigBucket = 2\n/\\ water_to_pour = 3"); + expectedTrace.add("/\\ action = \"empty small\"\n/\\ smallBucket = 0\n/\\ bigBucket = 2\n/\\ water_to_pour = 3"); + expectedTrace.add("/\\ action = \"pour big to small\"\n/\\ smallBucket = 2\n/\\ bigBucket = 0\n/\\ water_to_pour = 2"); + + expectedTrace.add("/\\ action = \"fill big\"\n/\\ smallBucket = 2\n/\\ bigBucket = 5\n/\\ water_to_pour = 2"); + + expectedTrace.add("/\\ action = \"pour big to small\"\n/\\ smallBucket = 3\n/\\ bigBucket = 4\n/\\ water_to_pour = 1"); + assertTraceWith(recorder.getRecords(EC.TLC_STATE_PRINT2), expectedTrace); + } +} diff --git a/tlatools/test/tlc2/tool/distributed/DistributedDoInitFunctorEvalExceptionTest.java b/tlatools/test/tlc2/tool/distributed/DistributedDoInitFunctorEvalExceptionTest.java new file mode 100644 index 0000000000000000000000000000000000000000..b0a80639754da196e541995d412bcd1a4a85d0c0 --- /dev/null +++ b/tlatools/test/tlc2/tool/distributed/DistributedDoInitFunctorEvalExceptionTest.java @@ -0,0 +1,52 @@ +/******************************************************************************* + * Copyright (c) 2016 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ + +package tlc2.tool.distributed; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.output.EC; + +public class DistributedDoInitFunctorEvalExceptionTest extends TLCServerTestCase { + + public DistributedDoInitFunctorEvalExceptionTest() { + super("DoInitFunctorEvalException", "DoInitFunctor"); + } + + @Test + public void testSpec() { + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertTrue(recorder.recorded(EC.TLC_STATS)); + assertFalse(recorder.recorded(EC.GENERAL)); + + assertTrue(recorder.recordedWithStringValues(EC.TLC_INITIAL_STATE, + "TLC expected a boolean value, but did not find one. line 15, col 15 to line 15, col 18 of module DoInitFunctorEvalException", + "x = 1\n")); + } +} diff --git a/tlatools/test/tlc2/tool/distributed/DistributedDoInitFunctorInvariantContinueTest.java b/tlatools/test/tlc2/tool/distributed/DistributedDoInitFunctorInvariantContinueTest.java new file mode 100644 index 0000000000000000000000000000000000000000..b2af922ee3c6544c9c695b1356e937a0d2322d07 --- /dev/null +++ b/tlatools/test/tlc2/tool/distributed/DistributedDoInitFunctorInvariantContinueTest.java @@ -0,0 +1,53 @@ +/******************************************************************************* + * Copyright (c) 2016 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ + +package tlc2.tool.distributed; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.output.EC; +import tlc2.tool.liveness.ModelCheckerTestCase; + +public class DistributedDoInitFunctorInvariantContinueTest extends ModelCheckerTestCase { + + public DistributedDoInitFunctorInvariantContinueTest() { + super("DoInitFunctorInvariant", "DoInitFunctor", new String[] {"-continue"}); + } + + @Test + public void testSpec() { + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "21", "11")); + assertFalse(recorder.recorded(EC.GENERAL)); + + assertTrue(recorder.recordedWithStringValues(EC.TLC_INVARIANT_VIOLATED_INITIAL, "NotNine", "x = 9\n")); + + assertZeroUncovered(); + } +} diff --git a/tlatools/test/tlc2/tool/distributed/DistributedDoInitFunctorInvariantTest.java b/tlatools/test/tlc2/tool/distributed/DistributedDoInitFunctorInvariantTest.java new file mode 100644 index 0000000000000000000000000000000000000000..da25cffc787b1917441045450700c4432b7c1bae --- /dev/null +++ b/tlatools/test/tlc2/tool/distributed/DistributedDoInitFunctorInvariantTest.java @@ -0,0 +1,52 @@ +/******************************************************************************* + * Copyright (c) 2016 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ + +package tlc2.tool.distributed; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.output.EC; +import tlc2.output.EC.ExitStatus; +import tlc2.tool.liveness.ModelCheckerTestCase; + +public class DistributedDoInitFunctorInvariantTest extends ModelCheckerTestCase { + + public DistributedDoInitFunctorInvariantTest() { + super("DoInitFunctorInvariant", "DoInitFunctor", ExitStatus.VIOLATION_SAFETY); + } + + @Test + public void testSpec() { + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertFalse(recorder.recorded(EC.TLC_STATS)); + assertFalse(recorder.recorded(EC.GENERAL)); + + assertTrue(recorder.recordedWithStringValues(EC.TLC_INVARIANT_VIOLATED_INITIAL, "NotNine", "x = 9\n")); + } +} diff --git a/tlatools/test/tlc2/tool/distributed/DistributedTLCTestCase.java b/tlatools/test/tlc2/tool/distributed/DistributedTLCTestCase.java new file mode 100644 index 0000000000000000000000000000000000000000..6155eecdcd5f95e363c2e6c427638ac9d7c97885 --- /dev/null +++ b/tlatools/test/tlc2/tool/distributed/DistributedTLCTestCase.java @@ -0,0 +1,184 @@ +/******************************************************************************* + * Copyright (c) 2016 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.distributed; + +import java.security.Permission; +import java.util.concurrent.CountDownLatch; + +import org.junit.After; +import org.junit.Assert; +import org.junit.Assume; +import org.junit.Before; + +import tlc2.TestMPRecorder; +import tlc2.output.EC; +import tlc2.output.MP; +import tlc2.tool.CommonTestCase; +import tlc2.tool.distributed.fp.DistributedFPSet; + +public abstract class DistributedTLCTestCase extends CommonTestCase { + + protected final String[] arguments; + protected final int fpSets; + + private SecurityManager securityManager; + + public DistributedTLCTestCase(String spec, String path) { + this(spec, path, new String[] {}); + } + + public DistributedTLCTestCase(String spec, String path, String[] args) { + this(spec, path, args, 0); + } + + public DistributedTLCTestCase(String spec, String path, String[] args, int fpSets) { + super(new FilteringTestMPRecorder()); + this.arguments = new String[args.length + 1]; + this.arguments[this.arguments.length - 1] = path + spec; // Add path to additional arguments + System.arraycopy(args, 0, arguments, 0, args.length); + + this.fpSets = fpSets; + } + + @Before + public void setUp() { + Assume.assumeTrue("DistributedTLCTestCase broken with OffHeapDiskFPSet.", false); + // Remember the original security manager and have it replaced with this + // custom one. Contrary to the original, this security manager + // intercepts calls to System.exit(int) and throws an exception instead + // of terminating the VM. This is done to prevent TLCWorker/TLCServer/... + // from terminating the JUnit test itself. To allow the JUnit test to + // later terminate the VM after test completion, we have to reinstate + // the original security manager again (see tearDown). + securityManager = System.getSecurityManager(); + System.setSecurityManager(new NoExitSecurityManager()); + + MP.setRecorder(recorder); + + // Wait for all processes to terminate before the setup itself is done + final CountDownLatch latch = new CountDownLatch(fpSets + 2); + + // Workers + new Thread(new Runnable() { + public void run() { + try { + TLCWorker.main(new String[] { "localhost" }); + } catch (Exception e) { + e.printStackTrace(); + } finally { + latch.countDown(); + } + } + }, "Worker").start(); + + // master + new Thread(new Runnable() { + public void run() { + try { + System.setProperty(TLCServer.class.getName() + ".expectedFPSetCount", Integer.toString(fpSets)); + TLCServer.main(arguments); + } catch (Exception e) { + e.printStackTrace(); + } finally { + latch.countDown(); + } + } + }, "Master").start(); + + // FPSet + if (fpSets > 0) { + new Thread(new Runnable() { + public void run() { + try { + DistributedFPSet.main(new String[] { "localhost" }); + } catch (Exception e) { + e.printStackTrace(); + } finally { + latch.countDown(); + } + } + }, "FPSet").start(); + } + + try { + latch.await(); + } catch (InterruptedException e) { + Assert.fail(); + } + } + + @After + public void tearDown() { + System.setSecurityManager(securityManager); + } + + private static class NoExitSecurityManager extends SecurityManager { + /* (non-Javadoc) + * @see java.lang.SecurityManager#checkPermission(java.security.Permission) + */ + public void checkPermission(Permission perm) { + // allow anything. + } + + /* (non-Javadoc) + * @see java.lang.SecurityManager#checkPermission(java.security.Permission, java.lang.Object) + */ + public void checkPermission(Permission perm, Object context) { + // allow anything. + } + + /* (non-Javadoc) + * @see java.lang.SecurityManager#checkExit(int) + */ + public void checkExit(int status) { + super.checkExit(status); + throw new NoExitException(); + } + } + + @SuppressWarnings("serial") + public static class NoExitException extends RuntimeException { + // Want an easily distinguishable exception. + } + + private static class FilteringTestMPRecorder extends TestMPRecorder { + /* (non-Javadoc) + * @see tlc2.TestMPRecorder#record(int, java.lang.Object[]) + */ + public void record(int code, Object... objects) { + if (EC.GENERAL == code && objects instanceof String[]) { + // GENERAL errors contain the exceptions thrown because of the + // intercepted System.exit(int) calls. Remove them so that + // tests can check for real EC.GENERAL errors. + final String msg = ((String[]) objects)[0]; + if (msg.contains(NoExitException.class.getName())) { + return; + } + } + super.record(code, objects); + } + } +} diff --git a/tlatools/test/tlc2/tool/distributed/EWD840DistributedTLCTest.java b/tlatools/test/tlc2/tool/distributed/EWD840DistributedTLCTest.java new file mode 100644 index 0000000000000000000000000000000000000000..881482d5eb733cb3d5a18ad54c6e8738b51d0b67 --- /dev/null +++ b/tlatools/test/tlc2/tool/distributed/EWD840DistributedTLCTest.java @@ -0,0 +1,52 @@ +/******************************************************************************* + * Copyright (c) 2016 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.distributed; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.io.File; + +import org.junit.Test; + +import tlc2.output.EC; + +public class EWD840DistributedTLCTest extends DistributedTLCTestCase { + + public EWD840DistributedTLCTest() { + super("MC06", BASE_PATH + "EWD840" + File.separator, new String[] {"-deadlock"}); + } + + @Test + public void test() { + // Can we do any assertions here? + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + // Number of generated states differs because of distributed TLC + assertTrue(recorder.recordedWithStringValueAt(EC.TLC_STATS, "114942", 1)); + assertTrue(recorder.recordedWithStringValueAt(EC.TLC_STATS, "0", 2)); + assertFalse(recorder.recorded(EC.GENERAL)); + } +} diff --git a/tlatools/test/tlc2/tool/distributed/EWD840DistributedWithFPSetTLCTest.java b/tlatools/test/tlc2/tool/distributed/EWD840DistributedWithFPSetTLCTest.java new file mode 100644 index 0000000000000000000000000000000000000000..0590e8c7e897ded458cc19e7089d13b56a136a1a --- /dev/null +++ b/tlatools/test/tlc2/tool/distributed/EWD840DistributedWithFPSetTLCTest.java @@ -0,0 +1,52 @@ +/******************************************************************************* + * Copyright (c) 2016 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.distributed; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.io.File; + +import org.junit.Test; + +import tlc2.output.EC; + +public class EWD840DistributedWithFPSetTLCTest extends DistributedTLCTestCase { + + public EWD840DistributedWithFPSetTLCTest() { + super("MC06", BASE_PATH + "EWD840" + File.separator, new String[] {"-deadlock"}, 1); + } + + @Test + public void test() { + // Can we do any assertions here? + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + // Number of generated states differs because of distributed TLC + assertTrue(recorder.recordedWithStringValueAt(EC.TLC_STATS, "114942", 1)); + assertTrue(recorder.recordedWithStringValueAt(EC.TLC_STATS, "0", 2)); + assertFalse(recorder.recorded(EC.GENERAL)); + } +} diff --git a/tlatools/test/tlc2/tool/distributed/TLCServerTestCase.java b/tlatools/test/tlc2/tool/distributed/TLCServerTestCase.java new file mode 100644 index 0000000000000000000000000000000000000000..c714c36cdd0f5d6bb817ec607dd1d2d791cf7f75 --- /dev/null +++ b/tlatools/test/tlc2/tool/distributed/TLCServerTestCase.java @@ -0,0 +1,107 @@ +/******************************************************************************* + * Copyright (c) 2015 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ + +package tlc2.tool.distributed; + +import static org.junit.Assert.fail; + +import java.io.File; +import java.io.IOException; +import java.rmi.RemoteException; + +import org.junit.Before; +import org.junit.runner.RunWith; +import org.junit.runners.BlockJUnit4ClassRunner; + +import tlc2.TLCGlobals; +import tlc2.output.MP; +import tlc2.tool.fp.FPSetConfiguration; +import tlc2.tool.fp.MSBDiskFPSet; +import tlc2.tool.liveness.ModelCheckerTestCase; +import util.ToolIO; + +@RunWith(BlockJUnit4ClassRunner.class) +public abstract class TLCServerTestCase extends ModelCheckerTestCase { + + public TLCServerTestCase(String spec) { + super(spec); + } + + public TLCServerTestCase(String spec, String path) { + super(spec, path); + } + + /* (non-Javadoc) + * @see tlc2.tool.liveness.ModelCheckerTestCase#setUp() + */ + @Before + public void setUp() { + try { + MP.setRecorder(recorder); + + // Never create checkpoints. They distort performance tests and are + // of no use anyway. + TLCGlobals.chkptDuration = 0; + + final String fqSpec = BASE_DIR + TEST_MODEL + path + File.separator + spec; + final FPSetConfiguration fpSetConfig = new DummyFPSetConfig(); + ToolIO.setUserDir(BASE_DIR + File.separator + TEST_MODEL + path + File.separator); + final TLCApp app = new TLCApp(fqSpec, spec, false, null, fpSetConfig); + final TLCServer server = new TLCServer(app); + server.modelCheck(); + //TODO Implement exit status for distributed TLC + actualExitStatus = 0; + } catch (Exception e) { + fail(e.getMessage()); + } + } + + @SuppressWarnings("serial") + private class DummyFPSetConfig extends FPSetConfiguration { + + /* (non-Javadoc) + * @see tlc2.tool.fp.FPSetConfiguration#getImplementation() + */ + public String getImplementation() { + return DummyFPSet.class.getName(); + } + } + + @SuppressWarnings("serial") + protected static class DummyFPSet extends MSBDiskFPSet { + + public DummyFPSet(FPSetConfiguration fpSetConfig) throws RemoteException { + super(fpSetConfig); + } + + /* (non-Javadoc) + * @see tlc2.tool.fp.DiskFPSet#exit(boolean) + */ + public void exit(boolean cleanup) throws IOException { + //ignore because superclass calls System.exit(0) but we want to check our assertions first. + } + } +} diff --git a/tlatools/test/tlc2/tool/liveness/Test057.java b/tlatools/test/tlc2/tool/distributed/TLCSetTest.java similarity index 78% rename from tlatools/test/tlc2/tool/liveness/Test057.java rename to tlatools/test/tlc2/tool/distributed/TLCSetTest.java index bb269600687a95fd40cb06bdfaf67822d987c69e..1ad6bbdc1d1d862b9db915dae18882ee27da3426 100644 --- a/tlatools/test/tlc2/tool/liveness/Test057.java +++ b/tlatools/test/tlc2/tool/distributed/TLCSetTest.java @@ -24,21 +24,28 @@ * Markus Alexander Kuppe - initial API and implementation ******************************************************************************/ -package tlc2.tool.liveness; +package tlc2.tool.distributed; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; import tlc2.output.EC; -public class Test057 extends ModelCheckerTestCase { +/** + * Tests support for TLCSet/TLCGet in TLCServer + */ +public class TLCSetTest extends TLCServerTestCase { - public Test057() { - super("test57"); + public TLCSetTest() { + super("TLCSet"); } - + + @Test public void testSpec() { - // ModelChecker has finished and generated the expected amount of states - assertTrue(recorder.recorded(EC.TLC_MODE_MC)); - assertTrue(recorder.recorded(EC.TLC_FINISHED)); assertTrue(recorder.recorded(EC.TLC_COMPUTING_INIT)); - assertTrue(recorder.recordedWithStringValues(EC.TLC_INVARIANT_VIOLATED_INITIAL, "Invariant1", "x = 1")); + assertTrue(recorder.recorded(EC.TLC_FEATURE_UNSUPPORTED)); + assertFalse(recorder.recorded(EC.GENERAL)); } } diff --git a/tlatools/test/tlc2/tool/distributed/TLCWorkerSmartProxyTest.java b/tlatools/test/tlc2/tool/distributed/TLCWorkerSmartProxyTest.java index a464be2ef48efe8dd477fce5220d6066d7d19ac7..6c0e2564ebb40799bd77b9702841a73ced8738bd 100644 --- a/tlatools/test/tlc2/tool/distributed/TLCWorkerSmartProxyTest.java +++ b/tlatools/test/tlc2/tool/distributed/TLCWorkerSmartProxyTest.java @@ -1,59 +1,72 @@ package tlc2.tool.distributed; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + import java.rmi.RemoteException; +import org.junit.Test; + import tlc2.tool.TLCState; import tlc2.tool.WorkerException; import tlc2.tool.distributed.selector.DummyTLCWorker; import tlc2.tool.queue.DummyTLCState; -import junit.framework.TestCase; -public class TLCWorkerSmartProxyTest extends TestCase { +public class TLCWorkerSmartProxyTest { private static final int ZERO = 0; private static final int ONE = 1; private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE * (1/10); + @Test public void testGetNetworkOverheadMaxStateOne() throws RemoteException, WorkerException { long calculationDuration = Long.MAX_VALUE; assertTrue(doTest(calculationDuration, new DummyTLCState[ONE]) > 0); } + @Test public void testGetNetworkOverheadMinStateOne() throws RemoteException, WorkerException { long calculationDuration = Long.MIN_VALUE; assertTrue(doTest(calculationDuration, new DummyTLCState[ONE]) > 0); } + @Test public void testGetNetworkOverheadZeroStateOne() throws RemoteException, WorkerException { long calculationDuration = ZERO; assertTrue(doTest(calculationDuration, new DummyTLCState[ONE]) > 0); } + @Test public void testGetNetworkOverheadMaxStateZero() throws RemoteException, WorkerException { long calculationDuration = Long.MAX_VALUE; assertTrue(doTest(calculationDuration, new DummyTLCState[ZERO]) > 0); } + @Test public void testGetNetworkOverheadMinStateZero() throws RemoteException, WorkerException { long calculationDuration = Long.MIN_VALUE; assertTrue(doTest(calculationDuration, new DummyTLCState[ZERO]) > 0); } + @Test public void testGetNetworkOverheadZeroStateZero() throws RemoteException, WorkerException { long calculationDuration = ZERO; assertTrue(doTest(calculationDuration, new DummyTLCState[ZERO]) > 0); } + @Test public void testGetNetworkOverheadMinStateMax() throws RemoteException, WorkerException { long calculationDuration = Long.MIN_VALUE; assertTrue(doTest(calculationDuration, new DummyTLCState[MAX_ARRAY_SIZE]) > 0); } + @Test public void testGetNetworkOverheadMaxStateMa() throws RemoteException, WorkerException { long calculationDuration = Long.MAX_VALUE; assertTrue(doTest(calculationDuration, new DummyTLCState[MAX_ARRAY_SIZE]) > 0); } + @Test public void testGetNetworkOverheadZeroStateMax() throws RemoteException, WorkerException { long calculationDuration = ZERO; assertTrue(doTest(calculationDuration, new DummyTLCState[MAX_ARRAY_SIZE]) > 0); diff --git a/tlatools/test/tlc2/tool/distributed/TSnapShotDistributedTLCTest.java b/tlatools/test/tlc2/tool/distributed/TSnapShotDistributedTLCTest.java new file mode 100644 index 0000000000000000000000000000000000000000..4ad162cbfb621885616611fe2f3e5c1c87425ac6 --- /dev/null +++ b/tlatools/test/tlc2/tool/distributed/TSnapShotDistributedTLCTest.java @@ -0,0 +1,53 @@ +/******************************************************************************* + * Copyright (c) 2016 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.distributed; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.io.File; + +import org.junit.Test; + +import tlc2.output.EC; +import util.TLAConstants; + +public class TSnapShotDistributedTLCTest extends DistributedTLCTestCase { + + public TSnapShotDistributedTLCTest() { + super(TLAConstants.Files.MODEL_CHECK_FILE_BASENAME, BASE_PATH + "TSnapShot" + File.separator, new String[] {"-deadlock"}); + } + + @Test + public void test() { + // Can we do any assertions here? + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertTrue(recorder.recordedWithStringValueAt(EC.TLC_STATS, "0", 2)); + assertFalse(recorder.recorded(EC.GENERAL)); + + assertTrue(recorder.recorded(EC.TLC_BEHAVIOR_UP_TO_THIS_POINT)); + } +} diff --git a/tlatools/test/tlc2/tool/distributed/fp/DynamicFPSetManagerTest.java b/tlatools/test/tlc2/tool/distributed/fp/DynamicFPSetManagerTest.java index 3beb8e2a8eaa67b395b7694ecc38f0b8f0e521ba..860ea3cef22b040a6fe29e34dd955e7c7c5d5908 100644 --- a/tlatools/test/tlc2/tool/distributed/fp/DynamicFPSetManagerTest.java +++ b/tlatools/test/tlc2/tool/distributed/fp/DynamicFPSetManagerTest.java @@ -2,6 +2,11 @@ package tlc2.tool.distributed.fp; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + import java.io.IOException; import java.rmi.RemoteException; import java.util.HashMap; @@ -10,17 +15,19 @@ import java.util.Map.Entry; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; -import junit.framework.TestCase; +import org.junit.Test; + import tlc2.tool.fp.FPSet; import tlc2.tool.fp.MemFPSet; import tlc2.util.BitVector; import tlc2.util.LongVec; -public class DynamicFPSetManagerTest extends TestCase { +public class DynamicFPSetManagerTest { /** * Test that the ctor rejects invalid values. */ + @Test public void testCtorInvalidZero() throws RemoteException { try { new DynamicFPSetManager(0); @@ -33,6 +40,7 @@ public class DynamicFPSetManagerTest extends TestCase { /** * Test that the ctor rejects invalid values. */ + @Test public void testCtorInvalidMin1() throws RemoteException { try { new DynamicFPSetManager(-1); @@ -46,6 +54,7 @@ public class DynamicFPSetManagerTest extends TestCase { * Test that the ctor correctly calculates its mask used to index fpset * servers for valid values. */ + @Test public void testCtor1() throws RemoteException { DynamicFPSetManager dynamicFPSetManager = new DynamicFPSetManager(1); long mask = dynamicFPSetManager.getMask(); @@ -56,6 +65,7 @@ public class DynamicFPSetManagerTest extends TestCase { * Test that the ctor correctly calculates its mask used to index fpset * servers for valid values. */ + @Test public void testCtor10() throws RemoteException { DynamicFPSetManager dynamicFPSetManager = new DynamicFPSetManager(10); long mask = dynamicFPSetManager.getMask(); @@ -66,6 +76,7 @@ public class DynamicFPSetManagerTest extends TestCase { * Test that the ctor correctly calculates its mask used to index fpset * servers for valid values. */ + @Test public void testCtor31() throws RemoteException { DynamicFPSetManager dynamicFPSetManager = new DynamicFPSetManager(31); long mask = dynamicFPSetManager.getMask(); @@ -76,6 +87,7 @@ public class DynamicFPSetManagerTest extends TestCase { * Test that the ctor correctly calculates its mask used to index fpset * servers for valid values. */ + @Test public void testCtor32() throws RemoteException { DynamicFPSetManager dynamicFPSetManager = new DynamicFPSetManager(32); long mask = dynamicFPSetManager.getMask(); @@ -86,6 +98,7 @@ public class DynamicFPSetManagerTest extends TestCase { * Test that the ctor correctly calculates its mask used to index fpset * servers for valid values. */ + @Test public void testCtor33() throws RemoteException { DynamicFPSetManager dynamicFPSetManager = new DynamicFPSetManager(33); long mask = dynamicFPSetManager.getMask(); @@ -96,6 +109,7 @@ public class DynamicFPSetManagerTest extends TestCase { * Test that the ctor correctly calculates its mask used to index fpset * servers for valid values. */ + @Test public void testCtorMax() throws RemoteException { DynamicFPSetManager dynamicFPSetManager = new DynamicFPSetManager(Integer.MAX_VALUE); long mask = dynamicFPSetManager.getMask(); @@ -106,6 +120,7 @@ public class DynamicFPSetManagerTest extends TestCase { * Test that the {@link DynamicFPSetManager} correctly indexes into the * table of FPSet servers. */ + @Test public void testGetIndexSingleFPSet() throws RemoteException { // Simple case with a single FPSet server final Map<Long, Integer> pairs = new HashMap<Long, Integer>(); @@ -121,6 +136,7 @@ public class DynamicFPSetManagerTest extends TestCase { * Test that the {@link DynamicFPSetManager} correctly indexes into the * table of FPSet servers. */ + @Test public void testGetIndex10FPSet() throws RemoteException { final Map<Long, Integer> pairs = new HashMap<Long, Integer>(); @@ -161,7 +177,7 @@ public class DynamicFPSetManagerTest extends TestCase { for (Entry<Long, Integer> pair : pairs.entrySet()) { long fp = pair.getKey(); - int index = dfm.getIndex(fp); + int index = dfm.getFPSetIndex(fp); int expected = pair.getValue(); assertEquals(expected, index); } @@ -170,6 +186,7 @@ public class DynamicFPSetManagerTest extends TestCase { /** * Tests that reassign doesn't accept invalid values */ + @Test public void testReassingInvalidMin1() throws RemoteException { int expectedNumOfServers = 1; final DynamicFPSetManager dfm = new DynamicFPSetManager(expectedNumOfServers); @@ -190,6 +207,7 @@ public class DynamicFPSetManagerTest extends TestCase { /** * Tests that reassign doesn't accept invalid values */ + @Test public void testReassingInvalid2() throws RemoteException { int expectedNumOfServers = 1; final DynamicFPSetManager dfm = new DynamicFPSetManager(expectedNumOfServers); @@ -211,6 +229,7 @@ public class DynamicFPSetManagerTest extends TestCase { * Tests that reassign correctly terminates with -1 when reassignment to * next FPSet impossible (no FPSets left) */ + @Test public void testReassingTerminate() throws RemoteException { int expectedNumOfServers = 1; final DynamicFPSetManager dfm = new DynamicFPSetManager(expectedNumOfServers); @@ -225,6 +244,7 @@ public class DynamicFPSetManagerTest extends TestCase { /** * Tests that reassign correctly assigns to the next FPSet */ + @Test public void testReassing() throws RemoteException { int expectedNumOfServers = 10; final DynamicFPSetManager dfm = new DynamicFPSetManager(expectedNumOfServers); @@ -268,6 +288,7 @@ public class DynamicFPSetManagerTest extends TestCase { /** * Tests if the {@link FPSetManager} correctly fails over to the replacement {@link FPSet} */ + @Test public void testFailoverPut() throws RemoteException { int expectedNumOfServers = 2; final DynamicFPSetManager dfm = new DynamicFPSetManager(expectedNumOfServers); @@ -276,7 +297,7 @@ public class DynamicFPSetManagerTest extends TestCase { final long fp = 2L; - assertEquals("Assert fingerprint corresponds to TestFPSet", 0, dfm.getIndex(fp)); + assertEquals("Assert fingerprint corresponds to TestFPSet", 0, dfm.getFPSetIndex(fp)); // Test DFM correctly behaves first time when TestFPSet works as expected assertFalse(dfm.put(fp)); @@ -292,6 +313,7 @@ public class DynamicFPSetManagerTest extends TestCase { /** * Tests if the {@link FPSetManager} correctly fails over to the replacement {@link FPSet} */ + @Test public void testFailoverPutBlock() throws RemoteException { int expectedNumOfServers = 2; final DynamicFPSetManager dfm = new DynamicFPSetManager(expectedNumOfServers); @@ -305,10 +327,10 @@ public class DynamicFPSetManagerTest extends TestCase { final LongVec[] fps = new LongVec[numOfServers]; fps[0] = new LongVec(); fps[0].addElement(0L); - assertEquals("Assert fingerprint corresponds to TestFPSet", 0, dfm.getIndex(0L)); + assertEquals("Assert fingerprint corresponds to TestFPSet", 0, dfm.getFPSetIndex(0L)); fps[1] = new LongVec(); fps[1].addElement(1L); - assertEquals("Assert fingerprint corresponds to TestFPSet", 1, dfm.getIndex(1L)); + assertEquals("Assert fingerprint corresponds to TestFPSet", 1, dfm.getFPSetIndex(1L)); /* Test DFM correctly behaves first time when TestFPSet works as expected */ @@ -345,6 +367,7 @@ public class DynamicFPSetManagerTest extends TestCase { /** * Tests if the {@link FPSetManager} correctly terminates if all nested FPSets fail */ + @Test public void testFailoverTerminationPutBlock() throws RemoteException { int expectedNumOfServers = 2; final DynamicFPSetManager dfm = new DynamicFPSetManager(expectedNumOfServers); @@ -358,10 +381,10 @@ public class DynamicFPSetManagerTest extends TestCase { final LongVec[] fps = new LongVec[numOfServers]; fps[0] = new LongVec(); fps[0].addElement(0L); - assertEquals("Assert fingerprint corresponds to TestFPSet", 0, dfm.getIndex(0L)); + assertEquals("Assert fingerprint corresponds to TestFPSet", 0, dfm.getFPSetIndex(0L)); fps[1] = new LongVec(); fps[1].addElement(1L); - assertEquals("Assert fingerprint corresponds to TestFPSet", 1, dfm.getIndex(1L)); + assertEquals("Assert fingerprint corresponds to TestFPSet", 1, dfm.getFPSetIndex(1L)); /* Test DFM correctly behaves first time when TestFPSet works as expected */ @@ -397,6 +420,7 @@ public class DynamicFPSetManagerTest extends TestCase { /** * Tests if the {@link FPSetManager} correctly terminates if all nested FPSets fail */ + @Test public void testFailoverTerminationPutBlockConcurrent() throws RemoteException { int expectedNumOfServers = 2; final DynamicFPSetManager dfm = new DynamicFPSetManager(expectedNumOfServers); @@ -411,10 +435,10 @@ public class DynamicFPSetManagerTest extends TestCase { final LongVec[] fps = new LongVec[numOfServers]; fps[0] = new LongVec(); fps[0].addElement(0L); - assertEquals("Assert fingerprint corresponds to TestFPSet", 0, dfm.getIndex(0L)); + assertEquals("Assert fingerprint corresponds to TestFPSet", 0, dfm.getFPSetIndex(0L)); fps[1] = new LongVec(); fps[1].addElement(1L); - assertEquals("Assert fingerprint corresponds to TestFPSet", 1, dfm.getIndex(1L)); + assertEquals("Assert fingerprint corresponds to TestFPSet", 1, dfm.getFPSetIndex(1L)); /* Test DFM correctly behaves first time when TestFPSet works as expected */ final ExecutorService es = Executors.newCachedThreadPool(); @@ -461,6 +485,7 @@ public class DynamicFPSetManagerTest extends TestCase { * {@link DynamicFPSetManager} is indeed faulty (pay attention to * intermittent test failures). */ + @Test public void testPutBlockConcurrentOrder() throws IOException { int expectedNumOfServers = 20; final DynamicFPSetManager dfm = new DynamicFPSetManager(expectedNumOfServers); diff --git a/tlatools/test/tlc2/tool/distributed/fp/FPSetManagerTest.java b/tlatools/test/tlc2/tool/distributed/fp/FPSetManagerTest.java new file mode 100644 index 0000000000000000000000000000000000000000..0420022a50e12491171c5b0a7cb9a01e8ed6b915 --- /dev/null +++ b/tlatools/test/tlc2/tool/distributed/fp/FPSetManagerTest.java @@ -0,0 +1,160 @@ +/******************************************************************************* + * Copyright (c) 2016 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ + +package tlc2.tool.distributed.fp; + +import java.io.File; +import java.io.IOException; +import java.rmi.RemoteException; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import tlc2.tool.distributed.fp.FPSetManager.FPSets; +import tlc2.tool.fp.FPSet; +import tlc2.tool.fp.FPSetConfiguration; +import tlc2.tool.fp.FPSetFactory; + +public class FPSetManagerTest { + + protected static final String tmpdir = System.getProperty("java.io.tmpdir") + File.separator + "FPSetTest" + + System.currentTimeMillis(); + + @Before + public void before() { + new File(tmpdir).mkdirs(); + } + + @Test + public void test2() throws IOException { + doTest(2); + } + + @Test + public void test3() throws IOException { + doTest(3); + } + + @Test + public void test4() throws IOException { + doTest(4); + } + + @Test + public void test5() throws IOException { + doTest(5); + } + + @Test + public void test8() throws IOException { + doTest(8); + } + + private void doTest(int expectedNumOfServers) throws RemoteException, IOException, FPSetManagerException { + final FPSetConfiguration fpSetConfiguration = new FPSetConfiguration(); + fpSetConfiguration.setFpBits(1); // two nested FPSets + + final List<FPSets> sets = new ArrayList<FPSets>(); + for (int i = 0; i < fpSetConfiguration.getMultiFPSetCnt(); i++) { + final FPSet fpSet = FPSetFactory.getFPSet(fpSetConfiguration); + fpSet.init(1, tmpdir, "test" + expectedNumOfServers); + sets.add(new FPSets(fpSet, "localhost" + i)); + } + + final IFPSetManager manager = new DynamicFPSetManager(expectedNumOfServers); + for (FPSets fpSets : sets) { + manager.register(fpSets.getFpset(), fpSets.getFpset().toString()); + } + + // Index uses LSBs + Assert.assertEquals(0, manager.getFPSetIndex(0)); + Assert.assertEquals(1, manager.getFPSetIndex(1)); + Assert.assertEquals(0, manager.getFPSetIndex(2)); + Assert.assertEquals(1, manager.getFPSetIndex(3)); + Assert.assertEquals(0, manager.getFPSetIndex((1L << 63) + 2L)); + Assert.assertEquals(1, manager.getFPSetIndex((1L << 63) + 1L)); + + final Set<Long> fps = new HashSet<Long>(); + // fps.add(0L); // Not accepted by nested FPSets + fps.add(1L); // 00...0001 + fps.add((1L << 62) + 1L); // 01...0001 + fps.add((1L << 63) + 1L); // 10...0001 + fps.add((3L << 62) + 1L); // 11...0001 + fps.add(2L); // 00...0010 + fps.add((1L << 62) + 2L); // 01...0010 + fps.add((1L << 63) + 2L); // 10...0010 + fps.add((3L << 62) + 2L); // 11...0010 + fps.add(3L); // 00...0011 + fps.add((1L << 62) + 3L); // 01...0011 + fps.add((1L << 63) + 3L); // 10...0011 + fps.add((3L << 62) + 3L); // 11...0011 + fps.add(4L); // 00...0100 + fps.add((1L << 62) + 4L); // 01...0100 + fps.add((1L << 63) + 4L); // 10...0100 + fps.add((3L << 62) + 4L); // 11...0100 + fps.add(5L); // 00...0101 + fps.add((1L << 62) + 5L); // 01...0101 + fps.add((1L << 63) + 5L); // 10...0101 + fps.add((3L << 62) + 5L); // 11...0101 + fps.add(6L); // 00...0110 + fps.add((1L << 62) + 6L); // 01...0110 + fps.add((1L << 63) + 6L); // 10...0110 + fps.add((3L << 62) + 6L); // 11...0110 + fps.add(7L); // 00...0110 + fps.add((1L << 62) + 7L); // 01...0111 + fps.add((1L << 63) + 7L); // 10...0111 + fps.add((3L << 62) + 7L); // 11...0111 + fps.add(8L); // 00...1000 + fps.add((1L << 62) + 8L); // 01...1000 + fps.add((1L << 63) + 8L); // 10...1000 + fps.add((3L << 62) + 8L); // 11...1000 + + final Set<Long> unseen = new HashSet<Long>(fps); + for (Long fp : fps) { + // Unseen fingerprints must not be in set. + for (Long unseenFP : unseen) { + Assert.assertFalse(manager.contains(unseenFP)); + } + Assert.assertTrue(unseen.remove(fp)); + + Assert.assertFalse(printBinaryString("", fp), manager.put(fp)); + Assert.assertTrue(printBinaryString("", fp), manager.contains(fp)); + } + + Assert.assertEquals(fps.size(), manager.size()); + + Assert.assertTrue(manager.checkInvariant()); + } + + private String printBinaryString(final String id, final long a) { + return String.format(id + ":%64s", Long.toBinaryString(a)).replace(' ', '0'); + } +} diff --git a/tlatools/test/tlc2/tool/doinitfunctor/DoInitFunctorEvalExceptionTest.java b/tlatools/test/tlc2/tool/doinitfunctor/DoInitFunctorEvalExceptionTest.java new file mode 100644 index 0000000000000000000000000000000000000000..71d6b10c37d507cf16c12c16ae256a859b069816 --- /dev/null +++ b/tlatools/test/tlc2/tool/doinitfunctor/DoInitFunctorEvalExceptionTest.java @@ -0,0 +1,56 @@ +/******************************************************************************* + * Copyright (c) 2015 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ + +package tlc2.tool.doinitfunctor; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.output.EC; +import tlc2.output.EC.ExitStatus; +import tlc2.tool.liveness.ModelCheckerTestCase; + +public class DoInitFunctorEvalExceptionTest extends ModelCheckerTestCase { + + public DoInitFunctorEvalExceptionTest() { + super("DoInitFunctorEvalException", "DoInitFunctor", ExitStatus.FAILURE_SPEC_EVAL); + } + + @Test + public void testSpec() { + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertTrue(recorder.recorded(EC.TLC_STATS)); + assertFalse(recorder.recorded(EC.GENERAL)); + + assertTrue(recorder.recordedWithStringValues(EC.TLC_INITIAL_STATE, + "TLC expected a boolean value, but did not find one. line 15, col 15 to line 15, col 18 of module DoInitFunctorEvalException", + "x = 1\n")); + + assertUncovered("line 9, col 9 to line 9, col 14 of module DoInitFunctorEvalException: 0"); + } +} diff --git a/tlatools/test/tlc2/tool/doinitfunctor/DoInitFunctorInvariantContinueTest.java b/tlatools/test/tlc2/tool/doinitfunctor/DoInitFunctorInvariantContinueTest.java new file mode 100644 index 0000000000000000000000000000000000000000..a68ee5cd21eaaa7cda6aa049981c7c3cc7ee3ce6 --- /dev/null +++ b/tlatools/test/tlc2/tool/doinitfunctor/DoInitFunctorInvariantContinueTest.java @@ -0,0 +1,64 @@ +/******************************************************************************* + * Copyright (c) 2015 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ + +package tlc2.tool.doinitfunctor; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.util.List; + +import org.junit.Test; + +import tlc2.output.EC; +import tlc2.tool.liveness.ModelCheckerTestCase; + +public class DoInitFunctorInvariantContinueTest extends ModelCheckerTestCase { + + public DoInitFunctorInvariantContinueTest() { + super("DoInitFunctorInvariantContinue", "DoInitFunctor", new String[] {"-continue"}/*, ExitStatus.VIOLATION_SAFETY*/); //TODO The exit status is incorrect because TLC shows "no error" regardless of the number of violations with "-continue" + } + + @Test + public void testSpec() { + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "21", "11")); + assertFalse(recorder.recorded(EC.GENERAL)); + + assertTrue(recorder.recordedWithStringValues(EC.TLC_INVARIANT_VIOLATED_INITIAL, "Inv", "x = 1\n")); + // Test that TLC - with continuation enabled - continues after finding the first inv violation. + final List<Object> records = recorder.getRecords(EC.TLC_INVARIANT_VIOLATED_INITIAL); + assertEquals(10, records.size()); + for (int j = 0; j < records.size(); j++) { + final String[] violation = (String[]) records.get(j); + assertEquals("Inv", violation[0]); + assertEquals("x = " + (j+1) + "\n", violation[1]); + } + + assertZeroUncovered(); + } +} diff --git a/tlatools/test/tlc2/tool/doinitfunctor/DoInitFunctorInvariantMinimalErrorStackTest.java b/tlatools/test/tlc2/tool/doinitfunctor/DoInitFunctorInvariantMinimalErrorStackTest.java new file mode 100644 index 0000000000000000000000000000000000000000..497dbbc4d6fdd5be04affefc435419e8d60c36e5 --- /dev/null +++ b/tlatools/test/tlc2/tool/doinitfunctor/DoInitFunctorInvariantMinimalErrorStackTest.java @@ -0,0 +1,58 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.doinitfunctor; + +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.output.EC; +import tlc2.output.EC.ExitStatus; +import tlc2.tool.liveness.ModelCheckerTestCase; + +public class DoInitFunctorInvariantMinimalErrorStackTest extends ModelCheckerTestCase { + + public DoInitFunctorInvariantMinimalErrorStackTest() { + super("DoInitFunctorMinimalErrorStack", "DoInitFunctor", ExitStatus.FAILURE_SPEC_EVAL); + } + + @Test + public void testSpec() { + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "1", "1", "1")); + + assertTrue(recorder.recordedWithStringValues(EC.TLC_MODULE_ARGUMENT_ERROR_AN, + "first", ">", "integer", "<<1, 1>>")); + + String errorStack = "0. Line 8, column 3 to line 9, column 13 in DoInitFunctorMinimalErrorStack\n" + + "1. Line 8, column 6 to line 8, column 25 in DoInitFunctorMinimalErrorStack\n" + + "2. Line 9, column 6 to line 9, column 13 in DoInitFunctorMinimalErrorStack\n" + + "3. Line 14, column 8 to line 14, column 13 in DoInitFunctorMinimalErrorStack\n\n"; + assertTrue(recorder.recordedWithStringValue(EC.TLC_NESTED_EXPRESSION, errorStack)); + + assertUncovered("line 11, col 20 to line 11, col 33 of module DoInitFunctorMinimalErrorStack: 0"); + } +} diff --git a/tlatools/test/tlc2/tool/doinitfunctor/DoInitFunctorInvariantNoContinueTest.java b/tlatools/test/tlc2/tool/doinitfunctor/DoInitFunctorInvariantNoContinueTest.java new file mode 100644 index 0000000000000000000000000000000000000000..c93e93ab0b51c519161d0204b1740c9190932b80 --- /dev/null +++ b/tlatools/test/tlc2/tool/doinitfunctor/DoInitFunctorInvariantNoContinueTest.java @@ -0,0 +1,54 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.doinitfunctor; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.output.EC; +import tlc2.output.EC.ExitStatus; +import tlc2.tool.liveness.ModelCheckerTestCase; + +public class DoInitFunctorInvariantNoContinueTest extends ModelCheckerTestCase { + + public DoInitFunctorInvariantNoContinueTest() { + super("DoInitFunctorInvariantContinue", "DoInitFunctor", ExitStatus.VIOLATION_SAFETY); + } + + @Test + public void testSpec() { + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertFalse(recorder.recorded(EC.TLC_STATS)); + assertFalse(recorder.recorded(EC.GENERAL)); + + assertTrue(recorder.recordedWithStringValues(EC.TLC_INVARIANT_VIOLATED_INITIAL, "Inv", "x = 1\n")); + // Test that TLC - with continuation disabled - stops after finding the first inv violation/finds exactly one violation. + assertEquals(1, recorder.getRecords(EC.TLC_INVARIANT_VIOLATED_INITIAL).size()); + } +} diff --git a/tlatools/test/tlc2/tool/doinitfunctor/DoInitFunctorInvariantTest.java b/tlatools/test/tlc2/tool/doinitfunctor/DoInitFunctorInvariantTest.java new file mode 100644 index 0000000000000000000000000000000000000000..06a7e815fc5af4d2c49a59e1e7059991fa787bd5 --- /dev/null +++ b/tlatools/test/tlc2/tool/doinitfunctor/DoInitFunctorInvariantTest.java @@ -0,0 +1,55 @@ +/******************************************************************************* + * Copyright (c) 2015 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ + +package tlc2.tool.doinitfunctor; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.output.EC; +import tlc2.output.EC.ExitStatus; +import tlc2.tool.liveness.ModelCheckerTestCase; + +public class DoInitFunctorInvariantTest extends ModelCheckerTestCase { + + public DoInitFunctorInvariantTest() { + super("DoInitFunctorInvariant", "DoInitFunctor", ExitStatus.VIOLATION_SAFETY); + } + + @Test + public void testSpec() { + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertFalse(recorder.recorded(EC.TLC_STATS)); + assertFalse(recorder.recorded(EC.GENERAL)); + + // Test that TLC - with continuation disabled - stops after finding the first inv violation/finds exactly one violation. + assertTrue(recorder.recordedWithStringValues(EC.TLC_INVARIANT_VIOLATED_INITIAL, "NotNine", "x = 9\n")); + assertEquals(1, recorder.getRecords(EC.TLC_INVARIANT_VIOLATED_INITIAL).size()); + } +} diff --git a/tlatools/test/tlc2/tool/doinitfunctor/DoInitFunctorPropertyTest.java b/tlatools/test/tlc2/tool/doinitfunctor/DoInitFunctorPropertyTest.java new file mode 100644 index 0000000000000000000000000000000000000000..d6133b07900ddcb2eead9cfbc83b9410a662a073 --- /dev/null +++ b/tlatools/test/tlc2/tool/doinitfunctor/DoInitFunctorPropertyTest.java @@ -0,0 +1,52 @@ +/******************************************************************************* + * Copyright (c) 2015 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ + +package tlc2.tool.doinitfunctor; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.output.EC; +import tlc2.output.EC.ExitStatus; +import tlc2.tool.liveness.ModelCheckerTestCase; + +public class DoInitFunctorPropertyTest extends ModelCheckerTestCase { + + public DoInitFunctorPropertyTest() { + super("DoInitFunctorProperty", "DoInitFunctor", ExitStatus.VIOLATION_LIVENESS); + } + + @Test + public void testSpec() { + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertFalse(recorder.recorded(EC.TLC_STATS)); + assertFalse(recorder.recorded(EC.GENERAL)); + + assertTrue(recorder.recordedWithStringValues(EC.TLC_PROPERTY_VIOLATED_INITIAL, "NotNine", "x = 9\n")); + } +} diff --git a/tlatools/test/tlc2/tool/evalorder/InitEvalOrder1Test.java b/tlatools/test/tlc2/tool/evalorder/InitEvalOrder1Test.java new file mode 100644 index 0000000000000000000000000000000000000000..40a246a50aecfbe6da630e1331642c54b93623b4 --- /dev/null +++ b/tlatools/test/tlc2/tool/evalorder/InitEvalOrder1Test.java @@ -0,0 +1,33 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.evalorder; + +public class InitEvalOrder1Test extends InitEvalOrderTest { + + public InitEvalOrder1Test() { + super("InitEvalOrder1.cfg"); + } +} diff --git a/tlatools/test/tlc2/tool/evalorder/InitEvalOrder2Test.java b/tlatools/test/tlc2/tool/evalorder/InitEvalOrder2Test.java new file mode 100644 index 0000000000000000000000000000000000000000..3e29ed4d47486714557136eb77ca6163781530c1 --- /dev/null +++ b/tlatools/test/tlc2/tool/evalorder/InitEvalOrder2Test.java @@ -0,0 +1,33 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.evalorder; + +public class InitEvalOrder2Test extends InitEvalOrderTest { + + public InitEvalOrder2Test() { + super("InitEvalOrder2.cfg"); + } +} diff --git a/tlatools/test/tlc2/tool/evalorder/InitEvalOrder3Test.java b/tlatools/test/tlc2/tool/evalorder/InitEvalOrder3Test.java new file mode 100644 index 0000000000000000000000000000000000000000..011bc55d58f001f3395839917f779584f6c3678d --- /dev/null +++ b/tlatools/test/tlc2/tool/evalorder/InitEvalOrder3Test.java @@ -0,0 +1,33 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.evalorder; + +public class InitEvalOrder3Test extends InitEvalOrderTest { + + public InitEvalOrder3Test() { + super("InitEvalOrder3.cfg"); + } +} diff --git a/tlatools/test/tlc2/tool/evalorder/InitEvalOrder4Test.java b/tlatools/test/tlc2/tool/evalorder/InitEvalOrder4Test.java new file mode 100644 index 0000000000000000000000000000000000000000..969fb147a5264c3ac821fa9097da5e381e539b37 --- /dev/null +++ b/tlatools/test/tlc2/tool/evalorder/InitEvalOrder4Test.java @@ -0,0 +1,33 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.evalorder; + +public class InitEvalOrder4Test extends InitEvalOrderTest { + + public InitEvalOrder4Test() { + super("InitEvalOrder4.cfg"); + } +} diff --git a/tlatools/test/tlc2/tool/evalorder/InitEvalOrderBasicTest.java b/tlatools/test/tlc2/tool/evalorder/InitEvalOrderBasicTest.java new file mode 100644 index 0000000000000000000000000000000000000000..4e942b61465700843427a280397637a8456c2f30 --- /dev/null +++ b/tlatools/test/tlc2/tool/evalorder/InitEvalOrderBasicTest.java @@ -0,0 +1,50 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.evalorder; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.output.EC; +import tlc2.tool.liveness.ModelCheckerTestCase; + +public class InitEvalOrderBasicTest extends ModelCheckerTestCase { + + public InitEvalOrderBasicTest() { + super("InitEvalOrderBasic", "EvalOrder"); + } + + @Test + public void test() { + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertFalse(recorder.recorded(EC.GENERAL)); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "2", "1", "0")); + + assertZeroUncovered(); + } +} diff --git a/tlatools/test/tlc2/tool/evalorder/InitEvalOrderTest.java b/tlatools/test/tlc2/tool/evalorder/InitEvalOrderTest.java new file mode 100644 index 0000000000000000000000000000000000000000..f50d2109880c9d4c6096efcd47acc1e517157e9a --- /dev/null +++ b/tlatools/test/tlc2/tool/evalorder/InitEvalOrderTest.java @@ -0,0 +1,48 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.evalorder; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.output.EC; +import tlc2.tool.liveness.ModelCheckerTestCase; + +public abstract class InitEvalOrderTest extends ModelCheckerTestCase { + + public InitEvalOrderTest(String config) { + super("InitEvalOrder", "EvalOrder", new String[] {"-config", config}); + } + + @Test + public void test() { + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertFalse(recorder.recorded(EC.GENERAL)); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "2", "1", "0")); + } +} diff --git a/tlatools/test/tlc2/tool/fp/AbstractFPSetTest.java b/tlatools/test/tlc2/tool/fp/AbstractFPSetTest.java index e0bc397d688e87de99573b1e70669470400ee877..fc0864fd1652de90f3404df6d412a52ac277dd17 100644 --- a/tlatools/test/tlc2/tool/fp/AbstractFPSetTest.java +++ b/tlatools/test/tlc2/tool/fp/AbstractFPSetTest.java @@ -7,9 +7,10 @@ import java.io.IOException; import java.text.DecimalFormat; import java.util.Date; -import junit.framework.TestCase; +import org.junit.After; +import org.junit.Before; -public abstract class AbstractFPSetTest extends TestCase { +public abstract class AbstractFPSetTest { protected static final long RNG_SEED = 15041980L; @@ -17,9 +18,11 @@ public abstract class AbstractFPSetTest extends TestCase { + System.currentTimeMillis(); protected static final String filename = "FPSetTestTest"; protected static final DecimalFormat df = new DecimalFormat("###,###.###"); + protected static final DecimalFormat pf = new DecimalFormat("#.##"); protected long previousTimestamp; protected long previousSize; + protected long startTimestamp; protected Date endTimeStamp; private File dir; @@ -28,14 +31,13 @@ public abstract class AbstractFPSetTest extends TestCase { /* (non-Javadoc) * @see junit.framework.TestCase#setUp() */ - protected void setUp() throws Exception { - super.setUp(); - + @Before + public void setUp() throws Exception { // create temp folder dir = new File(tmpdir); dir.mkdirs(); - previousTimestamp = System.currentTimeMillis(); + previousTimestamp = startTimestamp = System.currentTimeMillis(); previousSize = 0L; System.out.println("Test started at " + new Date()); @@ -44,6 +46,7 @@ public abstract class AbstractFPSetTest extends TestCase { /* (non-Javadoc) * @see junit.framework.TestCase#tearDown() */ + @After public void tearDown() { if (endTimeStamp == null) { endTimeStamp = new Date(); @@ -77,25 +80,46 @@ public abstract class AbstractFPSetTest extends TestCase { if (fpSet instanceof FPSetStatistic) { FPSetStatistic fpSetStats = (FPSetStatistic) fpSet; long maxTblCnt = fpSetStats.getMaxTblCnt(); - System.out.println("Maximum FPSet bucket count is: " + System.out.println("Maximum FPSet table count is: " + df.format(maxTblCnt) + " (approx: " + df.format(maxTblCnt * FPSet.LongSize >> 20) + " GiB)"); + System.out.println("FPSet lock count is: " + fpSetStats.getLockCnt()); + System.out.println("FPSet bucket count is: " + fpSetStats.getTblCapacity()); } System.out.println("Testing " + fpSet.getClass().getCanonicalName()); return fpSet; } - + // insertion speed - public void printInsertionSpeed(final long currentSize) { - final long currentTimestamp = System.currentTimeMillis(); + public void printInsertionSpeed(final FPSet fpSet) { // print every minute - final double factor = (currentTimestamp - previousTimestamp) / 60000d; + long now = System.currentTimeMillis(); + final double factor = (now - previousTimestamp) / 60000d; if (factor >= 1d) { + final long currentSize = fpSet.size(); long insertions = (long) ((currentSize - previousSize) * factor); - System.out.println(df.format(insertions) + " insertions/min"); - previousTimestamp = currentTimestamp; + if (fpSet instanceof FPSetStatistic) { + FPSetStatistic fpSetStatistics = (FPSetStatistic) fpSet; + System.out.println(System.currentTimeMillis() + " s (epoch); " + df.format(insertions) + " insertions/min; " + pf.format(fpSetStatistics.getLoadFactor()) + " load factor"); + } else { + System.out.println(System.currentTimeMillis() + " s (epoch); " + df.format(insertions) + " insertions/min"); + } + previousTimestamp = now; previousSize = currentSize; } } + + public void printInsertionSpeed(final FPSet fpSet, long start, long end) { + final long size = fpSet.size(); + // Normalize insertions to minutes. + final long duration = Math.max(end - start, 1); //ms (avoid div-by-zero) + final long insertions = (long) ((size / duration) * 60000L); + if (fpSet instanceof FPSetStatistic) { + FPSetStatistic fpSetStatistics = (FPSetStatistic) fpSet; + System.out.println(System.currentTimeMillis() + " s; " + df.format(insertions) + " insertions/min; " + pf.format(fpSetStatistics.getLoadFactor()) + " load factor"); + } else { + System.out.println(System.currentTimeMillis() + " s (epoch); " + df.format(insertions) + " insertions/min"); + } + } } diff --git a/tlatools/test/tlc2/tool/fp/AbstractHeapBasedDiskFPSetTest.java b/tlatools/test/tlc2/tool/fp/AbstractHeapBasedDiskFPSetTest.java index 1e0c623ee65e4a0b1ed59677e4db2737356a8b86..dc16b7db2e83ea897c2496027b4ce65011ed451f 100644 --- a/tlatools/test/tlc2/tool/fp/AbstractHeapBasedDiskFPSetTest.java +++ b/tlatools/test/tlc2/tool/fp/AbstractHeapBasedDiskFPSetTest.java @@ -1,66 +1,139 @@ // Copyright (c) 2012 Markus Alexander Kuppe. All rights reserved. package tlc2.tool.fp; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import java.io.IOException; import java.rmi.RemoteException; -import junit.framework.TestCase; +import org.junit.Test; + +import tlc2.tool.TLCState; +import tlc2.tool.TLCTrace; +import tlc2.tool.queue.DummyTLCState; -public abstract class AbstractHeapBasedDiskFPSetTest extends TestCase { +public abstract class AbstractHeapBasedDiskFPSetTest { /* Test the lower limits */ + @Test public void testCtorLLMinus1() throws RemoteException { doTest(getLowerLimit() - 1); } + @Test public void testCtorLL() throws RemoteException { doTest(getLowerLimit()); } + @Test public void testCtorLLPlus1() throws RemoteException { doTest(getLowerLimit() + 1); } + @Test public void testCtorLLNextPow2Min1() throws RemoteException { doTest((getLowerLimit() << 1) - 1); } /* Test with a power far away from upper/lower limits */ + @Test public void testCtorPow16Minus1() throws RemoteException { doTest((1L << 16) - 1); } + @Test public void testCtorPow16() throws RemoteException { doTest(1L << 16); } + @Test public void testCtorPow16Plus1() throws RemoteException { doTest((1L << 16) + 1); } + @Test public void testCtorPow16NextPow2Min1() throws RemoteException { doTest(((1L << 16) << 1) - 1); } /* Test the upper limits */ + @Test public void testCtorULMinus1() throws RemoteException { doTest(getUpperLimit() - 1); } + @Test public void testCtorUL() throws RemoteException { doTest(getUpperLimit()); } + @Test public void testCtorULPlus1() throws RemoteException { doTest(getUpperLimit() + 1); } + @Test public void testCtorULNextPow2Min1() throws RemoteException { doTest((getUpperLimit() << 1) - 1); } + @Test + public void testFPSetRecovery() throws IOException { + final int limit = 99999; + final String metadir = System.getProperty("java.io.tmpdir"); + final String filename = this.getClass().getCanonicalName(); + + // First, create a trace file to recover from. + final TLCTrace trace = new TLCTrace(metadir, filename, + null); + + // Fill the trace file with random fingerprints + final TLCState predecessor = new DummyTLCState(); + predecessor.uid = 1L; + // an init state + trace.writeState(predecessor.uid); + // successor states + for (long fp = predecessor.uid + 1; fp < limit; fp++) { + trace.writeState(predecessor, fp); + predecessor.uid = fp; + } + + // Create a checkpoint file + trace.beginChkpt(); + trace.commitChkpt(); + + // Create a DiskFPSet + final DiskFPSet fpSet = getDiskFPSet(new FPSetConfiguration()); + fpSet.init(1, metadir, filename); + fpSet.recover(trace); + + // Verify successful recovery + assertEquals(limit-1, fpSet.size()); + for (long fp = 1L; fp < limit; fp++) { + assertTrue(fpSet.contains(fp)); + } + } + + @Test + public void testFPSetRecovery2() throws IOException { + final String metadir = System.getProperty("java.io.tmpdir"); + final String filename = this.getClass().getCanonicalName() + "testFPSetRecovery2"; + + final DiskFPSet fpSet = getDiskFPSet(new FPSetConfiguration()); + fpSet.init(1, metadir, filename); + + // Make sure the FPSet tries to flush to disk. + fpSet.forceFlush(); + + for (long fp = 1; fp <= 1024; fp++) { + fpSet.recoverFP(fp); + } + } + /* Helper */ @SuppressWarnings("deprecation") diff --git a/tlatools/test/tlc2/tool/fp/Bug210DiskFPSetTest.java b/tlatools/test/tlc2/tool/fp/Bug210DiskFPSetTest.java index a7eb567f3300895d839b9d4d2e1529b9f2500ae5..ed22315a6bc97c30531fed3e26ad6fe1dfde51a4 100644 --- a/tlatools/test/tlc2/tool/fp/Bug210DiskFPSetTest.java +++ b/tlatools/test/tlc2/tool/fp/Bug210DiskFPSetTest.java @@ -1,8 +1,13 @@ // Copyright (c) 2011 Microsoft Corporation. All rights reserved. package tlc2.tool.fp; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.fail; + import java.io.IOException; +import org.junit.Test; + public class Bug210DiskFPSetTest extends AbstractFPSetTest { /* (non-Javadoc) @@ -16,9 +21,10 @@ public class Bug210DiskFPSetTest extends AbstractFPSetTest { } /** - * @see http://bugzilla.tlaplus.net/show_bug.cgi?id=210 + * @see Bug #210 in general/bugzilla/index.html * @throws IOException */ + @Test public void testDiskLookupWithOverflow() throws IOException { // set up an index whose upper bound is beyond 1/1024 of // Integer.MAX_VALUE diff --git a/tlatools/test/tlc2/tool/fp/Bug242DiskFPSetTest.java b/tlatools/test/tlc2/tool/fp/Bug242DiskFPSetTest.java index 20e232d50824f4680dba97630313803bf30e992a..71df2e53717d5244ee5ff03b4841c5ac4186f3ef 100644 --- a/tlatools/test/tlc2/tool/fp/Bug242DiskFPSetTest.java +++ b/tlatools/test/tlc2/tool/fp/Bug242DiskFPSetTest.java @@ -1,9 +1,13 @@ // Copyright (c) 2011 Microsoft Corporation. All rights reserved. package tlc2.tool.fp; +import static org.junit.Assert.fail; + import java.io.IOException; import java.rmi.RemoteException; +import org.junit.Test; + /** * */ @@ -26,8 +30,9 @@ public class Bug242DiskFPSetTest extends AbstractFPSetTest { } /** - * @see http://bugzilla.tlaplus.net/show_bug.cgi?id=242 + * @see Bug #242 in general/bugzilla/index.html */ + @Test public void testDiskFPSetWithHighMem() throws RemoteException { try { getFPSet(2097153638); @@ -38,6 +43,7 @@ public class Bug242DiskFPSetTest extends AbstractFPSetTest { fail(e.getMessage()); } } + @Test public void testDiskFPSetIntMaxValue() throws RemoteException { try { getFPSet(Integer.MAX_VALUE); @@ -48,6 +54,7 @@ public class Bug242DiskFPSetTest extends AbstractFPSetTest { fail(e.getMessage()); } } + @Test public void testDiskFPSetIntMinValue() throws RemoteException { try { getFPSet(Integer.MIN_VALUE); @@ -57,6 +64,7 @@ public class Bug242DiskFPSetTest extends AbstractFPSetTest { } fail(); } + @Test public void testDiskFPSetZero() throws RemoteException { try { getFPSet(0); @@ -64,6 +72,7 @@ public class Bug242DiskFPSetTest extends AbstractFPSetTest { fail(e.getMessage()); } } + @Test public void testDiskFPSetOne() throws RemoteException { try { getFPSet(1); diff --git a/tlatools/test/tlc2/tool/fp/Bug246DiskFPSetTest.java b/tlatools/test/tlc2/tool/fp/Bug246DiskFPSetTest.java index c7c07483b9562ba3f7b8257e0cf34d447ba30cf3..341fb5a7ccc99ce568b06661331c0319aa3e192b 100644 --- a/tlatools/test/tlc2/tool/fp/Bug246DiskFPSetTest.java +++ b/tlatools/test/tlc2/tool/fp/Bug246DiskFPSetTest.java @@ -2,20 +2,26 @@ package tlc2.tool.fp; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + import java.io.IOException; -import junit.framework.TestCase; +import org.junit.Test; + import util.TLCRuntime; /** * @author Markus Alexander Kuppe */ -public class Bug246DiskFPSetTest extends TestCase { +public class Bug246DiskFPSetTest { /** * Tests if the DiskFPSet gets correctly flushed to disk (if the fp spaces is unevenly distributed) or causes an {@link OutOfMemoryError} * @throws IOException */ + @Test @SuppressWarnings("deprecation") public void testLinearFillup() throws IOException { final long vmMaxMemory = Runtime.getRuntime().maxMemory(); @@ -77,6 +83,7 @@ public class Bug246DiskFPSetTest extends TestCase { } } + @Test public void testFlushDiskFPSet() throws IOException { // // Dedicate 90% of VM memory to DiskFPSet diff --git a/tlatools/test/tlc2/tool/fp/DummyFPSetConfiguration.java b/tlatools/test/tlc2/tool/fp/DummyFPSetConfiguration.java index 4d7a26d8af7bed453d888aa30004c2eed57de29b..4f0481ca2c5f321fc2bba66d88935a2cda29c96d 100644 --- a/tlatools/test/tlc2/tool/fp/DummyFPSetConfiguration.java +++ b/tlatools/test/tlc2/tool/fp/DummyFPSetConfiguration.java @@ -15,7 +15,7 @@ public class DummyFPSetConfiguration extends FPSetConfiguration { return memoryInBytes; } - public void setMemoryInFingerprintCnt(int numberOfFingerprints) { - this.memoryInBytes = numberOfFingerprints * FPSet.LongSize; + public void setMemoryInFingerprintCnt(long length) { + this.memoryInBytes = length * FPSet.LongSize; } } diff --git a/tlatools/test/tlc2/tool/fp/FPSetFactoryTest.java b/tlatools/test/tlc2/tool/fp/FPSetFactoryTest.java index 00950765cd3ea83f4a5053ade30a6b985990e835..8820e49907e56cbe526a368c1cb9810bb56c5ae2 100644 --- a/tlatools/test/tlc2/tool/fp/FPSetFactoryTest.java +++ b/tlatools/test/tlc2/tool/fp/FPSetFactoryTest.java @@ -1,20 +1,53 @@ // Copyright (c) 2012 Markus Alexander Kuppe. All rights reserved. package tlc2.tool.fp; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + import java.rmi.NoSuchObjectException; import java.rmi.RemoteException; -import junit.framework.TestCase; +import org.junit.Assume; +import org.junit.Test; + +import tlc2.tool.distributed.fp.FPSetRMI; +import util.TLCRuntime; @SuppressWarnings("deprecation") -public class FPSetFactoryTest extends TestCase { +public class FPSetFactoryTest { // Has to be larger than util.TLCRuntime.MinFpMemSize. For off-heap/non-heap // tests it should not exceed what the VM allocates by default (usually // 64mb). 64mb is also the default used by util.TLCRuntime.getNonHeapPhysicalMemory(). private static final long MEMORY = 64L * 1024L * 1024L; + /* Test diskfpset subclasses which always require two instances */ + + @Test + public void testGetDiskFPSet() { + assertTrue(FPSetFactory.isDiskFPSet(DiskFPSet.class.getName())); + assertTrue(FPSetFactory.isDiskFPSet(HeapBasedDiskFPSet.class.getName())); + assertTrue(FPSetFactory.isDiskFPSet(OffHeapDiskFPSet.class.getName())); + assertTrue(FPSetFactory.isDiskFPSet(LSBDiskFPSet.class.getName())); + assertTrue(FPSetFactory.isDiskFPSet(MSBDiskFPSet.class.getName())); + + assertFalse(FPSetFactory.isDiskFPSet(FPSet.class.getName())); + assertFalse(FPSetFactory.isDiskFPSet(FPSetRMI.class.getName())); + + assertFalse(FPSetFactory.isDiskFPSet(MultiFPSet.class.getName())); + + assertFalse(FPSetFactory.isDiskFPSet(MemFPSet.class.getName())); + assertFalse(FPSetFactory.isDiskFPSet(MemFPSet1.class.getName())); + assertFalse(FPSetFactory.isDiskFPSet(MemFPSet2.class.getName())); + + System.setProperty(FPSetFactory.IMPL_PROPERTY, MSBDiskFPSet.class.getName()); + final FPSetConfiguration fpSetConfiguration = new FPSetConfiguration(); + assertTrue(fpSetConfiguration.allowsNesting()); + } + /* Test single FPSet with default memory */ + @Test public void testGetFPSetMSB() throws RemoteException { // Explicitly set MSBDiskFPSet to overwrite any previous setting (if any) System.setProperty(FPSetFactory.IMPL_PROPERTY, MSBDiskFPSet.class.getName()); @@ -22,13 +55,16 @@ public class FPSetFactoryTest extends TestCase { doTestGetFPSet(MSBDiskFPSet.class, fpSetConfiguration); } + @Test public void testGetFPSetLSB() throws RemoteException { System.setProperty(FPSetFactory.IMPL_PROPERTY, LSBDiskFPSet.class.getName()); final FPSetConfiguration fpSetConfiguration = new FPSetConfiguration(); doTestGetFPSet(LSBDiskFPSet.class, fpSetConfiguration); } + @Test public void testGetFPSetOffHeap() throws RemoteException { + Assume.assumeTrue(TLCRuntime.getInstance().getArchitecture() == TLCRuntime.ARCH.x86_64); System.setProperty(FPSetFactory.IMPL_PROPERTY, OffHeapDiskFPSet.class.getName()); final FPSetConfiguration fpSetConfiguration = new FPSetConfiguration(); doTestGetFPSet(OffHeapDiskFPSet.class, fpSetConfiguration); @@ -36,6 +72,7 @@ public class FPSetFactoryTest extends TestCase { /* Test single FPSet with explicit memory */ + @Test public void testGetFPSetMSBWithMem() throws RemoteException { // Explicitly set MSBDiskFPSet to overwrite any previous setting (if any) System.setProperty(FPSetFactory.IMPL_PROPERTY, MSBDiskFPSet.class.getName()); @@ -44,13 +81,11 @@ public class FPSetFactoryTest extends TestCase { fpSetConfiguration.setRatio(1.0d); FPSet fpSet = doTestGetFPSet(MSBDiskFPSet.class, fpSetConfiguration); assertEquals(MEMORY, fpSet.getConfiguration().getMemoryInBytes()); - - // non-multifpsets can be cast to diskfpsets to expose statistics - // interface - final DiskFPSet diskFPSet = (DiskFPSet) fpSet; - assertTrue((MEMORY / FPSet.LongSize) > diskFPSet.getMaxTblCnt()); + + doTestNested(MSBDiskFPSet.class, fpSetConfiguration, (MultiFPSet) fpSet); } + @Test public void testGetFPSetLSBWithMem() throws RemoteException { System.setProperty(FPSetFactory.IMPL_PROPERTY, LSBDiskFPSet.class.getName()); final FPSetConfiguration fpSetConfiguration = new FPSetConfiguration(); @@ -59,30 +94,12 @@ public class FPSetFactoryTest extends TestCase { FPSet fpSet = doTestGetFPSet(LSBDiskFPSet.class, fpSetConfiguration); assertEquals(MEMORY, fpSet.getConfiguration().getMemoryInBytes()); - // non-multifpsets can be cast to diskfpsets to expose statistics - // interface - final DiskFPSet diskFPSet = (DiskFPSet) fpSet; - // LSB implementation can only allocate half of its memory for primary - // fingerprint storage (see auxiliary storage) - assertTrue(((MEMORY / FPSet.LongSize) / 2) > diskFPSet.getMaxTblCnt()); - } - - public void testGetFPSetOffHeapWithMem() throws RemoteException { - System.setProperty(FPSetFactory.IMPL_PROPERTY, OffHeapDiskFPSet.class.getName()); - final FPSetConfiguration fpSetConfiguration = new FPSetConfiguration(); - fpSetConfiguration.setMemory(MEMORY); - fpSetConfiguration.setRatio(1.0d); - FPSet fpSet = doTestGetFPSet(OffHeapDiskFPSet.class, fpSetConfiguration); - assertEquals(MEMORY, fpSet.getConfiguration().getMemoryInBytes()); - - // non-multifpsets can be cast to diskfpsets to expose statistics - // interface - final DiskFPSet diskFPSet = (DiskFPSet) fpSet; - assertEquals((MEMORY / FPSet.LongSize), diskFPSet.getMaxTblCnt()); + doTestNested(LSBDiskFPSet.class, fpSetConfiguration, (MultiFPSet) fpSet); } /* Test single FPSet with explicit memory and ratio */ + @Test public void testGetFPSetMSBWithMemAndRatio() throws RemoteException { // Explicitly set MSBDiskFPSet to overwrite any previous setting (if any) System.setProperty(FPSetFactory.IMPL_PROPERTY, MSBDiskFPSet.class.getName()); @@ -91,13 +108,11 @@ public class FPSetFactoryTest extends TestCase { fpSetConfiguration.setRatio(.5d); FPSet fpSet = doTestGetFPSet(MSBDiskFPSet.class, fpSetConfiguration); assertEquals(MEMORY / 2, fpSet.getConfiguration().getMemoryInBytes()); - - // non-multifpsets can be cast to diskfpsets to expose statistics - // interface - final DiskFPSet diskFPSet = (DiskFPSet) fpSet; - assertTrue((MEMORY / FPSet.LongSize) > diskFPSet.getMaxTblCnt()); + + doTestNested(MSBDiskFPSet.class, fpSetConfiguration, (MultiFPSet) fpSet); } + @Test public void testGetFPSetLSBWithMemAndRatio() throws RemoteException { System.setProperty(FPSetFactory.IMPL_PROPERTY, LSBDiskFPSet.class.getName()); final FPSetConfiguration fpSetConfiguration = new FPSetConfiguration(); @@ -106,43 +121,24 @@ public class FPSetFactoryTest extends TestCase { FPSet fpSet = doTestGetFPSet(LSBDiskFPSet.class, fpSetConfiguration); assertEquals(MEMORY / 2, fpSet.getConfiguration().getMemoryInBytes()); - // non-multifpsets can be cast to diskfpsets to expose statistics - // interface - final DiskFPSet diskFPSet = (DiskFPSet) fpSet; - // LSB implementation can only allocate half of its memory for primary - // fingerprint storage (see auxiliary storage) - assertTrue((MEMORY / FPSet.LongSize) > diskFPSet.getMaxTblCnt()); - } - - public void testGetFPSetOffHeapWithMemAndRatio() throws RemoteException { - System.setProperty(FPSetFactory.IMPL_PROPERTY, OffHeapDiskFPSet.class.getName()); - final FPSetConfiguration fpSetConfiguration = new FPSetConfiguration(); - fpSetConfiguration.setMemory(MEMORY); - fpSetConfiguration.setRatio(.5d); - FPSet fpSet = doTestGetFPSet(OffHeapDiskFPSet.class, fpSetConfiguration); - - // Offheap allocates all 100% of memory as it's the only consumer of - // non-heap memory - assertEquals(MEMORY, fpSet.getConfiguration().getMemoryInBytes()); - // non-multifpsets can be cast to diskfpsets to expose statistics - // interface - final DiskFPSet diskFPSet = (DiskFPSet) fpSet; - assertEquals((MEMORY / FPSet.LongSize), diskFPSet.getMaxTblCnt()); + doTestNested(LSBDiskFPSet.class, fpSetConfiguration, (MultiFPSet) fpSet); } /* Test MultiFPSet with default memory */ + @Test public void testGetFPSetMultiFPSet() throws RemoteException { // Explicitly set MSBDiskFPSet to overwrite any previous setting (if any) System.setProperty(FPSetFactory.IMPL_PROPERTY, MSBDiskFPSet.class.getName()); final FPSetConfiguration fpSetConfiguration = new FPSetConfiguration(); fpSetConfiguration.setFpBits(1); - final MultiFPSet mFPSet = (MultiFPSet) doTestGetFPSet(MSBMultiFPSet.class, fpSetConfiguration); + final MultiFPSet mFPSet = (MultiFPSet) doTestGetFPSet(MultiFPSet.class, fpSetConfiguration); doTestNested(MSBDiskFPSet.class, fpSetConfiguration, mFPSet); } + @Test public void testGetFPSetLSBMultiFPSet() throws RemoteException { System.setProperty(FPSetFactory.IMPL_PROPERTY, LSBDiskFPSet.class.getName()); final FPSetConfiguration fpSetConfiguration = new FPSetConfiguration(); @@ -152,17 +148,20 @@ public class FPSetFactoryTest extends TestCase { doTestNested(LSBDiskFPSet.class, fpSetConfiguration, mFPSet); } + @Test public void testGetFPSetOffHeapMultiFPSet() throws RemoteException { + Assume.assumeTrue(TLCRuntime.getInstance().getArchitecture() == TLCRuntime.ARCH.x86_64); System.setProperty(FPSetFactory.IMPL_PROPERTY, OffHeapDiskFPSet.class.getName()); final FPSetConfiguration fpSetConfiguration = new FPSetConfiguration(); fpSetConfiguration.setFpBits(1); - final MultiFPSet mFPSet = (MultiFPSet) doTestGetFPSet(MSBMultiFPSet.class, fpSetConfiguration); + final MultiFPSet mFPSet = (MultiFPSet) doTestGetFPSet(MultiFPSet.class, fpSetConfiguration); doTestNested(OffHeapDiskFPSet.class, fpSetConfiguration, mFPSet); } /* Test MultiFPSet with explicit memory */ + @Test public void testGetFPSetMultiFPSetWithMem() throws RemoteException { // Explicitly set MSBDiskFPSet to overwrite any previous setting (if any) System.setProperty(FPSetFactory.IMPL_PROPERTY, MSBDiskFPSet.class.getName()); @@ -170,11 +169,12 @@ public class FPSetFactoryTest extends TestCase { fpSetConfiguration.setMemory(MEMORY); fpSetConfiguration.setFpBits(1); fpSetConfiguration.setRatio(1.0d); - final MultiFPSet mFPSet = (MultiFPSet) doTestGetFPSet(MSBMultiFPSet.class, fpSetConfiguration); + final MultiFPSet mFPSet = (MultiFPSet) doTestGetFPSet(MultiFPSet.class, fpSetConfiguration); doTestNested(MSBDiskFPSet.class, fpSetConfiguration, mFPSet); } + @Test public void testGetFPSetLSBMultiFPSetWithMem() throws RemoteException { System.setProperty(FPSetFactory.IMPL_PROPERTY, LSBDiskFPSet.class.getName()); final FPSetConfiguration fpSetConfiguration = new FPSetConfiguration(); @@ -186,22 +186,60 @@ public class FPSetFactoryTest extends TestCase { doTestNested(LSBDiskFPSet.class, fpSetConfiguration, mFPSet); } + @Test public void testGetFPSetOffHeapMultiFPSetWithMem() throws RemoteException { + Assume.assumeTrue(TLCRuntime.getInstance().getArchitecture() == TLCRuntime.ARCH.x86_64); System.setProperty(FPSetFactory.IMPL_PROPERTY, OffHeapDiskFPSet.class.getName()); final FPSetConfiguration fpSetConfiguration = new FPSetConfiguration(); fpSetConfiguration.setMemory(MEMORY); fpSetConfiguration.setFpBits(1); fpSetConfiguration.setRatio(1.0d); - final MultiFPSet mFPSet = (MultiFPSet) doTestGetFPSet(MSBMultiFPSet.class, fpSetConfiguration); + final MultiFPSet mFPSet = (MultiFPSet) doTestGetFPSet(MultiFPSet.class, fpSetConfiguration); doTestNested(OffHeapDiskFPSet.class, fpSetConfiguration, mFPSet); } - + + @Test + public void testGetFPSetOffHeapMultiFPSet42() throws RemoteException { + System.setProperty(FPSetFactory.IMPL_PROPERTY, OffHeapDiskFPSet.class.getName()); + + final long nonHeapPhysicalMemory = TLCRuntime.getInstance().getNonHeapPhysicalMemory(); + + final FPSetConfiguration fpSetConfiguration = new FPSetConfiguration(); + assertEquals(nonHeapPhysicalMemory, fpSetConfiguration.getMemoryInBytes()); + assertEquals(nonHeapPhysicalMemory / FPSet.LongSize, fpSetConfiguration.getMemoryInFingerprintCnt()); + + final MultiFPSet mFPSet = (MultiFPSet) doTestGetFPSet(MultiFPSet.class, fpSetConfiguration); + + final FPSetConfiguration multiConfig = mFPSet.getConfiguration(); + assertEquals(OffHeapDiskFPSet.class.getName(), multiConfig.getImplementation()); + assertEquals(1, multiConfig.getFpBits()); + assertEquals(2, multiConfig.getMultiFPSetCnt()); + assertEquals(nonHeapPhysicalMemory, multiConfig.getMemoryInBytes()); + assertEquals(nonHeapPhysicalMemory / FPSet.LongSize, multiConfig.getMemoryInFingerprintCnt()); + + final FPSet[] fpSets = mFPSet.getFPSets(); + assertEquals(2, fpSets.length); + + for (FPSet fpSet : fpSets) { + final OffHeapDiskFPSet offFPset = (OffHeapDiskFPSet) fpSet; + final FPSetConfiguration offConfig = offFPset.getConfiguration(); + assertEquals(OffHeapDiskFPSet.class.getName(), offConfig.getImplementation()); + assertEquals(1, offConfig.getFpBits()); + assertEquals(2, offConfig.getMultiFPSetCnt()); + assertEquals(nonHeapPhysicalMemory / 2L, offConfig.getMemoryInBytes()); + assertEquals((nonHeapPhysicalMemory / FPSet.LongSize) / 2L, offConfig.getMemoryInFingerprintCnt()); + assertEquals((offConfig.getMemoryInBytes() / FPSet.LongSize), offConfig.getMemoryInFingerprintCnt()); + } + } + /* Helper methods */ private FPSet doTestGetFPSet(final Class<? extends FPSet> class1, final FPSetConfiguration fpSetConfig) throws RemoteException, NoSuchObjectException { final FPSet fpSet = FPSetFactory.getFPSet(fpSetConfig); - assertTrue(class1.isAssignableFrom(fpSet.getClass())); + if (!FPSetFactory.isDiskFPSet(class1.getName())) { + assertTrue(class1.isAssignableFrom(fpSet.getClass())); + } return fpSet; } diff --git a/tlatools/test/tlc2/tool/fp/LongArrayTest.java b/tlatools/test/tlc2/tool/fp/LongArrayTest.java new file mode 100644 index 0000000000000000000000000000000000000000..a697289756087f1b88734efd197fe9627e24a9a1 --- /dev/null +++ b/tlatools/test/tlc2/tool/fp/LongArrayTest.java @@ -0,0 +1,212 @@ +/******************************************************************************* + * Copyright (c) 2016 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.fp; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Random; + +import org.junit.Assume; +import org.junit.Before; +import org.junit.Test; + +import util.TLCRuntime; + +public class LongArrayTest { + + @Before + public void setup() { + Assume.assumeTrue(TLCRuntime.getInstance().getArchitecture() == TLCRuntime.ARCH.x86_64); + } + + @Test + public void testGetAndSet() throws IOException { + final int elements = 100; + + final LongArray array = new LongArray(elements); + array.zeroMemory(); + + for (long i = 0L; i < elements; i++) { + assertEquals(0L, array.get(i)); + } + + + for (long i = 0L; i < elements; i++) { + array.set(i, i); + } + for (long i = 0L; i < elements; i++) { + assertEquals(i, array.get(i)); + } + + + for (long i = 0L; i < elements; i++) { + array.set(i, Long.MAX_VALUE - i); + } + for (long i = 0L; i < elements; i++) { + assertEquals(Long.MAX_VALUE - i, array.get(i)); + } + + + for (long i = 0L; i < elements; i++) { + array.set(i, Long.MIN_VALUE + i); + } + for (long i = 0L; i < elements; i++) { + assertEquals(Long.MIN_VALUE + i, array.get(i)); + } + } + + @Test + public void testOutOfRangePositive() throws IOException { + final LongArray array = new LongArray(1); + try { + array.get(1); + } catch (AssertionError e) { + return; + } + fail(); + } + + @Test + public void testOutOfRangeNegative() throws IOException { + final LongArray array = new LongArray(1); + try { + array.get(-1); + } catch (AssertionError e) { + return; + } + fail(); + } + + @Test + public void testGetAndTrySet() throws IOException { + final int elements = 100; + + final LongArray array = new LongArray(elements); + array.zeroMemory(); + + // Assert zero successful + for (long i = 0L; i < elements; i++) { + assertEquals(0L, array.get(i)); + } + + // trySet linear elements. + for (long i = 0L; i < elements; i++) { + assertTrue(array.trySet(i, 0, i)); + } + for (long i = 0L; i < elements; i++) { + assertEquals(i, array.get(i)); + } + + // Replace with largest possible values + for (long i = 0L; i < elements; i++) { + array.trySet(i, i, Long.MAX_VALUE - i); + } + for (long i = 0L; i < elements; i++) { + assertEquals(Long.MAX_VALUE - i, array.get(i)); + } + + + // Replace with smallest possible values + for (long i = 0L; i < elements; i++) { + array.trySet(i, Long.MAX_VALUE - i, Long.MIN_VALUE + i); + } + for (long i = 0L; i < elements; i++) { + assertEquals(Long.MIN_VALUE + i, array.get(i)); + } + } + + @Test + public void testZeroMemory() throws IOException { + for (int k = 1; k < 8; k++) { + for (int i = 1; i < 128; i++) { + final LongArray array = new LongArray(i); + array.zeroMemory(k); + for (int j = 0; i < j; i++) { + assertEquals(0L, array.get(j)); + } + for (int j = 0; i < j; i++) { + array.set(j, -1L); + } + } + } + } + + @Test + public void testSwap() throws IOException { + final int elements = 10321; + + final LongArray array = new LongArray(elements); + array.zeroMemory(); + + for (long i = 0L; i < elements; i++) { + long value = Long.MAX_VALUE - i; + array.set(i, value); + } + + for (int i = 0; i < (elements / 2); i++) { + array.swapCopy(i, (elements - 1) - i); + } + + for (long i = 0L; i < elements; i++) { + assertEquals(Long.MAX_VALUE - (elements -1) + i, array.get(i)); + } + } + + @Test + public void testSwapRandom() throws IOException { + final int elements = 21383; + + final List<Long> vals = new ArrayList<Long>(); + final Random rnd = new Random(); + + for (int i = 0; i < elements; i++) { + vals.add(rnd.nextLong()); + } + + final LongArray array = new LongArray(elements); + array.zeroMemory(); + + for (int i = 0; i < elements; i++) { + array.set(i, vals.get(i)); + } + + for (int i = 0; i < (elements / 2); i++) { + array.swapCopy(i, (elements - 1) - i); + } + + Collections.reverse(vals); + + for (int i = 0; i < elements; i++) { + assertEquals((long) vals.get(i), array.get(i)); + } + } +} diff --git a/tlatools/test/tlc2/tool/fp/LongArraysTest.java b/tlatools/test/tlc2/tool/fp/LongArraysTest.java new file mode 100644 index 0000000000000000000000000000000000000000..9713ff757e698b1965a524934c0cad33acb0bda0 --- /dev/null +++ b/tlatools/test/tlc2/tool/fp/LongArraysTest.java @@ -0,0 +1,874 @@ +// Copyright (c) 2016 Markus Alexander Kuppe. All rights reserved. +package tlc2.tool.fp; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; +import static tlc2.tool.fp.OffHeapDiskFPSet.EMPTY; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.Assume; +import org.junit.Before; +import org.junit.Test; + +import tlc2.tool.fp.LongArrays.LongComparator; +import tlc2.tool.fp.OffHeapDiskFPSet.Indexer; + +public class LongArraysTest { + + @Before + public void setup() { + Assume.assumeTrue(LongArray.isSupported()); + } + + @Test + public void testEmpty1() { + doTest(new ArrayList<Long>(0), 1L, 0, new OffHeapDiskFPSet.Indexer(0, 1)); + } + + @Test + public void testEmpty2() { + final List<Long> expected = new ArrayList<Long>(); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + + doTest(expected, 1L, 2, new OffHeapDiskFPSet.Indexer(expected.size(), 1)); + } + + @Test + public void testBasic1() { + final List<Long> expected = new ArrayList<Long>(); + expected.add(5L); + expected.add(8L); + expected.add(1L); + expected.add(7L); + expected.add(0L); + expected.add(3L); + final LongArray array = new LongArray(expected); + LongArrays.sort(array); + + // This amounts to a regular/basic insertion sort because there are no + // sentinels in the array. doTest fails for this array, because the + // indices calculated by the indexer are invalid. + for (int i = 1; i < array.size(); i++) { + assertTrue(array.get(i - 1L) < array.get(i)); + } + } + + @Test + public void testBasic2() { + final List<Long> expected = new ArrayList<Long>(); + expected.add(74236458333421747L); + expected.add(9185197375878056627L); + expected.add(9017810141411942826L); + expected.add(481170446028802552L); + expected.add(587723185270146839L); + expected.add(764880467681476738L); + expected.add(1028380228728529428L); + expected.add(1246117495100367611L); + expected.add(1353681884824400499L); + expected.add(1963327988900916594L); + expected.add(2157942654452711468L); + expected.add(2211701751588391467L); + expected.add(2197266581704230150L); + expected.add(2391118405386569995L); + expected.add(2754416910109403115L); + expected.add(3528296600587602855L); + expected.add(3766154305485605955L); + expected.add(4172091881329434331L); + expected.add(4273360576593753745L); + expected.add(4338054185482857322L); + expected.add(4487790251341705673L); + expected.add(4760603841378765728L); + expected.add(4897534821030901381L); + expected.add(5057347369431494228L); + expected.add(5185984701076703188L); + expected.add(5255556356599253415L); + expected.add(4911921657882287345L); + expected.add(5512811886280168498L); + expected.add(5627022814159167180L); + expected.add(5630009759945037387L); + expected.add(5592096823142754761L); + expected.add(5880489878946290534L); + expected.add(6796173646113527960L); + expected.add(6887096685265647763L); + expected.add(6946033094922439935L); + expected.add(7100083311060830826L); + expected.add(7575172208974668528L); + expected.add(8240485391672917634L); + expected.add(8572429495433200993L); + expected.add(8804495173596718076L); + expected.add(8771524479740786626L); + expected.add(8986659781390119011L); + expected.add(9136953010061430590L); + expected.add(9195197379878056627L); + final LongArray array = new LongArray(expected); + LongArrays.sort(array); + + // This amounts to a regular/basic insertion sort because there are no + // sentinels in the array. doTest fails for this array, because the + // indices calculated by the indexer are invalid. + for (int i = 1; i < array.size(); i++) { + assertTrue(array.get(i - 1L) < array.get(i)); + } + } + + @Test + public void test0() { + final List<Long> expected = new ArrayList<Long>(); + expected.add(22102288204167208L); + expected.add(225160948165161873L); + expected.add(0L); + expected.add(1638602644344629957L); + expected.add(1644442600000000000L); + expected.add(0L); + + doTest(expected, 1L, 3, new OffHeapDiskFPSet.Indexer(expected.size(), 1)); + } + + @Test + public void test1() { + final List<Long> expected = new ArrayList<Long>(); + expected.add(22102288204167208L); + expected.add(225160948165161873L); + expected.add(0L); + expected.add(0L); + expected.add(810435887525385357L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(1638602644344629957L); + expected.add(0L); + expected.add(0L); + expected.add(2068351286375637679L); + expected.add(0L); + expected.add(2528370576879701538L); + expected.add(2453870502940122045L); + expected.add(0L); + expected.add(3145830401686811393L); + expected.add(3192897355035876677L); + expected.add(3527505876050247287L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(4563398963865761585L); + expected.add(0L); + expected.add(4858869653769863593L); + expected.add(5180223017321191209L); + expected.add(0L); + expected.add(0L); + expected.add(5635076245116608576L); + expected.add(5649139415351271641L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(6703691584433488410L); + expected.add(0L); + expected.add(7143040549630863225L); + expected.add(7205281130519852628L); + expected.add(7012967342342885117L); + expected.add(7709106021212022085L); + expected.add(7908712604546919197L); + expected.add(7246110956693059329L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(8781691546738212390L); + expected.add(8897195185152846807L); + expected.add(0L); + + doTest(expected); + } + + @Test + public void test2() { + final List<Long> expected = new ArrayList<Long>(); + expected.add(0L); + expected.add(0L); + expected.add(22102288204167208L); + expected.add(225160948165161873L); + expected.add(810435887525385357L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(1638602644344629957L); + expected.add(0L); + expected.add(0L); + expected.add(2068351286375637679L); + expected.add(0L); + expected.add(2528370576879701538L); + expected.add(2453870502940122045L); + expected.add(0L); + expected.add(3145830401686811393L); + expected.add(3192897355035876677L); + expected.add(3527505876050247287L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(4563398963865761585L); + expected.add(0L); + expected.add(4858869653769863593L); + expected.add(5180223017321191209L); + expected.add(0L); + expected.add(0L); + expected.add(5635076245116608576L); + expected.add(5649139415351271641L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(6703691584433488410L); + expected.add(0L); + expected.add(7143040549630863225L); + expected.add(7205281130519852628L); + expected.add(7012967342342885117L); + expected.add(7709106021212022085L); + expected.add(7908712604546919197L); + expected.add(7246110956693059329L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(8781691546738212390L); + expected.add(8897195185152846807L); + expected.add(0L); + + doTest(expected); + } + + @Test + public void test3() { + final List<Long> expected = new ArrayList<Long>(); + expected.add(9183932681676589496L); + expected.add(0L); + expected.add(0L); + expected.add(329728050397015749L); + expected.add(436139026681109109L); + expected.add(556905678415593173L); + expected.add(0L); + expected.add(796460649423573389L); + expected.add(797798112015065380L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(1632374027957690827L); + expected.add(1756811852021281877L); + expected.add(0L); + expected.add(1881448932687659007L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(2342821865031748924L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(2736147834640710575L); + expected.add(2864022862265935958L); + expected.add(2773542629236699928L); + expected.add(2957298868366608281L); + expected.add(0L); + expected.add(3330257111892751888L); + expected.add(3295675356431597478L); + expected.add(3395836867027940583L); + expected.add(3681469222400184316L); + expected.add(3754947896063147473L); + expected.add(3698681814958844261L); + expected.add(3951382885893085878L); + expected.add(0L); + expected.add(4188454649677385650L); + expected.add(4129247165607948084L); + expected.add(4365409305525871332L); + expected.add(4526757821913904014L); + expected.add(4254202026550171921L); + expected.add(4557871951994955815L); + expected.add(4806497834029622101L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(5236202638577037427L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(5936146187640212534L); + expected.add(0L); + expected.add(6127434886073515781L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(6547025209145878563L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(6931928829149329960L); + expected.add(0L); + expected.add(0L); + expected.add(7244186580741581738L); + expected.add(0L); + expected.add(0L); + expected.add(7634041392899269082L); + expected.add(7590982629575593986L); + expected.add(0L); + expected.add(7954723745221262664L); + expected.add(0L); + expected.add(8156105620374757718L); + expected.add(8305398393196381769L); + expected.add(8318253237689249492L); + expected.add(8487954051864981042L); + expected.add(8411933954485687818L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(9175849669163144218L); + + doTest(expected); + } + + @Test + public void test4() { + final List<Long> expected = new ArrayList<Long>(); + expected.add(9136953010061430590L); + expected.add(74236458333421747L); + expected.add(0L); + expected.add(0L); + expected.add(481170446028802552L); + expected.add(587723185270146839L); + expected.add(0L); + expected.add(764880467681476738L); + expected.add(0L); + expected.add(0L); + expected.add(1028380228728529428L); + expected.add(0L); + expected.add(1246117495100367611L); + expected.add(1353681884824400499L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(1963327988900916594L); + expected.add(0L); + expected.add(2157942654452711468L); + expected.add(2211701751588391467L); + expected.add(2197266581704230150L); + expected.add(2391118405386569995L); + expected.add(0L); + expected.add(0L); + expected.add(2754416910109403115L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(3528296600587602855L); + expected.add(0L); + expected.add(3766154305485605955L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(4172091881329434331L); + expected.add(4273360576593753745L); + expected.add(4338054185482857322L); + expected.add(4487790251341705673L); + expected.add(0L); + expected.add(4760603841378765728L); + expected.add(0L); + expected.add(4897534821030901381L); + expected.add(5057347369431494228L); + expected.add(5185984701076703188L); + expected.add(5255556356599253415L); + expected.add(4911921657882287345L); + expected.add(5512811886280168498L); + expected.add(5627022814159167180L); + expected.add(5630009759945037387L); + expected.add(5592096823142754761L); + expected.add(5880489878946290534L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(6796173646113527960L); + expected.add(6887096685265647763L); + expected.add(6946033094922439935L); + expected.add(7100083311060830826L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(7575172208974668528L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(8240485391672917634L); + expected.add(0L); + expected.add(0L); + expected.add(8572429495433200993L); + expected.add(0L); + expected.add(8804495173596718076L); + expected.add(8771524479740786626L); + expected.add(8986659781390119011L); + expected.add(9017810141411942826L); + expected.add(9195197379878056627L); + + doTest(expected); + } + + @Test + public void test5() { + final List<Long> expected = new ArrayList<Long>(); + expected.add(9185197375878056627L); + expected.add(74236458333421747L); + expected.add(9017810141411942826L); + expected.add(0L); + expected.add(481170446028802552L); + expected.add(587723185270146839L); + expected.add(0L); + expected.add(764880467681476738L); + expected.add(0L); + expected.add(0L); + expected.add(1028380228728529428L); + expected.add(0L); + expected.add(1246117495100367611L); + expected.add(1353681884824400499L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(1963327988900916594L); + expected.add(0L); + expected.add(2157942654452711468L); + expected.add(2211701751588391467L); + expected.add(2197266581704230150L); + expected.add(2391118405386569995L); + expected.add(0L); + expected.add(0L); + expected.add(2754416910109403115L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(3528296600587602855L); + expected.add(0L); + expected.add(3766154305485605955L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(4172091881329434331L); + expected.add(4273360576593753745L); + expected.add(4338054185482857322L); + expected.add(4487790251341705673L); + expected.add(0L); + expected.add(4760603841378765728L); + expected.add(0L); + expected.add(4897534821030901381L); + expected.add(5057347369431494228L); + expected.add(5185984701076703188L); + expected.add(5255556356599253415L); + expected.add(4911921657882287345L); + expected.add(5512811886280168498L); + expected.add(5627022814159167180L); + expected.add(5630009759945037387L); + expected.add(5592096823142754761L); + expected.add(5880489878946290534L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(6796173646113527960L); + expected.add(6887096685265647763L); + expected.add(6946033094922439935L); + expected.add(7100083311060830826L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(7575172208974668528L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(8240485391672917634L); + expected.add(0L); + expected.add(0L); + expected.add(8572429495433200993L); + expected.add(0L); + expected.add(8804495173596718076L); + expected.add(8771524479740786626L); + expected.add(8986659781390119011L); + expected.add(9136953010061430590L); + expected.add(9195197379878056627L); + + doTest(expected); + } + + @Test + public void test6() { + final List<Long> expected = new ArrayList<Long>(); + expected.add(1L); + expected.add(9185197375878056627L); + expected.add(9017810141411942826L); + expected.add(0L); + expected.add(481170446028802552L); + expected.add(587723185270146839L); + expected.add(0L); + expected.add(764880467681476738L); + expected.add(0L); + expected.add(0L); + expected.add(1028380228728529428L); + expected.add(0L); + expected.add(1246117495100367611L); + expected.add(1353681884824400499L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(1963327988900916594L); + expected.add(0L); + expected.add(2157942654452711468L); + expected.add(2211701751588391467L); + expected.add(2197266581704230150L); + expected.add(2391118405386569995L); + expected.add(0L); + expected.add(0L); + expected.add(2754416910109403115L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(3528296600587602855L); + expected.add(0L); + expected.add(3766154305485605955L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(4172091881329434331L); + expected.add(4273360576593753745L); + expected.add(4338054185482857322L); + expected.add(4487790251341705673L); + expected.add(0L); + expected.add(4760603841378765728L); + expected.add(0L); + expected.add(4897534821030901381L); + expected.add(5057347369431494228L); + expected.add(5185984701076703188L); + expected.add(5255556356599253415L); + expected.add(4911921657882287345L); + expected.add(5512811886280168498L); + expected.add(5627022814159167180L); + expected.add(5630009759945037387L); + expected.add(5592096823142754761L); + expected.add(5880489878946290534L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(6796173646113527960L); + expected.add(6887096685265647763L); + expected.add(6946033094922439935L); + expected.add(7100083311060830826L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(7575172208974668528L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(8240485391672917634L); + expected.add(0L); + expected.add(0L); + expected.add(8572429495433200993L); + expected.add(0L); + expected.add(8804495173596718076L); + expected.add(8771524479740786626L); + expected.add(8986659781390119011L); + expected.add(9136953010061430590L); + expected.add(9195197379878056627L); + + doTest(expected); + } + + @Test + public void test7() { + final List<Long> expected = new ArrayList<Long>(); + expected.add(1L); + expected.add(0L); + expected.add(4L); + expected.add(0L); + expected.add(6L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(13L); + + doTest(expected, 1, 0, new OffHeapDiskFPSet.Indexer(expected.size(), 1, 13)); + } + + @Test + public void test8() { + final List<Long> expected = new ArrayList<Long>(); + expected.add(1L); + expected.add(11L); + expected.add(3L); + expected.add(4L); + expected.add(5L); + expected.add(6L); + expected.add(7L); + expected.add(8L); + expected.add(9L); + expected.add(10L); + expected.add(12L); + + final OffHeapDiskFPSet.Indexer indexer = new OffHeapDiskFPSet.Indexer(expected.size(), 1, 12); + final LongArray array = new LongArray(expected); + final LongComparator comparator = getComparator(indexer); + LongArrays.sort(array, 0, array.size() - 1L + 3, comparator); + verify(expected, 3, indexer, array); + + } + + @Test + public void test9a() { + final List<Long> expected = new ArrayList<Long>(); + expected.add(12L); + expected.add(1L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(11L); + + doTest(expected, 1, 2, new OffHeapDiskFPSet.Indexer(expected.size(), 1, 13)); + } + + @Test + public void test9b() { + final List<Long> expected = new ArrayList<Long>(); + expected.add(11L); + expected.add(1L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(12L); + + doTest(expected, 1, 2, new OffHeapDiskFPSet.Indexer(expected.size(), 1, 13)); + } + + @Test + public void test9c() { + final List<Long> expected = new ArrayList<Long>(); + expected.add(1L); + expected.add(12L); + expected.add(0L); + expected.add(0L); + expected.add(0L); + expected.add(11L); + + doTest(expected, 1, 3, new OffHeapDiskFPSet.Indexer(expected.size(), 1, 13)); + } + + private void doTest(final List<Long> expected) { + final Indexer indexer = new OffHeapDiskFPSet.Indexer(expected.size(), 1); + for (int i = 1; i < (expected.size() / 2); i++) { + doTest(expected, i, 2, indexer); + } + } + + private void doTest(final List<Long> expected, final long partitions, final int reprobe, final Indexer indexer) { + final LongArray array = new LongArray(expected); + final LongComparator comparator = getComparator(indexer); + final long length = expected.size() / partitions; + + // Sort each disjunct partition. + for (long i = 0; i < partitions; i++) { + final long start = i * length; + final long end = i + 1L == partitions ? array.size() - 1L: start + length; + LongArrays.sort(array, start, end, comparator); + } + // Stitch the disjunct partitions together. Only need if more than one + // partition, but done with one partition anyway to see that it causes + // no harm. + for (long i = 0; i < partitions; i++) { + final long end = getEnd(partitions, array, length, i); + LongArrays.sort(array, end - reprobe, end + reprobe, comparator); + } + + verify(expected, reprobe, indexer, array); + } + + private long getEnd(final long partitions, final LongArray array, final long length, long idx) { + return idx + 1L == partitions ? array.size() - 1L: (idx + 1L) * length; + } + + private static LongComparator getComparator(final Indexer indexer) { + return new LongComparator() { + public int compare(final long fpA, final long posA, final long fpB, final long posB) { + // Elements not in Nat \ {0} remain at their current + // position. + if (fpA <= EMPTY || fpB <= EMPTY) { + return 0; + } + + final boolean wrappedA = indexer.getIdx(fpA) > posA; + final boolean wrappedB = indexer.getIdx(fpB) > posB; + + if (wrappedA == wrappedB && posA > posB) { + return fpA < fpB ? -1 : 1; + } else if ((wrappedA ^ wrappedB)) { + if (posA < posB && fpA < fpB) { + // Swap fpB, which is at the end of array a, with fpA. + // fpA is less than fpB. fpB was inserted into array a + // before fpA. + return -1; + } + if (posA > posB && fpA > fpB) { + return -1; + } + } + return 0; + } + }; + } + + private void verify(final List<Long> expected, final int reprobe, final Indexer indexer, final LongArray array) { + // Verify that negative and EMPTY elements remain at their position. + // Lets call them sentinels. + int sentinel = 0; + OUTER: for (int j = 0; j < expected.size(); j++) { + final long l = expected.get(j); + if (l == EMPTY) { + // EMPTY remain at their original positions. + assertEquals(EMPTY, array.get(j)); + sentinel++; + } else if (l < EMPTY) { + // Negative remain at their original positions. + assertEquals(l, array.get(j)); + sentinel++; + } else { + // Verify that all non-sentinels are still + // array members. + for (int k = 0; k < array.size(); k++) { + if (array.get(k) == l) { + continue OUTER; + } + } + fail(String.format("long %s not found.", l)); + } + } + + // Verify elements stayed within their lookup range. + for (int pos = 0; pos < array.size(); pos++) { + final long l = array.get(pos); + if (l <= EMPTY) { + continue; + } + final long idx = indexer.getIdx(l); + assertTrue(String.format("%s, pos: %s, idx: %s, r: %s (was at: %s)", l, pos, idx, reprobe, + expected.indexOf(l)), isInRange(idx, reprobe, pos, array.size())); + } + + // Verify that non-sentinels are sorted is ascending order. Take + // care of wrapped elements too. A) First find the first non-sentinel, + // non-wrapped element. + long pos = 0; + final List<Long> seen = new ArrayList<Long>(expected.size()); + while (pos < array.size()) { + long e = array.get(pos); + if (e <= EMPTY || indexer.getIdx(e) > pos) { + // Either sentinel or wrapped. + pos++; + continue; + } + seen.add(e); + pos++; + break; + } + // B) Collect all elements into seen but skip those at the beginning that + // wrapped, and those that didn't wrap at the end (array.size + reprobe). + for (; pos < array.size() + reprobe; pos++) { + long actual = array.get(pos % array.size()); + if (actual <= EMPTY) { + continue; + } + final long idx = indexer.getIdx(actual); + if (pos < array.size() && idx > pos) { + // When not wrapped, ignore elements belonging to the end that wrapped. + continue; + } + if (pos > array.size() - 1L && idx + reprobe < pos) { + // When wrapped, ignore elements at beginning which do not + // belong to the end. + continue; + } + seen.add(actual); + } + // C) Verify that all elements are sorted. + for (int i = 1; i < seen.size(); i++) { + final long lo = seen.get(i - 1); + final long hi = seen.get(i); + assertTrue(String.format("%s > %s", lo, hi), lo < hi); + } + // D) Verify we saw all expected elements. + assertEquals(expected.size() - sentinel, seen.size()); + } + + @Test + public void testIsInRange() { + assertTrue(isInRange(0, 0, 0, 4)); + + assertFalse(isInRange(0, 0, 1, 4)); + assertFalse(isInRange(0, 0, 2, 4)); + assertFalse(isInRange(0, 0, 3, 4)); + assertFalse(isInRange(0, 0, 4, 4)); + + assertTrue(isInRange(0, 1, 1, 4)); + assertFalse(isInRange(0, 1, 2, 4)); + assertTrue(isInRange(0, 2, 2, 4)); + assertFalse(isInRange(0, 2, 3, 4)); + assertTrue(isInRange(0, 3, 3, 4)); + assertFalse(isInRange(0, 3, 4, 4)); + + assertTrue(isInRange(3, 0, 3, 4)); + assertTrue(isInRange(3, 1, 0, 4)); + assertTrue(isInRange(3, 2, 1, 4)); + assertFalse(isInRange(3, 2, 2, 4)); + } + + private static boolean isInRange(long idx, int reprobe, int pos, long size) { + if (idx + reprobe >= size && pos < idx) { + return pos <= (idx + reprobe) % size; + } else { + return idx <= pos && pos <= idx + reprobe; + } + } +} \ No newline at end of file diff --git a/tlatools/test/tlc2/tool/fp/MSBDiskFPSetTest2.java b/tlatools/test/tlc2/tool/fp/MSBDiskFPSetTest2.java index e63d7d08339e639a8ac6905d8ec5e82931e0d13f..ad9daec5719ec4182332b68f91626f695ac0d237 100644 --- a/tlatools/test/tlc2/tool/fp/MSBDiskFPSetTest2.java +++ b/tlatools/test/tlc2/tool/fp/MSBDiskFPSetTest2.java @@ -1,10 +1,17 @@ // Copyright (c) 2012 Markus Alexander Kuppe. All rights reserved. package tlc2.tool.fp; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + import java.io.IOException; import java.rmi.RemoteException; import java.util.NoSuchElementException; +import org.junit.Test; + import tlc2.tool.fp.MSBDiskFPSet.TLCIterator; public class MSBDiskFPSetTest2 extends AbstractHeapBasedDiskFPSetTest { @@ -30,6 +37,7 @@ public class MSBDiskFPSetTest2 extends AbstractHeapBasedDiskFPSetTest { return 1L << 31; } + @Test public void testGetLast() throws IOException { final MSBDiskFPSet msbDiskFPSet = getMSBDiskFPSet(); @@ -62,6 +70,7 @@ public class MSBDiskFPSetTest2 extends AbstractHeapBasedDiskFPSetTest { fail(); } + @Test public void testHighFingerprint1() throws RemoteException, IOException { final MSBDiskFPSet msbDiskFPSet = getMSBDiskFPSet(); assertFalse(msbDiskFPSet.put(9223368718049406096L)); @@ -71,6 +80,7 @@ public class MSBDiskFPSetTest2 extends AbstractHeapBasedDiskFPSetTest { assertTrue(msbDiskFPSet.put(9223368718049406096L)); } + @Test public void testHighFingerprint2() throws RemoteException, IOException { final MSBDiskFPSet msbDiskFPSet = getMSBDiskFPSet(); assertFalse(msbDiskFPSet.put(9223335424116589377L)); @@ -83,6 +93,7 @@ public class MSBDiskFPSetTest2 extends AbstractHeapBasedDiskFPSetTest { /* * Try to get the last element with no elements in the set. */ + @Test public void testGetLastNoBuckets() throws IOException { final MSBDiskFPSet msbDiskFPSet = getMSBDiskFPSet(); @@ -95,39 +106,6 @@ public class MSBDiskFPSetTest2 extends AbstractHeapBasedDiskFPSetTest { } fail(); } - - public void testGetLastWithLowerBound() throws IOException { - final MSBDiskFPSet msbDiskFPSet = getMSBDiskFPSet(); - - // Add the largest possible fingerprint into the fpset. It will end up - // in the largest bucket. Check that the MSB iterator returns it. - final long highFP = 1L << 62; - msbDiskFPSet.put(highFP); - TLCIterator tlcIterator = new MSBDiskFPSet.TLCIterator(msbDiskFPSet.tbl); - assertEquals(highFP, tlcIterator.getLast()); - - // Flush the set to disk (simulating e.g. a checkpoint), a new iterator - // won't find the element anymore because it intentionally only searches - // for elements that are *not* on disk. - msbDiskFPSet.flusher.flushTable(); - new MSBDiskFPSet.TLCIterator(msbDiskFPSet.tbl); - try { - tlcIterator.getLast(); - } catch (NoSuchElementException e) { - // This exception is expected. - - // Now add the smallest possible element into the set. It will end - // up in the smallest bucket. - final long lowFP = 1; - msbDiskFPSet.put(lowFP); - // check that the iterator finds lower bound as the last element. - tlcIterator = new MSBDiskFPSet.TLCIterator(msbDiskFPSet.tbl); - final long lowerBound = highFP - 1L; - assertEquals(lowerBound, tlcIterator.getLast(lowerBound)); - return; - } - fail(); - } private MSBDiskFPSet getMSBDiskFPSet() throws RemoteException, IOException { // Create an MSBDiskFPSet usable in this unit test with memory allocated diff --git a/tlatools/test/tlc2/tool/fp/MultiFPSetTest.java b/tlatools/test/tlc2/tool/fp/MultiFPSetTest.java index 1ed2997cb7a6991c13d1d34f01ff8fefcf52680b..3800893be9604d1b3b65b70d14ae5091bf082b9e 100644 --- a/tlatools/test/tlc2/tool/fp/MultiFPSetTest.java +++ b/tlatools/test/tlc2/tool/fp/MultiFPSetTest.java @@ -1,28 +1,42 @@ // Copyright (c) 2011 Microsoft Corporation. All rights reserved. package tlc2.tool.fp; +import static org.junit.Assert.fail; + +import java.io.File; import java.io.IOException; +import java.util.HashSet; +import java.util.Set; -import junit.framework.TestCase; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; /** * @author Markus Alexander Kuppe */ -public class MultiFPSetTest extends TestCase { +public class MultiFPSetTest { + + protected static final String tmpdir = System.getProperty("java.io.tmpdir") + File.separator + "MultiFPSetTest" + + System.currentTimeMillis(); /* (non-Javadoc) * @see junit.framework.TestCase#setUp() */ - protected void setUp() throws Exception { - super.setUp(); + @Before + public void setUp() throws Exception { + new File(tmpdir).mkdirs(); } /** * Test method for {@link tlc2.tool.fp.MultiFPSet#new}. * @throws IOException Not supposed to happen */ + @Test public void testCTorLowerMin() throws IOException { + System.setProperty(FPSetFactory.IMPL_PROPERTY, MemFPSet.class.getName()); try { + System.setProperty(FPSetFactory.IMPL_PROPERTY, MemFPSet.class.getName()); FPSetConfiguration conf = new FPSetConfiguration(); conf.setFpBits(0); new MultiFPSet(conf); @@ -36,6 +50,7 @@ public class MultiFPSetTest extends TestCase { * Test method for {@link tlc2.tool.fp.MultiFPSet#new}. * @throws IOException Not supposed to happen */ + @Test public void testCTorMin() throws IOException { try { FPSetConfiguration conf = new FPSetConfiguration(); @@ -51,6 +66,7 @@ public class MultiFPSetTest extends TestCase { * Test method for {@link tlc2.tool.fp.MultiFPSet#new}. * @throws IOException Not supposed to happen */ + @Test public void testCTorMax() throws IOException { try { FPSetConfiguration conf = new FPSetConfiguration(); @@ -77,6 +93,7 @@ public class MultiFPSetTest extends TestCase { * Test method for {@link tlc2.tool.fp.MultiFPSet#new}. * @throws IOException Not supposed to happen */ + @Test public void testCTorHigherMax() throws IOException { try { FPSetConfiguration conf = new FPSetConfiguration(); @@ -92,6 +109,7 @@ public class MultiFPSetTest extends TestCase { * Test method for {@link tlc2.tool.fp.MultiFPSet#put(long)}. * @throws IOException Not supposed to happen */ + @Test public void testPutMax() throws IOException { FPSetConfiguration conf = new FPSetConfiguration(); conf.setFpBits(1); @@ -109,6 +127,7 @@ public class MultiFPSetTest extends TestCase { * Test method for {@link tlc2.tool.fp.MultiFPSet#put(long)}. * @throws IOException Not supposed to happen */ + @Test public void testPutMin() throws IOException { FPSetConfiguration conf = new FPSetConfiguration(); conf.setFpBits(1); @@ -126,6 +145,7 @@ public class MultiFPSetTest extends TestCase { * Test method for {@link tlc2.tool.fp.MultiFPSet#put(long)}. * @throws IOException Not supposed to happen */ + @Test public void testPutZero() throws IOException { FPSetConfiguration conf = new FPSetConfiguration(); conf.setFpBits(1); @@ -138,4 +158,623 @@ public class MultiFPSetTest extends TestCase { fail(); } } + + @Test + public void testGetFPSet() throws IOException { + System.setProperty(FPSetFactory.IMPL_PROPERTY, MSBDiskFPSet.class.getName()); + final FPSetConfiguration conf = new FPSetConfiguration(); + conf.setFpBits(1); + + MultiFPSet mfps = new MultiFPSet(conf); + mfps.init(1, tmpdir, "testGetFPSet"); + + final long a = (1L << 62) + 1; // 01...0 + printBinaryString("a01...1", a); + final long b = 1L; // 0...1 + printBinaryString("b00...1", b); + + FPSet aFPSet = mfps.getFPSet(a); + Assert.assertTrue(aFPSet == mfps.getFPSet(b)); + + // Initially neither a nor b are in the set. + Assert.assertFalse(aFPSet.contains(a)); + + Assert.assertFalse(mfps.contains(a)); + Assert.assertFalse(mfps.contains(b)); + + // Add a to the set and verify it's in the + // set and b isn't. + Assert.assertFalse(mfps.put(a)); + Assert.assertTrue(mfps.contains(a)); + Assert.assertFalse(mfps.contains(b)); + + // Add b to the set as well. Now both + // are supposed to be set members. + Assert.assertFalse(mfps.put(b)); + Assert.assertTrue(mfps.contains(a)); + Assert.assertTrue(mfps.contains(b)); + + Assert.assertTrue(aFPSet.contains(a)); + Assert.assertTrue(aFPSet.contains(b)); + Assert.assertEquals(2, aFPSet.size()); + + // Get the other FPSet + FPSet[] fpSets = mfps.getFPSets(); + Set<FPSet> s = new HashSet<FPSet>(); + for (int i = 0; i < fpSets.length; i++) { + s.add(fpSets[i]); + } + s.remove(aFPSet); + FPSet bFPSet = (FPSet) s.toArray()[0]; + + Assert.assertFalse(bFPSet.contains(a)); + Assert.assertFalse(bFPSet.contains(b)); + Assert.assertEquals(0, bFPSet.size()); + + Assert.assertTrue(mfps.checkInvariant()); + } + + @Test + public void testGetFPSet0() throws IOException { + System.setProperty(FPSetFactory.IMPL_PROPERTY, MSBDiskFPSet.class.getName()); + final FPSetConfiguration conf = new FPSetConfiguration(); + conf.setFpBits(1); + + MultiFPSet mfps = new MultiFPSet(conf); + mfps.init(1, tmpdir, "testGetFPSet0"); + + final long a = (1L << 63) + 1; // 10...1 + printBinaryString("a1...1", a); + final long b = 1L; // 00...1 + printBinaryString("b0...1", b); + final long c = (1L << 62) + 1; // 01...1 + printBinaryString("c1...1", c); + final long d = (3L << 62) + 1; // 11...1 + printBinaryString("d0...1", d); + + FPSet aFPSet = mfps.getFPSet(a); + FPSet bFPSet = mfps.getFPSet(b); + Assert.assertTrue(aFPSet != bFPSet); + + // Initially neither a nor b are in the set. + Assert.assertFalse(aFPSet.contains(a)); + Assert.assertFalse(bFPSet.contains(b)); + + Assert.assertFalse(mfps.contains(a)); + Assert.assertFalse(mfps.contains(b)); + Assert.assertFalse(mfps.contains(c)); + Assert.assertFalse(mfps.contains(d)); + + // Add a to the set and verify it's in the + // set and b isn't. + Assert.assertFalse(mfps.put(a)); + Assert.assertTrue(mfps.contains(a)); + Assert.assertFalse(mfps.contains(b)); + Assert.assertFalse(mfps.contains(c)); + Assert.assertFalse(mfps.contains(d)); + + // Add b to the set as well. Now both + // are supposed to be set members. + Assert.assertFalse(mfps.put(b)); + Assert.assertTrue(mfps.contains(a)); + Assert.assertTrue(mfps.contains(b)); + Assert.assertFalse(mfps.contains(c)); + Assert.assertFalse(mfps.contains(d)); + + Assert.assertFalse(mfps.put(c)); + Assert.assertTrue(mfps.contains(a)); + Assert.assertTrue(mfps.contains(b)); + Assert.assertTrue(mfps.contains(c)); + Assert.assertFalse(mfps.contains(d)); + + Assert.assertFalse(mfps.put(d)); + Assert.assertTrue(mfps.contains(a)); + Assert.assertTrue(mfps.contains(b)); + Assert.assertTrue(mfps.contains(c)); + Assert.assertTrue(mfps.contains(d)); + + for (FPSet fpSet : mfps.getFPSets()) { + Assert.assertEquals(2, fpSet.size()); + // Expect to have two buckets + Assert.assertEquals(2, ((FPSetStatistic) fpSet).getTblLoad()); + } + + Assert.assertTrue(mfps.checkInvariant()); + } + + @Test + public void testGetFPSet1() throws IOException { + System.setProperty(FPSetFactory.IMPL_PROPERTY, MSBDiskFPSet.class.getName()); + final FPSetConfiguration conf = new FPSetConfiguration(); + conf.setFpBits(2); + final MultiFPSet mfps = new MultiFPSet(conf); + mfps.init(1, tmpdir, "testGetFPSet1"); + + final long a = 1L; // 00...1 + printBinaryString("a02", a); + final long b = (1L << 62) + 1; // 01...1 + printBinaryString("b02", b); + final long c = (1L << 63) + 1; // 10...1 + printBinaryString("c02", c); + final long d = (3L << 62) + 1; // 11...1 + printBinaryString("d02", d); + + final Set<FPSet> s = new HashSet<FPSet>(); + final FPSet aFPSet = mfps.getFPSet(a); + s.add(aFPSet); + final FPSet bFPSet = mfps.getFPSet(b); + s.add(bFPSet); + final FPSet cFPSet = mfps.getFPSet(c); + s.add(cFPSet); + final FPSet dFPSet = mfps.getFPSet(d); + s.add(dFPSet); + Assert.assertEquals(4, s.size()); + + Assert.assertFalse(mfps.contains(a)); + Assert.assertFalse(mfps.contains(b)); + Assert.assertFalse(mfps.contains(c)); + Assert.assertFalse(mfps.contains(d)); + + Assert.assertFalse(mfps.put(a)); + Assert.assertTrue(mfps.contains(a)); + Assert.assertFalse(mfps.contains(b)); + Assert.assertFalse(mfps.contains(c)); + Assert.assertFalse(mfps.contains(d)); + + Assert.assertFalse(mfps.put(b)); + Assert.assertTrue(mfps.contains(a)); + Assert.assertTrue(mfps.contains(b)); + Assert.assertFalse(mfps.contains(c)); + Assert.assertFalse(mfps.contains(d)); + + Assert.assertFalse(mfps.put(c)); + Assert.assertTrue(mfps.contains(a)); + Assert.assertTrue(mfps.contains(b)); + Assert.assertTrue(mfps.contains(c)); + Assert.assertFalse(mfps.contains(d)); + + Assert.assertFalse(mfps.put(d)); + Assert.assertTrue(mfps.contains(a)); + Assert.assertTrue(mfps.contains(b)); + Assert.assertTrue(mfps.contains(c)); + Assert.assertTrue(mfps.contains(d)); + + for (FPSet fpSet : s) { + Assert.assertEquals(1, fpSet.size()); + // Expect to have two buckets + Assert.assertEquals(1, ((FPSetStatistic) fpSet).getTblLoad()); + } + + // a & c and b & d have collisions at the individual DiskFPSet level. + Assert.assertTrue(aFPSet.contains(a)); + Assert.assertFalse(aFPSet.contains(b)); + Assert.assertTrue(aFPSet.contains(c)); // expected collision + Assert.assertFalse(aFPSet.contains(d)); + + Assert.assertTrue(bFPSet.contains(b)); + Assert.assertFalse(bFPSet.contains(a)); + Assert.assertFalse(bFPSet.contains(c)); + Assert.assertTrue(bFPSet.contains(d)); // expected collision + + Assert.assertTrue(cFPSet.contains(c)); + Assert.assertFalse(cFPSet.contains(b)); + Assert.assertTrue(cFPSet.contains(a)); // expected collision + Assert.assertFalse(cFPSet.contains(d)); + + Assert.assertTrue(dFPSet.contains(d)); + Assert.assertTrue(dFPSet.contains(b)); // expected collision + Assert.assertFalse(dFPSet.contains(c)); + Assert.assertFalse(dFPSet.contains(a)); + + Assert.assertTrue(mfps.checkInvariant()); + } + + @Test + public void testGetFPSetL() throws IOException { + System.setProperty(FPSetFactory.IMPL_PROPERTY, LSBDiskFPSet.class.getName()); + final FPSetConfiguration conf = new FPSetConfiguration(); + conf.setFpBits(1); + + MultiFPSet mfps = new MultiFPSet(conf); + mfps.init(1, tmpdir, "testGetFPSetL"); + + final long a = (1L << 62) + 1; + printBinaryString("a01", a); + final long b = 1L; + printBinaryString("b01", b); + + FPSet aFPSet = mfps.getFPSet(a); + Assert.assertTrue(aFPSet == mfps.getFPSet(b)); + + // Initially neither a nor b are in the set. + Assert.assertFalse(aFPSet.contains(a)); + + Assert.assertFalse(mfps.contains(a)); + Assert.assertFalse(mfps.contains(b)); + + // Add a to the set and verify it's in the + // set and b isn't. + Assert.assertFalse(mfps.put(a)); + Assert.assertTrue(mfps.contains(a)); + Assert.assertFalse(mfps.contains(b)); + + // Add b to the set as well. Now both + // are supposed to be set members. + Assert.assertFalse(mfps.put(b)); + Assert.assertTrue(mfps.contains(a)); + Assert.assertTrue(mfps.contains(b)); + + Assert.assertTrue(aFPSet.contains(a)); + Assert.assertTrue(aFPSet.contains(b)); + Assert.assertEquals(2, aFPSet.size()); + + // Get the other FPSet + FPSet[] fpSets = mfps.getFPSets(); + Set<FPSet> s = new HashSet<FPSet>(); + for (int i = 0; i < fpSets.length; i++) { + s.add(fpSets[i]); + } + s.remove(aFPSet); + FPSet bFPSet = (FPSet) s.toArray()[0]; + + Assert.assertFalse(bFPSet.contains(a)); + Assert.assertFalse(bFPSet.contains(b)); + Assert.assertEquals(0, bFPSet.size()); + + Assert.assertTrue(mfps.checkInvariant()); + } + + @Test + public void testGetFPSet0L() throws IOException { + System.setProperty(FPSetFactory.IMPL_PROPERTY, LSBDiskFPSet.class.getName()); + final FPSetConfiguration conf = new FPSetConfiguration(); + conf.setFpBits(1); + + MultiFPSet mfps = new MultiFPSet(conf); + mfps.init(1, tmpdir, "testGetFPSet0L"); + + final long a = (1L << 63) + 1; + printBinaryString("a01", a); + final long b = 1L; + printBinaryString("b01", b); + + FPSet aFPSet = mfps.getFPSet(a); + FPSet bFPSet = mfps.getFPSet(b); + Assert.assertTrue(aFPSet != bFPSet); + + // Initially neither a nor b are in the set. + Assert.assertFalse(aFPSet.contains(a)); + Assert.assertFalse(bFPSet.contains(b)); + + Assert.assertFalse(mfps.contains(a)); + Assert.assertFalse(mfps.contains(b)); + + // Add a to the set and verify it's in the + // set and b isn't. + Assert.assertFalse(mfps.put(a)); + Assert.assertTrue(mfps.contains(a)); + Assert.assertFalse(mfps.contains(b)); + + // Add b to the set as well. Now both + // are supposed to be set members. + Assert.assertFalse(mfps.put(b)); + Assert.assertTrue(mfps.contains(a)); + Assert.assertTrue(mfps.contains(b)); + + Assert.assertTrue(mfps.checkInvariant()); + } + + @Test + public void testGetFPSet1L() throws IOException { + System.setProperty(FPSetFactory.IMPL_PROPERTY, LSBDiskFPSet.class.getName()); + final FPSetConfiguration conf = new FPSetConfiguration(); + conf.setFpBits(2); + final MultiFPSet mfps = new MultiFPSet(conf); + mfps.init(1, tmpdir, "testGetFPSet1L"); + + final long a = 1L; // 00...1 + printBinaryString("a02", a); + final long b = (1L << 62) + 1; // 01...1 + printBinaryString("b02", b); + final long c = (1L << 63) + 1; // 10...1 + printBinaryString("c02", c); + final long d = (3L << 62) + 1; // 11...1 + printBinaryString("d02", d); + + final Set<FPSet> s = new HashSet<FPSet>(); + final FPSet aFPSet = mfps.getFPSet(a); + s.add(aFPSet); + final FPSet bFPSet = mfps.getFPSet(b); + s.add(bFPSet); + final FPSet cFPSet = mfps.getFPSet(c); + s.add(cFPSet); + final FPSet dFPSet = mfps.getFPSet(d); + s.add(dFPSet); + Assert.assertEquals(4, s.size()); + + Assert.assertFalse(mfps.contains(a)); + Assert.assertFalse(mfps.contains(b)); + Assert.assertFalse(mfps.contains(c)); + Assert.assertFalse(mfps.contains(d)); + + Assert.assertFalse(mfps.put(a)); + Assert.assertTrue(mfps.contains(a)); + Assert.assertFalse(mfps.contains(b)); + Assert.assertFalse(mfps.contains(c)); + Assert.assertFalse(mfps.contains(d)); + + Assert.assertFalse(mfps.put(b)); + Assert.assertTrue(mfps.contains(a)); + Assert.assertTrue(mfps.contains(b)); + Assert.assertFalse(mfps.contains(c)); + Assert.assertFalse(mfps.contains(d)); + + Assert.assertFalse(mfps.put(c)); + Assert.assertTrue(mfps.contains(a)); + Assert.assertTrue(mfps.contains(b)); + Assert.assertTrue(mfps.contains(c)); + Assert.assertFalse(mfps.contains(d)); + + Assert.assertFalse(mfps.put(d)); + Assert.assertTrue(mfps.contains(a)); + Assert.assertTrue(mfps.contains(b)); + Assert.assertTrue(mfps.contains(c)); + Assert.assertTrue(mfps.contains(d)); + + for (FPSet fpSet : s) { + Assert.assertEquals(1, fpSet.size()); + } + + // a & c and b & d have collisions at the individual DiskFPSet level. + Assert.assertTrue(aFPSet.contains(a)); + Assert.assertFalse(aFPSet.contains(b)); + Assert.assertTrue(aFPSet.contains(c)); // expected collision + Assert.assertFalse(aFPSet.contains(d)); + + Assert.assertTrue(bFPSet.contains(b)); + Assert.assertFalse(bFPSet.contains(a)); + Assert.assertFalse(bFPSet.contains(c)); + Assert.assertTrue(bFPSet.contains(d)); // expected collision + + Assert.assertTrue(cFPSet.contains(c)); + Assert.assertFalse(cFPSet.contains(b)); + Assert.assertTrue(cFPSet.contains(a)); // expected collision + Assert.assertFalse(cFPSet.contains(d)); + + Assert.assertTrue(dFPSet.contains(d)); + Assert.assertTrue(dFPSet.contains(b)); // expected collision + Assert.assertFalse(dFPSet.contains(c)); + Assert.assertFalse(dFPSet.contains(a)); + + Assert.assertTrue(mfps.checkInvariant()); + } + + @Test + public void testGetFPSetOffHeap() throws IOException { + if (!System.getProperty("sun.arch.data.model").equals("64")) { + // LongArray only works on 64bit architectures. See comment in + // LongArray ctor. + return; + } + System.setProperty(FPSetFactory.IMPL_PROPERTY, OffHeapDiskFPSet.class.getName()); + final FPSetConfiguration conf = new FPSetConfiguration(); + conf.setFpBits(1); + + MultiFPSet mfps = new MultiFPSet(conf); + mfps.init(1, tmpdir, "testGetFPSetOffHeap"); + + final long a = (1L << 62) + 1; // 01...0 + printBinaryString("a01...1", a); + final long b = 1L; // 0...1 + printBinaryString("b00...1", b); + + FPSet aFPSet = mfps.getFPSet(a); + Assert.assertTrue(aFPSet == mfps.getFPSet(b)); + + // Initially neither a nor b are in the set. + Assert.assertFalse(aFPSet.contains(a)); + + Assert.assertFalse(mfps.contains(a)); + Assert.assertFalse(mfps.contains(b)); + + // Add a to the set and verify it's in the + // set and b isn't. + Assert.assertFalse(mfps.put(a)); + Assert.assertTrue(mfps.contains(a)); + Assert.assertFalse(mfps.contains(b)); + + // Add b to the set as well. Now both + // are supposed to be set members. + Assert.assertFalse(mfps.put(b)); + Assert.assertTrue(mfps.contains(a)); + Assert.assertTrue(mfps.contains(b)); + + Assert.assertTrue(aFPSet.contains(a)); + Assert.assertTrue(aFPSet.contains(b)); + Assert.assertEquals(2, aFPSet.size()); + + // Get the other FPSet + FPSet[] fpSets = mfps.getFPSets(); + Set<FPSet> s = new HashSet<FPSet>(); + for (int i = 0; i < fpSets.length; i++) { + s.add(fpSets[i]); + } + s.remove(aFPSet); + FPSet bFPSet = (FPSet) s.toArray()[0]; + + Assert.assertFalse(bFPSet.contains(a)); + Assert.assertFalse(bFPSet.contains(b)); + Assert.assertEquals(0, bFPSet.size()); + + Assert.assertTrue(mfps.checkInvariant()); + } + + @Test + public void testGetFPSetOffHeap0() throws IOException { + if (!System.getProperty("sun.arch.data.model").equals("64")) { + // LongArray only works on 64bit architectures. See comment in + // LongArray ctor. + return; + } + System.setProperty(FPSetFactory.IMPL_PROPERTY, OffHeapDiskFPSet.class.getName()); + final FPSetConfiguration conf = new FPSetConfiguration(); + conf.setFpBits(1); + + MultiFPSet mfps = new MultiFPSet(conf); + mfps.init(1, tmpdir, "testGetFPSetOffHeap0"); + + final long a = (1L << 63) + 1; // 10...1 + printBinaryString("a1...1", a); + final long b = 1L; // 00...1 + printBinaryString("b0...1", b); + final long c = (1L << 62) + 1; // 01...1 + printBinaryString("c1...1", c); + final long d = (3L << 62) + 1; // 11...1 + printBinaryString("d0...1", d); + + FPSet aFPSet = mfps.getFPSet(a); + FPSet bFPSet = mfps.getFPSet(b); + Assert.assertTrue(aFPSet != bFPSet); + + // Initially neither a nor b are in the set. + Assert.assertFalse(aFPSet.contains(a)); + Assert.assertFalse(bFPSet.contains(b)); + + Assert.assertFalse(mfps.contains(a)); + Assert.assertFalse(mfps.contains(b)); + Assert.assertFalse(mfps.contains(c)); + Assert.assertFalse(mfps.contains(d)); + + // Add a to the set and verify it's in the + // set and b isn't. + Assert.assertFalse(mfps.put(a)); + Assert.assertTrue(mfps.contains(a)); + Assert.assertFalse(mfps.contains(b)); + Assert.assertFalse(mfps.contains(c)); + Assert.assertFalse(mfps.contains(d)); + + // Add b to the set as well. Now both + // are supposed to be set members. + Assert.assertFalse(mfps.put(b)); + Assert.assertTrue(mfps.contains(a)); + Assert.assertTrue(mfps.contains(b)); + Assert.assertFalse(mfps.contains(c)); + Assert.assertFalse(mfps.contains(d)); + + Assert.assertFalse(mfps.put(c)); + Assert.assertTrue(mfps.contains(a)); + Assert.assertTrue(mfps.contains(b)); + Assert.assertTrue(mfps.contains(c)); + Assert.assertFalse(mfps.contains(d)); + + Assert.assertFalse(mfps.put(d)); + Assert.assertTrue(mfps.contains(a)); + Assert.assertTrue(mfps.contains(b)); + Assert.assertTrue(mfps.contains(c)); + Assert.assertTrue(mfps.contains(d)); + + for (FPSet fpSet : mfps.getFPSets()) { + Assert.assertEquals(2, fpSet.size()); + // Expect to have two buckets + Assert.assertEquals(2, ((FPSetStatistic) fpSet).getTblLoad()); + } + + Assert.assertTrue(mfps.checkInvariant()); + } + + @Test + public void testGetFPSetOffHeap1() throws IOException { + if (!System.getProperty("sun.arch.data.model").equals("64")) { + // LongArray only works on 64bit architectures. See comment in + // LongArray ctor. + return; + } + System.setProperty(FPSetFactory.IMPL_PROPERTY, OffHeapDiskFPSet.class.getName()); + final FPSetConfiguration conf = new FPSetConfiguration(); + conf.setFpBits(2); + final MultiFPSet mfps = new MultiFPSet(conf); + mfps.init(1, tmpdir, "testGetFPSetOffHeap1"); + + final long a = 1L; // 00...1 + printBinaryString("a02", a); + final long b = (1L << 62) + 1; // 01...1 + printBinaryString("b02", b); + final long c = (1L << 63) + 1; // 10...1 + printBinaryString("c02", c); + final long d = (3L << 62) + 1; // 11...1 + printBinaryString("d02", d); + + final Set<FPSet> s = new HashSet<FPSet>(); + final FPSet aFPSet = mfps.getFPSet(a); + s.add(aFPSet); + final FPSet bFPSet = mfps.getFPSet(b); + s.add(bFPSet); + final FPSet cFPSet = mfps.getFPSet(c); + s.add(cFPSet); + final FPSet dFPSet = mfps.getFPSet(d); + s.add(dFPSet); + Assert.assertEquals(4, s.size()); + + Assert.assertFalse(mfps.contains(a)); + Assert.assertFalse(mfps.contains(b)); + Assert.assertFalse(mfps.contains(c)); + Assert.assertFalse(mfps.contains(d)); + + Assert.assertFalse(mfps.put(a)); + Assert.assertTrue(mfps.contains(a)); + Assert.assertFalse(mfps.contains(b)); + Assert.assertFalse(mfps.contains(c)); + Assert.assertFalse(mfps.contains(d)); + + Assert.assertFalse(mfps.put(b)); + Assert.assertTrue(mfps.contains(a)); + Assert.assertTrue(mfps.contains(b)); + Assert.assertFalse(mfps.contains(c)); + Assert.assertFalse(mfps.contains(d)); + + Assert.assertFalse(mfps.put(c)); + Assert.assertTrue(mfps.contains(a)); + Assert.assertTrue(mfps.contains(b)); + Assert.assertTrue(mfps.contains(c)); + Assert.assertFalse(mfps.contains(d)); + + Assert.assertFalse(mfps.put(d)); + Assert.assertTrue(mfps.contains(a)); + Assert.assertTrue(mfps.contains(b)); + Assert.assertTrue(mfps.contains(c)); + Assert.assertTrue(mfps.contains(d)); + + for (FPSet fpSet : s) { + Assert.assertEquals(1, fpSet.size()); + // Expect to have two buckets + Assert.assertEquals(1, ((FPSetStatistic) fpSet).getTblLoad()); + } + + // a & c and b & d have collisions at the individual DiskFPSet level. + Assert.assertTrue(aFPSet.contains(a)); + Assert.assertFalse(aFPSet.contains(b)); + Assert.assertTrue(aFPSet.contains(c)); // expected collision + Assert.assertFalse(aFPSet.contains(d)); + + Assert.assertTrue(bFPSet.contains(b)); + Assert.assertFalse(bFPSet.contains(a)); + Assert.assertFalse(bFPSet.contains(c)); + Assert.assertTrue(bFPSet.contains(d)); // expected collision + + Assert.assertTrue(cFPSet.contains(c)); + Assert.assertFalse(cFPSet.contains(b)); + Assert.assertTrue(cFPSet.contains(a)); // expected collision + Assert.assertFalse(cFPSet.contains(d)); + + Assert.assertTrue(dFPSet.contains(d)); + Assert.assertTrue(dFPSet.contains(b)); // expected collision + Assert.assertFalse(dFPSet.contains(c)); + Assert.assertFalse(dFPSet.contains(a)); + + Assert.assertTrue(mfps.checkInvariant()); + } + + private void printBinaryString(final String id, final long a) { +// System.out.println(String.format(id + ":%64s", Long.toBinaryString(a)).replace(' ', '0')); + } } diff --git a/tlatools/test/tlc2/tool/fp/OffHeapDiskFPSetTest.java b/tlatools/test/tlc2/tool/fp/OffHeapDiskFPSetTest.java new file mode 100644 index 0000000000000000000000000000000000000000..3980851c00b6fc2fde73661eed700e38dddba699 --- /dev/null +++ b/tlatools/test/tlc2/tool/fp/OffHeapDiskFPSetTest.java @@ -0,0 +1,330 @@ +/******************************************************************************* + * Copyright (c) 2016 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.fp; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; +import static tlc2.tool.fp.DiskFPSet.MARK_FLUSHED; +import static tlc2.tool.fp.OffHeapDiskFPSet.EMPTY; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.rmi.RemoteException; +import java.util.Arrays; +import java.util.Random; +import java.util.SortedSet; +import java.util.TreeSet; + +import org.junit.Assume; +import org.junit.Before; +import org.junit.Test; + +import util.TLCRuntime; + +public class OffHeapDiskFPSetTest { + + protected static final String filename = "OffHeapDiskFPSetTest"; + + @Before + public void setup() { + Assume.assumeTrue(TLCRuntime.getInstance().getArchitecture() == TLCRuntime.ARCH.x86_64); + } + +// @Test +// public void testInsertAndEvictRnd() throws Exception { +// Random rnd = new Random(); +// for (int i = 0; i < 1000; i++) { +// doTest(System.currentTimeMillis(), rnd.nextInt(255) + 1); +// } +// } + + @Test + public void testInsertAndEvict1() throws Exception { + doTest(1473793977852L, 87); + } + + @Test + public void testInsertAndEvict2() throws Exception { + doTest(1473793976137L, 87); + } + + @Test + public void testInsertAndEvict3() throws Exception { + doTest(1473839150698L, 46); + } + + @Test + public void testInsertAndEvict4() throws Exception { + doTest(1473839150698L, 46); + } + + @Test + public void testInsertAndEvict5() throws Exception { + doTest(1473839322351L, 23); + } + + @Test + public void testInsertAndEvict6() throws Exception { + doTest(1473839380539L, 23); + } + + @Test + public void testInsertAndEvict7() throws Exception { + doTest(1473839422899L, 11); + } + + @Test + public void testInsertAndEvict8() throws Exception { + doTest(1473839543883L, 11); + } + + @Test + public void testInsertAndEvict9() throws Exception { + doTest(1473871461079L, 64); + } + + @Test + public void testInsertAndEvict10() throws Exception { + doTest(1473871462765L, 64); + } + + @Test + public void testInsertAndEvict11() throws Exception { + doTest(1473871522834L, 32); + } + + @Test + public void testInsertAndEvict12() throws Exception { + doTest(1473871526136L, 32); + } + + @Test + public void testInsertAndEvict13() throws Exception { + doTest(1473873732723L, 47); + } + + @Test + public void testInsertAndEvict14() throws Exception { + doTest(1473871294851L, 93); + } + + @Test + public void testInsertAndEvict15() throws Exception { + doTest(1473871365625L, 93); + } + + @Test + public void testInsertAndEvict16() throws Exception { + doTest(1473871209569L, 157); + } + + private void doTest(final long rgenseed, final long length) throws RemoteException, IOException, NoSuchFieldException, IllegalAccessException { + final DummyFPSetConfiguration fpSetConfig = new DummyFPSetConfiguration(); + fpSetConfig.setMemoryInFingerprintCnt(length); + + final DiskFPSet fpSet = new OffHeapDiskFPSet(fpSetConfig); + fpSet.init(1, createTmpFile(), filename); + + // Insert n randomly choosen positive longs. + Random random = new Random(rgenseed); + for (int i = 0; i < length / 2; i++) { + assertFalse(fpSet.put(getFingerprint(random))); + } + + // Get the current content of LongArray for later comparison of special elements. + Field field = OffHeapDiskFPSet.class.getDeclaredField("array"); + field.setAccessible(true); + final long[] expected = LongArrays.toArray((LongArray) field.get(fpSet)); + + // Flush/Evict the first time and assure its successful. + assertTrue(fpSet.getGrowDiskMark() == 0); + fpSet.forceFlush(); + fpSet.contains(1L); // contains triggers eviction + assertTrue(fpSet.getGrowDiskMark() == 1); + + // Special elements (EMPTY or marked evicted) do not change positions + // when sorted. + final LongArray actual = (LongArray) field.get(fpSet); + for (int i = 0; i < expected.length; i++) { + if (expected[i] == EMPTY) { + assertEquals( + String.format( + "Expected empty position with seed %sL and length %s.\n\nexpected: %s\n\nactual: %s", + new Object[] { rgenseed, length, Arrays.toString(expected), actual.toString() }), + EMPTY, actual.get(i)); + } else if (expected[i] < EMPTY) { + assertEquals( + String.format( + "Expected negative position with seed %sL and length %s.\n\nexpected: %s\n\nactual: %s", + new Object[] { rgenseed, length, Arrays.toString(expected), actual.toString() }), + EMPTY, actual.get(i)); + } + } + + random = new Random(rgenseed); + for (int i = 0; i < length / 2; i++) { + final long fp = getFingerprint(random); + assertTrue(String.format("Failed to find fp %s/%s with seed %sL and length %s.\n\nexpected: %s\n\nactual: %s", + new Object[] { fp, (fp | MARK_FLUSHED), rgenseed, length, Arrays.toString(expected), + actual.toString() }), + fpSet.contains(fp)); + } + + assertTrue( + String.format("Invariant violated with seed %sL and length %s.\n\nexpected: %s\n\nactual: %s", + new Object[] { rgenseed, length, Arrays.toString(expected), actual.toString() }), + fpSet.checkInvariant()); + + // Clear the index to secondary/disk which hides secondary from + // DiskFPSet. + field = DiskFPSet.class.getDeclaredField("index"); + field.setAccessible(true); + field.set(fpSet, null); + + // Confirm that even without secondary, it's possible to lookup the + // fingerprints. + random = new Random(rgenseed); + for (int i = 0; i < length / 2; i++) { + final long fp = getFingerprint(random); + assertTrue(String.format("Failed to find fp %s/%s with seed %sL and length %s.\n\nexpected: %s\n\nactual: %s", + new Object[] { fp, (fp | MARK_FLUSHED), rgenseed, length, Arrays.toString(expected), + actual.toString() }), + fpSet.contains(fp)); + } + + fpSet.close(); + } + + @Test + public void testOffset1Page() throws IOException, NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException { + final long length = DiskFPSet.NumEntriesPerPage; + doTestOffset(length, 1474536306841L); + } + + @Test + public void testOffset3Page() throws IOException, NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException { + long length = DiskFPSet.NumEntriesPerPage * 3L; + doTestOffset(length, 1474536306841L); + } + + @Test + public void testOffset5Page() throws IOException, NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException { + long length = DiskFPSet.NumEntriesPerPage * 5L; + doTestOffset(length, 1474536306841L); + } + + @Test + public void testOffset9Page() throws IOException, NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException { + long length = DiskFPSet.NumEntriesPerPage * 9L; + doTestOffset(length, 1474536306841L); + } + + private void doTestOffset(long length, long rgenseed) throws RemoteException, IOException, NoSuchMethodException, + IllegalAccessException, InvocationTargetException { + + final DummyFPSetConfiguration fpSetConfig = new DummyFPSetConfiguration(); + fpSetConfig.setMemoryInFingerprintCnt(length); + + final OffHeapDiskFPSet fpSet = new OffHeapDiskFPSet(fpSetConfig); + fpSet.init(1, createTmpFile(), filename); + + final SortedSet<Long> longs = new TreeSet<Long>(); + + // Insert n randomly chosen positive longs. + Random random = new Random(rgenseed); + for (int i = 0; i < length / 2; i++) { + long fingerprint = getFingerprint(random); + assertFalse(fpSet.put(fingerprint)); + longs.add(fingerprint); + } + fpSet.forceFlush(); + assertFalse(fpSet.contains(1L)); // contains triggers flush + + final Method field = OffHeapDiskFPSet.class.getDeclaredMethod("getDiskOffset", new Class[] {int.class, long.class}); + field.setAccessible(true); + + for (long i = 0L; i < longs.size(); i++) { + long fp = longs.first(); + assertEquals(String.format("Length: %s with seed: %s", length, rgenseed), i + 1L, + field.invoke(fpSet, 0, fp + 1L)); + longs.remove(fp); + } + } + + private static String createTmpFile() { + final String tmpdir = System.getProperty("java.io.tmpdir") + File.separator + "OffHeapDiskFPSetTest" + + System.currentTimeMillis(); + new File(tmpdir).mkdirs(); + return tmpdir; + } + + private static long getFingerprint(Random random) { + final long fp = (((long) random.nextInt(Integer.MAX_VALUE - 1) + 1) << 32) | (random.nextInt() & 0xffffffffL); + return fp; + } + + @Test + public void testWriteIndex() throws NoSuchFieldException, SecurityException, IllegalArgumentException, + IllegalAccessException, NoSuchMethodException, InvocationTargetException, IOException { + final DummyFPSetConfiguration fpSetConfig = new DummyFPSetConfiguration(); + fpSetConfig.setMemoryInFingerprintCnt(1); + + final OffHeapDiskFPSet fpSet = new OffHeapDiskFPSet(fpSetConfig); + final Method method = OffHeapDiskFPSet.class.getDeclaredMethod("writeIndex", + new Class[] { long[].class, java.io.RandomAccessFile.class, long.class }); + method.setAccessible(true); + + // length of array times NumEntriesPerPage has to exceed Integer.MAX_VALUE + final int length = 99999999; + assertTrue(length * DiskFPSet.NumEntriesPerPage < 1); + + try { + method.invoke(fpSet, new long[length], new DummyRandomAccessFile(File.createTempFile("foo", "bar"), "rw"), + Integer.MAX_VALUE); + } catch (InvocationTargetException e) { + Throwable targetException = e.getTargetException(); + fail(targetException.getMessage()); + } + } + + private static class DummyRandomAccessFile extends java.io.RandomAccessFile { + + public DummyRandomAccessFile(File file, String mode) throws FileNotFoundException { + super(file, mode); + } + + @Override + public int read() throws IOException { + return 0; + } + } +} diff --git a/tlatools/test/tlc2/tool/fp/OffHeapIndexerTest.java b/tlatools/test/tlc2/tool/fp/OffHeapIndexerTest.java new file mode 100644 index 0000000000000000000000000000000000000000..b3f8c35b5c93a890d2f58014727c0e96144f342c --- /dev/null +++ b/tlatools/test/tlc2/tool/fp/OffHeapIndexerTest.java @@ -0,0 +1,162 @@ +/******************************************************************************* + * Copyright (c) 2016 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.fp; + +import java.rmi.RemoteException; + +import org.junit.Assert; +import org.junit.Test; + +import tlc2.tool.fp.OffHeapDiskFPSet.Indexer; + +public class OffHeapIndexerTest { + + @Test + public void testBitshifting() throws RemoteException { + final int fpBits = 1; + final long positions = 128L; + final int logPos = 8; + doTest(fpBits, positions, logPos, new OffHeapDiskFPSet.BitshiftingIndexer(positions, fpBits)); + } + + @Test + public void testRescale() throws RemoteException { + final int fpBits = 1; + final long positions = 96L; + + final Indexer indexer = new OffHeapDiskFPSet.Indexer(positions, fpBits); + + // indexer spreads over all positions + Assert.assertEquals(0, indexer.getIdx(1, 0)); + Assert.assertEquals(48, indexer.getIdx(((0xFFFFFFFFFFFFFFFFL >>> fpBits) / 2L))); + Assert.assertEquals(positions - 1, indexer.getIdx((0xFFFFFFFFFFFFFFFFL >>> fpBits))); + + // Correctly wraps around when end of array is reached + Assert.assertEquals(0, indexer.getIdx((0xFFFFFFFFFFFFFFFFL >>> fpBits), 1)); + // Correctly wraps around when end of array is reached twice + Assert.assertEquals(0, indexer.getIdx((0xFFFFFFFFFFFFFFFFL >>> fpBits), (int)positions+1)); + } + + @Test + public void testRescale99() throws RemoteException { + final int fpBits = 1; + final long positions = 99L; + + final Indexer indexer = new OffHeapDiskFPSet.Indexer(positions, fpBits); + + // indexer spreads over all positions + Assert.assertEquals(0, indexer.getIdx(1)); + Assert.assertEquals(49, indexer.getIdx(((0xFFFFFFFFFFFFFFFFL >>> fpBits) / 2L))); + Assert.assertEquals(positions - 1, indexer.getIdx((0xFFFFFFFFFFFFFFFFL >>> fpBits))); + + // Correctly wraps around when end of array is reached + Assert.assertEquals(0, indexer.getIdx((0xFFFFFFFFFFFFFFFFL >>> fpBits), 1)); + // Correctly wraps around when end of array is reached twice + Assert.assertEquals(0, indexer.getIdx((0xFFFFFFFFFFFFFFFFL >>> fpBits), (int)positions+1)); + } + + @Test + public void testBitshifting2() throws RemoteException { + final int fpBits = 2; + final long positions = 128L; + final int logPos = 9; + doTest(fpBits, positions, logPos, new OffHeapDiskFPSet.BitshiftingIndexer(positions, fpBits)); + } + + @Test + public void testRescale2() throws RemoteException { + final int fpBits = 2; + final long positions = 96L; + + final Indexer indexer = new OffHeapDiskFPSet.Indexer(positions, fpBits); + + // indexer spreads over all positions + Assert.assertEquals(0, indexer.getIdx(1)); + Assert.assertEquals(48, indexer.getIdx(((0xFFFFFFFFFFFFFFFFL >>> fpBits) / 2L))); + Assert.assertEquals(95, indexer.getIdx((0xFFFFFFFFFFFFFFFFL >>> fpBits))); + + // Correctly wraps around when end of array is reached + Assert.assertEquals(0, indexer.getIdx((0xFFFFFFFFFFFFFFFFL >>> fpBits), 1)); + // Correctly wraps around when end of array is reached twice + Assert.assertEquals(0, indexer.getIdx((0xFFFFFFFFFFFFFFFFL >>> fpBits), 97)); + } + + @Test + public void testRescale299() throws RemoteException { + final int fpBits = 2; + final long positions = 99L; + + final Indexer indexer = new OffHeapDiskFPSet.Indexer(positions, fpBits); + + // indexer spreads over all positions + Assert.assertEquals(0, indexer.getIdx(1)); + Assert.assertEquals(49, indexer.getIdx(((0xFFFFFFFFFFFFFFFFL >>> fpBits) / 2L))); + Assert.assertEquals(positions - 1, indexer.getIdx((0xFFFFFFFFFFFFFFFFL >>> fpBits))); + + // Correctly wraps around when end of array is reached + Assert.assertEquals(0, indexer.getIdx((0xFFFFFFFFFFFFFFFFL >>> fpBits), 1)); + // Correctly wraps around when end of array is reached twice + Assert.assertEquals(0, indexer.getIdx((0xFFFFFFFFFFFFFFFFL >>> fpBits), (int)positions+1)); + } + + @Test + public void testRescaleMaximum() throws RemoteException { + final int fpBits = 1; + final long positions = 11L; + + final Indexer indexer = new OffHeapDiskFPSet.Indexer(positions, fpBits, 11L); + + // indexer spreads over all positions + Assert.assertEquals(0, indexer.getIdx(1)); + Assert.assertEquals(10, indexer.getIdx(11)); + + // Correctly wraps around when end of array is reached + Assert.assertEquals(0, indexer.getIdx(11, 1)); + } + + @Test + public void testBitshiftOvershoot() throws RemoteException { + final int fpBits = 1; + final long positions = 536870912L; + + final Indexer indexer = new OffHeapDiskFPSet.BitshiftingIndexer(positions, fpBits); + Assert.assertEquals(0, indexer.getIdx(9223371952792813846L, 5)); + } + + private void doTest(final int fpBits, final long positions, final int logPos, final Indexer indexer) { + Assert.assertTrue(Double.compare(Math.pow(2, logPos - fpBits), positions) == 0); + + Assert.assertEquals(fpBits, Long.numberOfLeadingZeros((positions << (Long.SIZE - logPos)) - 1)); + + for (long l = 0; l < positions; l++) { + final long fp = l << (Long.SIZE - logPos); + Assert.assertEquals(l, indexer.getIdx(fp)); + final long fpNext = ((l+1L) << (Long.SIZE - logPos)) - 1; + Assert.assertEquals(l, indexer.getIdx(fpNext)); + } + Assert.assertEquals(0, indexer.getIdx(positions << (Long.SIZE - logPos))); + } +} diff --git a/tlatools/test/tlc2/tool/fp/OffHeapIteratorTest.java b/tlatools/test/tlc2/tool/fp/OffHeapIteratorTest.java new file mode 100644 index 0000000000000000000000000000000000000000..e2e68d96e9ba38ec270eff8ec1765704ff19ddc5 --- /dev/null +++ b/tlatools/test/tlc2/tool/fp/OffHeapIteratorTest.java @@ -0,0 +1,70 @@ +package tlc2.tool.fp; + +import static org.junit.Assert.assertEquals; + +import org.junit.Assume; +import org.junit.Before; +import org.junit.Test; + +import tlc2.tool.fp.OffHeapDiskFPSet.Iterator; + +public class OffHeapIteratorTest { + + @Before + public void setup() { + Assume.assumeTrue(LongArray.isSupported()); + } + + @Test + public void testNext() { + final long elements = 32L; + final LongArray array = new LongArray(2L * elements); + for (int i = 0; i < array.size(); i++) { + array.set(i, i+1); + } + + final Iterator itr = new OffHeapDiskFPSet.Iterator(array, elements, + new OffHeapDiskFPSet.Indexer(2L * elements, 1)); + + long actual = 1L; + while (itr.hasNext()) { + long next = itr.next(); + assertEquals(actual, next); + actual++; + } + assertEquals(elements, actual - 1); + + for (int i = 0; i < array.size(); i++) { + assertEquals(i + 1, array.get(i)); + } + } + + @Test + public void testMarkNext() { + final int elements = 32; + final LongArray array = new LongArray(2L * elements); + for (int i = 0; i < array.size(); i++) { + array.set(i, i+1); + } + + final Iterator itr = new OffHeapDiskFPSet.Iterator(array, elements, + new OffHeapDiskFPSet.Indexer(2L * elements, 1)); + + long actual = 1L; + while (itr.hasNext()) { + long next = itr.markNext(); + assertEquals(actual, next); + actual++; + } + assertEquals(elements, actual - 1); + + for (int i = elements; i < array.size(); i++) { + assertEquals(elements + 1, array.get(elements)); + } + for (int i = 0; i < elements; i++) { + long expected = i + 1 | 0x8000000000000000L; + actual = array.get(i); + assertEquals(expected, actual); + } + } +} diff --git a/tlatools/test/tlc2/tool/fp/ShortDiskFPSetTest.java b/tlatools/test/tlc2/tool/fp/ShortDiskFPSetTest.java index 368810968d02fa695d04d7ade537e5a6b0bb4ee4..cfd4d3fa09bfd9ebf768156272bd4fe4ce716b22 100644 --- a/tlatools/test/tlc2/tool/fp/ShortDiskFPSetTest.java +++ b/tlatools/test/tlc2/tool/fp/ShortDiskFPSetTest.java @@ -1,11 +1,18 @@ // Copyright (c) 2011 Microsoft Corporation. All rights reserved. package tlc2.tool.fp; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + import java.io.IOException; import java.util.ArrayList; import java.util.Iterator; import java.util.List; +import org.junit.Test; + import tlc2.util.LongVec; public class ShortDiskFPSetTest extends AbstractFPSetTest { @@ -32,6 +39,7 @@ public class ShortDiskFPSetTest extends AbstractFPSetTest { * Tests if {@link DiskFPSet#diskLookup(long)} returns true for zero fp * @throws IOException */ + @Test public void testWithoutZeroFP() throws IOException { final DiskFPSet fpSet = (DiskFPSet) getFPSet(new FPSetConfiguration()); assertFalse("Succeeded to look up 0 fp", fpSet.contains(0l)); @@ -41,6 +49,7 @@ public class ShortDiskFPSetTest extends AbstractFPSetTest { * Tests if {@link DiskFPSet#diskLookup(long)} returns true for min fp * @throws IOException */ + @Test public void testWithoutMinFP() throws IOException { final DiskFPSet fpSet = (DiskFPSet) getFPSet(new FPSetConfiguration()); assertFalse("Succeeded to look up 0 fp", fpSet.contains(Long.MIN_VALUE)); @@ -50,6 +59,7 @@ public class ShortDiskFPSetTest extends AbstractFPSetTest { * Tests if {@link DiskFPSet#diskLookup(long)} returns true for max fp * @throws IOException */ + @Test public void testWithoutMaxFP() throws IOException { final DiskFPSet fpSet = (DiskFPSet) getFPSet(new FPSetConfiguration()); assertFalse("Succeeded to look up 0 fp", fpSet.contains(Long.MAX_VALUE)); @@ -59,12 +69,13 @@ public class ShortDiskFPSetTest extends AbstractFPSetTest { * Tests if {@link DiskFPSet#diskLookup(long)} accepts a 0 fp * @throws IOException */ + @Test public void testZeroFP() throws IOException { // skip known failures which aren't likely to be fixed anytime soon - // @see https://bugzilla.tlaplus.net/show_bug.cgi?id=213 + // @see Bug #213 in general/bugzilla/index.html if(!runKnownFailures) { System.out - .println("Skipping test failing due to https://bugzilla.tlaplus.net/show_bug.cgi?id=213"); + .println("Skipping test failing due to Bug #213 in general/bugzilla/index.html"); return; } @@ -77,12 +88,13 @@ public class ShortDiskFPSetTest extends AbstractFPSetTest { * Tests if {@link DiskFPSet#diskLookup(long)} accepts a min fp * @throws IOException */ + @Test public void testMinFP() throws IOException { // skip known failures which aren't likely to be fixed anytime soon - // @see https://bugzilla.tlaplus.net/show_bug.cgi?id=213 + // @see Bug #213 in general/bugzilla/index.html if(!runKnownFailures) { System.out - .println("Skipping test failing due to https://bugzilla.tlaplus.net/show_bug.cgi?id=213"); + .println("Skipping test failing due to Bug #213 in general/bugzilla/index.html"); return; } @@ -96,6 +108,7 @@ public class ShortDiskFPSetTest extends AbstractFPSetTest { * Tests if {@link DiskFPSet#diskLookup(long)} accepts a min - 1 fp * @throws IOException */ + @Test public void testMinMin1FP() throws IOException { final DiskFPSet fpSet = (DiskFPSet) getFPSet(new FPSetConfiguration()); // zeroing the msb in DiskFPSet turns Long.Min_Value into 0 @@ -108,6 +121,7 @@ public class ShortDiskFPSetTest extends AbstractFPSetTest { * Tests if {@link DiskFPSet#diskLookup(long)} accepts a -1 fp * @throws IOException */ + @Test public void testNeg1FP() throws IOException { final DiskFPSet fpSet = (DiskFPSet) getFPSet(new FPSetConfiguration()); assertFalse(fpSet.put(-1l)); @@ -118,6 +132,7 @@ public class ShortDiskFPSetTest extends AbstractFPSetTest { * Tests if {@link DiskFPSet#diskLookup(long)} accepts a +1 fp * @throws IOException */ + @Test public void testPos1FP() throws IOException { final DiskFPSet fpSet = (DiskFPSet) getFPSet(new FPSetConfiguration()); assertFalse(fpSet.put(1l)); @@ -128,6 +143,7 @@ public class ShortDiskFPSetTest extends AbstractFPSetTest { * Tests if {@link DiskFPSet#diskLookup(long)} accepts a max fp * @throws IOException */ + @Test public void testMaxFP() throws IOException { final DiskFPSet fpSet = (DiskFPSet) getFPSet(new FPSetConfiguration()); assertFalse(fpSet.put(Long.MAX_VALUE)); @@ -141,6 +157,7 @@ public class ShortDiskFPSetTest extends AbstractFPSetTest { * * @throws IOException */ + @Test public void testValues() throws IOException { final DiskFPSet fpSet = (DiskFPSet) getFPSet(new FPSetConfiguration()); @@ -267,6 +284,7 @@ public class ShortDiskFPSetTest extends AbstractFPSetTest { * * @throws IOException */ + @Test @SuppressWarnings("deprecation") public void testDiskLookupWithFpOnLoPage() throws IOException { int freeMemory = 1000; // causes 16 in memory entries @@ -290,12 +308,13 @@ public class ShortDiskFPSetTest extends AbstractFPSetTest { * * @throws IOException */ + @Test public void testMemLookupWithZeros() throws IOException { // skip known failures which aren't likely to be fixed anytime soon - // @see https://bugzilla.tlaplus.net/show_bug.cgi?id=213 + // @see Bug #213 in general/bugzilla/index.html if(!runKnownFailures) { System.out - .println("Skipping test failing due to https://bugzilla.tlaplus.net/show_bug.cgi?id=213"); + .println("Skipping test failing due to Bug #213 in general/bugzilla/index.html"); return; } @@ -310,12 +329,13 @@ public class ShortDiskFPSetTest extends AbstractFPSetTest { * * @throws IOException */ + @Test public void testMemLookupWithMin() throws IOException { // skip known failures which aren't likely to be fixed anytime soon - // @see https://bugzilla.tlaplus.net/show_bug.cgi?id=213 + // @see Bug #213 in general/bugzilla/index.html if(!runKnownFailures) { System.out - .println("Skipping test failing due to https://bugzilla.tlaplus.net/show_bug.cgi?id=213"); + .println("Skipping test failing due to Bug #213 in general/bugzilla/index.html"); return; } @@ -330,6 +350,7 @@ public class ShortDiskFPSetTest extends AbstractFPSetTest { * * @throws IOException */ + @Test public void testMemLookupWithMax() throws IOException { final DiskFPSet fpSet = (DiskFPSet) getFPSet(new FPSetConfiguration(.75d)); assertFalse(fpSet.memInsert(Long.MAX_VALUE)); @@ -342,6 +363,7 @@ public class ShortDiskFPSetTest extends AbstractFPSetTest { * * @throws IOException */ + @Test public void testDiskLookupWithZeros() throws IOException { final long fp = 0L; @@ -376,6 +398,7 @@ public class ShortDiskFPSetTest extends AbstractFPSetTest { * * @throws IOException */ + @Test public void testDiskLookupWithMin() throws IOException { final DiskFPSet fpSet = (DiskFPSet) getFPSet(new FPSetConfiguration()); assertFalse(fpSet.memInsert(Long.MIN_VALUE & 0x7FFFFFFFFFFFFFFFL)); @@ -391,6 +414,7 @@ public class ShortDiskFPSetTest extends AbstractFPSetTest { * * @throws IOException */ + @Test public void testDiskLookupWithMax() throws IOException { final DiskFPSet fpSet = (DiskFPSet) getFPSet(new FPSetConfiguration()); assertFalse(fpSet.memInsert(Long.MAX_VALUE)); @@ -405,6 +429,7 @@ public class ShortDiskFPSetTest extends AbstractFPSetTest { * * @throws IOException */ + @Test public void testDiskLookupWithMaxOnPage() throws IOException { testDiskLookupOnPage(Long.MAX_VALUE); } @@ -414,12 +439,13 @@ public class ShortDiskFPSetTest extends AbstractFPSetTest { * * @throws IOException */ + @Test public void testDiskLookupWithZerosOnPage() throws IOException { // skip known failures which aren't likely to be fixed anytime soon - // @see https://bugzilla.tlaplus.net/show_bug.cgi?id=213 + // @see Bug #213 in general/bugzilla/index.html if(!runKnownFailures) { System.out - .println("Skipping test failing due to https://bugzilla.tlaplus.net/show_bug.cgi?id=213"); + .println("Skipping test failing due to Bug #213 in general/bugzilla/index.html"); return; } testDiskLookupOnPage(0l); @@ -430,12 +456,13 @@ public class ShortDiskFPSetTest extends AbstractFPSetTest { * * @throws IOException */ + @Test public void testDiskLookupWithLongMinValueOnPage() throws IOException { // skip known failures which aren't likely to be fixed anytime soon - // @see https://bugzilla.tlaplus.net/show_bug.cgi?id=213 + // @see Bug #213 in general/bugzilla/index.html if(!runKnownFailures) { System.out - .println("Skipping test failing due to https://bugzilla.tlaplus.net/show_bug.cgi?id=213"); + .println("Skipping test failing due to Bug #213 in general/bugzilla/index.html"); return; } @@ -466,6 +493,7 @@ public class ShortDiskFPSetTest extends AbstractFPSetTest { * Test that both implementations - put(long) & putBlock(LongVec) - yield * the same results. */ + @Test public void testComparePutAndPutBlock() throws IOException { final FPSet putFpSet = (FPSet) getFPSetInitialized(); final FPSet putBlockFpSet = (FPSet) getFPSetInitialized(); @@ -483,6 +511,7 @@ public class ShortDiskFPSetTest extends AbstractFPSetTest { * Test that both implementations - contains(long) & containsBlock(LongVec) - yield * the same results. */ + @Test public void testCompareContainsAndContainsBlock() throws IOException { final FPSet containsFpSet = (FPSet) getFPSetInitialized(); final FPSet containsBlockFpSet = (FPSet) getFPSetInitialized(); @@ -496,6 +525,7 @@ public class ShortDiskFPSetTest extends AbstractFPSetTest { assertEquals(containsFpSet.contains(fp), containsBlockRes); } + @Test public void testContainsBlock() throws IOException { final FPSet fpSet = (FPSet) getFPSetInitialized(); @@ -512,6 +542,7 @@ public class ShortDiskFPSetTest extends AbstractFPSetTest { assertFalse(fpSet.containsBlock(fpv).get(0)); } + @Test public void testPutBlock() throws IOException { final FPSet fpSet = (FPSet) getFPSetInitialized(); diff --git a/tlatools/test/tlc2/tool/fp/iterator/TLCIteratorTest.java b/tlatools/test/tlc2/tool/fp/iterator/TLCIteratorTest.java index 07f347f360dc46568a3a506f3abbbf935dfa7149..7b79f655b62b0c605b1fcf3c04653cf97ac8dd28 100644 --- a/tlatools/test/tlc2/tool/fp/iterator/TLCIteratorTest.java +++ b/tlatools/test/tlc2/tool/fp/iterator/TLCIteratorTest.java @@ -2,21 +2,27 @@ package tlc2.tool.fp.iterator; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + import java.util.NoSuchElementException; -import tlc2.tool.fp.MSBDiskFPSet; +import org.junit.Before; +import org.junit.Test; -import junit.framework.TestCase; +import tlc2.tool.fp.MSBDiskFPSet; -public class TLCIteratorTest extends TestCase { +public class TLCIteratorTest { private MSBDiskFPSet.TLCIterator itr; /* (non-Javadoc) * @see junit.framework.TestCase#setUp() */ - protected void setUp() throws Exception { - super.setUp(); + @Before + public void setUp() throws Exception { itr = new MSBDiskFPSet.TLCIterator(getBuffer()); } @@ -52,6 +58,7 @@ public class TLCIteratorTest extends TestCase { /** * Test method for {@link tlc2.tool.fp.TLCIterator#next()}. */ + @Test public void testNext() { long predecessor = -1l; @@ -72,6 +79,7 @@ public class TLCIteratorTest extends TestCase { /** * Test method for {@link tlc2.tool.fp.TLCIterator#next()}. */ + @Test public void testNoNext() { // read up all real elements while (itr.hasNext()) { @@ -91,19 +99,8 @@ public class TLCIteratorTest extends TestCase { /** * Test method for {@link tlc2.tool.fp.TLCIterator#getLast()}. */ + @Test public void testGetLast() { assertEquals(getLast(), itr.getLast()); } - - public void testGetLastLowBound() { - assertEquals(getLast() + 1, itr.getLast(getLast() + 1)); - } - - /* - * Use a smaller lower bound element than last in the buffer. - */ - public void testGetLastLowerBoundNoHit() { - assertEquals(getLast(), itr.getLast(20)); - } - } diff --git a/tlatools/test/tlc2/tool/liveness/AbstractDiskGraph.JPG b/tlatools/test/tlc2/tool/liveness/AbstractDiskGraph.JPG new file mode 100644 index 0000000000000000000000000000000000000000..f3e3c82c44e40dfe061993d47d498799596bb3c5 Binary files /dev/null and b/tlatools/test/tlc2/tool/liveness/AbstractDiskGraph.JPG differ diff --git a/tlatools/test/tlc2/tool/liveness/April20aTest.java b/tlatools/test/tlc2/tool/liveness/April20aTest.java new file mode 100644 index 0000000000000000000000000000000000000000..213e7537a416e60d284460916c1d69131f3e87f6 --- /dev/null +++ b/tlatools/test/tlc2/tool/liveness/April20aTest.java @@ -0,0 +1,75 @@ +/******************************************************************************* + * Copyright (c) 2016 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.liveness; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.Ignore; +import org.junit.Test; + +import tlc2.output.EC; + +public class April20aTest extends ModelCheckerTestCase { + + public April20aTest() { + super("April20aMC", "symmetry"); + } + + @Test + @Ignore("Ignored for as long as symmetry is incorrectly handled by TLC with liveness checking.") + public void testSpec() { + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "5", "3", "0")); + assertFalse(recorder.recorded(EC.GENERAL)); + + // Assert TLC has found a temporal violation and a counter example + assertTrue(recorder.recorded(EC.TLC_TEMPORAL_PROPERTY_VIOLATED)); + assertTrue(recorder.recorded(EC.TLC_COUNTER_EXAMPLE)); + + // Assert the error trace + assertTrue(recorder.recorded(EC.TLC_STATE_PRINT2)); + final List<String> expectedTrace = new ArrayList<String>(6); + expectedTrace.add("/\\ x = 0\n" + + "/\\ y = 0"); + expectedTrace.add("/\\ x = m2\n" + + "/\\ y = 1"); + expectedTrace.add("/\\ x = m1\n" + + "/\\ y = 2"); + expectedTrace.add("/\\ x = 0\n" + + "/\\ y = 0"); + expectedTrace.add("/\\ x = m1\n" + + "/\\ y = 1"); + expectedTrace.add("/\\ x = m2\n" + + "/\\ y = 2"); + assertTraceWith(recorder.getRecords(EC.TLC_STATE_PRINT2), expectedTrace); + + assertBackToState(1, "<Action line 17, col 12 to line 19, col 20 of module April20a>"); + } +} diff --git a/tlatools/test/tlc2/tool/liveness/April20bTest.java b/tlatools/test/tlc2/tool/liveness/April20bTest.java new file mode 100644 index 0000000000000000000000000000000000000000..ee37bd5a7260643136777f79194eac2463a24d47 --- /dev/null +++ b/tlatools/test/tlc2/tool/liveness/April20bTest.java @@ -0,0 +1,75 @@ +/******************************************************************************* + * Copyright (c) 2016 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.liveness; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.Ignore; +import org.junit.Test; + +import tlc2.output.EC; + +public class April20bTest extends ModelCheckerTestCase { + + public April20bTest() { + super("April20bMC", "symmetry"); + } + + @Test + @Ignore("Ignored for as long as symmetry is incorrectly handled by TLC with liveness checking.") + public void testSpec() { + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "5", "3", "0")); + assertFalse(recorder.recorded(EC.GENERAL)); + + // Assert TLC has found a temporal violation and a counter example + assertTrue(recorder.recorded(EC.TLC_TEMPORAL_PROPERTY_VIOLATED)); + assertTrue(recorder.recorded(EC.TLC_COUNTER_EXAMPLE)); + + // Assert the error trace + assertTrue(recorder.recorded(EC.TLC_STATE_PRINT2)); + final List<String> expectedTrace = new ArrayList<String>(6); + expectedTrace.add("/\\ x = <<>>\n" + + "/\\ y = 0"); + expectedTrace.add("/\\ x = <<m2>>\n" + + "/\\ y = 1"); + expectedTrace.add("/\\ x = <<m2, m1>>\n" + + "/\\ y = 2"); + expectedTrace.add("/\\ x = <<>>\n" + + "/\\ y = 0"); + expectedTrace.add("/\\ x = <<m1>>\n" + + "/\\ y = 1"); + expectedTrace.add("/\\ x = <<m1, m2>>\n" + + "/\\ y = 2"); + assertTraceWith(recorder.getRecords(EC.TLC_STATE_PRINT2), expectedTrace); + + assertBackToState(1, "<Action line 17, col 12 to line 19, col 24 of module April20b>"); + } +} diff --git a/tlatools/test/tlc2/tool/liveness/April21Test.java b/tlatools/test/tlc2/tool/liveness/April21Test.java new file mode 100644 index 0000000000000000000000000000000000000000..a826dde2f5cdd69d514de87bfb6fc65cad857747 --- /dev/null +++ b/tlatools/test/tlc2/tool/liveness/April21Test.java @@ -0,0 +1,64 @@ +/******************************************************************************* + * Copyright (c) 2016 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.liveness; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.Ignore; +import org.junit.Test; + +import tlc2.output.EC; + +public class April21Test extends ModelCheckerTestCase { + + public April21Test() { + super("April21MC", "symmetry"); + } + + @Test + @Ignore("Ignored for as long as symmetry is incorrectly handled by TLC with liveness checking.") + public void testSpec() { + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertFalse(recorder.recorded(EC.GENERAL)); + + // Assert TLC has found a temporal violation and a counter example + assertTrue(recorder.recorded(EC.TLC_TEMPORAL_PROPERTY_VIOLATED)); + assertTrue(recorder.recorded(EC.TLC_COUNTER_EXAMPLE)); + + // Assert the error trace + assertTrue(recorder.recorded(EC.TLC_STATE_PRINT2)); + final List<String> expectedTrace = new ArrayList<String>(2); + expectedTrace.add("x = m1"); + expectedTrace.add("x = m2"); + assertTraceWith(recorder.getRecords(EC.TLC_STATE_PRINT2), expectedTrace); + + assertBackToState(1, "<Action line 10, col 9 to line 10, col 16 of module April21>"); + } +} diff --git a/tlatools/test/tlc2/tool/liveness/April22Test.java b/tlatools/test/tlc2/tool/liveness/April22Test.java new file mode 100644 index 0000000000000000000000000000000000000000..432d9d455b33c0db08d95d5475dfbca6473a32d7 --- /dev/null +++ b/tlatools/test/tlc2/tool/liveness/April22Test.java @@ -0,0 +1,52 @@ +/******************************************************************************* + * Copyright (c) 2016 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.liveness; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Ignore; +import org.junit.Test; + +import tlc2.output.EC; + +public class April22Test extends ModelCheckerTestCase { + + public April22Test() { + super("April22MC", "symmetry"); + } + + @Test + @Ignore("Ignored for as long as symmetry is incorrectly handled by TLC with liveness checking.") + public void testSpec() { + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertFalse(recorder.recorded(EC.GENERAL)); + + // Assert TLC has not found a temporal violation + assertFalse(recorder.recorded(EC.TLC_TEMPORAL_PROPERTY_VIOLATED)); + assertFalse(recorder.recorded(EC.TLC_COUNTER_EXAMPLE)); + } +} diff --git a/tlatools/test/tlc2/tool/liveness/April25Test.java b/tlatools/test/tlc2/tool/liveness/April25Test.java new file mode 100644 index 0000000000000000000000000000000000000000..7fa4922943f06b8e49bc304774c5596160fbe70f --- /dev/null +++ b/tlatools/test/tlc2/tool/liveness/April25Test.java @@ -0,0 +1,64 @@ +/******************************************************************************* + * Copyright (c) 2016 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.liveness; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.Ignore; +import org.junit.Test; + +import tlc2.output.EC; + +public class April25Test extends ModelCheckerTestCase { + + public April25Test() { + super("April25MC", "symmetry"); + } + + @Test + @Ignore("Ignored for as long as symmetry is incorrectly handled by TLC with liveness checking.") + public void testSpec() { + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertFalse(recorder.recorded(EC.GENERAL)); + + // Assert TLC has found a temporal violation and a counter example + assertTrue(recorder.recorded(EC.TLC_TEMPORAL_PROPERTY_VIOLATED)); + assertTrue(recorder.recorded(EC.TLC_COUNTER_EXAMPLE)); + + // Assert the error trace + assertTrue(recorder.recorded(EC.TLC_STATE_PRINT2)); + final List<String> expectedTrace = new ArrayList<String>(2); + expectedTrace.add("x = m1"); + expectedTrace.add("x = m2"); + assertTraceWith(recorder.getRecords(EC.TLC_STATE_PRINT2), expectedTrace); + + assertBackToState(1, "<Action line 10, col 9 to line 10, col 16 of module April25>"); + } +} diff --git a/tlatools/test/tlc2/tool/liveness/April29Test.java b/tlatools/test/tlc2/tool/liveness/April29Test.java new file mode 100644 index 0000000000000000000000000000000000000000..1d6e78a61d5652bf06b57ad99bafaa0725796537 --- /dev/null +++ b/tlatools/test/tlc2/tool/liveness/April29Test.java @@ -0,0 +1,119 @@ +/******************************************************************************* + * Copyright (c) 2016 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.liveness; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; + +import org.junit.Ignore; +import org.junit.Test; + +import tlc2.TLC; +import tlc2.output.EC; +import tlc2.tool.AbstractChecker; +import tlc2.tool.liveness.GraphNode.Transition; +import tlc2.util.BitVector; +import tlc2.util.LongVec; + +/** + * April29 and April29d exemplify how two different specifications have isomorph + * state graphs under symmetry. Without symmetry, April29dTest does *not* + * violate its liveness property. Under symmetry, TLC incorrectly finds a + * violation and produces a bogus (identical to April29Test) counterexample. + */ +public class April29Test extends ModelCheckerTestCase { + + public April29Test() { + super("April29MC", "symmetry"); + } + + @Test + @Ignore("Ignored for as long as symmetry is incorrectly handled by TLC with liveness checking.") + public void testSpec() throws IOException { + // Assert TLC has found a temporal violation and a counter example + assertTrue(recorder.recorded(EC.TLC_TEMPORAL_PROPERTY_VIOLATED)); + assertTrue(recorder.recorded(EC.TLC_COUNTER_EXAMPLE)); + + // Assert the error trace + assertTrue(recorder.recorded(EC.TLC_STATE_PRINT2)); + final List<String> expectedTrace = new ArrayList<String>(2); + expectedTrace.add("/\\ x = a\n/\\ y = 0"); + expectedTrace.add("/\\ x = a\n/\\ y = 1"); + assertTraceWith(recorder.getRecords(EC.TLC_STATE_PRINT2), expectedTrace); + + assertStuttering(3); + + // Verify the nodes and arcs in the behavior graph in terms of nodes and arcs. + final ILiveCheck liveCheck = (ILiveCheck) getField(AbstractChecker.class, "liveCheck", + getField(TLC.class, "instance", tlc)); + assertEquals(1, liveCheck.getNumChecker()); + + final ILiveChecker checker = liveCheck.getChecker(0); + final DiskGraph graph = (DiskGraph) checker.getDiskGraph(); + graph.makeNodePtrTbl(); + assertEquals(2, graph.size()); // two nodes + + final LongVec initNodes = graph.getInitNodes(); + assertEquals(2, initNodes.size()); // <<fp,tidx>>, thus a single init state + + final int slen = checker.getSolution().getCheckState().length; + assertEquals(0, slen); + final int alen = checker.getSolution().getCheckAction().length; + assertEquals(3, alen); + + final GraphNode init = graph.getNode(initNodes.elementAt(0)); + Set<Transition> transitions = init.getTransition(slen, alen); + assertEquals(2, transitions.size()); // One self-loop and one successor + + // Remove self loop from init's transitions. The remaining transitions are + // the true successors (here just a single). + final List<Transition> outs = new ArrayList<Transition>(); + for (Transition t : transitions) { + if (t.getFP() != init.stateFP) { + outs.add(t); + } + } + assertEquals(1, outs.size()); + + final GraphNode successor = graph.getNode(outs.get(0).getFP()); + transitions = successor.getTransition(slen, alen); + assertEquals(1, transitions.size()); + + // Verify both outgoing transitions are self loops with the expected + // action predicates. transitions is a Set and thus adding equal + // instance does not increase the set's size. + BitVector bv = new BitVector(alen + slen); + bv.set(0); + bv.set(2); + transitions.add(new Transition(successor.stateFP, -1, bv)); + assertEquals(1, transitions.size()); + } +} diff --git a/tlatools/test/tlc2/tool/liveness/April29dTest.java b/tlatools/test/tlc2/tool/liveness/April29dTest.java new file mode 100644 index 0000000000000000000000000000000000000000..122c49f550203ec5dae68340add6a583aeeb454a --- /dev/null +++ b/tlatools/test/tlc2/tool/liveness/April29dTest.java @@ -0,0 +1,114 @@ +/******************************************************************************* + * Copyright (c) 2016 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.liveness; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; + +import org.junit.Ignore; +import org.junit.Test; + +import tlc2.TLC; +import tlc2.output.EC; +import tlc2.tool.AbstractChecker; +import tlc2.tool.liveness.GraphNode.Transition; +import tlc2.util.BitVector; +import tlc2.util.LongVec; + +/** + * @see April29Test + */ +public class April29dTest extends ModelCheckerTestCase { + + public April29dTest() { + super("April29dMC", "symmetry"); + } + + @Test + @Ignore("Ignored for as long as symmetry is incorrectly handled by TLC with liveness checking.") + public void testSpec() throws IOException { + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertFalse(recorder.recorded(EC.GENERAL)); + + // Verify the nodes and arcs in the behavior graph in terms of nodes and arcs. + final ILiveCheck liveCheck = (ILiveCheck) getField(AbstractChecker.class, "liveCheck", + getField(TLC.class, "instance", tlc)); + assertEquals(1, liveCheck.getNumChecker()); + + final ILiveChecker checker = liveCheck.getChecker(0); + final DiskGraph graph = (DiskGraph) checker.getDiskGraph(); + graph.makeNodePtrTbl(); + assertEquals(2, graph.size()); // two nodes + + final LongVec initNodes = graph.getInitNodes(); + assertEquals(2, initNodes.size()); // <<fp,tidx>>, thus a single init state + + final int slen = checker.getSolution().getCheckState().length; + assertEquals(0, slen); + final int alen = checker.getSolution().getCheckAction().length; + assertEquals(3, alen); + + final GraphNode init = graph.getNode(initNodes.elementAt(0)); + Set<Transition> transitions = init.getTransition(slen, alen); + assertEquals(2, transitions.size()); // One self-loop and one successor + + // Remove self loop from init's transitions. The remaining transitions are + // the true successors (here just a single). + final List<Transition> outs = new ArrayList<Transition>(); + for (Transition t : transitions) { + if (t.getFP() != init.stateFP) { + outs.add(t); + } + } + assertEquals(1, outs.size()); + + final GraphNode successor = graph.getNode(outs.get(0).getFP()); + transitions = successor.getTransition(slen, alen); + assertEquals(2, transitions.size()); + + // Verify both outgoing transitions are self loops with the expected + // action predicates. transitions is a Set and thus adding equal + // instance does not increase the set's size. + BitVector bv = new BitVector(alen + slen); + bv.set(0); + bv.set(2); + transitions.add(new Transition(successor.stateFP, -1, bv)); + bv = new BitVector(alen + slen); + bv.set(2); + transitions.add(new Transition(successor.stateFP, -1, bv)); + assertEquals(2, transitions.size()); + + // Assert TLC has found a temporal violation and a counter example + assertFalse(recorder.recorded(EC.TLC_TEMPORAL_PROPERTY_VIOLATED)); + assertFalse(recorder.recorded(EC.TLC_COUNTER_EXAMPLE)); + } +} diff --git a/tlatools/test/tlc2/tool/liveness/BidirectionalTransitions1BTest.java b/tlatools/test/tlc2/tool/liveness/BidirectionalTransitions1BTest.java new file mode 100644 index 0000000000000000000000000000000000000000..d318270f571745d5f318f5fa53c71a54b92ceefc --- /dev/null +++ b/tlatools/test/tlc2/tool/liveness/BidirectionalTransitions1BTest.java @@ -0,0 +1,63 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.liveness; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.Test; + +import tlc2.output.EC; +import tlc2.output.EC.ExitStatus; + +public abstract class BidirectionalTransitions1BTest extends ModelCheckerTestCase { + + public BidirectionalTransitions1BTest(final String config) { + super("BidirectionalTransitions", new String[] {"-config", config}, ExitStatus.VIOLATION_LIVENESS); + } + + @Test + public void testSpec() { + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertFalse(recorder.recorded(EC.GENERAL)); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "13", "3", "0")); + + assertTrue(recorder.recorded(EC.TLC_TEMPORAL_PROPERTY_VIOLATED)); + assertTrue(recorder.recorded(EC.TLC_COUNTER_EXAMPLE)); + + assertTrue(recorder.recorded(EC.TLC_STATE_PRINT2)); + final List<String> expectedTrace = new ArrayList<String>(3); + expectedTrace.add("x = 0"); + expectedTrace.add("x = 2"); + expectedTrace.add("x = 1"); + assertTraceWith(recorder.getRecords(EC.TLC_STATE_PRINT2), expectedTrace); + + assertBackToState(1); + } +} diff --git a/tlatools/test/tlc2/tool/liveness/BidirectionalTransitions1BxTest.java b/tlatools/test/tlc2/tool/liveness/BidirectionalTransitions1BxTest.java new file mode 100644 index 0000000000000000000000000000000000000000..f723b7000bb15fc41b8bad6df7d9b31e56cbc6d6 --- /dev/null +++ b/tlatools/test/tlc2/tool/liveness/BidirectionalTransitions1BxTest.java @@ -0,0 +1,33 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.liveness; + +public class BidirectionalTransitions1BxTest extends BidirectionalTransitions1BTest { + + public BidirectionalTransitions1BxTest() { + super("BidirectionalTransitions1Bx.cfg"); + } +} diff --git a/tlatools/test/tlc2/tool/liveness/BidirectionalTransitions1ByTest.java b/tlatools/test/tlc2/tool/liveness/BidirectionalTransitions1ByTest.java new file mode 100644 index 0000000000000000000000000000000000000000..2ac3fb7c4262a0a4cedb8d0a5c9bcc3df1400979 --- /dev/null +++ b/tlatools/test/tlc2/tool/liveness/BidirectionalTransitions1ByTest.java @@ -0,0 +1,33 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.liveness; + +public class BidirectionalTransitions1ByTest extends BidirectionalTransitions1BTest { + + public BidirectionalTransitions1ByTest() { + super("BidirectionalTransitions1By.cfg"); + } +} diff --git a/tlatools/test/tlc2/tool/liveness/BidirectionalTransitions1Test.java b/tlatools/test/tlc2/tool/liveness/BidirectionalTransitions1Test.java new file mode 100644 index 0000000000000000000000000000000000000000..67b2717e6bb9a084b8e3fa68063e28106c5f963a --- /dev/null +++ b/tlatools/test/tlc2/tool/liveness/BidirectionalTransitions1Test.java @@ -0,0 +1,50 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.liveness; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.output.EC; + +public class BidirectionalTransitions1Test extends ModelCheckerTestCase { + + public BidirectionalTransitions1Test() { + super("BidirectionalTransitions", new String[] {"-config", "BidirectionalTransitions1.cfg"}); + } + + @Test + public void testSpec() { + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertFalse(recorder.recorded(EC.GENERAL)); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "13", "3", "0")); + assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "2")); + + assertZeroUncovered(); + } +} diff --git a/tlatools/test/tlc2/tool/liveness/BidirectionalTransitions2CTest.java b/tlatools/test/tlc2/tool/liveness/BidirectionalTransitions2CTest.java new file mode 100644 index 0000000000000000000000000000000000000000..6879a0c45624f5fd9d6ff1141c4b080e7592848d --- /dev/null +++ b/tlatools/test/tlc2/tool/liveness/BidirectionalTransitions2CTest.java @@ -0,0 +1,64 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.liveness; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.Test; + +import tlc2.output.EC; +import tlc2.output.EC.ExitStatus; + +public abstract class BidirectionalTransitions2CTest extends ModelCheckerTestCase { + + public BidirectionalTransitions2CTest(final String config) { + super("BidirectionalTransitions", new String[] { "-config", config }, ExitStatus.VIOLATION_LIVENESS); + } + + @Test + public void testSpec() { + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertFalse(recorder.recorded(EC.GENERAL)); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "9", "4", "0")); + + assertTrue(recorder.recorded(EC.TLC_TEMPORAL_PROPERTY_VIOLATED)); + assertTrue(recorder.recorded(EC.TLC_COUNTER_EXAMPLE)); + + assertTrue(recorder.recorded(EC.TLC_STATE_PRINT2)); + final List<String> expectedTrace = new ArrayList<String>(3); + expectedTrace.add("x = 0"); + expectedTrace.add("x = 1"); + expectedTrace.add("x = 2"); + expectedTrace.add("x = 3"); + assertTraceWith(recorder.getRecords(EC.TLC_STATE_PRINT2), expectedTrace); + + assertBackToState(1); + } +} diff --git a/tlatools/test/tlc2/tool/liveness/BidirectionalTransitions2CxTest.java b/tlatools/test/tlc2/tool/liveness/BidirectionalTransitions2CxTest.java new file mode 100644 index 0000000000000000000000000000000000000000..7475a9b6ea72b257b80afa6c73130ee9c336a3cd --- /dev/null +++ b/tlatools/test/tlc2/tool/liveness/BidirectionalTransitions2CxTest.java @@ -0,0 +1,33 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.liveness; + +public class BidirectionalTransitions2CxTest extends BidirectionalTransitions2CTest { + + public BidirectionalTransitions2CxTest() { + super("BidirectionalTransitions2Cx"); + } +} diff --git a/tlatools/test/tlc2/tool/liveness/BidirectionalTransitions2CyTest.java b/tlatools/test/tlc2/tool/liveness/BidirectionalTransitions2CyTest.java new file mode 100644 index 0000000000000000000000000000000000000000..e120364ce1bea66be87ca34a8063b68e91393b0a --- /dev/null +++ b/tlatools/test/tlc2/tool/liveness/BidirectionalTransitions2CyTest.java @@ -0,0 +1,33 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.liveness; + +public class BidirectionalTransitions2CyTest extends BidirectionalTransitions2CTest { + + public BidirectionalTransitions2CyTest() { + super("BidirectionalTransitions2Cy"); + } +} diff --git a/tlatools/test/tlc2/tool/liveness/BidirectionalTransitions2Test.java b/tlatools/test/tlc2/tool/liveness/BidirectionalTransitions2Test.java new file mode 100644 index 0000000000000000000000000000000000000000..e72686cf17f294df8c8bedc3a249a55705734bdf --- /dev/null +++ b/tlatools/test/tlc2/tool/liveness/BidirectionalTransitions2Test.java @@ -0,0 +1,50 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.liveness; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.output.EC; + +public class BidirectionalTransitions2Test extends ModelCheckerTestCase { + + public BidirectionalTransitions2Test() { + super("BidirectionalTransitions", new String[] {"-config", "BidirectionalTransitions2.cfg"}); + } + + @Test + public void testSpec() { + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertFalse(recorder.recorded(EC.GENERAL)); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "9", "4", "0")); + assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "3")); + + assertZeroUncovered(); + } +} diff --git a/tlatools/test/tlc2/tool/liveness/ChooseTableauSymmetryTest.java b/tlatools/test/tlc2/tool/liveness/ChooseTableauSymmetryTest.java new file mode 100644 index 0000000000000000000000000000000000000000..4cd7caa5a655c0c2f7071b0e918855e25cec985f --- /dev/null +++ b/tlatools/test/tlc2/tool/liveness/ChooseTableauSymmetryTest.java @@ -0,0 +1,71 @@ +/******************************************************************************* + * Copyright (c) 2015 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ + +package tlc2.tool.liveness; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.Ignore; +import org.junit.Test; + +import tlc2.output.EC; + +public class ChooseTableauSymmetryTest extends ModelCheckerTestCase { + + public ChooseTableauSymmetryTest() { + super("ChooseTableauSymmetryMC", "symmetry"); + } + + @Test + @Ignore("Ignored for as long as symmetry is incorrectly handled by TLC with liveness checking.") + public void testSpec() { + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "13", "6", "0")); + assertFalse(recorder.recorded(EC.GENERAL)); + + // Assert it has found the temporal violation and also a counter example + assertTrue(recorder.recorded(EC.TLC_TEMPORAL_PROPERTY_VIOLATED)); + assertTrue(recorder.recorded(EC.TLC_COUNTER_EXAMPLE)); + + // Assert the error trace + assertTrue(recorder.recorded(EC.TLC_STATE_PRINT2)); + final List<String> expectedTrace = new ArrayList<String>(7); + // Trace prefix + expectedTrace.add("arr = (a :> \"ready\" @@ b :> \"ready\")"); + expectedTrace.add("arr = (a :> \"busy\" @@ b :> \"ready\")"); + expectedTrace.add("arr = (a :> \"done\" @@ b :> \"ready\")"); + expectedTrace.add("arr = (a :> \"done\" @@ b :> \"busy\")"); + expectedTrace.add("arr = (a :> \"done\" @@ b :> \"done\")"); + expectedTrace.add("arr = (a :> \"done\" @@ b :> \"ready\")"); + assertTraceWith(recorder.getRecords(EC.TLC_STATE_PRINT2), expectedTrace); + + assertBackToState(4, "<Action line 7, col 13 to line 8, col 47 of module ChooseTableauSymmetry>"); + } +} diff --git a/tlatools/test/tlc2/tool/liveness/ChooseTableauSymmetryTestA.java b/tlatools/test/tlc2/tool/liveness/ChooseTableauSymmetryTestA.java new file mode 100644 index 0000000000000000000000000000000000000000..13198ab605c01fff06a9ce0542acd157c4219220 --- /dev/null +++ b/tlatools/test/tlc2/tool/liveness/ChooseTableauSymmetryTestA.java @@ -0,0 +1,71 @@ +/******************************************************************************* + * Copyright (c) 2015 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ + +package tlc2.tool.liveness; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.Test; + +import tlc2.output.EC; +import tlc2.output.EC.ExitStatus; + +public class ChooseTableauSymmetryTestA extends ModelCheckerTestCase { + + public ChooseTableauSymmetryTestA() { + super("ChooseTableauSymmetryMCa", "symmetry", ExitStatus.VIOLATION_LIVENESS); + } + + @Test + public void testSpec() { + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "13", "6", "0")); + assertFalse(recorder.recorded(EC.GENERAL)); + + // Assert it has found the temporal violation and also a counter example + assertTrue(recorder.recorded(EC.TLC_TEMPORAL_PROPERTY_VIOLATED)); + assertTrue(recorder.recorded(EC.TLC_COUNTER_EXAMPLE)); + + // Assert the error trace + assertTrue(recorder.recorded(EC.TLC_STATE_PRINT2)); + final List<String> expectedTrace = new ArrayList<String>(7); + // Trace prefix + expectedTrace.add("arr = (a :> \"ready\" @@ b :> \"ready\")"); + expectedTrace.add("arr = (a :> \"busy\" @@ b :> \"ready\")"); + expectedTrace.add("arr = (a :> \"busy\" @@ b :> \"busy\")"); + expectedTrace.add("arr = (a :> \"done\" @@ b :> \"busy\")"); + expectedTrace.add("arr = (a :> \"ready\" @@ b :> \"busy\")"); + assertTraceWith(recorder.getRecords(EC.TLC_STATE_PRINT2), expectedTrace); + + assertBackToState(3, "<Ready line 7, col 13 to line 8, col 47 of module ChooseTableauSymmetry>"); + + assertZeroUncovered(); + } +} diff --git a/tlatools/test/tlc2/tool/liveness/CodePlexBug08AgentRingTest.java b/tlatools/test/tlc2/tool/liveness/CodePlexBug08AgentRingTest.java index 3db7fa50cee308b072b1d8244c215d7f7f3e7f6e..a4d22559a1f8886d4feff0e96b74bc7d17412490 100644 --- a/tlatools/test/tlc2/tool/liveness/CodePlexBug08AgentRingTest.java +++ b/tlatools/test/tlc2/tool/liveness/CodePlexBug08AgentRingTest.java @@ -26,10 +26,16 @@ package tlc2.tool.liveness; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.util.ArrayList; import java.util.List; +import org.junit.Test; + import tlc2.output.EC; -import tlc2.tool.TLCStateInfo; +import tlc2.output.EC.ExitStatus; /** * see http://tlaplus.codeplex.com/workitem/8 @@ -37,78 +43,73 @@ import tlc2.tool.TLCStateInfo; public class CodePlexBug08AgentRingTest extends ModelCheckerTestCase { public CodePlexBug08AgentRingTest() { - super("AgentRingMC", "CodePlexBug08"); + super("AgentRingMC", "CodePlexBug08", ExitStatus.VIOLATION_LIVENESS); } + @Test public void testSpec() { // ModelChecker has finished and generated the expected amount of states assertTrue(recorder.recorded(EC.TLC_FINISHED)); assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "361", "120")); + assertFalse(recorder.recorded(EC.GENERAL)); // Assert it has found the temporal violation and also a counter example assertTrue(recorder.recorded(EC.TLC_TEMPORAL_PROPERTY_VIOLATED)); assertTrue(recorder.recorded(EC.TLC_COUNTER_EXAMPLE)); + + assertNodeAndPtrSizes(8496L, 2880L); // Assert the error trace assertTrue(recorder.recorded(EC.TLC_STATE_PRINT2)); - List<Object> records = recorder.getRecords(EC.TLC_STATE_PRINT2); - - int i = 0; // State's position in records - Object[] objs = (Object[]) records.get(i++); - TLCStateInfo stateInfo = (TLCStateInfo) objs[0]; - assertEquals("/\\ Agent = [Loc |-> 0, LastLoad |-> 0, ReadyToMove |-> TRUE, Task |-> 0]\n" + final List<String> expectedTrace = new ArrayList<String>(4); + expectedTrace.add("/\\ Agent = [Loc |-> 0, LastLoad |-> 0, ReadyToMove |-> TRUE, Task |-> 0]\n" + "/\\ CanCreate = TRUE\n" - + "/\\ Nodes = (0 :> [Load |-> 0] @@ 1 :> [Load |-> 0])", - stateInfo.toString().trim()); // trimmed to remove any newlines or whitespace - assertEquals(i, objs[1]); - - objs = (Object[]) records.get(i++); - stateInfo = (TLCStateInfo) objs[0]; - assertEquals("/\\ Agent = [Loc |-> 1, LastLoad |-> 0, ReadyToMove |-> FALSE, Task |-> 0]\n" + + "/\\ Nodes = (0 :> [Load |-> 0] @@ 1 :> [Load |-> 0])"); + expectedTrace.add("/\\ Agent = [Loc |-> 0, LastLoad |-> 0, ReadyToMove |-> TRUE, Task |-> 0]\n" + "/\\ CanCreate = TRUE\n" - + "/\\ Nodes = (0 :> [Load |-> 0] @@ 1 :> [Load |-> 0])", - stateInfo.toString().trim()); - assertEquals(i, objs[1]); - - objs = (Object[]) records.get(i++); - stateInfo = (TLCStateInfo) objs[0]; - assertEquals("/\\ Agent = [Loc |-> 1, LastLoad |-> 0, ReadyToMove |-> FALSE, Task |-> 0]\n" + + "/\\ Nodes = (0 :> [Load |-> 2] @@ 1 :> [Load |-> 0])"); + expectedTrace.add("/\\ Agent = [Loc |-> 1, LastLoad |-> 0, ReadyToMove |-> FALSE, Task |-> 0]\n" + "/\\ CanCreate = TRUE\n" - + "/\\ Nodes = (0 :> [Load |-> 2] @@ 1 :> [Load |-> 0])", - stateInfo.toString().trim()); - assertEquals(i, objs[1]); - - objs = (Object[]) records.get(i++); - stateInfo = (TLCStateInfo) objs[0]; - assertEquals("/\\ Agent = [Loc |-> 1, LastLoad |-> 0, ReadyToMove |-> FALSE, Task |-> 0]\n" + + "/\\ Nodes = (0 :> [Load |-> 2] @@ 1 :> [Load |-> 0])"); + expectedTrace.add("/\\ Agent = [Loc |-> 1, LastLoad |-> 0, ReadyToMove |-> FALSE, Task |-> 0]\n" + "/\\ CanCreate = TRUE\n" - + "/\\ Nodes = (0 :> [Load |-> 2] @@ 1 :> [Load |-> 2])", - stateInfo.toString().trim()); - assertEquals(i, objs[1]); - + + "/\\ Nodes = (0 :> [Load |-> 2] @@ 1 :> [Load |-> 2])"); // The two states below violate the liveness property [](~CanCreate /\ // (\A i,j \in NodeRange : Nodes[i].Load = Nodes[j].Load) => // [](Agent.Task = 0)). State 5 has CanCreate = FALSE and Task=0 and // state six changes Task back to 1. - - // state 5 - objs = (Object[]) records.get(i++); - stateInfo = (TLCStateInfo) objs[0]; - assertEquals("/\\ Agent = [Loc |-> 1, LastLoad |-> 0, ReadyToMove |-> FALSE, Task |-> 0]\n" + expectedTrace.add("/\\ Agent = [Loc |-> 1, LastLoad |-> 0, ReadyToMove |-> FALSE, Task |-> 0]\n" + + "/\\ CanCreate = FALSE\n" + + "/\\ Nodes = (0 :> [Load |-> 2] @@ 1 :> [Load |-> 2])"); + expectedTrace.add("/\\ Agent = [Loc |-> 1, LastLoad |-> 1, ReadyToMove |-> TRUE, Task |-> 1]\n" + "/\\ CanCreate = FALSE\n" - + "/\\ Nodes = (0 :> [Load |-> 2] @@ 1 :> [Load |-> 2])", - stateInfo.toString().trim()); - assertEquals(i, objs[1]); + + "/\\ Nodes = (0 :> [Load |-> 2] @@ 1 :> [Load |-> 1])"); - // state 6 - objs = (Object[]) records.get(i++); - stateInfo = (TLCStateInfo) objs[0]; - assertEquals("/\\ Agent = [Loc |-> 1, LastLoad |-> 1, ReadyToMove |-> TRUE, Task |-> 1]\n" + expectedTrace.add("/\\ Agent = [Loc |-> 0, LastLoad |-> 1, ReadyToMove |-> FALSE, Task |-> 1]\n" + "/\\ CanCreate = FALSE\n" - + "/\\ Nodes = (0 :> [Load |-> 2] @@ 1 :> [Load |-> 1])", - stateInfo.toString().trim()); - assertEquals(i, objs[1]); - - // ...more states to follow + + "/\\ Nodes = (0 :> [Load |-> 2] @@ 1 :> [Load |-> 1])"); + expectedTrace.add("/\\ Agent = [Loc |-> 0, LastLoad |-> 2, ReadyToMove |-> TRUE, Task |-> 1]\n" + + "/\\ CanCreate = FALSE\n" + + "/\\ Nodes = (0 :> [Load |-> 2] @@ 1 :> [Load |-> 1])"); + expectedTrace.add("/\\ Agent = [Loc |-> 1, LastLoad |-> 2, ReadyToMove |-> FALSE, Task |-> 1]\n" + + "/\\ CanCreate = FALSE\n" + + "/\\ Nodes = (0 :> [Load |-> 2] @@ 1 :> [Load |-> 1])"); + expectedTrace.add("/\\ Agent = [Loc |-> 1, LastLoad |-> 2, ReadyToMove |-> TRUE, Task |-> 0]\n" + + "/\\ CanCreate = FALSE\n" + + "/\\ Nodes = (0 :> [Load |-> 2] @@ 1 :> [Load |-> 2])"); + expectedTrace.add("/\\ Agent = [Loc |-> 0, LastLoad |-> 2, ReadyToMove |-> FALSE, Task |-> 0]\n" + + "/\\ CanCreate = FALSE\n" + + "/\\ Nodes = (0 :> [Load |-> 2] @@ 1 :> [Load |-> 2])"); + expectedTrace.add("/\\ Agent = [Loc |-> 0, LastLoad |-> 2, ReadyToMove |-> TRUE, Task |-> 0]\n" + + "/\\ CanCreate = FALSE\n" + + "/\\ Nodes = (0 :> [Load |-> 2] @@ 1 :> [Load |-> 2])"); + expectedTrace.add("/\\ Agent = [Loc |-> 1, LastLoad |-> 2, ReadyToMove |-> FALSE, Task |-> 0]\n" + + "/\\ CanCreate = FALSE\n" + + "/\\ Nodes = (0 :> [Load |-> 2] @@ 1 :> [Load |-> 2])"); + assertTraceWith(recorder.getRecords(EC.TLC_STATE_PRINT2), expectedTrace); + + assertBackToState(10); + + assertZeroUncovered(); } } diff --git a/tlatools/test/tlc2/tool/liveness/CodePlexBug08EWD840FL1Test.java b/tlatools/test/tlc2/tool/liveness/CodePlexBug08EWD840FL1Test.java index 066f3d233231e041f737e85090b72e6abb869c45..814dc1d96c20846863cd60240e83182ab0a0e029 100644 --- a/tlatools/test/tlc2/tool/liveness/CodePlexBug08EWD840FL1Test.java +++ b/tlatools/test/tlc2/tool/liveness/CodePlexBug08EWD840FL1Test.java @@ -26,10 +26,16 @@ package tlc2.tool.liveness; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.util.ArrayList; import java.util.List; +import org.junit.Test; + import tlc2.output.EC; -import tlc2.tool.TLCStateInfo; +import tlc2.output.EC.ExitStatus; /** * see http://tlaplus.codeplex.com/workitem/8 @@ -37,40 +43,69 @@ import tlc2.tool.TLCStateInfo; public class CodePlexBug08EWD840FL1Test extends ModelCheckerTestCase { public CodePlexBug08EWD840FL1Test() { - super("EWD840MC1", "CodePlexBug08"); + super("EWD840MC1", "CodePlexBug08", ExitStatus.VIOLATION_LIVENESS); } + @Test public void testSpec() { // ModelChecker has finished and generated the expected amount of states assertTrue(recorder.recorded(EC.TLC_FINISHED)); - assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "15986", "1566")); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "15986", "1566", "0")); + assertFalse(recorder.recorded(EC.GENERAL)); // Assert it has found the temporal violation and also a counter example assertTrue(recorder.recorded(EC.TLC_TEMPORAL_PROPERTY_VIOLATED)); assertTrue(recorder.recorded(EC.TLC_COUNTER_EXAMPLE)); + assertNodeAndPtrSizes(7560068L, 279616L); + // Assert the error trace assertTrue(recorder.recorded(EC.TLC_STATE_PRINT2)); - List<Object> records = recorder.getRecords(EC.TLC_STATE_PRINT2); + final List<String> expectedTrace = new ArrayList<String>(); + expectedTrace.add("/\\ tpos = 0\n" + + "/\\ active = (0 :> FALSE @@ 1 :> FALSE @@ 2 :> FALSE @@ 3 :> TRUE)\n" + + "/\\ tcolor = \"black\"\n" + + "/\\ color = (0 :> \"white\" @@ 1 :> \"white\" @@ 2 :> \"white\" @@ 3 :> \"white\")"); + expectedTrace.add("/\\ tpos = 3\n" + + "/\\ active = (0 :> FALSE @@ 1 :> FALSE @@ 2 :> FALSE @@ 3 :> TRUE)\n" + + "/\\ tcolor = \"white\"\n" + + "/\\ color = (0 :> \"white\" @@ 1 :> \"white\" @@ 2 :> \"white\" @@ 3 :> \"white\")"); + expectedTrace.add("/\\ tpos = 3\n" + + "/\\ active = (0 :> FALSE @@ 1 :> FALSE @@ 2 :> TRUE @@ 3 :> TRUE)\n" + + "/\\ tcolor = \"white\"\n" + + "/\\ color = (0 :> \"white\" @@ 1 :> \"white\" @@ 2 :> \"white\" @@ 3 :> \"white\")"); + expectedTrace.add("/\\ tpos = 3\n" + + "/\\ active = (0 :> TRUE @@ 1 :> FALSE @@ 2 :> TRUE @@ 3 :> TRUE)\n" + + "/\\ tcolor = \"white\"\n" + + "/\\ color = (0 :> \"white\" @@ 1 :> \"white\" @@ 2 :> \"white\" @@ 3 :> \"white\")"); + expectedTrace.add("/\\ tpos = 3\n" + + "/\\ active = (0 :> TRUE @@ 1 :> FALSE @@ 2 :> TRUE @@ 3 :> FALSE)\n" + + "/\\ tcolor = \"white\"\n" + + "/\\ color = (0 :> \"white\" @@ 1 :> \"white\" @@ 2 :> \"white\" @@ 3 :> \"white\")"); + expectedTrace.add("/\\ tpos = 3\n" + + "/\\ active = (0 :> FALSE @@ 1 :> FALSE @@ 2 :> TRUE @@ 3 :> FALSE)\n" + + "/\\ tcolor = \"white\"\n" + + "/\\ color = (0 :> \"white\" @@ 1 :> \"white\" @@ 2 :> \"white\" @@ 3 :> \"white\")"); + expectedTrace.add("/\\ tpos = 2\n" + + "/\\ active = (0 :> FALSE @@ 1 :> FALSE @@ 2 :> TRUE @@ 3 :> FALSE)\n" + + "/\\ tcolor = \"white\"\n" + + "/\\ color = (0 :> \"white\" @@ 1 :> \"white\" @@ 2 :> \"white\" @@ 3 :> \"white\")"); + expectedTrace.add("/\\ tpos = 2\n" + + "/\\ active = (0 :> FALSE @@ 1 :> FALSE @@ 2 :> TRUE @@ 3 :> TRUE)\n" + + "/\\ tcolor = \"white\"\n" + + "/\\ color = (0 :> \"white\" @@ 1 :> \"white\" @@ 2 :> \"black\" @@ 3 :> \"white\")"); + expectedTrace.add("/\\ tpos = 1\n" + + "/\\ active = (0 :> FALSE @@ 1 :> FALSE @@ 2 :> TRUE @@ 3 :> TRUE)\n" + + "/\\ tcolor = \"black\"\n" + + "/\\ color = (0 :> \"white\" @@ 1 :> \"white\" @@ 2 :> \"white\" @@ 3 :> \"white\")"); + expectedTrace.add("/\\ tpos = 1\n" + + "/\\ active = (0 :> FALSE @@ 1 :> FALSE @@ 2 :> FALSE @@ 3 :> TRUE)\n" + + "/\\ tcolor = \"black\"\n" + + "/\\ color = (0 :> \"white\" @@ 1 :> \"white\" @@ 2 :> \"white\" @@ 3 :> \"white\")"); + assertTraceWith(recorder.getRecords(EC.TLC_STATE_PRINT2), expectedTrace); - int i = 0; // State's position in records - Object[] objs = (Object[]) records.get(i++); - TLCStateInfo stateInfo = (TLCStateInfo) objs[0]; - assertEquals("/\\ tpos = 0\n" - + "/\\ active = (0 :> FALSE @@ 1 :> FALSE @@ 2 :> FALSE @@ 3 :> TRUE)\n" - + "/\\ tcolor = \"black\"\n" - + "/\\ color = (0 :> \"white\" @@ 1 :> \"white\" @@ 2 :> \"white\" @@ 3 :> \"white\")", - stateInfo.toString().trim()); // trimmed to remove any newlines or whitespace - assertEquals(i, objs[1]); - - // Omitted check of 8 in-between states, just make sure four more states exist - assertEquals("Expect five states prior to stuttering", 8, records.size()); - - // state six is back to state 1 - assertTrue(recorder.recorded(EC.TLC_BACK_TO_STATE)); - List<Object> stutter = recorder.getRecords(EC.TLC_BACK_TO_STATE); - assertTrue(stutter.size() > 0); - Object[] object = (Object[]) stutter.get(0); - assertEquals("1", object[0]); + assertBackToState(1); + + assertZeroUncovered(); } } diff --git a/tlatools/test/tlc2/tool/liveness/CodePlexBug08EWD840FL2FromCheckpointTest.java b/tlatools/test/tlc2/tool/liveness/CodePlexBug08EWD840FL2FromCheckpointTest.java new file mode 100644 index 0000000000000000000000000000000000000000..3af646ba5360d97194ec8577bbe43e01c7f2eb05 --- /dev/null +++ b/tlatools/test/tlc2/tool/liveness/CodePlexBug08EWD840FL2FromCheckpointTest.java @@ -0,0 +1,176 @@ +/******************************************************************************* + * Copyright (c) 2015 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ + +package tlc2.tool.liveness; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.List; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; + +import org.junit.Test; + +import tlc2.output.EC; +import tlc2.output.EC.ExitStatus; + +/** + * see http://tlaplus.codeplex.com/workitem/8 + */ +public class CodePlexBug08EWD840FL2FromCheckpointTest extends ModelCheckerTestCase { + + public CodePlexBug08EWD840FL2FromCheckpointTest() { + super("EWD840MC2", "CodePlexBug08", new String[] {"-gzip", "-recover", BASE_DIR + TEST_MODEL + "CodePlexBug08" + File.separator + "checkpoint"}, ExitStatus.VIOLATION_LIVENESS); + } + + + @Override + public void setUp() { + try { + /* Recreate checkpoint.zip whenever file format changes: + * + * 1) Run CodePlexBug08EWD840FL2Test with "-gzip" + * 2) Connect to running test via JMX and request TLC checkpoint to be taken + * 3) Terminate CodePlexBug08EWD840FL2Test once checkpoint successfully taken + * 4) Locate the directory with the checkpoint data + * 5) Replace the content of checkpoint.zip with the content of 4) + * 6) Update the number below on states found... + */ + String prefix = BASE_DIR + TEST_MODEL + "CodePlexBug08" + File.separator; + ZipFile zipFile = new ZipFile(prefix + "checkpoint.zip"); + Enumeration<?> enu = zipFile.entries(); + while (enu.hasMoreElements()) { + ZipEntry zipEntry = (ZipEntry) enu.nextElement(); + + File file = new File(prefix + zipEntry.getName()); + if (zipEntry.getName().endsWith("/")) { + file.mkdirs(); + continue; + } + + File parent = file.getParentFile(); + if (parent != null) { + parent.mkdirs(); + } + + InputStream is = zipFile.getInputStream(zipEntry); + FileOutputStream fos = new FileOutputStream(file); + byte[] bytes = new byte[1024]; + int length; + while ((length = is.read(bytes)) >= 0) { + fos.write(bytes, 0, length); + } + is.close(); + fos.close(); + + } + zipFile.close(); + } catch (IOException e) { + e.printStackTrace(); + } + super.setUp(); + } + + @Test + public void testSpec() { + assertTrue(recorder.recorded(EC.TLC_CHECKPOINT_RECOVER_START)); + // Recovery completed. 1032 states examined. 996 states on queue. + assertTrue(recorder.recordedWithStringValues(EC.TLC_CHECKPOINT_RECOVER_END, "1510", "39")); + // ModelChecker has finished and generated the expected amount of states + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "2334", "1566","0")); + assertFalse(recorder.recorded(EC.GENERAL)); + + // Assert it has found the temporal violation and also a counter example + assertTrue(recorder.recorded(EC.TLC_TEMPORAL_PROPERTY_VIOLATED)); + assertTrue(recorder.recorded(EC.TLC_COUNTER_EXAMPLE)); + + assertNodeAndPtrSizes(54038132L, 831296L); + + // Assert the error trace + assertTrue(recorder.recorded(EC.TLC_STATE_PRINT2)); + final List<String> expectedTrace = new ArrayList<String>(); + expectedTrace.add("/\\ tpos = 0\n" + + "/\\ active = (0 :> FALSE @@ 1 :> FALSE @@ 2 :> FALSE @@ 3 :> TRUE)\n" + + "/\\ tcolor = \"black\"\n" + + "/\\ color = (0 :> \"white\" @@ 1 :> \"white\" @@ 2 :> \"white\" @@ 3 :> \"white\")"); + expectedTrace.add("/\\ tpos = 3\n" + + "/\\ active = (0 :> FALSE @@ 1 :> FALSE @@ 2 :> FALSE @@ 3 :> TRUE)\n" + + "/\\ tcolor = \"white\"\n" + + "/\\ color = (0 :> \"white\" @@ 1 :> \"white\" @@ 2 :> \"white\" @@ 3 :> \"white\")"); + expectedTrace.add("/\\ tpos = 3\n" + + "/\\ active = (0 :> FALSE @@ 1 :> FALSE @@ 2 :> TRUE @@ 3 :> TRUE)\n" + + "/\\ tcolor = \"white\"\n" + + "/\\ color = (0 :> \"white\" @@ 1 :> \"white\" @@ 2 :> \"white\" @@ 3 :> \"white\")"); + expectedTrace.add("/\\ tpos = 3\n" + + "/\\ active = (0 :> TRUE @@ 1 :> FALSE @@ 2 :> TRUE @@ 3 :> TRUE)\n" + + "/\\ tcolor = \"white\"\n" + + "/\\ color = (0 :> \"white\" @@ 1 :> \"white\" @@ 2 :> \"white\" @@ 3 :> \"white\")"); + expectedTrace.add("/\\ tpos = 3\n" + + "/\\ active = (0 :> TRUE @@ 1 :> FALSE @@ 2 :> TRUE @@ 3 :> FALSE)\n" + + "/\\ tcolor = \"white\"\n" + + "/\\ color = (0 :> \"white\" @@ 1 :> \"white\" @@ 2 :> \"white\" @@ 3 :> \"white\")"); + expectedTrace.add("/\\ tpos = 3\n" + + "/\\ active = (0 :> FALSE @@ 1 :> FALSE @@ 2 :> TRUE @@ 3 :> FALSE)\n" + + "/\\ tcolor = \"white\"\n" + + "/\\ color = (0 :> \"white\" @@ 1 :> \"white\" @@ 2 :> \"white\" @@ 3 :> \"white\")"); + expectedTrace.add("/\\ tpos = 2\n" + + "/\\ active = (0 :> FALSE @@ 1 :> FALSE @@ 2 :> TRUE @@ 3 :> FALSE)\n" + + "/\\ tcolor = \"white\"\n" + + "/\\ color = (0 :> \"white\" @@ 1 :> \"white\" @@ 2 :> \"white\" @@ 3 :> \"white\")"); + expectedTrace.add("/\\ tpos = 2\n" + + "/\\ active = (0 :> FALSE @@ 1 :> FALSE @@ 2 :> TRUE @@ 3 :> TRUE)\n" + + "/\\ tcolor = \"white\"\n" + + "/\\ color = (0 :> \"white\" @@ 1 :> \"white\" @@ 2 :> \"black\" @@ 3 :> \"white\")"); + expectedTrace.add("/\\ tpos = 1\n" + + "/\\ active = (0 :> FALSE @@ 1 :> FALSE @@ 2 :> TRUE @@ 3 :> TRUE)\n" + + "/\\ tcolor = \"black\"\n" + + "/\\ color = (0 :> \"white\" @@ 1 :> \"white\" @@ 2 :> \"white\" @@ 3 :> \"white\")"); + expectedTrace.add("/\\ tpos = 1\n" + + "/\\ active = (0 :> FALSE @@ 1 :> FALSE @@ 2 :> FALSE @@ 3 :> TRUE)\n" + + "/\\ tcolor = \"black\"\n" + + "/\\ color = (0 :> \"white\" @@ 1 :> \"white\" @@ 2 :> \"white\" @@ 3 :> \"white\")"); + assertTraceWith(recorder.getRecords(EC.TLC_STATE_PRINT2), expectedTrace); + + // last state points back to state 1 + assertBackToState(1); + } + + + @Override + protected int getNumberOfThreads() { + return 3; + } + + +} diff --git a/tlatools/test/tlc2/tool/liveness/CodePlexBug08EWD840FL2Test.java b/tlatools/test/tlc2/tool/liveness/CodePlexBug08EWD840FL2Test.java index f38a278c40c879b6d4c8ebf4a5d4f9a2ca343ee3..d81be0bbfc73c0b8589dc61991156983cd13f989 100644 --- a/tlatools/test/tlc2/tool/liveness/CodePlexBug08EWD840FL2Test.java +++ b/tlatools/test/tlc2/tool/liveness/CodePlexBug08EWD840FL2Test.java @@ -26,11 +26,16 @@ package tlc2.tool.liveness; -import java.io.File; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.util.ArrayList; import java.util.List; -import tlc2.TLCGlobals; +import org.junit.Test; + import tlc2.output.EC; +import tlc2.output.EC.ExitStatus; /** * see http://tlaplus.codeplex.com/workitem/8 @@ -38,38 +43,68 @@ import tlc2.output.EC; public class CodePlexBug08EWD840FL2Test extends ModelCheckerTestCase { public CodePlexBug08EWD840FL2Test() { - super("EWD840MC2", "CodePlexBug08"); + super("EWD840MC2", "CodePlexBug08", ExitStatus.VIOLATION_LIVENESS); } + @Test public void testSpec() { // ModelChecker has finished and generated the expected amount of states assertTrue(recorder.recorded(EC.TLC_FINISHED)); - assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "15986", "1566")); - + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "15986", "1566","0")); + assertFalse(recorder.recorded(EC.GENERAL)); + // Assert it has found the temporal violation and also a counter example assertTrue(recorder.recorded(EC.TLC_TEMPORAL_PROPERTY_VIOLATED)); assertTrue(recorder.recorded(EC.TLC_COUNTER_EXAMPLE)); + assertNodeAndPtrSizes(54037212L, 831296L); + // Assert the error trace assertTrue(recorder.recorded(EC.TLC_STATE_PRINT2)); + final List<String> expectedTrace = new ArrayList<String>(); + expectedTrace.add("/\\ tpos = 0\n" + + "/\\ active = (0 :> FALSE @@ 1 :> FALSE @@ 2 :> FALSE @@ 3 :> TRUE)\n" + + "/\\ tcolor = \"black\"\n" + + "/\\ color = (0 :> \"white\" @@ 1 :> \"white\" @@ 2 :> \"white\" @@ 3 :> \"white\")"); + expectedTrace.add("/\\ tpos = 3\n" + + "/\\ active = (0 :> FALSE @@ 1 :> FALSE @@ 2 :> FALSE @@ 3 :> TRUE)\n" + + "/\\ tcolor = \"white\"\n" + + "/\\ color = (0 :> \"white\" @@ 1 :> \"white\" @@ 2 :> \"white\" @@ 3 :> \"white\")"); + expectedTrace.add("/\\ tpos = 3\n" + + "/\\ active = (0 :> FALSE @@ 1 :> FALSE @@ 2 :> TRUE @@ 3 :> TRUE)\n" + + "/\\ tcolor = \"white\"\n" + + "/\\ color = (0 :> \"white\" @@ 1 :> \"white\" @@ 2 :> \"white\" @@ 3 :> \"white\")"); + expectedTrace.add("/\\ tpos = 3\n" + + "/\\ active = (0 :> TRUE @@ 1 :> FALSE @@ 2 :> TRUE @@ 3 :> TRUE)\n" + + "/\\ tcolor = \"white\"\n" + + "/\\ color = (0 :> \"white\" @@ 1 :> \"white\" @@ 2 :> \"white\" @@ 3 :> \"white\")"); + expectedTrace.add("/\\ tpos = 3\n" + + "/\\ active = (0 :> TRUE @@ 1 :> FALSE @@ 2 :> TRUE @@ 3 :> FALSE)\n" + + "/\\ tcolor = \"white\"\n" + + "/\\ color = (0 :> \"white\" @@ 1 :> \"white\" @@ 2 :> \"white\" @@ 3 :> \"white\")"); + expectedTrace.add("/\\ tpos = 3\n" + + "/\\ active = (0 :> FALSE @@ 1 :> FALSE @@ 2 :> TRUE @@ 3 :> FALSE)\n" + + "/\\ tcolor = \"white\"\n" + + "/\\ color = (0 :> \"white\" @@ 1 :> \"white\" @@ 2 :> \"white\" @@ 3 :> \"white\")"); + expectedTrace.add("/\\ tpos = 2\n" + + "/\\ active = (0 :> FALSE @@ 1 :> FALSE @@ 2 :> TRUE @@ 3 :> FALSE)\n" + + "/\\ tcolor = \"white\"\n" + + "/\\ color = (0 :> \"white\" @@ 1 :> \"white\" @@ 2 :> \"white\" @@ 3 :> \"white\")"); + expectedTrace.add("/\\ tpos = 2\n" + + "/\\ active = (0 :> FALSE @@ 1 :> FALSE @@ 2 :> TRUE @@ 3 :> TRUE)\n" + + "/\\ tcolor = \"white\"\n" + + "/\\ color = (0 :> \"white\" @@ 1 :> \"white\" @@ 2 :> \"black\" @@ 3 :> \"white\")"); + expectedTrace.add("/\\ tpos = 1\n" + + "/\\ active = (0 :> FALSE @@ 1 :> FALSE @@ 2 :> TRUE @@ 3 :> TRUE)\n" + + "/\\ tcolor = \"black\"\n" + + "/\\ color = (0 :> \"white\" @@ 1 :> \"white\" @@ 2 :> \"white\" @@ 3 :> \"white\")"); + expectedTrace.add("/\\ tpos = 1\n" + + "/\\ active = (0 :> FALSE @@ 1 :> FALSE @@ 2 :> FALSE @@ 3 :> TRUE)\n" + + "/\\ tcolor = \"black\"\n" + + "/\\ color = (0 :> \"white\" @@ 1 :> \"white\" @@ 2 :> \"white\" @@ 3 :> \"white\")"); + assertTraceWith(recorder.getRecords(EC.TLC_STATE_PRINT2), expectedTrace); // last state points back to state 1 - assertTrue(recorder.recorded(EC.TLC_BACK_TO_STATE)); - List<Object> stutter = recorder.getRecords(EC.TLC_BACK_TO_STATE); - assertTrue(stutter.size() > 0); - Object[] object = (Object[]) stutter.get(0); - assertEquals("1", object[0]); - - // Check the file size of the AbstractDiskGraph files to check if the - // expected amount of ptrs and nodes (outgoing arcs) have been written - // to disk. - final String metadir = TLCGlobals.mainChecker.metadir; - assertNotNull(metadir); - final File nodes = new File(metadir + File.separator + "nodes_0"); - final File ptrs = new File(metadir + File.separator + "ptrs_0"); - assertTrue(nodes.exists()); - assertTrue(ptrs.exists()); - assertEquals(53958732L, nodes.length()); - assertEquals(831296L, ptrs.length()); + assertBackToState(1); } } diff --git a/tlatools/test/tlc2/tool/liveness/CodePlexBug08EWD840FL3Test.java b/tlatools/test/tlc2/tool/liveness/CodePlexBug08EWD840FL3Test.java index 571312fb4417d3aa612999fcafd06ea1eae94057..9181588a17ff35d7fd00ae31a79147898d420953 100644 --- a/tlatools/test/tlc2/tool/liveness/CodePlexBug08EWD840FL3Test.java +++ b/tlatools/test/tlc2/tool/liveness/CodePlexBug08EWD840FL3Test.java @@ -26,10 +26,16 @@ package tlc2.tool.liveness; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.util.ArrayList; import java.util.List; +import org.junit.Test; + import tlc2.output.EC; -import tlc2.tool.TLCStateInfo; +import tlc2.output.EC.ExitStatus; /** * see http://tlaplus.codeplex.com/workitem/8 @@ -37,40 +43,62 @@ import tlc2.tool.TLCStateInfo; public class CodePlexBug08EWD840FL3Test extends ModelCheckerTestCase { public CodePlexBug08EWD840FL3Test() { - super("EWD840MC3", "CodePlexBug08"); + super("EWD840MC3", "CodePlexBug08", ExitStatus.VIOLATION_LIVENESS); } + @Test public void testSpec() { // ModelChecker has finished and generated the expected amount of states assertTrue(recorder.recorded(EC.TLC_FINISHED)); - assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "15986", "1566")); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "15986", "1566", "0")); + assertFalse(recorder.recorded(EC.GENERAL)); // Assert it has found the temporal violation and also a counter example assertTrue(recorder.recorded(EC.TLC_TEMPORAL_PROPERTY_VIOLATED)); assertTrue(recorder.recorded(EC.TLC_COUNTER_EXAMPLE)); + assertNodeAndPtrSizes(143808L, 25040L); + // Assert the error trace assertTrue(recorder.recorded(EC.TLC_STATE_PRINT2)); - List<Object> records = recorder.getRecords(EC.TLC_STATE_PRINT2); - - int i = 0; // State's position in records - Object[] objs = (Object[]) records.get(i++); - TLCStateInfo stateInfo = (TLCStateInfo) objs[0]; - assertEquals("/\\ tpos = 0\n" - + "/\\ active = (0 :> FALSE @@ 1 :> FALSE @@ 2 :> FALSE @@ 3 :> TRUE)\n" - + "/\\ tcolor = \"black\"\n" - + "/\\ color = (0 :> \"white\" @@ 1 :> \"white\" @@ 2 :> \"white\" @@ 3 :> \"white\")", - stateInfo.toString().trim()); // trimmed to remove any newlines or whitespace - assertEquals(i, objs[1]); - - // Omitted check of 8 in-between states, just make sure four more states exist - assertEquals("Expect five states prior to stuttering", 8, records.size()); + final List<String> expectedTrace = new ArrayList<String>(); + expectedTrace.add("/\\ tpos = 0\n" + + "/\\ active = (0 :> FALSE @@ 1 :> FALSE @@ 2 :> FALSE @@ 3 :> TRUE)\n" + + "/\\ tcolor = \"black\"\n" + + "/\\ color = (0 :> \"white\" @@ 1 :> \"white\" @@ 2 :> \"white\" @@ 3 :> \"white\")"); + expectedTrace.add("/\\ tpos = 3\n" + + "/\\ active = (0 :> FALSE @@ 1 :> FALSE @@ 2 :> FALSE @@ 3 :> TRUE)\n" + + "/\\ tcolor = \"white\"\n" + + "/\\ color = (0 :> \"white\" @@ 1 :> \"white\" @@ 2 :> \"white\" @@ 3 :> \"white\")"); + expectedTrace.add("/\\ tpos = 3\n" + + "/\\ active = (0 :> FALSE @@ 1 :> FALSE @@ 2 :> TRUE @@ 3 :> TRUE)\n" + + "/\\ tcolor = \"white\"\n" + + "/\\ color = (0 :> \"white\" @@ 1 :> \"white\" @@ 2 :> \"white\" @@ 3 :> \"white\")"); + expectedTrace.add("/\\ tpos = 3\n" + + "/\\ active = (0 :> FALSE @@ 1 :> FALSE @@ 2 :> TRUE @@ 3 :> FALSE)\n" + + "/\\ tcolor = \"white\"\n" + + "/\\ color = (0 :> \"white\" @@ 1 :> \"white\" @@ 2 :> \"white\" @@ 3 :> \"white\")"); + expectedTrace.add("/\\ tpos = 2\n" + + "/\\ active = (0 :> FALSE @@ 1 :> FALSE @@ 2 :> TRUE @@ 3 :> FALSE)\n" + + "/\\ tcolor = \"white\"\n" + + "/\\ color = (0 :> \"white\" @@ 1 :> \"white\" @@ 2 :> \"white\" @@ 3 :> \"white\")"); + expectedTrace.add("/\\ tpos = 2\n" + + "/\\ active = (0 :> FALSE @@ 1 :> FALSE @@ 2 :> TRUE @@ 3 :> TRUE)\n" + + "/\\ tcolor = \"white\"\n" + + "/\\ color = (0 :> \"white\" @@ 1 :> \"white\" @@ 2 :> \"black\" @@ 3 :> \"white\")"); + expectedTrace.add("/\\ tpos = 1\n" + + "/\\ active = (0 :> FALSE @@ 1 :> FALSE @@ 2 :> TRUE @@ 3 :> TRUE)\n" + + "/\\ tcolor = \"black\"\n" + + "/\\ color = (0 :> \"white\" @@ 1 :> \"white\" @@ 2 :> \"white\" @@ 3 :> \"white\")"); + expectedTrace.add("/\\ tpos = 1\n" + + "/\\ active = (0 :> FALSE @@ 1 :> FALSE @@ 2 :> FALSE @@ 3 :> TRUE)\n" + + "/\\ tcolor = \"black\"\n" + + "/\\ color = (0 :> \"white\" @@ 1 :> \"white\" @@ 2 :> \"white\" @@ 3 :> \"white\")"); + assertTraceWith(recorder.getRecords(EC.TLC_STATE_PRINT2), expectedTrace); - // state eight points back to state 1 - assertTrue(recorder.recorded(EC.TLC_BACK_TO_STATE)); - List<Object> stutter = recorder.getRecords(EC.TLC_BACK_TO_STATE); - assertTrue(stutter.size() > 0); - Object[] object = (Object[]) stutter.get(0); - assertEquals("1", object[0]); + // last state loops back to state 1 + assertBackToState(1); + + assertZeroUncovered(); } } diff --git a/tlatools/test/tlc2/tool/liveness/CodePlexBug08EWD840FL4Test.java b/tlatools/test/tlc2/tool/liveness/CodePlexBug08EWD840FL4Test.java index 22074f3783d44c6e3488c5e77eb086905fa069d9..94a18f4f9e5e696f4fe02fb5a65ffa3074f44c33 100644 --- a/tlatools/test/tlc2/tool/liveness/CodePlexBug08EWD840FL4Test.java +++ b/tlatools/test/tlc2/tool/liveness/CodePlexBug08EWD840FL4Test.java @@ -26,10 +26,16 @@ package tlc2.tool.liveness; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.util.ArrayList; import java.util.List; +import org.junit.Test; + import tlc2.output.EC; -import tlc2.tool.TLCStateInfo; +import tlc2.output.EC.ExitStatus; /** * see http://tlaplus.codeplex.com/workitem/8 @@ -37,45 +43,38 @@ import tlc2.tool.TLCStateInfo; public class CodePlexBug08EWD840FL4Test extends ModelCheckerTestCase { public CodePlexBug08EWD840FL4Test() { - super("EWD840MC4", "CodePlexBug08"); + super("EWD840MC4", "CodePlexBug08", ExitStatus.VIOLATION_LIVENESS); } + @Test public void testSpec() { // ModelChecker has finished and generated the expected amount of states assertTrue(recorder.recorded(EC.TLC_FINISHED)); - assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "15986", "1566")); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "15986", "1566", "0")); + assertFalse(recorder.recorded(EC.GENERAL)); // Assert it has found the temporal violation and also a counter example assertTrue(recorder.recorded(EC.TLC_TEMPORAL_PROPERTY_VIOLATED)); assertTrue(recorder.recorded(EC.TLC_COUNTER_EXAMPLE)); + assertNodeAndPtrSizes(135540L, 23456L); + // Assert the error trace assertTrue(recorder.recorded(EC.TLC_STATE_PRINT2)); - List<Object> records = recorder.getRecords(EC.TLC_STATE_PRINT2); - - int i = 0; // State's position in records - Object[] objs = (Object[]) records.get(i++); - TLCStateInfo stateInfo = (TLCStateInfo) objs[0]; - assertEquals("/\\ tpos = 0\n" + final List<String> expectedTrace = new ArrayList<String>(); + expectedTrace.add("/\\ tpos = 0\n" + "/\\ active = (0 :> FALSE @@ 1 :> FALSE @@ 2 :> FALSE @@ 3 :> TRUE)\n" + "/\\ tcolor = \"black\"\n" - + "/\\ color = (0 :> \"white\" @@ 1 :> \"white\" @@ 2 :> \"white\" @@ 3 :> \"white\")", - stateInfo.toString().trim()); // trimmed to remove any newlines or whitespace - assertEquals(i, objs[1]); - - objs = (Object[]) records.get(i++); - stateInfo = (TLCStateInfo) objs[0]; - assertEquals("/\\ tpos = 3\n" + + "/\\ color = (0 :> \"white\" @@ 1 :> \"white\" @@ 2 :> \"white\" @@ 3 :> \"white\")"); + expectedTrace.add("/\\ tpos = 3\n" + "/\\ active = (0 :> FALSE @@ 1 :> FALSE @@ 2 :> FALSE @@ 3 :> TRUE)\n" + "/\\ tcolor = \"white\"\n" - + "/\\ color = (0 :> \"white\" @@ 1 :> \"white\" @@ 2 :> \"white\" @@ 3 :> \"white\")", - stateInfo.toString().trim()); + + "/\\ color = (0 :> \"white\" @@ 1 :> \"white\" @@ 2 :> \"white\" @@ 3 :> \"white\")"); + assertTraceWith(recorder.getRecords(EC.TLC_STATE_PRINT2), expectedTrace); // state 3 is stuttering - assertTrue(recorder.recorded(EC.TLC_STATE_PRINT3)); - List<Object> stutter = recorder.getRecords(EC.TLC_STATE_PRINT3); - assertTrue(stutter.size() > 0); - Object[] object = (Object[]) stutter.get(0); - assertEquals(3, object[1]); + assertStuttering(3); + + assertZeroUncovered(); } } diff --git a/tlatools/test/tlc2/tool/liveness/CodePlexBug08Test.java b/tlatools/test/tlc2/tool/liveness/CodePlexBug08Test.java index 3009401350f143c2068b1dee1b5c9e4ebbde03f4..cc1e1f50b4efee1321b65153a8b898674815c7e5 100644 --- a/tlatools/test/tlc2/tool/liveness/CodePlexBug08Test.java +++ b/tlatools/test/tlc2/tool/liveness/CodePlexBug08Test.java @@ -26,10 +26,17 @@ package tlc2.tool.liveness; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.util.ArrayList; import java.util.List; +import org.junit.Test; + import tlc2.output.EC; -import tlc2.tool.TLCStateInfo; +import tlc2.output.EC.ExitStatus; +import util.TLAConstants; /** * see http://tlaplus.codeplex.com/workitem/8 @@ -37,57 +44,35 @@ import tlc2.tool.TLCStateInfo; public class CodePlexBug08Test extends ModelCheckerTestCase { public CodePlexBug08Test() { - super("MC", "CodePlexBug08"); + super(TLAConstants.Files.MODEL_CHECK_FILE_BASENAME, "CodePlexBug08", ExitStatus.VIOLATION_LIVENESS); } + @Test public void testSpec() { // ModelChecker has finished and generated the expected amount of states assertTrue(recorder.recorded(EC.TLC_FINISHED)); - assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "18", "11")); - + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "18", "11", "0")); + assertFalse(recorder.recorded(EC.GENERAL)); + // Assert it has found the temporal violation and also a counter example assertTrue(recorder.recorded(EC.TLC_TEMPORAL_PROPERTY_VIOLATED)); assertTrue(recorder.recorded(EC.TLC_COUNTER_EXAMPLE)); + assertNodeAndPtrSizes(744L, 320L); + // Assert the error trace assertTrue(recorder.recorded(EC.TLC_STATE_PRINT2)); - List<Object> records = recorder.getRecords(EC.TLC_STATE_PRINT2); + final List<String> expectedTrace = new ArrayList<String>(4); + expectedTrace.add("/\\ b = FALSE\n/\\ x = 2"); + expectedTrace.add("/\\ b = TRUE\n/\\ x = 3"); + expectedTrace.add("/\\ b = FALSE\n/\\ x = 3"); + expectedTrace.add("/\\ b = TRUE\n/\\ x = 4"); + expectedTrace.add("/\\ b = FALSE\n/\\ x = 4"); + expectedTrace.add("/\\ b = TRUE\n/\\ x = 5"); + assertTraceWith(recorder.getRecords(EC.TLC_STATE_PRINT2), expectedTrace); - int i = 0; // State's position in records - Object[] objs = (Object[]) records.get(i++); - TLCStateInfo stateInfo = (TLCStateInfo) objs[0]; - assertEquals("/\\ b = FALSE\n/\\ x = 2", stateInfo.toString().trim()); // trimmed to remove any newlines or whitespace - assertEquals(i, objs[1]); - - objs = (Object[]) records.get(i++); - stateInfo = (TLCStateInfo) objs[0]; - assertEquals("/\\ b = TRUE\n/\\ x = 3", stateInfo.toString().trim()); - assertEquals(i, objs[1]); - - objs = (Object[]) records.get(i++); - stateInfo = (TLCStateInfo) objs[0]; - assertEquals("/\\ b = FALSE\n/\\ x = 3", stateInfo.toString().trim()); - assertEquals(i, objs[1]); + assertStuttering(7); - objs = (Object[]) records.get(i++); - stateInfo = (TLCStateInfo) objs[0]; - assertEquals("/\\ b = TRUE\n/\\ x = 4", stateInfo.toString().trim()); - assertEquals(i, objs[1]); - - objs = (Object[]) records.get(i++); - stateInfo = (TLCStateInfo) objs[0]; - assertEquals("/\\ b = FALSE\n/\\ x = 4", stateInfo.toString().trim()); - assertEquals(i, objs[1]); - - objs = (Object[]) records.get(i++); - stateInfo = (TLCStateInfo) objs[0]; - assertEquals("/\\ b = TRUE\n/\\ x = 5", stateInfo.toString().trim()); - assertEquals(i, objs[1]); - - // Assert the error trace contains a stuttering step at position 5 - assertTrue(recorder.recorded(EC.TLC_STATE_PRINT3)); - records = recorder.getRecords(EC.TLC_STATE_PRINT3); - objs = (Object[]) records.get(0); - assertEquals(++i, objs[1]); + assertZeroUncovered(); } } diff --git a/tlatools/test/tlc2/tool/liveness/CodePlexBug08aTest.java b/tlatools/test/tlc2/tool/liveness/CodePlexBug08aTest.java index e08f7c820af45c0992fbde3f271c88a20e66fa80..da9653beb6618bafa8e1eb9793e54b7647555d57 100644 --- a/tlatools/test/tlc2/tool/liveness/CodePlexBug08aTest.java +++ b/tlatools/test/tlc2/tool/liveness/CodePlexBug08aTest.java @@ -26,10 +26,16 @@ package tlc2.tool.liveness; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.util.ArrayList; import java.util.List; +import org.junit.Test; + import tlc2.output.EC; -import tlc2.tool.TLCStateInfo; +import tlc2.output.EC.ExitStatus; /** * see http://tlaplus.codeplex.com/workitem/8 @@ -37,67 +43,38 @@ import tlc2.tool.TLCStateInfo; public class CodePlexBug08aTest extends ModelCheckerTestCase { public CodePlexBug08aTest() { - super("MCa", "CodePlexBug08"); + super("MCa", "CodePlexBug08", ExitStatus.VIOLATION_LIVENESS); } + @Test public void testSpec() { // ModelChecker has finished and generated the expected amount of states assertTrue(recorder.recorded(EC.TLC_FINISHED)); - assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "18", "11")); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "18", "11", "0")); + assertFalse(recorder.recorded(EC.GENERAL)); // Assert it has found the temporal violation and also a counter example assertTrue(recorder.recorded(EC.TLC_TEMPORAL_PROPERTY_VIOLATED)); assertTrue(recorder.recorded(EC.TLC_COUNTER_EXAMPLE)); + assertNodeAndPtrSizes(816L, 352L); + // Assert the error trace assertTrue(recorder.recorded(EC.TLC_STATE_PRINT2)); - List<Object> records = recorder.getRecords(EC.TLC_STATE_PRINT2); - - int i = 0; // State's position in records - Object[] objs = (Object[]) records.get(i++); - TLCStateInfo stateInfo = (TLCStateInfo) objs[0]; - assertEquals("/\\ b = FALSE\n/\\ x = 1", stateInfo.toString().trim()); // trimmed to remove any newlines or whitespace - assertEquals(i, objs[1]); - - objs = (Object[]) records.get(i++); - stateInfo = (TLCStateInfo) objs[0]; - assertEquals("/\\ b = TRUE\n/\\ x = 2", stateInfo.toString().trim()); - assertEquals(i, objs[1]); - - objs = (Object[]) records.get(i++); - stateInfo = (TLCStateInfo) objs[0]; - assertEquals("/\\ b = FALSE\n/\\ x = 2", stateInfo.toString().trim()); - assertEquals(i, objs[1]); - - objs = (Object[]) records.get(i++); - stateInfo = (TLCStateInfo) objs[0]; - assertEquals("/\\ b = TRUE\n/\\ x = 3", stateInfo.toString().trim()); - assertEquals(i, objs[1]); - - objs = (Object[]) records.get(i++); - stateInfo = (TLCStateInfo) objs[0]; - assertEquals("/\\ b = FALSE\n/\\ x = 3", stateInfo.toString().trim()); - assertEquals(i, objs[1]); - - objs = (Object[]) records.get(i++); - stateInfo = (TLCStateInfo) objs[0]; - assertEquals("/\\ b = TRUE\n/\\ x = 4", stateInfo.toString().trim()); - assertEquals(i, objs[1]); - - objs = (Object[]) records.get(i++); - stateInfo = (TLCStateInfo) objs[0]; - assertEquals("/\\ b = FALSE\n/\\ x = 4", stateInfo.toString().trim()); - assertEquals(i, objs[1]); - - objs = (Object[]) records.get(i++); - stateInfo = (TLCStateInfo) objs[0]; - assertEquals("/\\ b = TRUE\n/\\ x = 5", stateInfo.toString().trim()); - assertEquals(i, objs[1]); + final List<String> expectedTrace = new ArrayList<String>(4); + expectedTrace.add("/\\ b = FALSE\n/\\ x = 1"); + expectedTrace.add("/\\ b = TRUE\n/\\ x = 2"); + expectedTrace.add("/\\ b = FALSE\n/\\ x = 2"); + expectedTrace.add("/\\ b = TRUE\n/\\ x = 3"); + expectedTrace.add("/\\ b = FALSE\n/\\ x = 3"); + expectedTrace.add("/\\ b = TRUE\n/\\ x = 4"); + expectedTrace.add("/\\ b = FALSE\n/\\ x = 4"); + expectedTrace.add("/\\ b = TRUE\n/\\ x = 5"); + assertTraceWith(recorder.getRecords(EC.TLC_STATE_PRINT2), expectedTrace); // Assert the error trace contains a stuttering step at position 5 - assertTrue(recorder.recorded(EC.TLC_STATE_PRINT3)); - records = recorder.getRecords(EC.TLC_STATE_PRINT3); - objs = (Object[]) records.get(0); - assertEquals(++i, objs[1]); + assertStuttering(9); + + assertZeroUncovered(); } } diff --git a/tlatools/test/tlc2/tool/liveness/DiskGraphTest.java b/tlatools/test/tlc2/tool/liveness/DiskGraphTest.java index 8150f3bc59f1acdbc73de6c5b81fe84f649b319b..daa793eb5aeea0eaef8a7114ee73a3cfa10c09d7 100644 --- a/tlatools/test/tlc2/tool/liveness/DiskGraphTest.java +++ b/tlatools/test/tlc2/tool/liveness/DiskGraphTest.java @@ -26,18 +26,25 @@ package tlc2.tool.liveness; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotSame; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + import java.io.File; import java.io.IOException; -import junit.framework.TestCase; +import org.junit.Test; + import tlc2.util.BitVector; import tlc2.util.LongVec; -import tlc2.util.statistics.BucketStatistics; +import tlc2.util.statistics.FixedSizedBucketStatistics; import tlc2.util.statistics.IBucketStatistics; -public class DiskGraphTest extends TestCase { +public class DiskGraphTest { - private static final IBucketStatistics GRAPH_STATS = new BucketStatistics("Test Dummy", 16); + private static final IBucketStatistics GRAPH_STATS = new FixedSizedBucketStatistics("Test Dummy", 16); private static final int NUMBER_OF_SOLUTIONS = 1; private static final int NO_TABLEAU = -1; private static final int NUMBER_OF_ACTIONS = 0; @@ -62,6 +69,7 @@ public class DiskGraphTest extends TestCase { } // No init node makes DiskGraph#getPath never break from the while loop + @Test public void testGetPathWithoutInitNoTableau() throws IOException { final AbstractDiskGraph dg = getDiskGraph(); dg.addNode(new GraphNode(1L, NO_TABLEAU)); @@ -76,6 +84,7 @@ public class DiskGraphTest extends TestCase { // Create a linear minimal graph (2 nodes) and check if the graph is // returned by getPath afterwards. + @Test public void testGetMinimalPathWithoutTableau() throws IOException { final AbstractDiskGraph dg = getDiskGraph(); @@ -119,8 +128,9 @@ public class DiskGraphTest extends TestCase { * * The specialty here is that there are *two* init nodes and one of them has *no* successors. * - * @see https://bugzilla.tlaplus.net/show_bug.cgi?id=293 + * @see Bug #293 in general/bugzilla/index.html */ + @Test public void testPathWithTwoInitNodes() throws IOException { final AbstractDiskGraph dg = getDiskGraph(); @@ -168,6 +178,7 @@ public class DiskGraphTest extends TestCase { /* * Make sure the same logical node isn't counted twice. */ + @Test public void testAddSameGraphNodeTwice() throws IOException { final AbstractDiskGraph dg = getDiskGraph(); dg.addNode(new GraphNode(1L, 1)); @@ -179,6 +190,7 @@ public class DiskGraphTest extends TestCase { /* * Test that it is possible to "update" a GraphNode's outgoing transitions. */ + @Test public void testLookupExistingNode() throws IOException { final AbstractDiskGraph dg = getDiskGraph(); @@ -226,6 +238,7 @@ public class DiskGraphTest extends TestCase { * Test that adding a GraphNode twice (same fingerprint & tableau idx) but * with different successors afterwards yields the union of the successors. */ + @Test public void testAddSameGraphNodeTwiceCorrectSuccessors() throws IOException { final AbstractDiskGraph dg = getDiskGraph(); @@ -266,6 +279,7 @@ public class DiskGraphTest extends TestCase { * checking runs periodically on an incomplete state/behavior graph, a * liveness violation is found and the path of the error trace gets explored. */ + @Test public void testGetPathPartialGraph() throws IOException { final AbstractDiskGraph dg = getDiskGraph(); diff --git a/tlatools/test/tlc2/tool/liveness/EmptyOrderOfSolutionsTest.java b/tlatools/test/tlc2/tool/liveness/EmptyOrderOfSolutionsTest.java new file mode 100644 index 0000000000000000000000000000000000000000..a336a1d3c846f4936f8ac34636c7fd298935c29d --- /dev/null +++ b/tlatools/test/tlc2/tool/liveness/EmptyOrderOfSolutionsTest.java @@ -0,0 +1,47 @@ +/******************************************************************************* + * Copyright (c) 2019 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.liveness; + +import static org.junit.Assert.assertTrue; + +import java.io.IOException; + +import org.junit.Test; + +import tlc2.output.EC; +import tlc2.output.EC.ExitStatus; + +public class EmptyOrderOfSolutionsTest extends ModelCheckerTestCase { + + public EmptyOrderOfSolutionsTest() { + super("EmptyOrderOfSolutions", ExitStatus.FAILURE_LIVENESS_EVAL); + } + + @Test + public void testSpec() throws IOException { + assertTrue(recorder.recorded(EC.TLC_LIVE_FORMULA_TAUTOLOGY)); + } +} diff --git a/tlatools/test/tlc2/tool/liveness/ErrorTraceConstructionTest.java b/tlatools/test/tlc2/tool/liveness/ErrorTraceConstructionTest.java new file mode 100644 index 0000000000000000000000000000000000000000..088864085c1ab0e6e1770608d46ec3b5afa0be86 --- /dev/null +++ b/tlatools/test/tlc2/tool/liveness/ErrorTraceConstructionTest.java @@ -0,0 +1,76 @@ +/******************************************************************************* + * Copyright (c) 2015 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ + +package tlc2.tool.liveness; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.Test; + +import tlc2.output.EC; +import tlc2.output.EC.ExitStatus; + +public class ErrorTraceConstructionTest extends ModelCheckerTestCase { + + public ErrorTraceConstructionTest() { + super("ErrorTraceConstructionMC", "symmetry", ExitStatus.VIOLATION_LIVENESS); + } + + @Test + public void testSpec() { + // ModelChecker has finished and generated the expected amount of states + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "9", "8", "0")); + assertFalse(recorder.recorded(EC.GENERAL)); + + // Assert it has found the temporal violation and also a counter example + assertTrue(recorder.recorded(EC.TLC_TEMPORAL_PROPERTY_VIOLATED)); + assertTrue(recorder.recorded(EC.TLC_COUNTER_EXAMPLE)); + + assertNodeAndPtrSizes(288L, 128L); + + // Assert the error trace + assertTrue(recorder.recorded(EC.TLC_STATE_PRINT2)); + final List<String> expectedTrace = new ArrayList<String>(4); + expectedTrace.add("/\\ x = 0\n/\\ y = 0"); + expectedTrace.add("/\\ x = 0\n/\\ y = 1"); + expectedTrace.add("/\\ x = 0\n/\\ y = 2"); + expectedTrace.add("/\\ x = 0\n/\\ y = 3"); + expectedTrace.add("/\\ x = 0\n/\\ y = 4"); + expectedTrace.add("/\\ x = 1\n/\\ y = 5"); + expectedTrace.add("/\\ x = 0\n/\\ y = 6"); + expectedTrace.add("/\\ x = 0\n/\\ y = 7"); + assertTraceWith(recorder.getRecords(EC.TLC_STATE_PRINT2), expectedTrace); + + assertBackToState(4, "<N7 line 32, col 7 to line 34, col 19 of module ErrorTraceConstruction>"); + + assertZeroUncovered(); + } +} diff --git a/tlatools/test/tlc2/tool/liveness/GraphNodeTest.java b/tlatools/test/tlc2/tool/liveness/GraphNodeTest.java index ac2592cf1a156f0bc61cec09c1f7823c4960dc1a..ccd827982cb68113ca6026035c68e85819dc4a87 100644 --- a/tlatools/test/tlc2/tool/liveness/GraphNodeTest.java +++ b/tlatools/test/tlc2/tool/liveness/GraphNodeTest.java @@ -26,14 +26,18 @@ package tlc2.tool.liveness; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + import java.util.ArrayList; import java.util.List; import java.util.Random; -import junit.framework.TestCase; +import org.junit.Test; -public class GraphNodeTest extends TestCase { +public class GraphNodeTest { + @Test public void testAllocateRealign() { // Create a random graph node (fingerprint/tableau don't matter) final GraphNode node = new GraphNode(0, 0); @@ -48,13 +52,14 @@ public class GraphNodeTest extends TestCase { int overallocated = node.realign(); assertTrue("Allocation overallocated", overallocated == 0); - assertTrue("Lost a transition during this allocation business", node.transExists(1, -1)); - assertTrue("Lost a transition during this allocation business", node.transExists(2, -1)); - assertTrue("Lost a transition during this allocation business", node.transExists(3, -1)); - assertTrue("Lost a transition during this allocation business", node.transExists(4, -1)); - assertTrue("Lost a transition during this allocation business", node.transExists(5, -1)); + assertTrue("Lost a transition during the allocation business", node.transExists(1, -1)); + assertTrue("Lost a transition during the allocation business", node.transExists(2, -1)); + assertTrue("Lost a transition during the allocation business", node.transExists(3, -1)); + assertTrue("Lost a transition during the allocation business", node.transExists(4, -1)); + assertTrue("Lost a transition during the allocation business", node.transExists(5, -1)); } + @Test public void testRealign() { // Create a random graph node (fingerprint/tableau don't matter) final GraphNode node = new GraphNode(0, 0); @@ -67,9 +72,10 @@ public class GraphNodeTest extends TestCase { overallocated = node.realign(); assertTrue("Allocation overallocated", overallocated == 0); - assertTrue("Lost a transition during this allocation business", node.transExists(1, -1)); + assertTrue("Lost a transition during the allocation business", node.transExists(1, -1)); } + @Test public void testAllocateNested() { // Create a random graph node (fingerprint/tableau don't matter) final GraphNode node = new GraphNode(0, 0); @@ -91,6 +97,7 @@ public class GraphNodeTest extends TestCase { } } + @Test public void testAllocateNestedRandom() { // Create a random graph node (fingerprint/tableau don't matter) final GraphNode node = new GraphNode(0, 0); @@ -117,6 +124,7 @@ public class GraphNodeTest extends TestCase { } } + @Test public void testAllocateNegative() { // Create a random graph node (fingerprint/tableau don't matter) final GraphNode node = new GraphNode(0, 0); @@ -124,6 +132,7 @@ public class GraphNodeTest extends TestCase { assertTrue("overallocated", node.realign() == 0); } + @Test public void testAllocateAndSuccessorSize() { // Hint to allocate 100 transitions and make sure the actual number of // transitions is 1. diff --git a/tlatools/test/tlc2/tool/liveness/LivenessConstraintWarning.java b/tlatools/test/tlc2/tool/liveness/LivenessConstraintWarning.java new file mode 100644 index 0000000000000000000000000000000000000000..e2eee814faaffc7d8228ab3f179c63048e779e26 --- /dev/null +++ b/tlatools/test/tlc2/tool/liveness/LivenessConstraintWarning.java @@ -0,0 +1,47 @@ +/******************************************************************************* + * Copyright (c) 2019 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.liveness; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.output.EC; + +public class LivenessConstraintWarning extends ModelCheckerTestCase { + + public LivenessConstraintWarning() { + super("LivenessConstraintWarning"); + } + + @Test + public void testSpec() { + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertTrue(recorder.recorded(EC.TLC_FEATURE_LIVENESS_CONSTRAINTS)); + assertFalse(recorder.recorded(EC.GENERAL)); + } +} diff --git a/tlatools/test/tlc2/tool/liveness/LivenessSymmetryWarning.java b/tlatools/test/tlc2/tool/liveness/LivenessSymmetryWarning.java new file mode 100644 index 0000000000000000000000000000000000000000..d2ca8be69fc2ffb7fc0fdd5ef9562c342f012884 --- /dev/null +++ b/tlatools/test/tlc2/tool/liveness/LivenessSymmetryWarning.java @@ -0,0 +1,47 @@ +/******************************************************************************* + * Copyright (c) 2017 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.liveness; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.output.EC; + +public class LivenessSymmetryWarning extends ModelCheckerTestCase { + + public LivenessSymmetryWarning() { + super("April25MC", "symmetry"); + } + + @Test + public void testSpec() { + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertTrue(recorder.recorded(EC.TLC_FEATURE_UNSUPPORTED_LIVENESS_SYMMETRY)); + assertFalse(recorder.recorded(EC.GENERAL)); + } +} diff --git a/tlatools/test/tlc2/tool/liveness/LoopTest.java b/tlatools/test/tlc2/tool/liveness/LoopTest.java new file mode 100644 index 0000000000000000000000000000000000000000..0bd0f27d98c21afdc48ebe8c29f32380871ef528 --- /dev/null +++ b/tlatools/test/tlc2/tool/liveness/LoopTest.java @@ -0,0 +1,85 @@ +/******************************************************************************* + * Copyright (c) 2015 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ + +package tlc2.tool.liveness; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.Test; + +import tlc2.output.EC; +import tlc2.output.EC.ExitStatus; + +/** + * System LOOP as described by Manna & Pneuli on page 423ff + */ +public class LoopTest extends ModelCheckerTestCase { + + public LoopTest() { + super("SystemLoop", "Loop", ExitStatus.VIOLATION_LIVENESS); + } + + @Test + public void testSpec() { + // ModelChecker has finished and generated the expected amount of states + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "5", "4", "0")); + assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "1")); + assertFalse(recorder.recorded(EC.GENERAL)); + + // Assert it has found the temporal violation and also a counter example + assertTrue(recorder.recorded(EC.TLC_TEMPORAL_PROPERTY_VIOLATED)); + assertTrue(recorder.recorded(EC.TLC_COUNTER_EXAMPLE)); + + assertNodeAndPtrSizes(136L, 64L); + + // Assert the error trace + assertTrue(recorder.recorded(EC.TLC_STATE_PRINT2)); + final List<String> expectedTrace = new ArrayList<String>(4); + expectedTrace.add("x = 0"); + expectedTrace.add("x = 1"); + expectedTrace.add("x = 2"); + assertTraceWith(recorder.getRecords(EC.TLC_STATE_PRINT2), expectedTrace); + + // Without any fairness defined, state 4 is stuttering instead of moving + // on to state x=3. + assertStuttering(4); + + //TODO This error trace is not the shortest one. The shortest one would + // be stuttering after the initial state x=0 and not after x=2 with x=3 + // as the last successor in the behavior. However, the SCC search + // implemented in LiveWorker#checkSccs checks the path end to start and + // not start to end. + // If liveness is (forcefully) triggered after the initial state, stuttering + // after the initial state is correctly detected. + + assertZeroUncovered(); + } +} diff --git a/tlatools/test/tlc2/tool/liveness/LoopTestForcedPartial.java b/tlatools/test/tlc2/tool/liveness/LoopTestForcedPartial.java new file mode 100644 index 0000000000000000000000000000000000000000..f1cb43c74a5696db1cf8af9b80267b3fc402f572 --- /dev/null +++ b/tlatools/test/tlc2/tool/liveness/LoopTestForcedPartial.java @@ -0,0 +1,79 @@ +/******************************************************************************* + * Copyright (c) 2015 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ + +package tlc2.tool.liveness; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.Test; + +import tlc2.output.EC; +import tlc2.output.EC.ExitStatus; +import tlc2.tool.AbstractChecker; + +/** + * Identical to {@link LoopTest}, except that liveness checking uses + * {@link AddAndCheckLiveCheck}. This way, TLC correctly produces the shortest + * possible counterexample. + */ +public class LoopTestForcedPartial extends ModelCheckerTestCase { + + static { + AbstractChecker.LIVENESS_TESTING_IMPLEMENTATION = true; + } + + public LoopTestForcedPartial() { + super("SystemLoop", "Loop", ExitStatus.VIOLATION_LIVENESS); + } + + @Test + public void testSpec() { + // ModelChecker has finished and generated the expected amount of states + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "5", "4", "0")); + assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "1")); + assertFalse(recorder.recorded(EC.GENERAL)); + + // Assert it has found the temporal violation and also a counter example + assertTrue(recorder.recorded(EC.TLC_TEMPORAL_PROPERTY_VIOLATED)); + assertTrue(recorder.recorded(EC.TLC_COUNTER_EXAMPLE)); + + // Assert the error trace + assertTrue(recorder.recorded(EC.TLC_STATE_PRINT2)); + final List<String> expectedTrace = new ArrayList<String>(4); + expectedTrace.add("x = 0"); + assertTraceWith(recorder.getRecords(EC.TLC_STATE_PRINT2), expectedTrace); + + // Stuttering after the init state. + assertStuttering(2); + + assertZeroUncovered(); + } +} diff --git a/tlatools/test/tlc2/tool/liveness/LoopTestWeakFair.java b/tlatools/test/tlc2/tool/liveness/LoopTestWeakFair.java new file mode 100644 index 0000000000000000000000000000000000000000..bf8a7900bb5ed5e9c5ed2c9b437d6d025696fd9a --- /dev/null +++ b/tlatools/test/tlc2/tool/liveness/LoopTestWeakFair.java @@ -0,0 +1,58 @@ +/******************************************************************************* + * Copyright (c) 2015 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ + +package tlc2.tool.liveness; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.output.EC; + +/** + * System LOOP as described by Manna & Pneuli on page 423ff + */ +public class LoopTestWeakFair extends ModelCheckerTestCase { + + public LoopTestWeakFair() { + super("SystemLoop", "Loop", new String[] { "-config", "SystemLoopWeakFair" }); + } + + @Test + public void testSpec() { + // ModelChecker has finished and generated the expected amount of states + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertFalse(recorder.recorded(EC.GENERAL)); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "5", "4", "0")); + assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, "1")); + assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "4")); + + assertTrue(recorder.recordedWithStringValues(EC.TLC_CHECKING_TEMPORAL_PROPS, "complete", "4")); + + assertZeroUncovered(); + } +} diff --git a/tlatools/test/tlc2/tool/liveness/May09Test.java b/tlatools/test/tlc2/tool/liveness/May09Test.java new file mode 100644 index 0000000000000000000000000000000000000000..1534c255879b67f6e80ceb80d5b48ca6aa1085dd --- /dev/null +++ b/tlatools/test/tlc2/tool/liveness/May09Test.java @@ -0,0 +1,89 @@ +/******************************************************************************* + * Copyright (c) 2016 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.liveness; + +import static org.junit.Assert.assertTrue; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import org.junit.Ignore; +import org.junit.Test; + +import tlc2.output.EC; + +/** + * May09Test and its counterpart May09dTest prove that the algorithm with + * redundant arcs with different action predicate labels in the liveness graph + * does not work even when liveness properties containing conjuncts over the + * elements of the symmetry set are rewritten to a disjunct and out-arcs are + * counted (compare to {@link April29Test} and {@link April29dTest}). + * + * <pre> + * []<>P(a) /\ []<>P(b) rewritten to: []<>P(a) \/ []<>P(b) + * </pre> + * + * (where P is either a State or Action predicate and a,b are all elements of + * the symmetry set) + * + * Trying to deduce []<>P(b) from the occurrence of []<>P(a) (or vice versa) + * fails because the two specs (Spec & SpecD) under symmetry produce the same + * state graph. Without symmetry, Spec does not satisfy []<>P(a) /\ []<>P(b), + * only SpecD does. Under symmetry, a version of TLC that does not rewrite the + * liveness property finds the bogus counterexample for SpecD. + * + * A yet unexplored research hypothesis is, that labeling all arcs with the + * permutation that the to-state represents in the liveness graph. This information + * would allow us to distinguish the graphs of May09 and May09d. + */ +public class May09Test extends ModelCheckerTestCase { + + public May09Test() { + super("May09MC", "symmetry"); + } + + @Test + @Ignore("Ignored for as long as symmetry is incorrectly handled by TLC with liveness checking.") + public void testSpec() throws IOException { + // Assert TLC has found a temporal violation and a counter example + assertTrue(recorder.recorded(EC.TLC_TEMPORAL_PROPERTY_VIOLATED)); + assertTrue(recorder.recorded(EC.TLC_COUNTER_EXAMPLE)); + + // Assert the error trace + assertTrue(recorder.recorded(EC.TLC_STATE_PRINT2)); + final List<String> expectedTrace = new ArrayList<String>(2); + expectedTrace.add("/\\ x = a\n/\\ y = 0"); + expectedTrace.add("/\\ x = a\n/\\ y = 1"); + expectedTrace.add("/\\ x = a\n/\\ y = 2"); + expectedTrace.add("/\\ x = a\n/\\ y = 3"); + expectedTrace.add("/\\ x = a\n/\\ y = 4"); + expectedTrace.add("/\\ x = a\n/\\ y = 5"); + assertTraceWith(recorder.getRecords(EC.TLC_STATE_PRINT2), expectedTrace); + + assertBackToState(2, "<Action line 19, col 10 to line 21, col 18 of module May09>"); + } +} diff --git a/tlatools/test/tlc2/tool/liveness/May09dTest.java b/tlatools/test/tlc2/tool/liveness/May09dTest.java new file mode 100644 index 0000000000000000000000000000000000000000..6150fe234bf68ecb59e306546747518ac5fbfb5a --- /dev/null +++ b/tlatools/test/tlc2/tool/liveness/May09dTest.java @@ -0,0 +1,52 @@ +/******************************************************************************* + * Copyright (c) 2016 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.liveness; + +import static org.junit.Assert.assertFalse; + +import java.io.IOException; + +import org.junit.Ignore; +import org.junit.Test; + +import tlc2.output.EC; + +/** + * see {@link May09dTest}May09Test + */ +public class May09dTest extends ModelCheckerTestCase { + + public May09dTest() { + super("May09dMC", "symmetry"); + } + + @Test + @Ignore("Ignored for as long as symmetry is incorrectly handled by TLC with liveness checking.") + public void testSpec() throws IOException { + assertFalse(recorder.recorded(EC.TLC_TEMPORAL_PROPERTY_VIOLATED)); + assertFalse(recorder.recorded(EC.TLC_COUNTER_EXAMPLE)); + } +} diff --git a/tlatools/test/tlc2/tool/liveness/ModelCheckerTestCase.java b/tlatools/test/tlc2/tool/liveness/ModelCheckerTestCase.java index ba50d0b6e2c57183fc956e6939d662b7abb8edeb..7249b45070590c4e1a3ee56bf347607299016422 100644 --- a/tlatools/test/tlc2/tool/liveness/ModelCheckerTestCase.java +++ b/tlatools/test/tlc2/tool/liveness/ModelCheckerTestCase.java @@ -25,50 +25,105 @@ ******************************************************************************/ package tlc2.tool.liveness; -import java.io.File; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + +import java.lang.reflect.Field; import java.util.ArrayList; import java.util.Arrays; import java.util.List; -import junit.framework.TestCase; +import org.junit.After; +import org.junit.Before; + import tlc2.TLC; +import tlc2.TLCGlobals; import tlc2.TestMPRecorder; +import tlc2.output.EC; +import tlc2.output.EC.ExitStatus; import tlc2.output.MP; +import tlc2.tool.CommonTestCase; +import tlc2.tool.ModelChecker; +import util.FileUtil; +import util.SimpleFilenameToStream; import util.ToolIO; -public abstract class ModelCheckerTestCase extends TestCase { - - private static final String BASE_DIR = System.getProperty("basedir", ""); - private static final String TEST_MODEL = "test-model" + File.separator; +public abstract class ModelCheckerTestCase extends CommonTestCase { - private String path = ""; - private final String spec; - protected final TestMPRecorder recorder = new TestMPRecorder(); - private String[] extraArguments = new String[0]; - + protected String path = ""; + protected String spec; + protected String[] extraArguments = new String[0]; + protected TLC tlc; + protected int actualExitStatus = -1; + protected int expectedExitStatus = ExitStatus.SUCCESS; public ModelCheckerTestCase(String spec) { - this.spec = spec; + this(spec, ExitStatus.SUCCESS); + } + + public ModelCheckerTestCase(String spec, final int exitStatus) { + this(spec, "", exitStatus); } public ModelCheckerTestCase(String spec, String path) { - this(spec); - this.path = path; + this(spec, path, ExitStatus.SUCCESS); + } + + public ModelCheckerTestCase(String spec, String[] extraArguments) { + this(spec, "", extraArguments, ExitStatus.SUCCESS); + } + + public ModelCheckerTestCase(String spec, String[] extraArguments, final int exitStatus) { + this(spec, "", extraArguments, exitStatus); } public ModelCheckerTestCase(String spec, String path, String[] extraArguments) { - this(spec, path); + this(spec, path, extraArguments, ExitStatus.SUCCESS); + } + + public ModelCheckerTestCase(String spec, String path, String[] extraArguments, final int exitStatus) { + this(spec, path, exitStatus); this.extraArguments = extraArguments; } + public ModelCheckerTestCase(final String spec, final String path, final int exitStatus) { + super(new TestMPRecorder()); + this.spec = spec; + this.path = path; + this.expectedExitStatus = exitStatus; + } + + protected void beforeSetUp() { + // No-op + } + + /* (non-Javadoc) + * @see junit.framework.TestCase#setUp() + */ + @Before public void setUp() { + beforeSetUp(); + + // some tests might want to access the liveness graph after model + // checking completed. Thus, prevent the liveness graph from being + // closed too earlier. + System.setProperty(ModelChecker.class.getName() + ".vetoCleanup", "true"); + try { // TEST_MODEL is where TLC should look for user defined .tla files ToolIO.setUserDir(BASE_DIR + TEST_MODEL + path); MP.setRecorder(recorder); - final TLC tlc = new TLC(); + // Increase the liveness checking threshold to prevent liveness + // checking of an incomplete graph. Most tests check that the + // state queue is empty and fail if not. This is only given + // when liveness checking is executed when all states have been + // generated. + TLCGlobals.livenessThreshold = Double.MAX_VALUE; + + tlc = new TLC(); + tlc.setResolver(new SimpleFilenameToStream()); // * We want *no* deadlock checking to find the violation of the // temporal formula // * We use (unless overridden) a single worker to simplify @@ -80,7 +135,17 @@ public abstract class ModelCheckerTestCase extends TestCase { // *Don't* check for deadlocks. All tests are interested in liveness // checks which are shielded away by deadlock checking. TLC finds a // deadlock (if it exists) before it finds most liveness violations. - args.add("-deadlock"); + if (!checkDeadLock()) { + args.add("-deadlock"); + } + + args.add("-fp"); + args.add("0"); + + if (doCoverage()) { + args.add("-coverage"); + args.add("1"); + } args.add("-workers"); args.add(Integer.toString(getNumberOfThreads())); @@ -88,7 +153,14 @@ public abstract class ModelCheckerTestCase extends TestCase { // Never create checkpoints. They distort performance tests and are // of no use anyway. args.add("-checkpoint"); - args.add("0"); + args.add(Integer.toString(doCheckpoint())); + + // Always print the state graph in dot file notation. + if (doDump()) { + args.add("-dump"); + args.add("dot"); + args.add("${metadir}" + FileUtil.separator + getClass().getCanonicalName() + ".dot"); + } args.addAll(Arrays.asList(extraArguments)); @@ -96,14 +168,78 @@ public abstract class ModelCheckerTestCase extends TestCase { tlc.handleParameters(args.toArray(new String[args.size()])); // Run the ModelChecker - tlc.process(); + final int errorCode = tlc.process(); + actualExitStatus = EC.ExitStatus.errorConstantToExitStatus(errorCode); } catch (Exception e) { fail(e.getMessage()); } } + protected void beforeTearDown() { + // No-op + } + + @After + public void tearDown() { + beforeTearDown(); + + assertExitStatus(); + } + + protected void assertExitStatus() { + assertEquals(expectedExitStatus, actualExitStatus); + } + + protected boolean doCoverage() { + return true; + } + + /** + * @return True if TLC is to be called with "-deadlock". + */ + protected boolean checkDeadLock() { + return false; + } + + protected boolean doDump() { + return true; + } + + protected int doCheckpoint() { + return 0; + } + + /** + * @return The number of worker threads TLC should use. + */ protected int getNumberOfThreads() { return 1; } + + protected void setExitStatus(final int exitStatus) { + this.expectedExitStatus = exitStatus; + } + + /** + * E.g. + * ILiveCheck liveCheck = (ILiveCheck) getField(AbstractChecker.class, "liveCheck", + * getField(TLC.class, "instance", tlc)); + */ + protected Object getField(Class<?> targetClass, String fieldName, Object instance) { + try { + Field field = targetClass.getDeclaredField(fieldName); + field.setAccessible(true); + return field.get(instance); + } catch (NoSuchFieldException e) { + e.printStackTrace(); + } catch (SecurityException e) { + e.printStackTrace(); + } catch (IllegalArgumentException e) { + e.printStackTrace(); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + return null; + } } diff --git a/tlatools/test/tlc2/tool/liveness/NQTest.java b/tlatools/test/tlc2/tool/liveness/NQTest.java new file mode 100644 index 0000000000000000000000000000000000000000..2a31ef1797f5fb027c89346ea9eb7ad715965ad9 --- /dev/null +++ b/tlatools/test/tlc2/tool/liveness/NQTest.java @@ -0,0 +1,70 @@ +/******************************************************************************* + * Copyright (c) 2015 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ + +package tlc2.tool.liveness; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.io.File; + +import org.junit.Ignore; +import org.junit.Test; + +import tlc2.output.EC; +import util.TLAConstants; + +public class NQTest extends ModelCheckerTestCase { + + public NQTest() { + super(TLAConstants.Files.MODEL_CHECK_FILE_BASENAME, "symmetry" + File.separator + "NQ"); + } + + /* + * WRT @Ignore: + * Without symmetry defined, TLC finds no counterexample. With symmetry, + * which is the case for this test, TLC incorrectly finds a counterexample + * which it subsequently fails to recreate. In the end, this causes TLC to + * claim that the spec is not symmetric. + * + * So why does TLC think the PossibleErrorModel is P-Satisfiable and + * consequently tries to produce a counterexample? + */ + @Test + @Ignore("Ignored for as long as symmetry is incorrectly handled by TLC with liveness checking.") + public void testSpec() { + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "1049", "363", "0")); + assertFalse(recorder.recorded(EC.GENERAL)); + + // Assert it has *not* found a temporal violation and a counter example + assertFalse(recorder.recorded(EC.TLC_TEMPORAL_PROPERTY_VIOLATED)); + assertFalse(recorder.recorded(EC.TLC_COUNTER_EXAMPLE)); + + // Assert *no* error trace + assertFalse(recorder.recorded(EC.TLC_STATE_PRINT2)); + } +} diff --git a/tlatools/test/tlc2/tool/liveness/NQaTest.java b/tlatools/test/tlc2/tool/liveness/NQaTest.java new file mode 100644 index 0000000000000000000000000000000000000000..cdb678cf644c0faae5a4c2f7d0076807143b4813 --- /dev/null +++ b/tlatools/test/tlc2/tool/liveness/NQaTest.java @@ -0,0 +1,62 @@ +/******************************************************************************* + * Copyright (c) 2015 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ + +package tlc2.tool.liveness; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import java.io.File; + +import org.junit.Ignore; +import org.junit.Test; + +import tlc2.output.EC; + +public class NQaTest extends ModelCheckerTestCase { + + public NQaTest() { + super("MCa", "symmetry" + File.separator + "NQ"); + } + + @Test + @Ignore("Ignored for as long as symmetry is incorrectly handled by TLC with liveness checking.") + public void testSpec() { + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "1049", "363", "0")); + assertFalse(recorder.recorded(EC.GENERAL)); + + // Assert it has found the temporal violation and also a counter example + assertTrue(recorder.recorded(EC.TLC_TEMPORAL_PROPERTY_VIOLATED)); + assertTrue(recorder.recorded(EC.TLC_COUNTER_EXAMPLE)); + + // Assert an error trace + assertTrue(recorder.recorded(EC.TLC_STATE_PRINT2)); + + fail("Check actual error trace and its completeness"); + } +} diff --git a/tlatools/test/tlc2/tool/liveness/NoSymmetryTableauModelCheckerTest.java b/tlatools/test/tlc2/tool/liveness/NoSymmetryTableauModelCheckerTest.java new file mode 100644 index 0000000000000000000000000000000000000000..a03385af36d77ad0a9daaca19cec43ccfe1810d5 --- /dev/null +++ b/tlatools/test/tlc2/tool/liveness/NoSymmetryTableauModelCheckerTest.java @@ -0,0 +1,60 @@ +/******************************************************************************* + * Copyright (c) 2015 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ + +package tlc2.tool.liveness; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.output.EC; + +public class NoSymmetryTableauModelCheckerTest extends ModelCheckerTestCase { + + public NoSymmetryTableauModelCheckerTest() { + super("NoSymmetryLivenessTableauMC", "symmetry"); + } + + @Test + public void testSpec() { + // ModelChecker intends to check liveness + assertTrue(recorder.recordedWithStringValues(EC.TLC_LIVE_IMPLIED, "2")); + assertTrue(recorder.recordedWithStringValues(EC.TLC_INIT_GENERATED1, "8", "s")); + + // ModelChecker has finished and generated the expected amount of states + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "5492", "1272", "0")); + assertFalse(recorder.recorded(EC.GENERAL)); + + // Assert it has not found a temporal violation nor a counter example + assertFalse(recorder.recorded(EC.TLC_TEMPORAL_PROPERTY_VIOLATED)); + assertFalse(recorder.recorded(EC.TLC_COUNTER_EXAMPLE)); + assertFalse(recorder.recorded(EC.TLC_STATE_PRINT2)); + + assertZeroUncovered(); + } +} diff --git a/tlatools/test/tlc2/tool/liveness/OneBitMutexNoSymmetryTest.java b/tlatools/test/tlc2/tool/liveness/OneBitMutexNoSymmetryTest.java new file mode 100644 index 0000000000000000000000000000000000000000..765435664b315f101d36e9287be4a31fecf5aa31 --- /dev/null +++ b/tlatools/test/tlc2/tool/liveness/OneBitMutexNoSymmetryTest.java @@ -0,0 +1,170 @@ +/******************************************************************************* + * Copyright (c) 2015 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ + +package tlc2.tool.liveness; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; + +import org.junit.Test; + +import tlc2.output.EC; +import tlc2.output.EC.ExitStatus; + +public class OneBitMutexNoSymmetryTest extends ModelCheckerTestCase { + + public OneBitMutexNoSymmetryTest() { + super("OneBitMutexNoSymmetryMC", "symmetry" + File.separator + "OneBitMutex", ExitStatus.VIOLATION_LIVENESS); + } + + @Test + public void testSpec() { + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "244", "127", "0")); + assertFalse(recorder.recorded(EC.GENERAL)); + + // Assert it has found the temporal violation and also a counter example + assertTrue(recorder.recorded(EC.TLC_TEMPORAL_PROPERTY_VIOLATED)); + assertTrue(recorder.recorded(EC.TLC_COUNTER_EXAMPLE)); + + assertNodeAndPtrSizes(11700L, 3728L); + + // Assert the error trace + assertTrue(recorder.recorded(EC.TLC_STATE_PRINT2)); + final List<String> expectedTrace = new ArrayList<String>(17); + //1 + expectedTrace.add("/\\ unchecked = (A :> {} @@ B :> {})\n" + + "/\\ other = (A :> A @@ B :> A)\n" + + "/\\ x = (A :> FALSE @@ B :> FALSE)\n" + + "/\\ pc = (A :> \"ncs\" @@ B :> \"ncs\")"); + //2 + expectedTrace.add("/\\ unchecked = (A :> {} @@ B :> {})\n" + + "/\\ other = (A :> A @@ B :> A)\n" + + "/\\ x = (A :> FALSE @@ B :> FALSE)\n" + + "/\\ pc = (A :> \"e1\" @@ B :> \"ncs\")"); + //3 + expectedTrace.add("/\\ unchecked = (A :> {} @@ B :> {})\n" + + "/\\ other = (A :> A @@ B :> A)\n" + + "/\\ x = (A :> FALSE @@ B :> FALSE)\n" + + "/\\ pc = (A :> \"e1\" @@ B :> \"e1\")"); + //4 + expectedTrace.add("/\\ unchecked = (A :> {} @@ B :> {A})\n" + + "/\\ other = (A :> A @@ B :> A)\n" + + "/\\ x = (A :> FALSE @@ B :> TRUE)\n" + + "/\\ pc = (A :> \"e1\" @@ B :> \"e2\")"); + //5 + expectedTrace.add("/\\ unchecked = (A :> {} @@ B :> {})\n" + + "/\\ other = (A :> A @@ B :> A)\n" + + "/\\ x = (A :> FALSE @@ B :> TRUE)\n" + + "/\\ pc = (A :> \"e1\" @@ B :> \"e3\")"); + //6 + expectedTrace.add("/\\ unchecked = (A :> {} @@ B :> {})\n" + + "/\\ other = (A :> A @@ B :> A)\n" + + "/\\ x = (A :> FALSE @@ B :> TRUE)\n" + + "/\\ pc = (A :> \"e1\" @@ B :> \"e2\")"); + //7 + expectedTrace.add("/\\ unchecked = (A :> {} @@ B :> {})\n" + + "/\\ other = (A :> A @@ B :> A)\n" + + "/\\ x = (A :> FALSE @@ B :> TRUE)\n" + + "/\\ pc = (A :> \"e1\" @@ B :> \"cs\")"); + //8 (Loops back to) + expectedTrace.add("/\\ unchecked = (A :> {B} @@ B :> {})\n" + + "/\\ other = (A :> A @@ B :> A)\n" + + "/\\ x = (A :> TRUE @@ B :> TRUE)\n" + + "/\\ pc = (A :> \"e2\" @@ B :> \"cs\")"); + //9 + expectedTrace.add("/\\ unchecked = (A :> {} @@ B :> {})\n" + + "/\\ other = (A :> B @@ B :> A)\n" + + "/\\ x = (A :> TRUE @@ B :> TRUE)\n" + + "/\\ pc = (A :> \"e3\" @@ B :> \"cs\")"); + //10 + expectedTrace.add("/\\ unchecked = (A :> {} @@ B :> {})\n" + + "/\\ other = (A :> B @@ B :> A)\n" + + "/\\ x = (A :> TRUE @@ B :> TRUE)\n" + + "/\\ pc = (A :> \"e3\" @@ B :> \"f\")"); + //11 + expectedTrace.add("/\\ unchecked = (A :> {} @@ B :> {})\n" + + "/\\ other = (A :> B @@ B :> A)\n" + + "/\\ x = (A :> TRUE @@ B :> TRUE)\n" + + "/\\ pc = (A :> \"e4\" @@ B :> \"f\")"); + //12 + expectedTrace.add("/\\ unchecked = (A :> {} @@ B :> {})\n" + + "/\\ other = (A :> B @@ B :> A)\n" + + "/\\ x = (A :> TRUE @@ B :> FALSE)\n" + + "/\\ pc = (A :> \"e4\" @@ B :> \"ncs\")"); + //13 + expectedTrace.add("/\\ unchecked = (A :> {} @@ B :> {})\n" + + "/\\ other = (A :> B @@ B :> A)\n" + + "/\\ x = (A :> TRUE @@ B :> FALSE)\n" + + "/\\ pc = (A :> \"e4\" @@ B :> \"e1\")"); + //14 + expectedTrace.add("/\\ unchecked = (A :> {} @@ B :> {})\n" + + "/\\ other = (A :> B @@ B :> A)\n" + + "/\\ x = (A :> FALSE @@ B :> FALSE)\n" + + "/\\ pc = (A :> \"e5\" @@ B :> \"e1\")"); + //15 + expectedTrace.add("/\\ unchecked = (A :> {} @@ B :> {})\n" + + "/\\ other = (A :> B @@ B :> A)\n" + + "/\\ x = (A :> FALSE @@ B :> FALSE)\n" + + "/\\ pc = (A :> \"e1\" @@ B :> \"e1\")"); + //16 + expectedTrace.add("/\\ unchecked = (A :> {} @@ B :> {A})\n" + + "/\\ other = (A :> B @@ B :> A)\n" + + "/\\ x = (A :> FALSE @@ B :> TRUE)\n" + + "/\\ pc = (A :> \"e1\" @@ B :> \"e2\")"); + //17 + expectedTrace.add("/\\ unchecked = (A :> {} @@ B :> {})\n" + + "/\\ other = (A :> B @@ B :> A)\n" + + "/\\ x = (A :> FALSE @@ B :> TRUE)\n" + + "/\\ pc = (A :> \"e1\" @@ B :> \"e3\")"); + //18 + expectedTrace.add("/\\ unchecked = (A :> {} @@ B :> {})\n" + + "/\\ other = (A :> B @@ B :> A)\n" + + "/\\ x = (A :> FALSE @@ B :> TRUE)\n" + + "/\\ pc = (A :> \"e1\" @@ B :> \"e2\")"); + //19 + expectedTrace.add("/\\ unchecked = (A :> {B} @@ B :> {})\n" + + "/\\ other = (A :> B @@ B :> A)\n" + + "/\\ x = (A :> TRUE @@ B :> TRUE)\n" + + "/\\ pc = (A :> \"e2\" @@ B :> \"e2\")"); + //20 + expectedTrace.add("/\\ unchecked = (A :> {} @@ B :> {})\n" + + "/\\ other = (A :> B @@ B :> A)\n" + + "/\\ x = (A :> TRUE @@ B :> TRUE)\n" + + "/\\ pc = (A :> \"e3\" @@ B :> \"e2\")"); + assertTraceWith(recorder.getRecords(EC.TLC_STATE_PRINT2), expectedTrace); + + assertBackToState(9, "<e2 line 66, col 13 to line 74, col 21 of module OneBitMutex>"); + + assertUncovered("line 80, col 38 to line 80, col 69 of module OneBitMutex: 0\n" + + "line 96, col 16 to line 96, col 47 of module OneBitMutex: 0\n" + + "line 97, col 16 to line 97, col 50 of module OneBitMutex: 0"); + } +} diff --git a/tlatools/test/tlc2/tool/liveness/OneBitMutexTest.java b/tlatools/test/tlc2/tool/liveness/OneBitMutexTest.java new file mode 100644 index 0000000000000000000000000000000000000000..fce4d244ed2bd8174673b3760ce9ad5646d14482 --- /dev/null +++ b/tlatools/test/tlc2/tool/liveness/OneBitMutexTest.java @@ -0,0 +1,132 @@ +/******************************************************************************* + * Copyright (c) 2015 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ + +package tlc2.tool.liveness; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; + +import org.junit.Ignore; +import org.junit.Test; + +import tlc2.output.EC; + +public class OneBitMutexTest extends ModelCheckerTestCase { + + public OneBitMutexTest() { + super("OneBitMutexMC", "symmetry" + File.separator + "OneBitMutex"); + } + + @Test + @Ignore("Ignored for as long as symmetry is incorrectly handled by TLC with liveness checking.") + public void testSpec() { + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "133", "68", "0")); + assertFalse(recorder.recorded(EC.GENERAL)); + + // Assert it has found the temporal violation and also a counter example + assertTrue(recorder.recorded(EC.TLC_TEMPORAL_PROPERTY_VIOLATED)); + assertTrue(recorder.recorded(EC.TLC_COUNTER_EXAMPLE)); + + assertNodeAndPtrSizes(7380L, 2368L); + + // Assert the error trace + assertTrue(recorder.recorded(EC.TLC_STATE_PRINT2)); + final List<String> expectedTrace = new ArrayList<String>(17); + //1 + expectedTrace.add("/\\ unchecked = (A :> {} @@ B :> {})\n" + + "/\\ other = (A :> B @@ B :> A)\n" + + "/\\ x = (A :> FALSE @@ B :> FALSE)\n" + + "/\\ pc = (A :> \"ncs\" @@ B :> \"ncs\")"); + //2 + expectedTrace.add("/\\ unchecked = (A :> {} @@ B :> {})\n" + + "/\\ other = (A :> B @@ B :> A)\n" + + "/\\ x = (A :> FALSE @@ B :> FALSE)\n" + + "/\\ pc = (A :> \"e1\" @@ B :> \"ncs\")"); + //3 + expectedTrace.add("/\\ unchecked = (A :> {B} @@ B :> {})\n" + + "/\\ other = (A :> B @@ B :> A)\n" + + "/\\ x = (A :> TRUE @@ B :> FALSE)\n" + + "/\\ pc = (A :> \"e2\" @@ B :> \"ncs\")"); + //4 + expectedTrace.add("/\\ unchecked = (A :> {B} @@ B :> {})\n" + + "/\\ other = (A :> B @@ B :> A)\n" + + "/\\ x = (A :> TRUE @@ B :> FALSE)\n" + + "/\\ pc = (A :> \"e2\" @@ B :> \"e1\")"); + //5 + expectedTrace.add("/\\ unchecked = (A :> {} @@ B :> {})\n" + + "/\\ other = (A :> B @@ B :> A)\n" + + "/\\ x = (A :> TRUE @@ B :> FALSE)\n" + + "/\\ pc = (A :> \"e3\" @@ B :> \"e1\")"); + //6 + expectedTrace.add("/\\ unchecked = (A :> {} @@ B :> {A})\n" + + "/\\ other = (A :> B @@ B :> A)\n" + + "/\\ x = (A :> TRUE @@ B :> TRUE)\n" + + "/\\ pc = (A :> \"e3\" @@ B :> \"e2\")"); + //7 + expectedTrace.add("/\\ unchecked = (A :> {} @@ B :> {})\n" + + "/\\ other = (A :> B @@ B :> A)\n" + + "/\\ x = (A :> TRUE @@ B :> TRUE)\n" + + "/\\ pc = (A :> \"e3\" @@ B :> \"e3\")"); + //8 (Loops back to) + expectedTrace.add("/\\ unchecked = (A :> {} @@ B :> {})\n" + + "/\\ other = (A :> B @@ B :> A)\n" + + "/\\ x = (A :> TRUE @@ B :> TRUE)\n" + + "/\\ pc = (A :> \"e4\" @@ B :> \"e3\")"); + //9 + expectedTrace.add("/\\ unchecked = (A :> {} @@ B :> {})\n" + + "/\\ other = (A :> B @@ B :> A)\n" + + "/\\ x = (A :> TRUE @@ B :> TRUE)\n" + + "/\\ pc = (A :> \"e4\" @@ B :> \"e4\")"); + //10 + expectedTrace.add("/\\ unchecked = (A :> {} @@ B :> {})\n" + + "/\\ other = (A :> B @@ B :> A)\n" + + "/\\ x = (A :> FALSE @@ B :> TRUE)\n" + + "/\\ pc = (A :> \"e5\" @@ B :> \"e4\")"); + //11 + expectedTrace.add("/\\ unchecked = (A :> {} @@ B :> {})\n" + + "/\\ other = (A :> B @@ B :> A)\n" + + "/\\ x = (A :> FALSE @@ B :> FALSE)\n" + + "/\\ pc = (A :> \"e5\" @@ B :> \"e5\")"); + //12 + expectedTrace.add("/\\ unchecked = (A :> {} @@ B :> {})\n" + + "/\\ other = (A :> B @@ B :> A)\n" + + "/\\ x = (A :> FALSE @@ B :> FALSE)\n" + + "/\\ pc = (A :> \"e1\" @@ B :> \"e5\")"); + //13 + expectedTrace.add("/\\ unchecked = (A :> {} @@ B :> {})\n" + + "/\\ other = (A :> B @@ B :> A)\n" + + "/\\ x = (A :> FALSE @@ B :> FALSE)\n" + + "/\\ pc = (A :> \"e1\" @@ B :> \"e1\")"); + assertTraceWith(recorder.getRecords(EC.TLC_STATE_PRINT2), expectedTrace); + + assertBackToState(4, "<Action line 60, col 13 to line 64, col 29 of module OneBitMutex>"); + } +} diff --git a/tlatools/test/tlc2/tool/liveness/SymmetryModelCheckerTest3.java b/tlatools/test/tlc2/tool/liveness/SymmetryModelCheckerTest3.java new file mode 100644 index 0000000000000000000000000000000000000000..9590eef845a27ab8dd32ab836b1aed0c2052946e --- /dev/null +++ b/tlatools/test/tlc2/tool/liveness/SymmetryModelCheckerTest3.java @@ -0,0 +1,94 @@ +/******************************************************************************* + * Copyright (c) 2015 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ + +package tlc2.tool.liveness; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.Ignore; +import org.junit.Test; + +import tlc2.output.EC; +import util.TLAConstants; + +public class SymmetryModelCheckerTest3 extends ModelCheckerTestCase { + + public SymmetryModelCheckerTest3() { + super(TLAConstants.Files.MODEL_CHECK_FILE_BASENAME, "symmetry"); + } + + @Test + @Ignore("Ignored for as long as symmetry is incorrectly handled by TLC with liveness checking.") + public void testSpec() { + // Violates Prop1 with Spec2. TLC finds no violation. + // + // The reason why TLC does not find the violation is due to the fact that + // the action predicate from state s to t is not being taken into + // consideration during the liveness graph checks. It is not + // being considered because it is a redundant and thus omitted arc in + // the liveness graph, but its label (which corresponds to the action + // property evaluation) is different. + // + // Without symmetry, the liveness graph consists of four distinct nodes + // {(x=a, y=0), (x=a, y=1), (x=b, y=0), (x=b, y=1)} with transitions + // (ignoring self-loops) from {1:2, 2:1, 2:3, 3:4, 4:3, 4:1}. The arc + // that violates Prop1 (<>[][x'=x] "x does not change") is the trans. + // from node 2. (x=a, y=1) to node 3. (x=b, y=0). TLC correctly finds + // a violation and produces the correct counterexample which is asserted + // below. + // + // See test-model/symmetry/MC_Graph.png for a visualization. + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "5", "2", "0")); + assertFalse(recorder.recorded(EC.GENERAL)); + + // Assert it has found the temporal violation and also a counter example + assertTrue(recorder.recorded(EC.TLC_TEMPORAL_PROPERTY_VIOLATED)); + assertTrue(recorder.recorded(EC.TLC_COUNTER_EXAMPLE)); + + assertNodeAndPtrSizes(84L, 32L); + + // Assert the error trace + assertTrue(recorder.recorded(EC.TLC_STATE_PRINT2)); + final List<String> expectedTrace = new ArrayList<String>(4); + expectedTrace.add("/\\ x = a\n/\\ y = 0"); + expectedTrace.add("/\\ x = a\n/\\ y = 1"); // <= x changes after this state + expectedTrace.add("/\\ x = b\n/\\ y = 0"); + expectedTrace.add("/\\ x = b\n/\\ y = 1"); + assertTraceWith(recorder.getRecords(EC.TLC_STATE_PRINT2), expectedTrace); + + assertBackToState(1, "<Action line 27, col 12 to line 29, col 27 of module SymmetryLiveness3>"); + + // With symmetry defined, the state/liveness graph is collapsed into + // just two states: {(x=a, y=0), (x=b, y=1)} and transitions going + // from {1:2, 2:1} (self-loops omitted). Thus, TLC never checks the + // transition from (x=a, y=1) -> (x=b, y=0) which violates Prop1. + } +} diff --git a/tlatools/test/tlc2/tool/liveness/SymmetryModelCheckerTest3a.java b/tlatools/test/tlc2/tool/liveness/SymmetryModelCheckerTest3a.java new file mode 100644 index 0000000000000000000000000000000000000000..e2d1ee5821880900693fdf440c15392568dd9215 --- /dev/null +++ b/tlatools/test/tlc2/tool/liveness/SymmetryModelCheckerTest3a.java @@ -0,0 +1,95 @@ +/******************************************************************************* + * Copyright (c) 2015 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ + +package tlc2.tool.liveness; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.Ignore; +import org.junit.Test; + +import tlc2.output.EC; + +public class SymmetryModelCheckerTest3a extends ModelCheckerTestCase { + + public SymmetryModelCheckerTest3a() { + super("MCa", "symmetry"); + } + + @Test + @Ignore("Ignored for as long as symmetry is incorrectly handled by TLC with liveness checking.") + public void testSpec() { + // Violates Prop1 of Spec1 which TLC correctly detects. However, it + // produced a bogus counterexample. It does not use the symmetric state + // that is actually supposed to be on the error trace. It uses the + // smallest state under symmetry (the one all symmetric states get + // mapped to). + // + // Spec1 and its action N1 more precisely can never make Prop1 eventually + // _globally_ true. The second disjunct flips x's state forever. This + // violation is correctly detected by TLC. However, due to the fact that + // the x=b state is symmetry reduced and thus discarded from the + // liveness graph causes the counter-example to incorrectly show a + // violating state with x=a (where x=a logically does not violate + // Prop1). + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "4", "2", "0")); + assertFalse(recorder.recorded(EC.GENERAL)); + + // Assert it has found the temporal violation and also a counter example + assertTrue(recorder.recorded(EC.TLC_TEMPORAL_PROPERTY_VIOLATED)); + assertTrue(recorder.recorded(EC.TLC_COUNTER_EXAMPLE)); + + assertNodeAndPtrSizes(72L, 32L); + + // Assert the error trace + assertTrue(recorder.recorded(EC.TLC_STATE_PRINT2)); + final List<String> expectedTrace = new ArrayList<String>(4); + expectedTrace.add("/\\ x = a\n/\\ y = 0"); + expectedTrace.add("/\\ x = a\n/\\ y = 1"); // <= x changes after this state + expectedTrace.add("/\\ x = b\n/\\ y = 0"); + expectedTrace.add("/\\ x = b\n/\\ y = 1"); + assertTraceWith(recorder.getRecords(EC.TLC_STATE_PRINT2), expectedTrace); + + assertBackToState(1); + + // Surprisingly enough, it turns out that LiveWorker#printTrace actually + // generates a partially valid trace but simply fails to correctly print + // it. It omits the final/last state with x=b from being printed. The + // reason why printTrace(..) correctly generates the states is due to + // the way the error trace gets generated. Starting from an init state, + // it uses the predecessor and the successors fingerprint to generate + // the successor state. For this model this means the error trace + // contains the states <<(x=a, y=0), (x=a, y=1), (x=b, y=0)>>. The state + // (x=b, y=0) is generated from its fingerprint f and its predecessor + // state (x=a, y=1) AND the action N1. Since N1 mandates that x flips, + // tool generates a state with x=b despite the declared symmetry. + } +} diff --git a/tlatools/test/tlc2/tool/liveness/SymmetryModelCheckerTestLong.java b/tlatools/test/tlc2/tool/liveness/SymmetryModelCheckerTestLong.java new file mode 100644 index 0000000000000000000000000000000000000000..897af5e7d1c0d4c4461a3a2046d7ffe831979854 --- /dev/null +++ b/tlatools/test/tlc2/tool/liveness/SymmetryModelCheckerTestLong.java @@ -0,0 +1,76 @@ +/******************************************************************************* + * Copyright (c) 2015 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ + +package tlc2.tool.liveness; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.Ignore; +import org.junit.Test; + +import tlc2.output.EC; + +public class SymmetryModelCheckerTestLong extends ModelCheckerTestCase { + + public SymmetryModelCheckerTestLong() { + super("LongMC", "symmetry"); + } + + @Test + @Ignore("Ignored for as long as symmetry is incorrectly handled by TLC with liveness checking.") + public void testSpec() { + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "8", "5", "0")); + assertFalse(recorder.recorded(EC.GENERAL)); + + // Assert it has found the temporal violation and also a counter example + assertTrue(recorder.recorded(EC.TLC_TEMPORAL_PROPERTY_VIOLATED)); + assertTrue(recorder.recorded(EC.TLC_COUNTER_EXAMPLE)); + + assertNodeAndPtrSizes(192L, 80L); + + // Assert the error trace + assertTrue(recorder.recorded(EC.TLC_STATE_PRINT2)); + final List<String> expectedTrace = new ArrayList<String>(4); + expectedTrace.add("/\\ x = a\n/\\ y = 0"); + expectedTrace.add("/\\ x = a\n/\\ y = 1"); + expectedTrace.add("/\\ x = a\n/\\ y = 2"); + expectedTrace.add("/\\ x = a\n/\\ y = 3"); + expectedTrace.add("/\\ x = a\n/\\ y = 4"); // <= x changes after this state + expectedTrace.add("/\\ x = b\n/\\ y = 0"); + expectedTrace.add("/\\ x = b\n/\\ y = 1"); + expectedTrace.add("/\\ x = b\n/\\ y = 2"); + expectedTrace.add("/\\ x = b\n/\\ y = 3"); + expectedTrace.add("/\\ x = b\n/\\ y = 4"); + assertTraceWith(recorder.getRecords(EC.TLC_STATE_PRINT2), expectedTrace); + + assertBackToState(1, "<Action line 47, col 12 to line 49, col 27 of module SymmetryLivenessLong>"); + } +} diff --git a/tlatools/test/tlc2/tool/liveness/SymmetryModelCheckerTestLonga.java b/tlatools/test/tlc2/tool/liveness/SymmetryModelCheckerTestLonga.java new file mode 100644 index 0000000000000000000000000000000000000000..b92e7632b62a070edc93ea1c3952998e1a484b91 --- /dev/null +++ b/tlatools/test/tlc2/tool/liveness/SymmetryModelCheckerTestLonga.java @@ -0,0 +1,76 @@ +/******************************************************************************* + * Copyright (c) 2015 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ + +package tlc2.tool.liveness; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.Ignore; +import org.junit.Test; + +import tlc2.output.EC; + +public class SymmetryModelCheckerTestLonga extends ModelCheckerTestCase { + + public SymmetryModelCheckerTestLonga() { + super("LongMCa", "symmetry"); + } + + @Test + @Ignore("Ignored for as long as symmetry is incorrectly handled by TLC with liveness checking.") + public void testSpec() { + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "7", "5", "0")); + assertFalse(recorder.recorded(EC.GENERAL)); + + // Assert it has found the temporal violation and also a counter example + assertTrue(recorder.recorded(EC.TLC_TEMPORAL_PROPERTY_VIOLATED)); + assertTrue(recorder.recorded(EC.TLC_COUNTER_EXAMPLE)); + + assertNodeAndPtrSizes(180L, 80L); + + // Assert the error trace + assertTrue(recorder.recorded(EC.TLC_STATE_PRINT2)); + final List<String> expectedTrace = new ArrayList<String>(4); + expectedTrace.add("/\\ x = a\n/\\ y = 0"); + expectedTrace.add("/\\ x = a\n/\\ y = 1"); + expectedTrace.add("/\\ x = a\n/\\ y = 2"); + expectedTrace.add("/\\ x = a\n/\\ y = 3"); + expectedTrace.add("/\\ x = a\n/\\ y = 4"); // <= x changes after this state + expectedTrace.add("/\\ x = b\n/\\ y = 0"); + expectedTrace.add("/\\ x = b\n/\\ y = 1"); + expectedTrace.add("/\\ x = b\n/\\ y = 2"); + expectedTrace.add("/\\ x = b\n/\\ y = 3"); + expectedTrace.add("/\\ x = b\n/\\ y = 4"); + assertTraceWith(recorder.getRecords(EC.TLC_STATE_PRINT2), expectedTrace); + + assertBackToState(1, "<Action line 25, col 12 to line 27, col 27 of module SymmetryLivenessLong>"); + } +} diff --git a/tlatools/test/tlc2/tool/liveness/SymmetryTableauLiveCheckTest.java b/tlatools/test/tlc2/tool/liveness/SymmetryTableauLiveCheckTest.java new file mode 100644 index 0000000000000000000000000000000000000000..508212d0c01ed43009be3cd8d3f74a3303a9e6a4 --- /dev/null +++ b/tlatools/test/tlc2/tool/liveness/SymmetryTableauLiveCheckTest.java @@ -0,0 +1,380 @@ +/******************************************************************************* + * Copyright (c) 2015 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ + +package tlc2.tool.liveness; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import java.io.IOException; +import java.util.Set; + +import org.easymock.Capture; +import org.easymock.EasyMock; +import org.easymock.IAnswer; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Ignore; +import org.junit.Test; + +import tlc2.tool.Action; +import tlc2.tool.ITool; +import tlc2.tool.StateVec; +import tlc2.tool.TLCState; +import tlc2.tool.liveness.GraphNode.Transition; +import tlc2.tool.queue.DummyTLCState; +import tlc2.util.BitVector; +import tlc2.util.SetOfStates; +import tlc2.util.statistics.DummyBucketStatistics; + +public class SymmetryTableauLiveCheckTest { + + private ITool tool; + + @Before + public void createTool() { + this.tool = EasyMock.createNiceMock(ITool.class); + } + + @Test + @Ignore("Ignored for as long as symmetry is incorrectly handled by TLC with liveness checking.") + public void testTableau() throws IOException { + final ILiveCheck lc = getLiveCheckWithTwoNodeTableau(); + + final SetOfStates nexts = new SetOfStates(1); + final AbstractDiskGraph diskGraph = lc.getChecker(0).getDiskGraph(); + + // Add init state v + final TLCState v = new DummyTLCState(100L); + lc.addInitState(tool, v, v.fingerPrint()); + + // one init node (two elements in LongVec) + assertEquals(1, diskGraph.getInitNodes().size() / 2); + + // Add v > s + final TLCState s = new DummyTLCState(200L); + nexts.put(s); + lc.addNextState(null, v, v.fingerPrint(), nexts); + + assertEquals(2, diskGraph.getNode(v.fingerPrint(), 0).succSize()); + assertEquals(0, diskGraph.getNode(v.fingerPrint(), 1).succSize()); // only tidx0 is an init node + + assertEquals(0, diskGraph.getNode(s.fingerPrint(), 0).succSize()); + assertEquals(0, diskGraph.getNode(s.fingerPrint(), 1).succSize()); + + // Add s > t + nexts.clear(); + final TLCState t = new DummyTLCState(300L); + nexts.put(t); + lc.addNextState(null, s, s.fingerPrint(), nexts); + + assertEquals(2, diskGraph.getNode(v.fingerPrint(), 0).succSize()); + assertEquals(0, diskGraph.getNode(v.fingerPrint(), 1).succSize()); + + assertEquals(2, diskGraph.getNode(s.fingerPrint(), 0).succSize()); + assertEquals(0, diskGraph.getNode(s.fingerPrint(), 1).succSize()); + + assertEquals(0, diskGraph.getNode(t.fingerPrint(), 0).succSize()); + assertEquals(0, diskGraph.getNode(t.fingerPrint(), 1).succSize()); + + // add s > u + nexts.clear(); + final TLCState u = new DummyTLCState(400L); + nexts.put(u); + lc.addNextState(null, s, s.fingerPrint(), nexts); + + Assert.fail("finish incomplete test! Assertions below are partially bogus."); + + assertEquals(2, diskGraph.getNode(v.fingerPrint(), 0).succSize()); + assertEquals(0, diskGraph.getNode(v.fingerPrint(), 1).succSize()); + + assertEquals(4, diskGraph.getNode(s.fingerPrint(), 0).succSize()); + assertEquals(0, diskGraph.getNode(s.fingerPrint(), 1).succSize()); + + assertEquals(0, diskGraph.getNode(t.fingerPrint(), 0).succSize()); + assertEquals(0, diskGraph.getNode(t.fingerPrint(), 1).succSize()); + + assertEquals(0, diskGraph.getNode(u.fingerPrint(), 0).succSize()); + assertEquals(0, diskGraph.getNode(u.fingerPrint(), 1).succSize()); + + assertTrue(diskGraph.checkInvariants(0, 0)); + } + + private ILiveCheck getLiveCheckWithTwoNodeTableau() throws IOException { + final TBGraphNode node1 = EasyMock.createNiceMock(TBGraphNode.class); + EasyMock.expect(node1.isConsistent((TLCState) EasyMock.anyObject(), (ITool) EasyMock.anyObject())) + .andReturn(true).anyTimes(); + EasyMock.expect(node1.nextSize()).andReturn(0).anyTimes(); + EasyMock.expect(node1.getIndex()).andReturn(1).anyTimes(); + EasyMock.replay(node1); + + final TBGraphNode node0 = EasyMock.createMock(TBGraphNode.class); + EasyMock.expect(node0.isConsistent((TLCState) EasyMock.anyObject(), (ITool) EasyMock.anyObject())) + .andReturn(true).anyTimes(); + EasyMock.expect(node0.nextSize()).andReturn(2).anyTimes(); + EasyMock.expect(node0.nextAt(0)).andReturn(node0).anyTimes(); + EasyMock.expect(node0.nextAt(1)).andReturn(node1).anyTimes(); + EasyMock.expect(node0.getIndex()).andReturn(0).anyTimes(); + EasyMock.replay(node0); + + final TBGraph tbGraph = new TBGraph(); + tbGraph.addElement(node0); + tbGraph.addElement(node1); + tbGraph.setInitCnt(1); + + // Configure OOS mock to react to the subsequent invocation. This is a + // essentially the list of calls being made on OOS during + // LiveCheck#addInitState and LiveCheck#addNextState + final OrderOfSolution oos = EasyMock.createNiceMock(OrderOfSolution.class); + EasyMock.expect(oos.hasTableau()).andReturn(true); + EasyMock.expect(oos.getTableau()).andReturn(tbGraph).anyTimes(); + EasyMock.expect(oos.getCheckAction()).andReturn(new LiveExprNode[0]).anyTimes(); + EasyMock.expect(oos.getCheckState()).andReturn(new LiveExprNode[0]).anyTimes(); + EasyMock.expect(oos.checkState(null, (TLCState) EasyMock.anyObject())).andReturn(new boolean[0]).anyTimes(); + EasyMock.replay(oos); + + return new LiveCheck(tool, + new OrderOfSolution[] { oos }, System.getProperty("java.io.tmpdir"), new DummyBucketStatistics(), null); + } + + @Test + @Ignore("Ignored for as long as symmetry is incorrectly handled by TLC with liveness checking.") + public void testSymmetry() throws IOException { + final TLCState s = new DummyTLCState(200L); + final TLCState s1 = new DummyTLCState(s.fingerPrint()); // symmetric sibling of s + final TLCState t = new DummyTLCState(300L); + + final ILiveCheck lc = getLiveCheckWithTwoNodeTableauSymmetry(s, s1, t); + + final SetOfStates nexts = new SetOfStates(1); + final AbstractDiskGraph diskGraph = lc.getChecker(0).getDiskGraph(); + + // Add init state v + final TLCState v = new DummyTLCState(100L); + lc.addInitState(tool, v, v.fingerPrint()); + + // one init node (two elements in LongVec) + assertEquals(1, diskGraph.getInitNodes().size() / 2); + + // Add v > s + nexts.put(s); + lc.addNextState(null, v, v.fingerPrint(), nexts); + + GraphNode vgn = diskGraph.getNode(v.fingerPrint(), 0); + assertEquals(2, vgn.succSize()); // s is consistent with tidx0 and tidx1, but not with tidx2 + final Set<Transition> tvgn = vgn.getTransition(); + assertTrue(tvgn.contains(new Transition(s.fingerPrint(), 0, new BitVector(0)))); + assertTrue(tvgn.contains(new Transition(s.fingerPrint(), 1, new BitVector(0)))); + assertEquals(0, diskGraph.getNode(v.fingerPrint(), 1).succSize()); // only tidx0 is an init node + assertEquals(0, diskGraph.getNode(v.fingerPrint(), 2).succSize()); // only tidx0 is an init node + + assertEquals(0, diskGraph.getNode(s.fingerPrint(), 0).succSize()); + assertEquals(0, diskGraph.getNode(s.fingerPrint(), 1).succSize()); + assertEquals(0, diskGraph.getNode(s.fingerPrint(), 2).succSize()); + + // Add s > t + nexts.clear(); + nexts.put(t); + lc.addNextState(null, s, s.fingerPrint(), nexts); + + assertEquals(2, diskGraph.getNode(v.fingerPrint(), 0).succSize()); + assertEquals(0, diskGraph.getNode(v.fingerPrint(), 1).succSize()); + assertEquals(0, diskGraph.getNode(v.fingerPrint(), 2).succSize()); + + // Even though t is consistent with all three tableau nodes (0,1,2), + // (fingerprint X tableau idx) node <<200.0>> checks only its direct + // succeccors tidx0, tidx1. Not tidx2; + assertEquals(2, diskGraph.getNode(s.fingerPrint(), 0).succSize()); // 300.0, 300.1 + // <<200,1>> checks tidx1 and tidx2 + assertEquals(2, diskGraph.getNode(s.fingerPrint(), 1).succSize()); // 300.1, 300.2 + assertEquals(0, diskGraph.getNode(s.fingerPrint(), 2).succSize()); + + assertEquals(0, diskGraph.getNode(t.fingerPrint(), 0).succSize()); + assertEquals(0, diskGraph.getNode(t.fingerPrint(), 1).succSize()); + assertEquals(0, diskGraph.getNode(t.fingerPrint(), 2).succSize()); + + // Add additional init state u + final TLCState u = new DummyTLCState(400L); + lc.addInitState(tool, u, u.fingerPrint()); + + // two init nodes now (four elements in LongVec) + assertEquals(2, diskGraph.getInitNodes().size() / 2); + + assertEquals(2, diskGraph.getNode(v.fingerPrint(), 0).succSize()); + assertEquals(0, diskGraph.getNode(v.fingerPrint(), 1).succSize()); + assertEquals(0, diskGraph.getNode(v.fingerPrint(), 2).succSize()); + + assertEquals(2, diskGraph.getNode(s.fingerPrint(), 0).succSize()); + assertEquals(2, diskGraph.getNode(s.fingerPrint(), 1).succSize()); + assertEquals(0, diskGraph.getNode(s.fingerPrint(), 2).succSize()); + + assertEquals(0, diskGraph.getNode(t.fingerPrint(), 0).succSize()); + assertEquals(0, diskGraph.getNode(t.fingerPrint(), 1).succSize()); + assertEquals(0, diskGraph.getNode(t.fingerPrint(), 2).succSize()); + + assertEquals(0, diskGraph.getNode(u.fingerPrint(), 0).succSize()); + assertEquals(0, diskGraph.getNode(u.fingerPrint(), 1).succSize()); + assertEquals(0, diskGraph.getNode(u.fingerPrint(), 2).succSize()); + + // add a symmetric s1 (same fingerprint as s) + nexts.clear(); + nexts.put(s1); + lc.addNextState(null, u, u.fingerPrint(), nexts); + + assertEquals(2, diskGraph.getNode(v.fingerPrint(), 0).succSize()); + assertEquals(0, diskGraph.getNode(v.fingerPrint(), 1).succSize()); + assertEquals(0, diskGraph.getNode(v.fingerPrint(), 2).succSize()); + + assertEquals(0, diskGraph.getNode(t.fingerPrint(), 0).succSize()); + assertEquals(0, diskGraph.getNode(t.fingerPrint(), 1).succSize()); + assertEquals(0, diskGraph.getNode(t.fingerPrint(), 2).succSize()); + + assertEquals(1, diskGraph.getNode(u.fingerPrint(), 0).succSize()); + assertEquals(0, diskGraph.getNode(u.fingerPrint(), 1).succSize()); + assertEquals(0, diskGraph.getNode(u.fingerPrint(), 2).succSize()); + + Assert.fail("finish incomplete test! Assertions below are partially bogus."); + + //TODO GN are equals in terms of <<state, tidx>> but not necessarily transitions + assertEquals(diskGraph.getNode(s1.fingerPrint(), 0), diskGraph.getNode(s.fingerPrint(), 0)); + assertEquals(diskGraph.getNode(s1.fingerPrint(), 1), diskGraph.getNode(s.fingerPrint(), 1)); + assertEquals(diskGraph.getNode(s1.fingerPrint(), 2), diskGraph.getNode(s.fingerPrint(), 2)); + + // s1 and s are represented by the same GraphNode + final GraphNode node200_0 = diskGraph.getNode(s.fingerPrint(), 0); + assertEquals(2, node200_0.succSize()); + final Set<Transition> transitions200_0 = node200_0.getTransition(); + assertTrue(transitions200_0.contains(new Transition(t.fingerPrint(), 0, new BitVector(0)))); + assertTrue(transitions200_0.contains(new Transition(t.fingerPrint(), 1, new BitVector(0)))); + + final GraphNode node200_1 = diskGraph.getNode(s.fingerPrint(), 1); + assertEquals(2, node200_1.succSize()); + final Set<Transition> transitions200_1 = node200_1.getTransition(); + assertTrue(transitions200_1.contains(new Transition(t.fingerPrint(), 1, new BitVector(0)))); + assertTrue(transitions200_1.contains(new Transition(t.fingerPrint(), 2, new BitVector(0)))); + + final GraphNode node200_2 = diskGraph.getNode(s.fingerPrint(), 2); + assertEquals(0, node200_2.succSize()); + + assertTrue(diskGraph.checkInvariants(0, 0)); + } + + /** + * @param s The smallest state under symmetry + * @param sSymmetric A symmetric state to s + */ + private ILiveCheck getLiveCheckWithTwoNodeTableauSymmetry(final TLCState s, final TLCState sSymmetric, final TLCState t) throws IOException { + final TBGraphNode node2 = EasyMock.createMock(TBGraphNode.class); + // consistency + final Capture<TLCState> capture = new Capture<TLCState>(); + EasyMock.expect(node2.isConsistent(EasyMock.capture(capture), (ITool) EasyMock.anyObject())).andAnswer(new IAnswer<Boolean>() { + public Boolean answer() throws Throwable { + final TLCState value = capture.getValue(); + if (value == s) { + return false; + } + return true; + } + }).anyTimes(); + // index + EasyMock.expect(node2.getIndex()).andReturn(2).anyTimes(); + // nextSize + EasyMock.expect(node2.nextSize()).andReturn(1).anyTimes(); + // nextAt + EasyMock.expect(node2.nextAt(0)).andReturn(node2).anyTimes(); + EasyMock.replay(node2); + + final TBGraphNode node1 = EasyMock.createMock(TBGraphNode.class); + // consistency + final Capture<TLCState> capture1 = new Capture<TLCState>(); + EasyMock.expect(node1.isConsistent(EasyMock.capture(capture1), (ITool) EasyMock.anyObject())).andAnswer(new IAnswer<Boolean>() { + public Boolean answer() throws Throwable { + final TLCState value = capture1.getValue(); + if (value == sSymmetric) { + return false; + } + return true; + } + }).anyTimes(); + // index + EasyMock.expect(node1.getIndex()).andReturn(1).anyTimes(); + // nextSize + EasyMock.expect(node1.nextSize()).andReturn(2).anyTimes(); + // nextAt + EasyMock.expect(node1.nextAt(0)).andReturn(node1).anyTimes(); + EasyMock.expect(node1.nextAt(1)).andReturn(node2).anyTimes(); + EasyMock.replay(node1); + + final TBGraphNode node0 = EasyMock.createMock(TBGraphNode.class); + // consistency (simpler to node1 and node2) + EasyMock.expect(node0.isConsistent((TLCState) EasyMock.anyObject(), (ITool) EasyMock.anyObject())).andReturn(true).anyTimes(); + // index + EasyMock.expect(node0.getIndex()).andReturn(0).anyTimes(); + // nextSize + EasyMock.expect(node0.nextSize()).andReturn(2).anyTimes(); + // nextAt + EasyMock.expect(node0.nextAt(0)).andReturn(node0).anyTimes(); + EasyMock.expect(node0.nextAt(1)).andReturn(node1).anyTimes(); + EasyMock.replay(node0); + + final TBGraph tbGraph = new TBGraph(); + tbGraph.addElement(node0); + tbGraph.addElement(node1); + tbGraph.addElement(node2); + tbGraph.setInitCnt(1); + + // Configure OOS mock to react to the subsequent invocation. This is a + // essentially the list of calls being made on OOS during + // LiveCheck#addInitState and LiveCheck#addNextState + final OrderOfSolution oos = EasyMock.createNiceMock(OrderOfSolution.class); + EasyMock.expect(oos.hasTableau()).andReturn(true); + EasyMock.expect(oos.getTableau()).andReturn(tbGraph).anyTimes(); + EasyMock.expect(oos.getCheckAction()).andReturn(new LiveExprNode[0]).anyTimes(); + EasyMock.expect(oos.getCheckState()).andReturn(new LiveExprNode[0]).anyTimes(); + EasyMock.expect(oos.checkState(null, (TLCState) EasyMock.anyObject())).andReturn(new boolean[0]).anyTimes(); + EasyMock.replay(oos); + + final ITool tool = EasyMock.createNiceMock(ITool.class); + EasyMock.expect(tool.hasSymmetry()).andReturn(true); + final Capture<TLCState> nextStates = new Capture<TLCState>(); + EasyMock.expect(tool.getNextStates((Action) EasyMock.anyObject(), EasyMock.capture(nextStates))).andAnswer(new IAnswer<StateVec>() { + public StateVec answer() throws Throwable { + final StateVec nss = new StateVec(0); + // s > t for sSymmetric + TLCState state = nextStates.getValue(); + if (state == sSymmetric) { + nss.addElement(t); + } + return nss; + } + }); + EasyMock.expect(tool.isInModel((TLCState) EasyMock.anyObject())).andReturn(true).anyTimes(); + EasyMock.expect(tool.isInActions((TLCState) EasyMock.anyObject(), (TLCState) EasyMock.anyObject())).andReturn(true).anyTimes(); + EasyMock.replay(tool); + return new LiveCheck(tool, + new OrderOfSolution[] { oos }, "states", new DummyBucketStatistics(), null); + } +} diff --git a/tlatools/test/tlc2/tool/liveness/SymmetryTableauModelCheckerTest.java b/tlatools/test/tlc2/tool/liveness/SymmetryTableauModelCheckerTest.java new file mode 100644 index 0000000000000000000000000000000000000000..286fa498606ad4b5ed650ae382a5a43de291ecf0 --- /dev/null +++ b/tlatools/test/tlc2/tool/liveness/SymmetryTableauModelCheckerTest.java @@ -0,0 +1,68 @@ +/******************************************************************************* + * Copyright (c) 2015 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ + +package tlc2.tool.liveness; + +import static org.junit.Assert.assertTrue; + +import org.junit.Ignore; +import org.junit.Test; + +import tlc2.output.EC; + +public class SymmetryTableauModelCheckerTest extends ModelCheckerTestCase { + + public SymmetryTableauModelCheckerTest() { + super("SymmetryLivenessTableauMC", "symmetry"); + } + + @Test + @Ignore("Ignored for as long as symmetry is incorrectly handled by TLC with liveness checking.") + public void testSpec() { + // ModelChecker intends to check liveness + assertTrue(recorder.recordedWithStringValues(EC.TLC_LIVE_IMPLIED, "2")); + assertTrue(recorder.recordedWithStringValues(EC.TLC_INIT_GENERATED2, "8", "s", "2")); + + // ModelChecker has finished and generated the expected amount of states + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "779", "168", "0")); + + // Assert it has found a temporal violation and a counter example + assertTrue(recorder.recorded(EC.TLC_TEMPORAL_PROPERTY_VIOLATED)); + assertTrue(recorder.recorded(EC.TLC_COUNTER_EXAMPLE)); + + // The spec's 'NoVal' value is what violates symmetry. + assertTrue(recorder.recordedWithStringValue(EC.GENERAL, + "TLC threw an unexpected exception.\n" + + "This was probably caused by an error in the spec or model.\n" + + "The error occurred when TLC was checking liveness.\n" + + "The exception was a tlc2.tool.EvalException\n" + + ": Failed to recover the next state from its fingerprint during\n" + + "liveness error trace re-construction. This indicates that the\n" + + "spec is in fact not symmetric (Please report a TLC bug if the\n" + + "spec is known to be symmetric).")); + } +} diff --git a/tlatools/test/tlc2/tool/liveness/TableauDiskGraphTest.java b/tlatools/test/tlc2/tool/liveness/TableauDiskGraphTest.java index 9eaa09015f05e1e71f0aafb6573c3f3401cf861d..ac0cb958289c0c260e94c75f76868b84de3a3737 100644 --- a/tlatools/test/tlc2/tool/liveness/TableauDiskGraphTest.java +++ b/tlatools/test/tlc2/tool/liveness/TableauDiskGraphTest.java @@ -26,16 +26,23 @@ package tlc2.tool.liveness; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + import java.io.IOException; +import org.junit.Test; + import tlc2.util.BitVector; import tlc2.util.LongVec; -import tlc2.util.statistics.BucketStatistics; +import tlc2.util.statistics.FixedSizedBucketStatistics; import tlc2.util.statistics.IBucketStatistics; public class TableauDiskGraphTest extends DiskGraphTest { - private static final IBucketStatistics GRAPH_STATS = new BucketStatistics("Test Dummy", 16); + private static final IBucketStatistics GRAPH_STATS = new FixedSizedBucketStatistics("Test Dummy", 16); private static final int NUMBER_OF_SOLUTIONS = 1; private static final int NUMBER_OF_ACTIONS = 0; private static final BitVector NO_ACTIONS = null; @@ -70,6 +77,7 @@ public class TableauDiskGraphTest extends DiskGraphTest { * +------+ * */ + @Test public void testGetShortestPath() throws IOException { final AbstractDiskGraph dg = getDiskGraph(); @@ -157,6 +165,7 @@ public class TableauDiskGraphTest extends DiskGraphTest { * * (Drawn with http://asciiflow.com/) */ + @Test public void testUnifyingNodeInPath() throws IOException { final AbstractDiskGraph dg = getDiskGraph(); @@ -243,6 +252,7 @@ public class TableauDiskGraphTest extends DiskGraphTest { * | | * +---------+ */ + @Test public void testUnifyingNodeShortestPath() throws IOException { final AbstractDiskGraph dg = getDiskGraph(); @@ -332,8 +342,9 @@ public class TableauDiskGraphTest extends DiskGraphTest { * * The specialty here is that there are *two* init nodes and one them has *no* successors. * - * @see https://bugzilla.tlaplus.net/show_bug.cgi?id=293 + * @see Bug #293 in general/bugzilla/index.html */ + @Test public void testPathWithTwoInitNodesWithTableau() throws IOException { final AbstractDiskGraph dg = getDiskGraph(); @@ -382,6 +393,7 @@ public class TableauDiskGraphTest extends DiskGraphTest { * Test that getPath returns the correct init state if the graph contains * multiple initial states with the same fingerprint but different tableau idxs. */ + @Test public void testGetPathWithTwoInits() throws IOException { final AbstractDiskGraph dg = getDiskGraph(); @@ -414,6 +426,7 @@ public class TableauDiskGraphTest extends DiskGraphTest { /* * Test how an initial node transitions through the done state */ + @Test public void testNodeSetDone() throws IOException { final TableauDiskGraph dg = (TableauDiskGraph) getDiskGraph(); final long fingerprint = 1L; @@ -436,6 +449,7 @@ public class TableauDiskGraphTest extends DiskGraphTest { * Tests a path where both states have an identical fingerprint and only * differ in the tableau idx. */ + @Test public void testGetPathWithTwoNodesWithSameFingerprint() throws IOException { final AbstractDiskGraph dg = getDiskGraph(); @@ -465,6 +479,7 @@ public class TableauDiskGraphTest extends DiskGraphTest { /* * Test that it is possible to "update" a GraphNode's outgoing transitions. */ + @Test public void testLookupExistingNodeWithTidx() throws IOException { final TableauDiskGraph dg = (TableauDiskGraph) getDiskGraph(); diff --git a/tlatools/test/tlc2/tool/liveness/TableauNodePtrTableTest.java b/tlatools/test/tlc2/tool/liveness/TableauNodePtrTableTest.java index 6c9dfcfabf00fe687ef6147880554e9ac9de9145..c2c1c300a8171085df5c6036443b4a7a6d30dffa 100644 --- a/tlatools/test/tlc2/tool/liveness/TableauNodePtrTableTest.java +++ b/tlatools/test/tlc2/tool/liveness/TableauNodePtrTableTest.java @@ -26,12 +26,17 @@ package tlc2.tool.liveness; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + import java.util.Arrays; -import junit.framework.TestCase; +import org.junit.Test; -public class TableauNodePtrTableTest extends TestCase { +public class TableauNodePtrTableTest { + @Test public void testSetDone() { final TableauNodePtrTable tbl = new TableauNodePtrTable(0); // init with 0 so that grow is tested @@ -56,6 +61,7 @@ public class TableauNodePtrTableTest extends TestCase { } // Test various methods which apparently all yield pretty much the same result + @Test public void testRedundantMethodYieldSameResult() { final TableauNodePtrTable tbl = new TableauNodePtrTable(0); // init with 0 so that grow is tested diff --git a/tlatools/test/tlc2/tool/liveness/TableauSymmetryTest.java b/tlatools/test/tlc2/tool/liveness/TableauSymmetryTest.java new file mode 100644 index 0000000000000000000000000000000000000000..697e7aef2110aae13024b0584056dd95e1318f99 --- /dev/null +++ b/tlatools/test/tlc2/tool/liveness/TableauSymmetryTest.java @@ -0,0 +1,73 @@ +/******************************************************************************* + * Copyright (c) 2015 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ + +package tlc2.tool.liveness; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.Ignore; +import org.junit.Test; + +import tlc2.output.EC; + +public class TableauSymmetryTest extends ModelCheckerTestCase { + + public TableauSymmetryTest() { + super("TableauSymmetryMC", "symmetry"); + } + + @Test + @Ignore("Ignored for as long as symmetry is incorrectly handled by TLC with liveness checking.") + public void testSpec() { + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "13", "6", "0")); + assertFalse(recorder.recorded(EC.GENERAL)); + + // Assert it has found the temporal violation and also a counter example + assertTrue(recorder.recorded(EC.TLC_TEMPORAL_PROPERTY_VIOLATED)); + assertTrue(recorder.recorded(EC.TLC_COUNTER_EXAMPLE)); + + assertNodeAndPtrSizes(624L, 224L); + + // Assert the error trace + assertTrue(recorder.recorded(EC.TLC_STATE_PRINT2)); + final List<String> expectedTrace = new ArrayList<String>(7); + // Trace prefix + expectedTrace.add("arr = (a :> \"ready\" @@ b :> \"ready\")"); + expectedTrace.add("arr = (a :> \"busy\" @@ b :> \"ready\")"); + expectedTrace.add("arr = (a :> \"done\" @@ b :> \"ready\")"); + expectedTrace.add("arr = (a :> \"done\" @@ b :> \"busy\")"); + expectedTrace.add("arr = (a :> \"done\" @@ b :> \"done\")"); + expectedTrace.add("arr = (a :> \"done\" @@ b :> \"ready\")"); + assertTraceWith(recorder.getRecords(EC.TLC_STATE_PRINT2), expectedTrace); + + assertBackToState(4, "<Action line 7, col 13 to line 8, col 47 of module TableauSymmetry>"); + } +} diff --git a/tlatools/test/tlc2/tool/liveness/Test3.java b/tlatools/test/tlc2/tool/liveness/Test3.java index 6067d34831692d2a3d27809834b009fd105e2780..d87879af307516db66a7b62ae037b2b2e33258f1 100644 --- a/tlatools/test/tlc2/tool/liveness/Test3.java +++ b/tlatools/test/tlc2/tool/liveness/Test3.java @@ -26,21 +26,29 @@ package tlc2.tool.liveness; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.util.ArrayList; import java.util.List; +import org.junit.Test; + import tlc2.output.EC; -import tlc2.tool.TLCStateInfo; +import tlc2.output.EC.ExitStatus; public class Test3 extends ModelCheckerTestCase { public Test3() { - super("Test3"); + super("Test3", ExitStatus.VIOLATION_LIVENESS); } + @Test public void testSpec() { // ModelChecker has finished and generated the expected amount of states assertTrue(recorder.recorded(EC.TLC_FINISHED)); - assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "5", "3")); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "5", "3", "0")); + assertFalse(recorder.recorded(EC.GENERAL)); // Assert it has found the temporal violation and also a counter example assertTrue(recorder.recorded(EC.TLC_TEMPORAL_PROPERTY_VIOLATED)); @@ -48,33 +56,15 @@ public class Test3 extends ModelCheckerTestCase { // Assert the error trace assertTrue(recorder.recorded(EC.TLC_STATE_PRINT2)); - List<Object> records = recorder.getRecords(EC.TLC_STATE_PRINT2); - - int i = 0; // State's position in records - Object[] objs = (Object[]) records.get(i++); - TLCStateInfo stateInfo = (TLCStateInfo) objs[0]; - assertEquals("x = 0", stateInfo.toString().trim()); // trimmed to remove any newlines or whitespace - assertEquals(i, objs[1]); - - objs = (Object[]) records.get(i++); - stateInfo = (TLCStateInfo) objs[0]; - assertEquals("x = 1", stateInfo.toString().trim()); - assertEquals(i, objs[1]); + final List<String> expectedTrace = new ArrayList<String>(4); + expectedTrace.add("x = 0"); + expectedTrace.add("x = 1"); + expectedTrace.add("x = 0"); + expectedTrace.add("x = 2"); + assertTraceWith(recorder.getRecords(EC.TLC_STATE_PRINT2), expectedTrace); - objs = (Object[]) records.get(i++); - stateInfo = (TLCStateInfo) objs[0]; - assertEquals("x = 0", stateInfo.toString().trim()); - assertEquals(i, objs[1]); - - objs = (Object[]) records.get(i++); - stateInfo = (TLCStateInfo) objs[0]; - assertEquals("x = 2", stateInfo.toString().trim()); - assertEquals(i, objs[1]); - - // Assert the error trace ends with "Back to state 1" - assertTrue(recorder.recorded(EC.TLC_BACK_TO_STATE)); - records = recorder.getRecords(EC.TLC_BACK_TO_STATE); - objs = (Object[]) records.get(0); - assertEquals("1", objs[0]); + assertBackToState(1); + + assertZeroUncovered(); } } diff --git a/tlatools/test/tlc2/tool/liveness/TwoPhaseCommitTest.java b/tlatools/test/tlc2/tool/liveness/TwoPhaseCommitTest.java new file mode 100644 index 0000000000000000000000000000000000000000..4ce85eba6de9539e5aa5c3b822aef521895abfc5 --- /dev/null +++ b/tlatools/test/tlc2/tool/liveness/TwoPhaseCommitTest.java @@ -0,0 +1,49 @@ +/******************************************************************************* + * Copyright (c) 2016 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.liveness; + +import static org.junit.Assert.assertFalse; + +import java.io.File; + +import org.junit.Test; + +import tlc2.output.EC; +import util.TLAConstants; + +public class TwoPhaseCommitTest extends ModelCheckerTestCase { + + public TwoPhaseCommitTest() { + super(TLAConstants.Files.MODEL_CHECK_FILE_BASENAME, "symmetry" + File.separator + "TwoPhaseCommit"); + } + + @Test + public void testSpec() { + assertFalse(recorder.recorded(EC.GENERAL)); + + assertZeroUncovered(); + } +} diff --git a/tlatools/test/tlc2/tool/liveness/UnsymmetricModelCheckerTestA.java b/tlatools/test/tlc2/tool/liveness/UnsymmetricModelCheckerTestA.java new file mode 100644 index 0000000000000000000000000000000000000000..a469e74413a17fb5d5873d06ab9b9dbf43982a69 --- /dev/null +++ b/tlatools/test/tlc2/tool/liveness/UnsymmetricModelCheckerTestA.java @@ -0,0 +1,71 @@ +/******************************************************************************* + * Copyright (c) 2015 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ + +package tlc2.tool.liveness; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.Test; + +import tlc2.output.EC; +import tlc2.output.EC.ExitStatus; + +public class UnsymmetricModelCheckerTestA extends ModelCheckerTestCase { + + public UnsymmetricModelCheckerTestA() { + super("UnsymmetricMCA", "symmetry", ExitStatus.VIOLATION_LIVENESS); + } + + @Test + public void testSpec() { + // ModelChecker intends to check liveness + assertTrue(recorder.recordedWithStringValues(EC.TLC_LIVE_IMPLIED, "1")); + assertTrue(recorder.recordedWithStringValues(EC.TLC_INIT_GENERATED2, "2", "s", "1")); + + // ModelChecker has finished and generated the expected amount of states + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertFalse(recorder.recorded(EC.GENERAL)); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "5", "2", "0")); + + // Assert it has found a temporal violation and a counter example + assertTrue(recorder.recorded(EC.TLC_TEMPORAL_PROPERTY_VIOLATED)); + assertTrue(recorder.recorded(EC.TLC_COUNTER_EXAMPLE)); + + assertTrue(recorder.recorded(EC.TLC_STATE_PRINT2)); + final List<String> expectedTrace = new ArrayList<String>(2); + expectedTrace.add("x = a"); + expectedTrace.add("x = 1"); + assertTraceWith(recorder.getRecords(EC.TLC_STATE_PRINT2), expectedTrace); + + assertBackToState(1); + + assertUncovered("line 19, col 31 to line 19, col 36 of module Unsymmetric: 0"); + } +} diff --git a/tlatools/test/tlc2/tool/liveness/UnsymmetricModelCheckerTestB.java b/tlatools/test/tlc2/tool/liveness/UnsymmetricModelCheckerTestB.java new file mode 100644 index 0000000000000000000000000000000000000000..1012a8feb1de618e944903d26bc6536c95ecbc13 --- /dev/null +++ b/tlatools/test/tlc2/tool/liveness/UnsymmetricModelCheckerTestB.java @@ -0,0 +1,58 @@ +/******************************************************************************* + * Copyright (c) 2015 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ + +package tlc2.tool.liveness; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.output.EC; + +public class UnsymmetricModelCheckerTestB extends ModelCheckerTestCase { + + public UnsymmetricModelCheckerTestB() { + super("UnsymmetricMCB", "symmetry"); + } + + @Test + public void testSpec() { + // ModelChecker intends to check liveness + assertTrue(recorder.recordedWithStringValues(EC.TLC_LIVE_IMPLIED, "1")); + assertTrue(recorder.recordedWithStringValues(EC.TLC_INIT_GENERATED2, "2", "s", "1")); + + // ModelChecker has finished and generated the expected amount of states + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertFalse(recorder.recorded(EC.GENERAL)); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "5", "2", "0")); + + // Contrary to UMCTA, B doesn't find a counter-example. This is due to + // the CHOOSE on S and the selected initial state. + + assertUncovered("line 37, col 31 to line 37, col 36 of module Unsymmetric: 0"); + } +} diff --git a/tlatools/test/tlc2/tool/liveness/simulation/AbstractExampleTestCase.java b/tlatools/test/tlc2/tool/liveness/simulation/AbstractExampleTestCase.java index 4d8398abe178ab98538a93540f6592fe644ec02f..192c88e8a186bad2ce5b5ccc4648f99dd63633bd 100644 --- a/tlatools/test/tlc2/tool/liveness/simulation/AbstractExampleTestCase.java +++ b/tlatools/test/tlc2/tool/liveness/simulation/AbstractExampleTestCase.java @@ -26,9 +26,16 @@ package tlc2.tool.liveness.simulation; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + import java.util.List; +import org.junit.Test; + import tlc2.output.EC; +import tlc2.output.EC.ExitStatus; import tlc2.tool.TLCStateInfo; import tlc2.tool.liveness.ModelCheckerTestCase; @@ -37,13 +44,15 @@ public abstract class AbstractExampleTestCase extends ModelCheckerTestCase { public AbstractExampleTestCase(final String cfg) { // Checks the depth parameter too. Depth <= 100 will cause simluation to // go on forever. - super(cfg, "simulation", new String[] {"-simulate", "-depth", "11"}); + super(cfg, "simulation", new String[] { "-simulate", "-depth", "11" }, ExitStatus.VIOLATION_LIVENESS); } + @Test public void testSpec() { // ModelChecker has finished and generated the expected amount of states assertTrue(recorder.recorded(EC.TLC_FINISHED)); assertTrue(recorder.recordedWithStringValue(EC.TLC_STATS_SIMU, "12")); + assertFalse(recorder.recorded(EC.GENERAL)); // Assert it has found the temporal violation and also a counter example assertTrue(recorder.recorded(EC.TLC_TEMPORAL_PROPERTY_VIOLATED)); diff --git a/tlatools/test/tlc2/tool/liveness/simulation/LiveCheckSimulationTest2.java b/tlatools/test/tlc2/tool/liveness/simulation/LiveCheckSimulationTest2.java index 55fc30dde773c7b7d7eebf5a090f35ef03f07b80..a2ee64447c80ac68c598b663bc82f30e9bb4439c 100644 --- a/tlatools/test/tlc2/tool/liveness/simulation/LiveCheckSimulationTest2.java +++ b/tlatools/test/tlc2/tool/liveness/simulation/LiveCheckSimulationTest2.java @@ -37,6 +37,6 @@ public class LiveCheckSimulationTest2 extends SuccessfulSimulationTestCase { public LiveCheckSimulationTest2() { super("Test2", "/", new String[] {"-simulate", "-depth", "10"}); - TLC.traceNum = 50; + TLC.setTraceNum(50); } } diff --git a/tlatools/test/tlc2/tool/liveness/simulation/LiveCheckSimulationTest2a.java b/tlatools/test/tlc2/tool/liveness/simulation/LiveCheckSimulationTest2a.java index c18e2b3b3d72f78cc7f1c6a9f8301eb868b0a575..26413ee3bc93b791f59f6ef0589d40c21f42c0f0 100644 --- a/tlatools/test/tlc2/tool/liveness/simulation/LiveCheckSimulationTest2a.java +++ b/tlatools/test/tlc2/tool/liveness/simulation/LiveCheckSimulationTest2a.java @@ -26,10 +26,17 @@ package tlc2.tool.liveness.simulation; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + import java.util.List; +import org.junit.Test; + import tlc2.TLC; import tlc2.output.EC; +import tlc2.output.EC.ExitStatus; import tlc2.tool.Simulator; import tlc2.tool.TLCStateInfo; import tlc2.tool.liveness.ModelCheckerTestCase; @@ -44,16 +51,18 @@ public class LiveCheckSimulationTest2a extends ModelCheckerTestCase { } public LiveCheckSimulationTest2a() { - super("Test2a", "/", new String[] {"-simulate", "-depth", "10"}); + super("Test2a", "/", new String[] {"-simulate", "-depth", "10"}, ExitStatus.VIOLATION_LIVENESS); // Stop after 100 traces due to lack of general timeout regardless of outcome - TLC.traceNum = 100; + TLC.setTraceNum(100); } + @Test public void testSpec() { // ModelChecker has finished and generated the expected amount of states assertTrue(recorder.recorded(EC.TLC_FINISHED)); assertTrue(recorder.recorded(EC.TLC_STATS_SIMU)); + assertFalse(recorder.recorded(EC.GENERAL)); // Assert it has found the temporal violation and also a counter example assertTrue(recorder.recorded(EC.TLC_TEMPORAL_PROPERTY_VIOLATED)); diff --git a/tlatools/test/tlc2/tool/liveness/simulation/LiveCheckTest.java b/tlatools/test/tlc2/tool/liveness/simulation/LiveCheckTest.java index 9c353715ef591a824bacfc9fcf603cb9baaf6df1..b090a8f57ee02036357c58d966134a5ee12c7d22 100644 --- a/tlatools/test/tlc2/tool/liveness/simulation/LiveCheckTest.java +++ b/tlatools/test/tlc2/tool/liveness/simulation/LiveCheckTest.java @@ -26,16 +26,17 @@ package tlc2.tool.liveness.simulation; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + import java.io.IOException; -import java.util.Enumeration; import org.easymock.EasyMock; +import org.junit.Before; +import org.junit.Test; -import junit.framework.TestCase; -import tlc2.tool.Action; -import tlc2.tool.StateVec; +import tlc2.tool.ITool; import tlc2.tool.TLCState; -import tlc2.tool.Tool; import tlc2.tool.liveness.AbstractDiskGraph; import tlc2.tool.liveness.ILiveCheck; import tlc2.tool.liveness.LiveCheck; @@ -44,10 +45,17 @@ import tlc2.tool.liveness.OrderOfSolution; import tlc2.tool.liveness.TBGraph; import tlc2.tool.liveness.TBGraphNode; import tlc2.tool.queue.DummyTLCState; -import tlc2.util.LongVec; +import tlc2.util.SetOfStates; import tlc2.util.statistics.DummyBucketStatistics; -public class LiveCheckTest extends TestCase { +public class LiveCheckTest { + + private ITool tool; + + @Before + public void createTool() { + this.tool = EasyMock.createNiceMock(ITool.class); + } // Add identical state twice, which can happen in simulation mode when the // trace is: <<100L, 200L, ..., 100L, 200L>> (as in a cycle that is @@ -56,10 +64,12 @@ public class LiveCheckTest extends TestCase { // change. // Note that adding/updating the state would result in a larger // on-disk file, but doesn't seem to invalidate the simulation validity. + @Test public void testAddIdenticalNodeTwiceNoTableau() throws IOException { addIdenticalNodeTwice(false, -1); } + @Test public void testAddIdenticalNodeTwiceWithTableau() throws IOException { addIdenticalNodeTwice(true, 0); } @@ -77,61 +87,49 @@ public class LiveCheckTest extends TestCase { assertNotNull(diskGraph); final TLCState state = new DummyTLCState(); - liveCheck.addInitState(state, 100L); + liveCheck.addInitState(tool, state, 100L); - final StateVec stateVec = new StateVec(1); - stateVec.addElement(new DummyTLCState()); - - final LongVec longVec = new LongVec(1); - longVec.addElement(200L); + final SetOfStates setOfStates = new SetOfStates(1); + setOfStates.put(200L, new DummyTLCState(200L)); // Add state 100L the first time, then add its successor - liveCheck.addNextState(state, 100L, stateVec, longVec); - liveCheck.addNextState(state, 200L, stateVec, longVec); + liveCheck.addNextState(tool, state, 100L, setOfStates); + liveCheck.addNextState(tool, state, 200L, setOfStates); assertEquals(0, diskGraph.getPtr(100L, tableauId)); // Add state 100L again and check that it does *not* // end up in disk graph. - liveCheck.addNextState(state, 100L, stateVec, longVec); + liveCheck.addNextState(tool, state, 100L, setOfStates); assertEquals(0, diskGraph.getPtr(100L, tableauId)); } private ILiveCheck getLiveCheck() throws IOException { + final ITool tool = EasyMock.createNiceMock(ITool.class); // Configure OOS mock to react to the subsequent invocation. This is // essentially the list of calls being made on OOS during // LiveCheck#addInitState and LiveCheck#addNextState final OrderOfSolution oos = EasyMock.createNiceMock(OrderOfSolution.class); EasyMock.expect(oos.hasTableau()).andReturn(false); EasyMock.expect(oos.getCheckAction()).andReturn(new LiveExprNode[0]).anyTimes(); - EasyMock.expect(oos.checkState((TLCState) EasyMock.anyObject())).andReturn(new boolean[0]).anyTimes(); + EasyMock.expect(oos.checkState((ITool) EasyMock.anyObject(), (TLCState) EasyMock.anyObject())).andReturn(new boolean[0]).anyTimes(); EasyMock.replay(oos); - return new LiveCheck(EasyMock.createNiceMock(Tool.class), new Action[0], + return new LiveCheck(tool, new OrderOfSolution[] { oos }, System.getProperty("java.io.tmpdir"), new DummyBucketStatistics()); } - + private ILiveCheck getLiveCheckWithTableau() throws IOException { final TBGraphNode node = EasyMock.createMock(TBGraphNode.class); - EasyMock.expect(node.isConsistent((TLCState) EasyMock.anyObject(), (Tool) EasyMock.anyObject())).andReturn(true) + EasyMock.expect(node.isConsistent((TLCState) EasyMock.anyObject(), (ITool) EasyMock.anyObject())).andReturn(true) .anyTimes(); EasyMock.expect(node.nextSize()).andReturn(1).anyTimes(); EasyMock.expect(node.nextAt(0)).andReturn(node).anyTimes(); + EasyMock.expect(node.getIndex()).andReturn(0).anyTimes(); EasyMock.replay(node); - final TBGraph tbGraph = EasyMock.createNiceMock(TBGraph.class); - EasyMock.expect(tbGraph.elements()).andReturn(new Enumeration<TBGraphNode>() { - boolean has = true; - public boolean hasMoreElements() { - return has; - } - public TBGraphNode nextElement() { - has = false; - return node; - } - }).anyTimes(); - EasyMock.expect(tbGraph.getInitCnt()).andReturn(1).anyTimes(); - EasyMock.expect(tbGraph.getNode(EasyMock.anyInt())).andReturn(node).anyTimes(); - EasyMock.replay(tbGraph); + final TBGraph tbGraph = new TBGraph(); + tbGraph.addElement(node); + tbGraph.setInitCnt(1); // Configure OOS mock to react to the subsequent invocation. This is a // essentially the list of calls being made on OOS during @@ -140,10 +138,10 @@ public class LiveCheckTest extends TestCase { EasyMock.expect(oos.hasTableau()).andReturn(true); EasyMock.expect(oos.getTableau()).andReturn(tbGraph).anyTimes(); EasyMock.expect(oos.getCheckAction()).andReturn(new LiveExprNode[0]).anyTimes(); - EasyMock.expect(oos.checkState((TLCState) EasyMock.anyObject())).andReturn(new boolean[0]).anyTimes(); + EasyMock.expect(oos.checkState((ITool) EasyMock.anyObject(), (TLCState) EasyMock.anyObject())).andReturn(new boolean[0]).anyTimes(); EasyMock.replay(oos); - return new LiveCheck(EasyMock.createNiceMock(Tool.class), new Action[0], + return new LiveCheck(tool, new OrderOfSolution[] { oos }, System.getProperty("java.io.tmpdir"), new DummyBucketStatistics()); } } diff --git a/tlatools/test/tlc2/tool/liveness/simulation/SimulationTest2.java b/tlatools/test/tlc2/tool/liveness/simulation/SimulationTest2.java index bcd53fdd3ceb1a6aeca9b8010a2b29afc6527ab9..1cb5fffdf0ab1f0edf267dfdc6261bc2f33db371 100644 --- a/tlatools/test/tlc2/tool/liveness/simulation/SimulationTest2.java +++ b/tlatools/test/tlc2/tool/liveness/simulation/SimulationTest2.java @@ -32,6 +32,6 @@ public class SimulationTest2 extends SuccessfulSimulationTestCase { public SimulationTest2() { super("Test2", "/", new String[] {"-simulate", "-depth", "6"}); - TLC.traceNum = 50; + TLC.setTraceNum(50); } } diff --git a/tlatools/test/tlc2/tool/liveness/simulation/SimulationTest2a.java b/tlatools/test/tlc2/tool/liveness/simulation/SimulationTest2a.java index 22ddaa47664d0b012006718ff467d16d8a074b90..2fab67fdfef3c27cedd857c5dc40acbbc32578c8 100644 --- a/tlatools/test/tlc2/tool/liveness/simulation/SimulationTest2a.java +++ b/tlatools/test/tlc2/tool/liveness/simulation/SimulationTest2a.java @@ -26,9 +26,16 @@ package tlc2.tool.liveness.simulation; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + import java.util.List; +import org.junit.Test; + import tlc2.output.EC; +import tlc2.output.EC.ExitStatus; import tlc2.tool.TLCStateInfo; import tlc2.tool.liveness.ModelCheckerTestCase; @@ -38,13 +45,15 @@ import tlc2.tool.liveness.ModelCheckerTestCase; public class SimulationTest2a extends ModelCheckerTestCase { public SimulationTest2a() { - super("Test2a", "/", new String[] {"-simulate", "-depth", "6"}); + super("Test2a", "/", new String[] {"-simulate", "-depth", "6"}, ExitStatus.VIOLATION_LIVENESS); } + @Test public void testSpec() { // ModelChecker has finished and generated the expected amount of states assertTrue(recorder.recorded(EC.TLC_FINISHED)); assertTrue(recorder.recorded(EC.TLC_STATS_SIMU)); + assertFalse(recorder.recorded(EC.GENERAL)); // Assert it has found the temporal violation and also a counter example assertTrue(recorder.recorded(EC.TLC_TEMPORAL_PROPERTY_VIOLATED)); diff --git a/tlatools/test/tlc2/tool/liveness/simulation/SimulationTestAssumption.java b/tlatools/test/tlc2/tool/liveness/simulation/SimulationTestAssumption.java new file mode 100644 index 0000000000000000000000000000000000000000..5f2729afb2ce1be36dc18a3d71f4a5e392c38c45 --- /dev/null +++ b/tlatools/test/tlc2/tool/liveness/simulation/SimulationTestAssumption.java @@ -0,0 +1,46 @@ +/******************************************************************************* + * Copyright (c) 2020 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ + +package tlc2.tool.liveness.simulation; + +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.output.EC; + +public class SimulationTestAssumption extends SuccessfulSimulationTestCase { + + public SimulationTestAssumption() { + super("Test4", "/", new String[] { "-simulate", "num=1" }, EC.ExitStatus.VIOLATION_ASSUMPTION); + } + + @Test + public void testSpec() { + assertTrue(recorder.recorded(EC.TLC_ASSUMPTION_FALSE)); + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + } +} diff --git a/tlatools/test/tlc2/tool/liveness/simulation/StutteringTest.java b/tlatools/test/tlc2/tool/liveness/simulation/StutteringTest.java index 66d3c3b40b8d0abbdc0b53d91f8530bb94cc4e87..78577e5c81fec309767be51bf0c0b48539c2e41c 100644 --- a/tlatools/test/tlc2/tool/liveness/simulation/StutteringTest.java +++ b/tlatools/test/tlc2/tool/liveness/simulation/StutteringTest.java @@ -26,19 +26,28 @@ package tlc2.tool.liveness.simulation; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + import tlc2.output.EC; +import tlc2.output.EC.ExitStatus; import tlc2.tool.liveness.ModelCheckerTestCase; +import util.TLAConstants; public class StutteringTest extends ModelCheckerTestCase { public StutteringTest() { - super("MC", "CodePlexBug08", new String[] { "-simulate" }); + super(TLAConstants.Files.MODEL_CHECK_FILE_BASENAME, "CodePlexBug08", new String[] { "-simulate" }, ExitStatus.VIOLATION_LIVENESS); } + @Test public void testSpec() { // Simulation has finished and generated states assertTrue(recorder.recorded(EC.TLC_FINISHED)); assertTrue(recorder.recorded(EC.TLC_STATS_SIMU)); + assertFalse(recorder.recorded(EC.GENERAL)); // Assert it has found the temporal violation and also a counter example assertTrue(recorder.recorded(EC.TLC_TEMPORAL_PROPERTY_VIOLATED)); diff --git a/tlatools/test/tlc2/tool/liveness/simulation/SuccessfulSimulationTestCase.java b/tlatools/test/tlc2/tool/liveness/simulation/SuccessfulSimulationTestCase.java index 35e7b77443fe372d31b073554a885efb609a783d..ba1f69f9f0baf3785a86d9186d014f8fb1e9b352 100644 --- a/tlatools/test/tlc2/tool/liveness/simulation/SuccessfulSimulationTestCase.java +++ b/tlatools/test/tlc2/tool/liveness/simulation/SuccessfulSimulationTestCase.java @@ -26,6 +26,11 @@ package tlc2.tool.liveness.simulation; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + import tlc2.output.EC; import tlc2.tool.liveness.ModelCheckerTestCase; @@ -43,6 +48,11 @@ public abstract class SuccessfulSimulationTestCase extends ModelCheckerTestCase super(spec, path, extraArguments); } + public SuccessfulSimulationTestCase(String spec, String path, String[] extraArguments, int exitStatus) { + super(spec, path, extraArguments, exitStatus); + } + + @Test public void testSpec() { // Simulation must *NOT* show a counterexample. Regular model-checking // shows that the liveness property holds. @@ -53,6 +63,7 @@ public abstract class SuccessfulSimulationTestCase extends ModelCheckerTestCase // Finished... assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertFalse(recorder.recorded(EC.GENERAL)); // No temporal violation assertFalse(recorder.recorded(EC.TLC_TEMPORAL_PROPERTY_VIOLATED)); // No counterexample diff --git a/tlatools/test/tlc2/tool/other/CheckFP.java b/tlatools/test/tlc2/tool/other/CheckFP.java index ae8025227726e77ca5737874ce65324fbbb6f675..2ad6f3227304dda705998a3e13c35ec6d85ad1b5 100644 --- a/tlatools/test/tlc2/tool/other/CheckFP.java +++ b/tlatools/test/tlc2/tool/other/CheckFP.java @@ -19,7 +19,8 @@ import tlc2.util.BufferedRandomAccessFile; public class CheckFP { public static void main(String args[]) { try { - BufferedRandomAccessFile raf = new BufferedRandomAccessFile(args[0], "r"); + @SuppressWarnings("resource") + BufferedRandomAccessFile raf = new BufferedRandomAccessFile(args[0], "r"); long fileLen = raf.length(); long dis = Long.MAX_VALUE; int cnt = 0; diff --git a/tlatools/test/tlc2/tool/other/FileClassLoader.java b/tlatools/test/tlc2/tool/other/FileClassLoader.java index c65f4b08e9a1a470b84c1ce7c7ab824382e5563c..41f30d21a50e9fda82cd30daf01bfc152ef7a849 100644 --- a/tlatools/test/tlc2/tool/other/FileClassLoader.java +++ b/tlatools/test/tlc2/tool/other/FileClassLoader.java @@ -14,6 +14,7 @@ import java.io.IOException; * it seems to be a helper utility used during the development * @deprecated according to the paths it is not used (SZ February 19, 2009) */ +@SuppressWarnings({ "rawtypes", "unchecked" }) public class FileClassLoader extends ClassLoader { /* Load a class from a file. */ private String dir; diff --git a/tlatools/test/tlc2/tool/queue/DummyTLCState.java b/tlatools/test/tlc2/tool/queue/DummyTLCState.java index 7120cd6de9544f1bd653f9535087805fc1cea9ae..dbaedada12287e4da6a399cfef909e974239a3c9 100644 --- a/tlatools/test/tlc2/tool/queue/DummyTLCState.java +++ b/tlatools/test/tlc2/tool/queue/DummyTLCState.java @@ -1,31 +1,41 @@ package tlc2.tool.queue; -import tla2sany.semantic.SemanticNode; +import java.util.HashSet; +import java.util.Set; + +import tla2sany.semantic.OpDeclNode; import tla2sany.semantic.SymbolNode; import tlc2.tool.StateVec; import tlc2.tool.TLCState; -import tlc2.value.Value; +import tlc2.value.IValue; import util.UniqueString; @SuppressWarnings("serial") public class DummyTLCState extends TLCState { + private final long fp; + public DummyTLCState() { uid = 0; TLCState.Empty = this; + this.fp = 0L; } + public DummyTLCState(long fp) { + this.fp = fp; + } + /* (non-Javadoc) * @see tlc2.tool.TLCState#bind(util.UniqueString, tlc2.value.Value, tla2sany.semantic.SemanticNode) */ - public TLCState bind(UniqueString name, Value value, SemanticNode expr) { + public TLCState bind(UniqueString name, IValue value) { return null; } /* (non-Javadoc) * @see tlc2.tool.TLCState#bind(tla2sany.semantic.SymbolNode, tlc2.value.Value, tla2sany.semantic.SemanticNode) */ - public TLCState bind(SymbolNode id, Value value, SemanticNode expr) { + public TLCState bind(SymbolNode id, IValue value) { return null; } @@ -39,7 +49,7 @@ public class DummyTLCState extends TLCState { /* (non-Javadoc) * @see tlc2.tool.TLCState#lookup(util.UniqueString) */ - public Value lookup(UniqueString var) { + public IValue lookup(UniqueString var) { return null; } @@ -82,7 +92,7 @@ public class DummyTLCState extends TLCState { * @see tlc2.tool.TLCState#fingerPrint() */ public long fingerPrint() { - return 0; + return this.fp; } /* (non-Javadoc) @@ -92,6 +102,13 @@ public class DummyTLCState extends TLCState { return false; } + /* (non-Javadoc) + * @see tlc2.tool.TLCState#getUnassigned() + */ + public final Set<OpDeclNode> getUnassigned() { + return new HashSet<OpDeclNode>(); + } + /* (non-Javadoc) * @see tlc2.tool.TLCState#createEmpty() */ @@ -103,7 +120,7 @@ public class DummyTLCState extends TLCState { * @see tlc2.tool.TLCState#toString() */ public String toString() { - return "Dummy#" + uid; + return "Dummy#" + uid + ":" + fp; } /* (non-Javadoc) diff --git a/tlatools/test/tlc2/tool/queue/StateQueueTest.java b/tlatools/test/tlc2/tool/queue/StateQueueTest.java index f86316116e316ccf2a5c39d9b7026ae5bf437e64..855d34066ccafda706839c925f641a162cd47ef0 100644 --- a/tlatools/test/tlc2/tool/queue/StateQueueTest.java +++ b/tlatools/test/tlc2/tool/queue/StateQueueTest.java @@ -1,21 +1,29 @@ package tlc2.tool.queue; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import org.junit.Before; +import org.junit.Test; + import tlc2.tool.TLCState; -import junit.framework.TestCase; -public class StateQueueTest extends TestCase { +public class StateQueueTest { protected IStateQueue sQueue; /* (non-Javadoc) * @see junit.framework.TestCase#setUp() */ - protected void setUp() throws Exception { - super.setUp(); + @Before + public void setUp() throws Exception { sQueue = new MemStateQueue(""); } // add and remove a single state + @Test public void testEnqueue() { final TLCState expected = new DummyTLCState(); sQueue.enqueue(expected); @@ -24,18 +32,21 @@ public class StateQueueTest extends TestCase { } // dequeue from empty + @Test public void testsDequeueEmpty() { TLCState state = sQueue.sDequeue(); assertNull(state); } // dequeue from empty + @Test public void testDequeueEmpty() { TLCState state = sQueue.dequeue(); assertNull(state); } // dequeue from not empty + @Test public void testsDequeueNotEmpty() { DummyTLCState expected = new DummyTLCState(); sQueue.sEnqueue(expected); @@ -46,6 +57,7 @@ public class StateQueueTest extends TestCase { } // dequeue from not empty + @Test public void testDequeueNotEmpty() { DummyTLCState expected = new DummyTLCState(); sQueue.enqueue(expected); @@ -56,6 +68,7 @@ public class StateQueueTest extends TestCase { } // add 10 states and check size + @Test public void testEnqueueAddNotSame() { final int j = 10; for (int i = 0; i < j; i++) { @@ -65,6 +78,7 @@ public class StateQueueTest extends TestCase { } // add same states 10 times and check size + @Test public void testEnqueueAddSame() { final DummyTLCState state = new DummyTLCState(); final int j = 10; @@ -75,6 +89,7 @@ public class StateQueueTest extends TestCase { } // uncommon input with empty queue sDequeue + @Test public void testsDequeueAbuseEmpty() { expectRuntimeException(sQueue, 0); expectRuntimeException(sQueue, -1); @@ -84,6 +99,7 @@ public class StateQueueTest extends TestCase { // uncommon input with non-empty queue // unfortunately sDequeue behaves differently depending what's its internal state + @Test public void testsDequeueAbuseNonEmpty() { sQueue.sEnqueue(new DummyTLCState()); // make sure isAvail = true @@ -97,7 +113,7 @@ public class StateQueueTest extends TestCase { private void expectRuntimeException(IStateQueue aQueue, int size) { try { aQueue.sDequeue(size); - } catch(RuntimeException e) { + } catch(RuntimeException|AssertionError e) { return; } fail("expected to throw RuntimeException with <= input"); diff --git a/tlatools/test/tlc2/tool/simulation/NQSpecTest.java b/tlatools/test/tlc2/tool/simulation/NQSpecTest.java new file mode 100644 index 0000000000000000000000000000000000000000..6bd0830733ac9dcf07a6c996698f45e47585aae1 --- /dev/null +++ b/tlatools/test/tlc2/tool/simulation/NQSpecTest.java @@ -0,0 +1,54 @@ +/******************************************************************************* + * Copyright (c) 2015 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ + +package tlc2.tool.simulation; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.io.File; + +import org.junit.Test; + +import tlc2.TLC; +import tlc2.output.EC; +import tlc2.tool.liveness.ModelCheckerTestCase; +import util.TLAConstants; + +public class NQSpecTest extends ModelCheckerTestCase { + + public NQSpecTest() { + super(TLAConstants.Files.MODEL_CHECK_FILE_BASENAME, "simulation" + File.separator + "NQSpec", new String[] { "-simulate" }); + TLC.setTraceNum(100); + } + + @Test + public void testSpec() { + // Simulation has finished and generated states + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertFalse(recorder.recorded(EC.GENERAL)); + } +} diff --git a/tlatools/test/tlc2/tool/simulation/SimulationWorkerTest.java b/tlatools/test/tlc2/tool/simulation/SimulationWorkerTest.java new file mode 100644 index 0000000000000000000000000000000000000000..96afcecf19f228ad4f8d340deb3f506e6464fac8 --- /dev/null +++ b/tlatools/test/tlc2/tool/simulation/SimulationWorkerTest.java @@ -0,0 +1,430 @@ +package tlc2.tool.simulation; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.io.File; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.atomic.LongAdder; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import tlc2.TLCGlobals; +import tlc2.TestMPRecorder; +import tlc2.output.EC; +import tlc2.tool.CommonTestCase; +import tlc2.tool.ITool; +import tlc2.tool.SimulationWorker; +import tlc2.tool.SimulationWorker.SimulationWorkerError; +import tlc2.tool.SimulationWorker.SimulationWorkerResult; +import tlc2.tool.StateVec; +import tlc2.tool.TLCState; +import tlc2.tool.impl.FastTool; +import tlc2.tool.impl.Tool; +import tlc2.tool.liveness.ILiveCheck; +import tlc2.tool.liveness.NoOpLiveCheck; +import util.FileUtil; +import util.SimpleFilenameToStream; +import util.TLAConstants; +import util.ToolIO; +import util.UniqueString; + +/** + * Correctness tests for the SimulationWorker. + */ +public class SimulationWorkerTest extends CommonTestCase { + + public SimulationWorkerTest() { + super(new TestMPRecorder()); + } + + @Before + public void setUp() throws Exception{ + ToolIO.setUserDir(BASE_PATH + File.separator + "simulation" + File.separator + "BasicMultiTrace"); + } + + @After + public void tearDown() throws Exception{ + FileUtil.deleteDir(TLCGlobals.metaRoot, true); + } + + /** + * Return a value from a TLCState as a string. + */ + public String getStateVal(TLCState s, String name) { + UniqueString us = UniqueString.uniqueStringOf(name); + return s.getVals().get(us).toString(); + } + + @Test + public void testSuccessfulRun() throws Exception { + Tool tool = new FastTool("", "BasicMultiTrace", TLAConstants.Files.MODEL_CHECK_FILE_BASENAME, new SimpleFilenameToStream()); + + ILiveCheck liveCheck = new NoOpLiveCheck(tool, "BasicMultiTrace"); + StateVec initStates = tool.getInitStates(); + BlockingQueue<SimulationWorkerResult> resultQueue = new LinkedBlockingQueue<>(); + SimulationWorker worker = new SimulationWorker(0, tool, resultQueue, 0, 100, 1000, false, null, + liveCheck, new LongAdder(), new LongAdder()); + worker.start(initStates); + SimulationWorkerResult res = resultQueue.take(); + assertFalse(res.isError()); + worker.join(); + assertFalse(worker.isAlive()); + } + + @Test + public void testInvariantViolation() throws Exception { + Tool tool = new FastTool("", "BasicMultiTrace", "MCInv", new SimpleFilenameToStream()); + + ILiveCheck liveCheck = new NoOpLiveCheck(tool, "BasicMultiTrace"); + StateVec initStates = tool.getInitStates(); + BlockingQueue<SimulationWorkerResult> resultQueue = new LinkedBlockingQueue<>(); + int maxTraceNum = 3; + SimulationWorker worker = new SimulationWorker(0, tool, resultQueue, 0, 100, maxTraceNum, false, + null, liveCheck, new LongAdder(), new LongAdder()); + worker.start(initStates); + SimulationWorkerResult res = resultQueue.take(); + + assertTrue(res.isError()); + SimulationWorkerError err = res.error(); + assertEquals(EC.TLC_INVARIANT_VIOLATED_BEHAVIOR, err.errorCode); + assertEquals(3, err.stateTrace.size()); + + // Check the generated trace. + assertEquals("0", getStateVal(err.stateTrace.elementAt(0), "depth")); + assertEquals("0", getStateVal(err.stateTrace.elementAt(0), "branch")); + + assertEquals("0", getStateVal(err.stateTrace.elementAt(1), "depth")); + assertEquals("6", getStateVal(err.stateTrace.elementAt(1), "branch")); + + assertEquals("1", getStateVal(err.stateTrace.elementAt(2), "depth")); + assertEquals("6", getStateVal(err.stateTrace.elementAt(2), "branch")); + + assertEquals("2", getStateVal(err.state, "depth")); + assertEquals("6", getStateVal(err.state, "branch")); + + // The worker should continue to generate random traces even after an invariant violation, so we should be + // able to receive more results. The worker should generate 2 more results before hitting the maximum trace count. + // For the next traces generated, we check their contents to make sure that the worker is actually producing traces + // "randomly" i.e. not generating the same trace every time. + res = resultQueue.take(); + assertTrue(res.isError()); + err = res.error(); + assertEquals(EC.TLC_INVARIANT_VIOLATED_BEHAVIOR, err.errorCode); + assertEquals(3, err.stateTrace.size()); + + // Check the generated trace. + assertEquals("0", getStateVal(err.stateTrace.elementAt(0), "depth")); + assertEquals("0", getStateVal(err.stateTrace.elementAt(0), "branch")); + + assertEquals("0", getStateVal(err.stateTrace.elementAt(1), "depth")); + assertEquals("2", getStateVal(err.stateTrace.elementAt(1), "branch")); + + assertEquals("1", getStateVal(err.stateTrace.elementAt(2), "depth")); + assertEquals("2", getStateVal(err.stateTrace.elementAt(2), "branch")); + + res = resultQueue.take(); + assertTrue(res.isError()); + err = res.error(); + assertEquals(EC.TLC_INVARIANT_VIOLATED_BEHAVIOR, err.errorCode); + assertEquals(3, err.stateTrace.size()); + + // Check the generated trace. + assertEquals("0", getStateVal(err.stateTrace.elementAt(0), "depth")); + assertEquals("0", getStateVal(err.stateTrace.elementAt(0), "branch")); + + assertEquals("0", getStateVal(err.stateTrace.elementAt(1), "depth")); + assertEquals("5", getStateVal(err.stateTrace.elementAt(1), "branch")); + + assertEquals("1", getStateVal(err.stateTrace.elementAt(2), "depth")); + assertEquals("5", getStateVal(err.stateTrace.elementAt(2), "branch")); + + // The worker should push one final OK result onto the queue upon termination. + res = resultQueue.take(); + assertFalse(res.isError()); + + worker.join(); + assertFalse(worker.isAlive()); + } + + @Test + public void testActionPropertyViolation() throws Exception { + ITool tool = new FastTool("", "BasicMultiTrace", "MCActionProp", new SimpleFilenameToStream()); + + StateVec initStates = tool.getInitStates(); + ILiveCheck liveCheck = new NoOpLiveCheck(tool, "BasicMultiTrace"); + BlockingQueue<SimulationWorkerResult> resultQueue = new LinkedBlockingQueue<>(); + SimulationWorker worker = new SimulationWorker(0, tool, resultQueue, 0, 100, 100, false, null, + liveCheck, new LongAdder(), new LongAdder()); + worker.start(initStates); + + SimulationWorkerResult res = resultQueue.take(); + assertTrue(res.isError()); + SimulationWorkerError err = res.error(); + assertEquals(EC.TLC_ACTION_PROPERTY_VIOLATED_BEHAVIOR, err.errorCode); + assertEquals(2, err.stateTrace.size()); + + // Check the generated trace. + assertEquals("0", getStateVal(err.stateTrace.elementAt(0), "depth")); + assertEquals("0", getStateVal(err.stateTrace.elementAt(0), "branch")); + + assertEquals("0", getStateVal(err.stateTrace.elementAt(1), "depth")); + assertEquals("6", getStateVal(err.stateTrace.elementAt(1), "branch")); + + assertEquals("1", getStateVal(err.state, "depth")); + assertEquals("6", getStateVal(err.state, "branch")); + + // Check another result. + res = resultQueue.take(); + assertTrue(res.isError()); + err = res.error(); + assertEquals(EC.TLC_ACTION_PROPERTY_VIOLATED_BEHAVIOR, err.errorCode); + assertEquals(2, err.stateTrace.size()); + + // Check the generated trace. + assertEquals("0", getStateVal(err.stateTrace.elementAt(0), "depth")); + assertEquals("0", getStateVal(err.stateTrace.elementAt(0), "branch")); + + assertEquals("0", getStateVal(err.stateTrace.elementAt(1), "depth")); + assertEquals("10", getStateVal(err.stateTrace.elementAt(1), "branch")); + + assertEquals("1", getStateVal(err.state, "depth")); + assertEquals("10", getStateVal(err.state, "branch")); + + worker.join(); + assertFalse(worker.isAlive()); + } + + @Test + public void testInvariantBadEval() throws Exception { + ITool tool = new FastTool("", "BasicMultiTrace", "MCBadInvNonInitState", new SimpleFilenameToStream()); + + StateVec initStates = tool.getInitStates(); + ILiveCheck liveCheck = new NoOpLiveCheck(tool, "BasicMultiTrace"); + BlockingQueue<SimulationWorkerResult> resultQueue = new LinkedBlockingQueue<>(); + SimulationWorker worker = new SimulationWorker(0, tool, resultQueue, 0, 100, 100, false, null, + liveCheck, new LongAdder(), new LongAdder()); + worker.start(initStates); + SimulationWorkerResult res = resultQueue.take(); + + assertTrue(res.isError()); + SimulationWorkerError err = res.error(); + assertEquals(EC.TLC_INVARIANT_EVALUATION_FAILED, err.errorCode); + assertEquals(1, err.stateTrace.size()); + + // Check the generated trace. + assertEquals("0", getStateVal(err.stateTrace.elementAt(0), "depth")); + assertEquals("0", getStateVal(err.stateTrace.elementAt(0), "branch")); + + assertEquals("0", getStateVal(err.state, "depth")); + assertEquals("1", getStateVal(err.state, "branch")); + + worker.join(); + assertFalse(worker.isAlive()); + } + + @Test + public void testActionPropertyBadEval() throws Exception { + ITool tool = new FastTool("", "BasicMultiTrace", "MCActionPropBadEval", new SimpleFilenameToStream()); + + StateVec initStates = tool.getInitStates(); + ILiveCheck liveCheck = new NoOpLiveCheck(tool, "BasicMultiTrace"); + BlockingQueue<SimulationWorkerResult> resultQueue = new LinkedBlockingQueue<>(); + SimulationWorker worker = new SimulationWorker(0, tool, resultQueue, 0, 100, 100, false, null, + liveCheck, new LongAdder(), new LongAdder()); + worker.start(initStates); + + SimulationWorkerResult res = resultQueue.take(); + + assertTrue(res.isError()); + SimulationWorkerError err = res.error(); + assertEquals(EC.TLC_ACTION_PROPERTY_EVALUATION_FAILED, err.errorCode); + + worker.join(); + assertFalse(worker.isAlive()); + } + + @Test + public void testUnderspecifiedNext() throws Exception { + ITool tool = new FastTool("", "BasicMultiTrace", "MCUnderspecNext", new SimpleFilenameToStream()); + + StateVec initStates = tool.getInitStates(); + ILiveCheck liveCheck = new NoOpLiveCheck(tool, "BasicMultiTrace"); + BlockingQueue<SimulationWorkerResult> resultQueue = new LinkedBlockingQueue<>(); + SimulationWorker worker = new SimulationWorker(0, tool, resultQueue, 0, 100, 100, false, null, + liveCheck, new LongAdder(), new LongAdder()); + worker.start(initStates); + SimulationWorkerResult res = resultQueue.take(); + + assertTrue(res.isError()); + SimulationWorkerError err = res.error(); + assertEquals(EC.TLC_STATE_NOT_COMPLETELY_SPECIFIED_NEXT, err.errorCode); + assertEquals(1, err.stateTrace.size()); + + // Check the generated trace. + assertEquals("0", getStateVal(err.stateTrace.elementAt(0), "depth")); + assertEquals("0", getStateVal(err.stateTrace.elementAt(0), "branch")); + + assertEquals(null, err.state.getVals().get(UniqueString.uniqueStringOf("depth"))); + assertEquals("0", getStateVal(err.state, "branch")); + + worker.join(); + assertFalse(worker.isAlive()); + } + + @Test + public void testDeadlock() throws Exception { + ITool tool = new FastTool("", "BasicMultiTrace", TLAConstants.Files.MODEL_CHECK_FILE_BASENAME, new SimpleFilenameToStream()); + + StateVec initStates = tool.getInitStates(); + ILiveCheck liveCheck = new NoOpLiveCheck(tool, "BasicMultiTrace"); + BlockingQueue<SimulationWorkerResult> resultQueue = new LinkedBlockingQueue<>(); + SimulationWorker worker = new SimulationWorker(0, tool, resultQueue, 0, 100, 100, true, null, + liveCheck, new LongAdder(), new LongAdder()); + worker.start(initStates); + SimulationWorkerResult res = resultQueue.take(); + + assertTrue(res.isError()); + SimulationWorkerError err = res.error(); + assertEquals(EC.TLC_DEADLOCK_REACHED, err.errorCode); + + System.out.println(err.stateTrace.toString()); + assertEquals(7, err.stateTrace.size()); + + // Check the generated trace. + assertEquals("0", getStateVal(err.stateTrace.elementAt(0), "depth")); + assertEquals("0", getStateVal(err.stateTrace.elementAt(0), "branch")); + + assertEquals("0", getStateVal(err.stateTrace.elementAt(1), "depth")); + assertEquals("6", getStateVal(err.stateTrace.elementAt(1), "branch")); + + assertEquals("1", getStateVal(err.stateTrace.elementAt(2), "depth")); + assertEquals("6", getStateVal(err.stateTrace.elementAt(2), "branch")); + + assertEquals("2", getStateVal(err.stateTrace.elementAt(3), "depth")); + assertEquals("6", getStateVal(err.stateTrace.elementAt(3), "branch")); + + assertEquals("3", getStateVal(err.stateTrace.elementAt(4), "depth")); + assertEquals("6", getStateVal(err.stateTrace.elementAt(4), "branch")); + + assertEquals("4", getStateVal(err.stateTrace.elementAt(5), "depth")); + assertEquals("6", getStateVal(err.stateTrace.elementAt(5), "branch")); + + assertEquals("5", getStateVal(err.stateTrace.elementAt(6), "depth")); + assertEquals("6", getStateVal(err.stateTrace.elementAt(6), "branch")); + + worker.join(); + assertFalse(worker.isAlive()); + } + + @Test + public void testModelStateConstraint() throws Exception { + ITool tool = new FastTool("", "BasicMultiTrace", "MCWithConstraint", new SimpleFilenameToStream()); + + StateVec initStates = tool.getInitStates(); + ILiveCheck liveCheck = new NoOpLiveCheck(tool, "BasicMultiTrace"); + BlockingQueue<SimulationWorkerResult> resultQueue = new LinkedBlockingQueue<>(); + SimulationWorker worker = new SimulationWorker(0, tool, resultQueue, 0, 100, 100, false, null, + liveCheck, new LongAdder(), new LongAdder()); + worker.start(initStates); + SimulationWorkerResult res = resultQueue.take(); + assertFalse(res.isError()); + worker.join(); + assertTrue(resultQueue.isEmpty()); + assertFalse(worker.isAlive()); + } + + @Test + public void testModelActionConstraint() throws Exception { + ITool tool = new FastTool("", "BasicMultiTrace", "MCWithActionConstraint", new SimpleFilenameToStream()); + + StateVec initStates = tool.getInitStates(); + ILiveCheck liveCheck = new NoOpLiveCheck(tool, "BasicMultiTrace"); + BlockingQueue<SimulationWorkerResult> resultQueue = new LinkedBlockingQueue<>(); + SimulationWorker worker = new SimulationWorker(0, tool, resultQueue, 0, 100, 100, false, null, + liveCheck, new LongAdder(), new LongAdder()); + worker.start(initStates); + SimulationWorkerResult res = resultQueue.take(); + assertFalse(res.isError()); + worker.join(); + assertTrue(resultQueue.isEmpty()); + assertFalse(worker.isAlive()); + } + + @Test + public void testWorkerInterruption() throws Exception { + ITool tool = new FastTool("", "BasicMultiTrace", "MCInv", new SimpleFilenameToStream()); + + StateVec initStates = tool.getInitStates(); + ILiveCheck liveCheck = new NoOpLiveCheck(tool, "BasicMultiTrace"); + BlockingQueue<SimulationWorkerResult> resultQueue = new LinkedBlockingQueue<>(); + + // If we set the trace limit to the max, the worker should effectively run forever. We verify that after it generates + // a result, we can cancel it and the worker will terminate. + long traceNum = Long.MAX_VALUE; + SimulationWorker worker = new SimulationWorker(0, tool, resultQueue, 0, 100, traceNum, false, null, + liveCheck, new LongAdder(), new LongAdder()); + worker.start(initStates); + + // Check one result. + SimulationWorkerResult res = resultQueue.take(); + + assertTrue(res.isError()); + SimulationWorkerError err = res.error(); + assertEquals(EC.TLC_INVARIANT_VIOLATED_BEHAVIOR, err.errorCode); + assertEquals(3, err.stateTrace.size()); + + // Cancel the worker. + worker.interrupt(); + worker.join(); + assertFalse(worker.isAlive()); + } + + @Test + public void testTraceDepthObeyed() throws Exception { + ITool tool = new FastTool("", "BasicMultiTrace", "MCInv", new SimpleFilenameToStream()); + + StateVec initStates = tool.getInitStates(); + ILiveCheck liveCheck = new NoOpLiveCheck(tool, "BasicMultiTrace"); + BlockingQueue<SimulationWorkerResult> resultQueue = new LinkedBlockingQueue<>(); + + // At this trace depth, the worker should never find the invariant violation. + long traceDepth = 1; + SimulationWorker worker = new SimulationWorker(0, tool, resultQueue, 0, traceDepth, 100, false, + null, liveCheck, new LongAdder(), new LongAdder()); + worker.start(initStates); + SimulationWorkerResult res = resultQueue.take(); + assertFalse(res.isError()); + + worker.join(); + assertTrue(resultQueue.isEmpty()); + assertFalse(worker.isAlive()); + } + + @Test + public void testStateAndTraceGenerationCount() throws Exception { + ITool tool = new FastTool("", "BasicMultiTrace", TLAConstants.Files.MODEL_CHECK_FILE_BASENAME, new SimpleFilenameToStream()); + + StateVec initStates = tool.getInitStates(); + ILiveCheck liveCheck = new NoOpLiveCheck(tool, "BasicMultiTrace"); + BlockingQueue<SimulationWorkerResult> resultQueue = new LinkedBlockingQueue<>(); + + // Have the worker generate a specified number of traces of a fixed length. + LongAdder numOfGenStates = new LongAdder(); + LongAdder numOfGenTraces = new LongAdder(); + long traceDepth = 5; + long traceNum = 5; + SimulationWorker worker = new SimulationWorker(0, tool, resultQueue, 0, traceDepth, traceNum, false, + null, liveCheck, numOfGenStates, numOfGenTraces); + + worker.start(initStates); + worker.join(); + assertFalse(worker.isAlive()); + assertEquals(70, numOfGenStates.longValue()); + assertEquals(5, numOfGenTraces.longValue()); + } +} diff --git a/tlatools/test/tlc2/tool/simulation/SimulatorMultiThreadTest.java b/tlatools/test/tlc2/tool/simulation/SimulatorMultiThreadTest.java new file mode 100644 index 0000000000000000000000000000000000000000..2623a1ff89498c1889f5d100dcf8e15c8c1489d3 --- /dev/null +++ b/tlatools/test/tlc2/tool/simulation/SimulatorMultiThreadTest.java @@ -0,0 +1,18 @@ +package tlc2.tool.simulation; + +/** + * Correctness tests of a multi-threaded Simulator. + * + * This test intends to verify that, from a correctness perspective, running the + * Simulator with multiple threads is equivalent to running it with a single + * thread i.e. it should pass all of the same tests. + */ +public class SimulatorMultiThreadTest extends SimulatorTest { + + public int numWorkers() { + // A somewhat arbitrary value, but 4 threads seems like a reasonable amount of + // parallelism. + return 4; + } + +} diff --git a/tlatools/test/tlc2/tool/simulation/SimulatorTest.java b/tlatools/test/tlc2/tool/simulation/SimulatorTest.java new file mode 100644 index 0000000000000000000000000000000000000000..70756893fda447ef31d7fcdd00b242bdafae2138 --- /dev/null +++ b/tlatools/test/tlc2/tool/simulation/SimulatorTest.java @@ -0,0 +1,155 @@ +package tlc2.tool.simulation; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import java.io.File; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import tlc2.TLCGlobals; +import tlc2.TestMPRecorder; +import tlc2.output.EC; +import tlc2.output.MP; +import tlc2.tool.CommonTestCase; +import tlc2.tool.Simulator; +import tlc2.util.FP64; +import tlc2.util.RandomGenerator; +import util.FileUtil; +import util.SimpleFilenameToStream; +import util.TLAConstants; +import util.ToolIO; + +/** + * Correctness tests for a Simulator. + */ +public class SimulatorTest extends CommonTestCase { + + RandomGenerator rng = new RandomGenerator(); + + public SimulatorTest() { + super(new TestMPRecorder()); + MP.setRecorder(recorder); + } + + @Before + public void setUp() throws Exception{ + // Make the each unit test execution as deterministic as possible. + rng = new RandomGenerator(0); + ToolIO.setUserDir(BASE_PATH + File.separator + "simulation" + File.separator + "BasicMultiTrace"); + + // Printing the error trace entails fingerprint its states. + FP64.Init(); + } + + @After + public void tearDown() throws Exception{ + FileUtil.deleteDir(TLCGlobals.metaRoot, true); + } + + /** + * The number of threads to run the Simulator with. Can be overridden by + * sub-classes that want to test with different values. + */ + public int numWorkers() { + return 1; + } + + public void runSimulatorTest(String specFile, String configFile, Boolean deadlock, int traceDepth, long traceNum) { + try { + Simulator simulator = new Simulator(specFile, configFile, null, deadlock, traceDepth, traceNum, rng, 0, + new SimpleFilenameToStream(), numWorkers()); + simulator.simulate(); + } catch (Exception e) { + e.printStackTrace(); + fail(e.getMessage()); + } + } + + @Test + public void testSuccessfulSimulation() { + runSimulatorTest("BasicMultiTrace", TLAConstants.Files.MODEL_CHECK_FILE_BASENAME, false, 100, 100); + assertFalse(recorder.recorded(EC.TLC_INVARIANT_VIOLATED_INITIAL)); + assertTrue(recorder.recorded(EC.TLC_PROGRESS_SIMU)); + } + + @Test + public void testInvariantViolationInitialState() { + runSimulatorTest("BasicMultiTrace", "MCInvInitState", false, 100, 100); + assertTrue(recorder.recordedWithStringValue(EC.TLC_INVARIANT_VIOLATED_INITIAL, "InvInitState")); + } + + @Test + public void testInvariantViolation() { + runSimulatorTest("BasicMultiTrace", "MCInv", false, 100, 100); + assertTrue(recorder.recordedWithStringValue(EC.TLC_COMPUTING_INIT_PROGRESS, "1")); + assertTrue(recorder.recordedWithStringValue(EC.TLC_INVARIANT_VIOLATED_BEHAVIOR, "Inv")); + } + + @Test + public void testInvariantBadEvalInitState() { + runSimulatorTest("BasicMultiTrace", "MCBadInvInitState", false, 100, 100); + assertTrue(recorder.recordedWithStringValue(EC.TLC_COMPUTING_INIT_PROGRESS, "1")); + assertTrue(recorder.recorded(EC.TLC_INITIAL_STATE)); + } + + @Test + public void testInvariantBadEvalNonInitState() { + runSimulatorTest("BasicMultiTrace", "MCBadInvNonInitState", false, 100, 100); + assertTrue(recorder.recordedWithStringValue(EC.TLC_COMPUTING_INIT_PROGRESS, "1")); + assertTrue(recorder.recordedWithStringValue(EC.TLC_INVARIANT_EVALUATION_FAILED, "InvBadEvalNonInitState")); + } + + @Test + public void testUnderspecifiedInit() { + runSimulatorTest("BasicMultiTrace", "MCUnderspecInit", false, 100, 100); + assertTrue(recorder.recordedWithStringValue(EC.TLC_COMPUTING_INIT_PROGRESS, "1")); + assertTrue(recorder.recorded(EC.TLC_STATE_NOT_COMPLETELY_SPECIFIED_INITIAL)); + } + + @Test + public void testInvariantViolationContinue() { + TLCGlobals.continuation = true; + runSimulatorTest("BasicMultiTrace", "MCInv", false, 100, 100); + assertTrue(recorder.recordedWithStringValue(EC.TLC_COMPUTING_INIT_PROGRESS, "1")); + assertTrue(recorder.recordedWithStringValue(EC.TLC_INVARIANT_VIOLATED_BEHAVIOR, "Inv")); + } + + @Test + public void testDontContinueOnRuntimeSpecError() { + // We should only continue simulating if a worker encounters a safety or liveness violation. + // Errors in the spec e.g. runtime evaluation errors should terminate all workers, even if + // continue=true. We set the traceCnt to something extremely high, so that this test should + // hang if the simulator does not terminate on the first error. + TLCGlobals.continuation = true; + runSimulatorTest("BasicMultiTrace", "MCBadInvNonInitState", false, 100, Long.MAX_VALUE); + assertTrue(recorder.recordedWithStringValue(EC.TLC_COMPUTING_INIT_PROGRESS, "1")); + assertTrue(recorder.recordedWithStringValue(EC.TLC_INVARIANT_EVALUATION_FAILED, "InvBadEvalNonInitState")); + } + + @Test + public void testLivenessViolation() { + FP64.Init(); + runSimulatorTest("BasicMultiTrace", "MCLivenessProp", false, 100, 100); + assertTrue(recorder.recordedWithStringValue(EC.TLC_COMPUTING_INIT_PROGRESS, "1")); + assertTrue(recorder.recorded(EC.TLC_TEMPORAL_PROPERTY_VIOLATED)); + assertTrue(recorder.recorded(EC.TLC_COUNTER_EXAMPLE)); + } + + @Test + public void testLivenessViolationIgnoresContinue() { + FP64.Init(); + TLCGlobals.continuation = true; + // We should always terminate after the first liveness violation, regardless of + // the "continue" parameter. + runSimulatorTest("BasicMultiTrace", "MCLivenessProp", false, 100, Long.MAX_VALUE); + assertTrue(recorder.recordedWithStringValue(EC.TLC_COMPUTING_INIT_PROGRESS, "1")); + assertTrue(recorder.recorded(EC.TLC_TEMPORAL_PROPERTY_VIOLATED)); + assertTrue(recorder.recorded(EC.TLC_COUNTER_EXAMPLE)); + } + + +} diff --git a/tlatools/test/tlc2/tool/suite/ETest1.java b/tlatools/test/tlc2/tool/suite/ETest1.java new file mode 100644 index 0000000000000000000000000000000000000000..07ddcc64130234566fe126674c71d0ef81dc14d7 --- /dev/null +++ b/tlatools/test/tlc2/tool/suite/ETest1.java @@ -0,0 +1,43 @@ +/******************************************************************************* + * Copyright (c) 2016 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.suite; + +import org.junit.Test; + +import tlc2.output.EC.ExitStatus; + +public class ETest1 extends SuiteETestCase { + + public ETest1() { + super(ExitStatus.ERROR_SPEC_PARSE); + } + + @Test + public void testSpec() { + assertSubstring("*** Errors: 1\n\n" + "line 18, col 12 to line 18, col 17 of module etest1\n\n" + + "The operator Foo requires 1 arguments."); + } +} diff --git a/tlatools/test/tlc2/tool/suite/ETest10.java b/tlatools/test/tlc2/tool/suite/ETest10.java new file mode 100644 index 0000000000000000000000000000000000000000..f69ed798ecb498dc2bde45471b126ea605fd7e20 --- /dev/null +++ b/tlatools/test/tlc2/tool/suite/ETest10.java @@ -0,0 +1,48 @@ +/******************************************************************************* + * Copyright (c) 2016 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.suite; + +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.output.EC; +import tlc2.output.EC.ExitStatus; + +public class ETest10 extends SuiteETestCase { + + public ETest10() { + super(ExitStatus.FAILURE_SPEC_EVAL); + } + + @Test + public void testSpec() { + assertTrue(recorder.recordedWithSubStringValue(EC.GENERAL, + "Attempted to check if the value:\n\"a\"\nis an element of Int.")); + + assertUncovered("line 13, col 12 to line 13, col 15 of module etest10: 0"); + } +} diff --git a/tlatools/test/tlc2/tool/suite/ETest11.java b/tlatools/test/tlc2/tool/suite/ETest11.java new file mode 100644 index 0000000000000000000000000000000000000000..a06acff72eb2f69a433f3a5062c42e110aff6abb --- /dev/null +++ b/tlatools/test/tlc2/tool/suite/ETest11.java @@ -0,0 +1,48 @@ +/******************************************************************************* + * Copyright (c) 2016 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.suite; + +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.output.EC; +import tlc2.output.EC.ExitStatus; + +public class ETest11 extends SuiteETestCase { + + public ETest11() { + super(ExitStatus.FAILURE_SPEC_EVAL); + } + + @Test + public void testSpec() { + assertTrue(recorder.recordedWithSubStringValue(EC.GENERAL, + "Attempted to check if the value:\n1\nis an element of STRING.")); + + assertUncovered("line 13, col 12 to line 13, col 15 of module etest11: 0"); + } +} diff --git a/tlatools/test/tlc2/tool/suite/ETest12.java b/tlatools/test/tlc2/tool/suite/ETest12.java new file mode 100644 index 0000000000000000000000000000000000000000..3f2a55bf4dd48f0f66ae9403d62b26b8b77e985a --- /dev/null +++ b/tlatools/test/tlc2/tool/suite/ETest12.java @@ -0,0 +1,50 @@ +/******************************************************************************* + * Copyright (c) 2016 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.suite; + +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.output.EC; +import tlc2.output.EC.ExitStatus; + +public class ETest12 extends SuiteETestCase { + + public ETest12() { + super(ExitStatus.VIOLATION_ASSUMPTION); + } + + @Test + public void testSpec() { + assertTrue(recorder.toString(), recorder.recordedWithSubStringValue(EC.TLC_ASSUMPTION_EVALUATION_ERROR, + "Attempted to apply the operator overridden by the Java method\n" + + "public static tlc2.value.impl.IntValue tlc2.module.FiniteSets.Cardinality(tlc2.value.impl.Value),\n" + + "but it produced the following error:\n" + + "Overflow when computing the number of elements in:" + + "\n[0..2 -> 1..2000]")); + } +} diff --git a/tlatools/test/tlc2/tool/suite/ETest13.java b/tlatools/test/tlc2/tool/suite/ETest13.java new file mode 100644 index 0000000000000000000000000000000000000000..732b0d5859cc46915e9f8fbfd95881e57d46bc20 --- /dev/null +++ b/tlatools/test/tlc2/tool/suite/ETest13.java @@ -0,0 +1,50 @@ +/******************************************************************************* + * Copyright (c) 2016 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.suite; + +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.output.EC; +import tlc2.output.EC.ExitStatus; + +public class ETest13 extends SuiteETestCase { + + public ETest13() { + super(ExitStatus.VIOLATION_ASSUMPTION); + } + + @Test + public void testSpec() { + assertTrue(recorder.toString(), recorder.recordedWithSubStringValue(EC.TLC_ASSUMPTION_EVALUATION_ERROR, + "Attempted to apply the operator overridden by the Java method\n" + + "public static tlc2.value.impl.IntValue tlc2.module.FiniteSets.Cardinality(tlc2.value.impl.Value),\n" + + "but it produced the following error:\n" + + "Overflow when computing the number of elements in:" + + "\n[0..2 -> 1..2000]")); + } +} diff --git a/tlatools/test/tlc2/tool/suite/ETest14.java b/tlatools/test/tlc2/tool/suite/ETest14.java new file mode 100644 index 0000000000000000000000000000000000000000..d5d33ce56d52c8d204493e8c3f7c14b4e5e1b96b --- /dev/null +++ b/tlatools/test/tlc2/tool/suite/ETest14.java @@ -0,0 +1,46 @@ +/******************************************************************************* + * Copyright (c) 2016 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.suite; + +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.output.EC; +import tlc2.output.EC.ExitStatus; + +public class ETest14 extends SuiteETestCase { + + public ETest14() { + super(ExitStatus.VIOLATION_ASSUMPTION); + } + + @Test + public void testSpec() { + assertTrue(recorder.toString(), recorder.recordedWithSubStringValue(EC.TLC_ASSUMPTION_EVALUATION_ERROR, + "Attempted to compute the value of an expression of form\nCHOOSE x \\in S: P, but no element of S satisfied P.\nline 5, col 7 to line 5, col 28 of module etest14")); + } +} diff --git a/tlatools/test/tlc2/tool/suite/ETest15.java b/tlatools/test/tlc2/tool/suite/ETest15.java new file mode 100644 index 0000000000000000000000000000000000000000..e6d6cbdb919a2f535e230628766a5718eae4eaac --- /dev/null +++ b/tlatools/test/tlc2/tool/suite/ETest15.java @@ -0,0 +1,46 @@ +/******************************************************************************* + * Copyright (c) 2016 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.suite; + +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.output.EC; +import tlc2.output.EC.ExitStatus; + +public class ETest15 extends SuiteETestCase { + + public ETest15() { + super(ExitStatus.VIOLATION_ASSUMPTION); + } + + @Test + public void testSpec() { + assertTrue(recorder.toString(), recorder.recordedWithSubStringValue(EC.TLC_ASSUMPTION_EVALUATION_ERROR, + "Attempted to compare integer 2 with non-integer:\n<<3>>")); + } +} diff --git a/tlatools/test/tlc2/tool/suite/ETest16.java b/tlatools/test/tlc2/tool/suite/ETest16.java new file mode 100644 index 0000000000000000000000000000000000000000..7113568ae522c1e05142479d1e141b8aa6de3e70 --- /dev/null +++ b/tlatools/test/tlc2/tool/suite/ETest16.java @@ -0,0 +1,45 @@ +/******************************************************************************* + * Copyright (c) 2016 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.suite; + +import org.junit.Test; + +import tlc2.output.EC.ExitStatus; + +public class ETest16 extends SuiteETestCase { + + public ETest16() { + super(ExitStatus.ERROR_SPEC_PARSE); + } + + @Test + public void testSpec() { + assertSubstring("*** Errors: 2\n\n" + "line 5, col 27 to line 5, col 27 of module etest16\n\n" + + "Non-unique fields in constructor.\n\n\n" + + "line 7, col 27 to line 7, col 27 of module etest16\n\n" + + "Non-unique fields in constructor."); + } +} diff --git a/tlatools/test/tlc2/tool/suite/ETest2.java b/tlatools/test/tlc2/tool/suite/ETest2.java new file mode 100644 index 0000000000000000000000000000000000000000..be48e0e0ad51854db5457670ab687510521fa165 --- /dev/null +++ b/tlatools/test/tlc2/tool/suite/ETest2.java @@ -0,0 +1,48 @@ +/******************************************************************************* + * Copyright (c) 2016 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.suite; + +import static org.junit.Assert.assertFalse; + +import org.junit.Test; + +import tlc2.output.EC; +import tlc2.output.EC.ExitStatus; + +public class ETest2 extends SuiteETestCase { + + public ETest2() { + super(ExitStatus.ERROR_SPEC_PARSE); + } + + @Test + public void testSpec() { + assertFalse(recorder.recorded(EC.GENERAL)); + assertSubstring("*** Errors: 1\n\n" + + "line 18, col 12 to line 18, col 14 of module etest2\n\n" + + "The operator Foo requires 2 arguments."); + } +} diff --git a/tlatools/test/tlc2/tool/suite/ETest3.java b/tlatools/test/tlc2/tool/suite/ETest3.java new file mode 100644 index 0000000000000000000000000000000000000000..9a67bf0c00d91124b7b41c04d065fcd2d481181a --- /dev/null +++ b/tlatools/test/tlc2/tool/suite/ETest3.java @@ -0,0 +1,58 @@ +/******************************************************************************* + * Copyright (c) 2016 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.suite; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.output.EC; +import tlc2.output.EC.ExitStatus; + +public class ETest3 extends SuiteETestCase { + + public ETest3() { + super(ExitStatus.VIOLATION_DEADLOCK); + } + + /* (non-Javadoc) + * @see tlc2.tool.liveness.ModelCheckerTestCase#checkDeadLock() + */ + protected boolean checkDeadLock() { + return true; + } + + @Test + public void testSpec() { + assertTrue(recorder.recorded(EC.TLC_DEADLOCK_REACHED)); + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "2", "2", "0")); + assertFalse(recorder.recorded(EC.GENERAL)); + + assertZeroUncovered(); + } +} diff --git a/tlatools/test/tlc2/tool/suite/ETest4.java b/tlatools/test/tlc2/tool/suite/ETest4.java new file mode 100644 index 0000000000000000000000000000000000000000..e6747c7502d159cd65fc5d6dfb4d0a29146ada2c --- /dev/null +++ b/tlatools/test/tlc2/tool/suite/ETest4.java @@ -0,0 +1,53 @@ +/******************************************************************************* + * Copyright (c) 2016 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.suite; + +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.output.EC; +import tlc2.output.EC.ExitStatus; + +public class ETest4 extends SuiteETestCase { + + public ETest4() { + super(ExitStatus.FAILURE_SPEC_EVAL); + } + + @Test + public void testSpec() { + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "0", "0", "0")); + String s = "0. Line 13, column 9 to line 16, column 51 in etest4\n" + + "1. Line 13, column 12 to line 13, column 16 in etest4\n" + + "2. Line 15, column 12 to line 15, column 26 in etest4\n" + + "3. Line 15, column 12 to line 15, column 22 in etest4\n\n"; + assertTrue(recorder.recordedWithStringValues(EC.TLC_NESTED_EXPRESSION, s)); + + assertUncovered("line 18, col 9 to line 18, col 19 of module etest4: 0"); + } +} diff --git a/tlatools/test/tlc2/tool/suite/ETest5.java b/tlatools/test/tlc2/tool/suite/ETest5.java new file mode 100644 index 0000000000000000000000000000000000000000..59238d5adf5394df92f0cfbc4c95fdedd747660d --- /dev/null +++ b/tlatools/test/tlc2/tool/suite/ETest5.java @@ -0,0 +1,48 @@ +/******************************************************************************* + * Copyright (c) 2016 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.suite; + +import static org.junit.Assert.assertFalse; + +import org.junit.Test; + +import tlc2.output.EC; +import tlc2.output.EC.ExitStatus; + +public class ETest5 extends SuiteETestCase { + + public ETest5() { + super(ExitStatus.ERROR_SPEC_PARSE); + } + + @Test + public void testSpec() { + assertFalse(recorder.recorded(EC.GENERAL)); + assertSubstring("*** Errors: 1\n\n" + + "line 13, col 17 to line 13, col 20 of module etest5\n\n" + + "Unknown operator: `Init'."); + } +} diff --git a/tlatools/test/tlc2/tool/suite/ETest6.java b/tlatools/test/tlc2/tool/suite/ETest6.java new file mode 100644 index 0000000000000000000000000000000000000000..874649a8ebb5c24447ab0a375c98a61d3ff7ccb0 --- /dev/null +++ b/tlatools/test/tlc2/tool/suite/ETest6.java @@ -0,0 +1,49 @@ +/******************************************************************************* + * Copyright (c) 2016 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.suite; + +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.output.EC; +import tlc2.output.EC.ExitStatus; + +public class ETest6 extends SuiteETestCase { + + public ETest6() { + super(ExitStatus.FAILURE_SPEC_EVAL); + } + + @Test + public void testSpec() { + assertTrue(recorder.recordedWithSubStringValue(EC.GENERAL, + "In evaluation, the identifier x is either undefined or not an operator.\nline 16, col 23 to line 16, col 23 of module etest6")); + + assertUncovered("line 13, col 14 to line 13, col 43 of module etest6: 0\n" + + "line 19, col 13 to line 19, col 23 of module etest6: 0"); + } +} diff --git a/tlatools/test/tlc2/tool/suite/ETest7.java b/tlatools/test/tlc2/tool/suite/ETest7.java new file mode 100644 index 0000000000000000000000000000000000000000..95f4e38bdaf398e33f9cc93099336b1a55313bf2 --- /dev/null +++ b/tlatools/test/tlc2/tool/suite/ETest7.java @@ -0,0 +1,46 @@ +/******************************************************************************* + * Copyright (c) 2016 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.suite; + +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.output.EC; +import tlc2.output.EC.ExitStatus; + +public class ETest7 extends SuiteETestCase { + + public ETest7() { + super(ExitStatus.ERROR_CONFIG_PARSE); + } + + @Test + public void testSpec() { + assertTrue(recorder.recordedWithStringValues(EC.TLC_CONFIG_SUBSTITUTION_NON_CONSTANT, + "C", "Foo")); + } +} diff --git a/tlatools/test/tlc2/tool/suite/ETest8.java b/tlatools/test/tlc2/tool/suite/ETest8.java new file mode 100644 index 0000000000000000000000000000000000000000..c08b0c2b2b21ac4942a60f11557cbaf65b07d076 --- /dev/null +++ b/tlatools/test/tlc2/tool/suite/ETest8.java @@ -0,0 +1,57 @@ +/******************************************************************************* + * Copyright (c) 2016 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.suite; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.output.EC; +import tlc2.output.EC.ExitStatus; + +public class ETest8 extends SuiteETestCase { + + public ETest8() { + super(new String[]{"-simulate"}, ExitStatus.ERROR /*ExitStatus.VIOLATION_ASSERT*/); //TODO Simulator doesn't report correct exit status. + } + + /* (non-Javadoc) + * @see tlc2.tool.liveness.ModelCheckerTestCase#checkDeadLock() + */ + protected boolean checkDeadLock() { + return false; + } + + @Test + public void testSpec() { + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertTrue(recorder.recorded(EC.TLC_STATS_SIMU)); + assertFalse(recorder.recorded(EC.TLC_DEADLOCK_REACHED)); + + assertUncovered("line 18, col 15 to line 18, col 22 of module etest8: 0\n"); + } +} diff --git a/tlatools/test/tlc2/tool/suite/ETest9.java b/tlatools/test/tlc2/tool/suite/ETest9.java new file mode 100644 index 0000000000000000000000000000000000000000..5546e91b72110c79dcfe637d41ff27e2b4934cfc --- /dev/null +++ b/tlatools/test/tlc2/tool/suite/ETest9.java @@ -0,0 +1,48 @@ +/******************************************************************************* + * Copyright (c) 2016 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.suite; + +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.output.EC; +import tlc2.output.EC.ExitStatus; + +public class ETest9 extends SuiteETestCase { + + public ETest9() { + super(ExitStatus.FAILURE_SPEC_EVAL); + } + + @Test + public void testSpec() { + assertTrue(recorder.recordedWithSubStringValue(EC.GENERAL, + "Attempted to check if the value:\n\"a\"\nis an element of Nat.")); + + assertUncovered("line 13, col 12 to line 13, col 15 of module etest9: 0\n"); + } +} diff --git a/tlatools/test/tlc2/tool/suite/SuiteETestCase.java b/tlatools/test/tlc2/tool/suite/SuiteETestCase.java new file mode 100644 index 0000000000000000000000000000000000000000..87924b0ab44dd6c2808997301d2281e8926c8012 --- /dev/null +++ b/tlatools/test/tlc2/tool/suite/SuiteETestCase.java @@ -0,0 +1,100 @@ +/******************************************************************************* + * Copyright (c) 2016 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ + +package tlc2.tool.suite; +import static org.junit.Assert.fail; + +import java.io.PipedOutputStream; +import java.io.PrintStream; +import java.util.ArrayList; +import java.util.List; + +import tlc2.output.EC.ExitStatus; +import tlc2.tool.liveness.ModelCheckerTestCase; +import util.ToolIO; + +public abstract class SuiteETestCase extends ModelCheckerTestCase { + + private TestPrintStream testPrintStream = new TestPrintStream(); + + public SuiteETestCase() { + this(ExitStatus.SUCCESS); + } + + public SuiteETestCase(int exitStatus) { + super("setBySetUp", "suite", exitStatus); + } + + public SuiteETestCase(String[] params) { + this(params, ExitStatus.SUCCESS); + } + + public SuiteETestCase(String[] params, int exitStatus) { + super("setBySetUp", "suite", params, exitStatus); + } + + /* (non-Javadoc) + * @see tlc2.tool.liveness.ModelCheckerTestCase#setUp() + */ + public void setUp() { + // Set spec name to the name of the unit tests + spec = getClass().getSimpleName().toLowerCase(); + + // Intercept tool out to check SANY parser errors + ToolIO.out = testPrintStream; + ToolIO.err = testPrintStream; + + super.setUp(); + } + + + protected void assertSubstring(String substring) { + for (String string : testPrintStream.strings) { + if (string.contains(substring)) { + return; + } + } + fail("Substring not found"); + } + + private static class TestPrintStream extends PrintStream { + + private final List<String> strings = new ArrayList<String>(); + + public TestPrintStream() { + super(new PipedOutputStream()); + } + + /* (non-Javadoc) + * @see java.io.PrintStream#println(java.lang.String) + */ + public void println(String x) { + strings.add(x); + System.out.println(x); + super.println(x); + } + } +} diff --git a/tlatools/test/tlc2/tool/suite/SuiteTestCase.java b/tlatools/test/tlc2/tool/suite/SuiteTestCase.java new file mode 100644 index 0000000000000000000000000000000000000000..8d3ff58afa48b3a7e5f155382fdc581800cd494c --- /dev/null +++ b/tlatools/test/tlc2/tool/suite/SuiteTestCase.java @@ -0,0 +1,83 @@ +/******************************************************************************* + * Copyright (c) 2016 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ + +package tlc2.tool.suite; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.output.EC; +import tlc2.tool.liveness.ModelCheckerTestCase; + +public abstract class SuiteTestCase extends ModelCheckerTestCase { + + private String initStates = "1"; + private String leftStates = "0"; + private String distinctStates = "1"; + private String stateGenerated = "2"; + private String uncovered; + + public SuiteTestCase() { + super("setBySetUp", "suite"); + } + + public SuiteTestCase(String stateGenerated, String distinctStates, String leftStates, String initStates) { + this(); + this.stateGenerated = stateGenerated; + this.distinctStates = distinctStates; + this.leftStates = leftStates; + this.initStates = initStates; + } + + public SuiteTestCase(String stateGenerated, String distinctStates, String leftStates, String initStates, final String uncovered) { + this(stateGenerated, distinctStates, leftStates, initStates); + this.uncovered = uncovered; + } + /* (non-Javadoc) + * @see tlc2.tool.liveness.ModelCheckerTestCase#setUp() + */ + public void setUp() { + // Set spec name to the name of the unit tests + spec = getClass().getSimpleName().toLowerCase(); + + super.setUp(); + } + + @Test + public void testSpec() { + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, stateGenerated, distinctStates, leftStates)); + assertTrue(recorder.recordedWithStringValue(EC.TLC_INIT_GENERATED1, initStates)); + assertFalse(recorder.recorded(EC.GENERAL)); + + if (this.uncovered != null) { + assertUncovered(this.uncovered); + } else { + assertZeroUncovered(); + } + } +} diff --git a/tlatools/test/tlc2/tool/suite/Test1.java b/tlatools/test/tlc2/tool/suite/Test1.java new file mode 100644 index 0000000000000000000000000000000000000000..9e825ddc217a79a8d06d4cfbfa48bcf30773b436 --- /dev/null +++ b/tlatools/test/tlc2/tool/suite/Test1.java @@ -0,0 +1,30 @@ +/******************************************************************************* + * Copyright (c) 2016 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.suite; + +public class Test1 extends SuiteTestCase { + +} diff --git a/tlatools/test/tlc2/tool/suite/Test10.java b/tlatools/test/tlc2/tool/suite/Test10.java new file mode 100644 index 0000000000000000000000000000000000000000..a384f5ea6b9d1b9eb41d029c56eadc294132fec1 --- /dev/null +++ b/tlatools/test/tlc2/tool/suite/Test10.java @@ -0,0 +1,29 @@ +/******************************************************************************* + * Copyright (c) 2016 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.suite; + +public class Test10 extends SuiteTestCase { +} diff --git a/tlatools/test/tlc2/tool/suite/Test11.java b/tlatools/test/tlc2/tool/suite/Test11.java new file mode 100644 index 0000000000000000000000000000000000000000..0b2eee23bdf974c42875609fe8d1bb9611a6e6e3 --- /dev/null +++ b/tlatools/test/tlc2/tool/suite/Test11.java @@ -0,0 +1,29 @@ +/******************************************************************************* + * Copyright (c) 2016 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.suite; + +public class Test11 extends SuiteTestCase { +} diff --git a/tlatools/test/tlc2/tool/suite/Test12.java b/tlatools/test/tlc2/tool/suite/Test12.java new file mode 100644 index 0000000000000000000000000000000000000000..21b4ede643f1de455a62774e6112f22bdd2cc922 --- /dev/null +++ b/tlatools/test/tlc2/tool/suite/Test12.java @@ -0,0 +1,29 @@ +/******************************************************************************* + * Copyright (c) 2016 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.suite; + +public class Test12 extends SuiteTestCase { +} diff --git a/tlatools/test/tlc2/tool/suite/Test13.java b/tlatools/test/tlc2/tool/suite/Test13.java new file mode 100644 index 0000000000000000000000000000000000000000..f82a5bd4ae74e9f3af821918a02ea20ead1123be --- /dev/null +++ b/tlatools/test/tlc2/tool/suite/Test13.java @@ -0,0 +1,30 @@ +/******************************************************************************* + * Copyright (c) 2016 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.suite; + +public class Test13 extends SuiteTestCase { + +} diff --git a/tlatools/test/tlc2/tool/suite/Test14.java b/tlatools/test/tlc2/tool/suite/Test14.java new file mode 100644 index 0000000000000000000000000000000000000000..b296543ddab0197a42981d3b1ae6f19ccf6e9c77 --- /dev/null +++ b/tlatools/test/tlc2/tool/suite/Test14.java @@ -0,0 +1,30 @@ +/******************************************************************************* + * Copyright (c) 2016 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.suite; + +public class Test14 extends SuiteTestCase { + +} diff --git a/tlatools/test/tlc2/tool/suite/Test15.java b/tlatools/test/tlc2/tool/suite/Test15.java new file mode 100644 index 0000000000000000000000000000000000000000..b69446cbb1bd85e9dea3942195cc99957fd52e58 --- /dev/null +++ b/tlatools/test/tlc2/tool/suite/Test15.java @@ -0,0 +1,30 @@ +/******************************************************************************* + * Copyright (c) 2016 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.suite; + +public class Test15 extends SuiteTestCase { + +} diff --git a/tlatools/test/tlc2/tool/suite/Test16.java b/tlatools/test/tlc2/tool/suite/Test16.java new file mode 100644 index 0000000000000000000000000000000000000000..ae6e21b654de39c4c9fbf3f30719bc21f921f541 --- /dev/null +++ b/tlatools/test/tlc2/tool/suite/Test16.java @@ -0,0 +1,30 @@ +/******************************************************************************* + * Copyright (c) 2016 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.suite; + +public class Test16 extends SuiteTestCase { + +} diff --git a/tlatools/test/tlc2/tool/suite/Test17.java b/tlatools/test/tlc2/tool/suite/Test17.java new file mode 100644 index 0000000000000000000000000000000000000000..3ed73263a9ea7e940f58e90011ab57f254f96ba0 --- /dev/null +++ b/tlatools/test/tlc2/tool/suite/Test17.java @@ -0,0 +1,33 @@ +/******************************************************************************* + * Copyright (c) 2016 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.suite; + +public class Test17 extends SuiteTestCase { + + public Test17() { + super("7", "2", "0", "1", "line 101, col 20 to line 101, col 23 of module test17: 0"); + } +} diff --git a/tlatools/test/tlc2/tool/suite/Test18.java b/tlatools/test/tlc2/tool/suite/Test18.java new file mode 100644 index 0000000000000000000000000000000000000000..d042607122e44878e758534cd5982985defdbbca --- /dev/null +++ b/tlatools/test/tlc2/tool/suite/Test18.java @@ -0,0 +1,32 @@ +/******************************************************************************* + * Copyright (c) 2016 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.suite; + +public class Test18 extends SuiteTestCase { + public Test18() { + super("14", "1", "0", "1"); + } +} diff --git a/tlatools/test/tlc2/tool/suite/Test19.java b/tlatools/test/tlc2/tool/suite/Test19.java new file mode 100644 index 0000000000000000000000000000000000000000..8c690fa6834cdb998bdcb2bd3972b1cdb0f3a674 --- /dev/null +++ b/tlatools/test/tlc2/tool/suite/Test19.java @@ -0,0 +1,32 @@ +/******************************************************************************* + * Copyright (c) 2016 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.suite; + +public class Test19 extends SuiteTestCase { + public Test19() { + super("800000", "400000", "0", "400000"); + } +} diff --git a/tlatools/test/tlc2/tool/suite/Test2.java b/tlatools/test/tlc2/tool/suite/Test2.java new file mode 100644 index 0000000000000000000000000000000000000000..fca4bbefb021ad215436e2cc6cbe736acf14c830 --- /dev/null +++ b/tlatools/test/tlc2/tool/suite/Test2.java @@ -0,0 +1,29 @@ +/******************************************************************************* + * Copyright (c) 2016 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.suite; + +public class Test2 extends SuiteTestCase { +} diff --git a/tlatools/test/tlc2/tool/suite/Test20.java b/tlatools/test/tlc2/tool/suite/Test20.java new file mode 100644 index 0000000000000000000000000000000000000000..75505ceb7e84c0db434a5f047352b5b3f7674294 --- /dev/null +++ b/tlatools/test/tlc2/tool/suite/Test20.java @@ -0,0 +1,41 @@ +/******************************************************************************* + * Copyright (c) 2016 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.suite; + +public class Test20 extends SuiteTestCase { + public Test20() { + super("5", "1", "0", "1", " |||line 16, col 9 to line 16, col 12 of module test20: 0\n" + + " |||line 17, col 9 to line 17, col 12 of module test20: 0\n" + + " |||line 25, col 16 to line 25, col 33 of module test20: 0\n" + + " |||line 29, col 17 to line 29, col 34 of module test20: 0\n" + + " |||line 34, col 17 to line 34, col 34 of module test20: 0\n" + + " ||||line 16, col 9 to line 16, col 12 of module test20: 0\n" + + " ||||line 17, col 9 to line 17, col 12 of module test20: 0\n" + + " ||||line 25, col 16 to line 25, col 33 of module test20: 0\n" + + " ||||line 29, col 17 to line 29, col 34 of module test20: 0\n" + + " ||||line 34, col 17 to line 34, col 34 of module test20: 0"); + } +} diff --git a/tlatools/test/tlc2/tool/suite/Test201.java b/tlatools/test/tlc2/tool/suite/Test201.java new file mode 100644 index 0000000000000000000000000000000000000000..a7d3a47d1ce7fd585e0cfcf81fd801ae970e84de --- /dev/null +++ b/tlatools/test/tlc2/tool/suite/Test201.java @@ -0,0 +1,32 @@ +/******************************************************************************* + * Copyright (c) 2016 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.suite; + +public class Test201 extends SuiteTestCase { + public Test201() { + super("0", "0", "0", "0"); + } +} diff --git a/tlatools/test/tlc2/tool/suite/Test202.java b/tlatools/test/tlc2/tool/suite/Test202.java new file mode 100644 index 0000000000000000000000000000000000000000..32b0aeee9089bacd1ef3a960173edcf659779f57 --- /dev/null +++ b/tlatools/test/tlc2/tool/suite/Test202.java @@ -0,0 +1,32 @@ +/******************************************************************************* + * Copyright (c) 2016 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.suite; + +public class Test202 extends SuiteTestCase { + public Test202() { + super("0", "0", "0", "0"); + } +} diff --git a/tlatools/test/tlc2/tool/suite/Test203.java b/tlatools/test/tlc2/tool/suite/Test203.java new file mode 100644 index 0000000000000000000000000000000000000000..579c071bef6bca72efc53859f7488eadb78274bc --- /dev/null +++ b/tlatools/test/tlc2/tool/suite/Test203.java @@ -0,0 +1,32 @@ +/******************************************************************************* + * Copyright (c) 2016 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.suite; + +public class Test203 extends SuiteTestCase { + public Test203() { + super("0", "0", "0", "0"); + } +} diff --git a/tlatools/test/tlc2/tool/suite/Test204.java b/tlatools/test/tlc2/tool/suite/Test204.java new file mode 100644 index 0000000000000000000000000000000000000000..7f294a1837f8f7049d2e100bf6315b351cb2cc7a --- /dev/null +++ b/tlatools/test/tlc2/tool/suite/Test204.java @@ -0,0 +1,32 @@ +/******************************************************************************* + * Copyright (c) 2016 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.suite; + +public class Test204 extends SuiteTestCase { + public Test204() { + super("0", "0", "0", "0"); + } +} diff --git a/tlatools/test/tlc2/tool/suite/Test205.java b/tlatools/test/tlc2/tool/suite/Test205.java new file mode 100644 index 0000000000000000000000000000000000000000..168e55ac3088f8d633969937f4298dd84b90bf57 --- /dev/null +++ b/tlatools/test/tlc2/tool/suite/Test205.java @@ -0,0 +1,32 @@ +/******************************************************************************* + * Copyright (c) 2016 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.suite; + +public class Test205 extends SuiteTestCase { + public Test205() { + super("0", "0", "0", "0"); + } +} diff --git a/tlatools/test/tlc2/tool/suite/Test206.java b/tlatools/test/tlc2/tool/suite/Test206.java new file mode 100644 index 0000000000000000000000000000000000000000..7436d21083dcd1f903eea86a4ece23cd18371489 --- /dev/null +++ b/tlatools/test/tlc2/tool/suite/Test206.java @@ -0,0 +1,32 @@ +/******************************************************************************* + * Copyright (c) 2016 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.suite; + +public class Test206 extends SuiteTestCase { + public Test206() { + super("0", "0", "0", "0"); + } +} diff --git a/tlatools/test/tlc2/tool/suite/Test207.java b/tlatools/test/tlc2/tool/suite/Test207.java new file mode 100644 index 0000000000000000000000000000000000000000..a65c36343fd9ccd6348b833e8f5b08baedcf25dc --- /dev/null +++ b/tlatools/test/tlc2/tool/suite/Test207.java @@ -0,0 +1,32 @@ +/******************************************************************************* + * Copyright (c) 2016 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.suite; + +public class Test207 extends SuiteTestCase { + public Test207() { + super("0", "0", "0", "0"); + } +} diff --git a/tlatools/test/tlc2/tool/suite/Test208.java b/tlatools/test/tlc2/tool/suite/Test208.java new file mode 100644 index 0000000000000000000000000000000000000000..249a535e9678e4f7e8fcddf7c2472074be5be9bd --- /dev/null +++ b/tlatools/test/tlc2/tool/suite/Test208.java @@ -0,0 +1,32 @@ +/******************************************************************************* + * Copyright (c) 2016 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.suite; + +public class Test208 extends SuiteTestCase { + public Test208() { + super("0", "0", "0", "0"); + } +} diff --git a/tlatools/test/tlc2/tool/suite/Test209.java b/tlatools/test/tlc2/tool/suite/Test209.java new file mode 100644 index 0000000000000000000000000000000000000000..c32c251ec76dea3dfaf312902322cac666b57d96 --- /dev/null +++ b/tlatools/test/tlc2/tool/suite/Test209.java @@ -0,0 +1,32 @@ +/******************************************************************************* + * Copyright (c) 2016 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.suite; + +public class Test209 extends SuiteTestCase { + public Test209() { + super("0", "0", "0", "0"); + } +} diff --git a/tlatools/test/tlc2/tool/suite/Test21.java b/tlatools/test/tlc2/tool/suite/Test21.java new file mode 100644 index 0000000000000000000000000000000000000000..344b694f43559ef73e07d2658023ee448be1392d --- /dev/null +++ b/tlatools/test/tlc2/tool/suite/Test21.java @@ -0,0 +1,33 @@ +/******************************************************************************* + * Copyright (c) 2016 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.suite; + +public class Test21 extends SuiteTestCase { + public Test21() { + super("8", "2", "0", "1", "line 59, col 16 to line 59, col 21 of module test21: 0\n" + + "line 60, col 16 to line 60, col 19 of module test21: 0\n"); + } +} diff --git a/tlatools/test/tlc2/tool/suite/Test210.java b/tlatools/test/tlc2/tool/suite/Test210.java new file mode 100644 index 0000000000000000000000000000000000000000..9974e3a5c6b6d9f669c80dc4e13e14e6d4178fae --- /dev/null +++ b/tlatools/test/tlc2/tool/suite/Test210.java @@ -0,0 +1,100 @@ +/******************************************************************************* + * Copyright (c) 2016 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.suite; + +import static org.junit.Assert.assertFalse; + +import org.junit.Test; + +import tlc2.output.EC; +import tlc2.output.EC.ExitStatus; + +public class Test210 extends SuiteETestCase { + + public Test210() { + super(ExitStatus.ERROR_SPEC_PARSE); + } + + @Test + public void testSpec() { + assertFalse(recorder.recorded(EC.GENERAL)); + assertSubstring("Semantic errors:\n" + + "\n" + + "*** Errors: 9\n" + + "\n" + + "line 33, col 16 to line 33, col 19 of module test210\n" + + "\n" + + "Accessing subexpression labeled `laby' of ASSUME/PROVE clause within the scope of a declaration\n" + + " from outside that declaration's scope.\n" + + "\n" + + "\n" + + "line 35, col 16 to line 35, col 16 of module test210\n" + + "\n" + + "Accessing ASSUME/PROVE clause within the scope of a declaration\n" + + " from outside that declaration's scope.\n" + + "\n" + + "\n" + + "line 42, col 41 to line 42, col 55 of module test210\n" + + "\n" + + "Label not allowed within scope of declaration in nested ASSUME/PROVE.\n" + + "\n" + + "\n" + + "line 43, col 30 to line 43, col 44 of module test210\n" + + "\n" + + "Label not allowed within scope of declaration in nested ASSUME/PROVE.\n" + + "\n" + + "\n" + + "line 55, col 16 to line 55, col 16 of module test210\n" + + "\n" + + "Accessing non-existent subexpression of a SUFFICES\n" + + "\n" + + "\n" + + "line 62, col 15 to line 62, col 18 of module test210\n" + + "\n" + + "Accessing subexpression labeled `laby' of ASSUME/PROVE clause within the scope of a declaration\n" + + " from outside that declaration's scope.\n" + + "\n" + + "\n" + + "line 63, col 15 to line 63, col 18 of module test210\n" + + "\n" + + "Accessing subexpression labeled `labi' of ASSUME/PROVE clause within the scope of a declaration\n" + + " from outside that declaration's scope.\n" + + "\n" + + "\n" + + "line 64, col 15 to line 64, col 18 of module test210\n" + + "\n" + + "Accessing subexpression labeled `labu' of ASSUME/PROVE clause within the scope of a declaration\n" + + " from outside that declaration's scope.\n" + + "\n" + + "\n" + + "line 65, col 15 to line 65, col 15 of module test210\n" + + "\n" + + "Accessing ASSUME/PROVE clause within the scope of a declaration\n" + + " from outside that declaration's scope.\n" + + "\n" + + "\n"); + } +} diff --git a/tlatools/test/tlc2/tool/suite/Test212.java b/tlatools/test/tlc2/tool/suite/Test212.java new file mode 100644 index 0000000000000000000000000000000000000000..5a1cb6cec586378cf63e7653158785c48952867d --- /dev/null +++ b/tlatools/test/tlc2/tool/suite/Test212.java @@ -0,0 +1,86 @@ +/******************************************************************************* + * Copyright (c) 2016 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.suite; + +import static org.junit.Assert.assertFalse; + +import org.junit.Test; + +import tlc2.output.EC; +import tlc2.output.EC.ExitStatus; + +public class Test212 extends SuiteETestCase { + + public Test212() { + super(ExitStatus.ERROR_SPEC_PARSE); + } + + @Test + public void testSpec() { + assertFalse(recorder.recorded(EC.GENERAL)); + assertSubstring("Semantic errors:\n" + + "\n" + + "*** Errors: 6\n" + + "\n" + + "line 27, col 1 to line 27, col 62 of module test212\n" + + "\n" + + "Error in instantiating module 'test212a':\n" + + " A non-Leibniz operator substituted for 'Op2'.\n" + + "\n" + + "\n" + + "line 28, col 1 to line 28, col 62 of module test212\n" + + "\n" + + "Error in instantiating module 'test212a':\n" + + " A non-Leibniz operator substituted for 'Op'.\n" + + "\n" + + "\n" + + "line 29, col 1 to line 29, col 62 of module test212\n" + + "\n" + + "Error in instantiating module 'test212a':\n" + + " A non-Leibniz operator substituted for 'Op'.\n" + + "\n" + + "\n" + + "line 30, col 1 to line 30, col 64 of module test212\n" + + "\n" + + "Error in instantiating module 'test212a':\n" + + " A non-Leibniz operator substituted for 'Op2'.\n" + + "\n" + + "\n" + + "line 31, col 1 to line 31, col 63 of module test212\n" + + "\n" + + "Error in instantiating module 'test212a':\n" + + " A non-Leibniz operator substituted for 'Op2'.\n" + + "\n" + + "\n" + + "line 32, col 1 to line 32, col 63 of module test212\n" + + "\n" + + "Error in instantiating module 'test212a':\n" + + " A non-Leibniz operator substituted for 'Op2'.\n" + + "\n" + + "\n" + + ""); + } +} diff --git a/tlatools/test/tlc2/tool/suite/Test213.java b/tlatools/test/tlc2/tool/suite/Test213.java new file mode 100644 index 0000000000000000000000000000000000000000..e11f6b635b2df304e4a1ee36ab1c7ef35e49949a --- /dev/null +++ b/tlatools/test/tlc2/tool/suite/Test213.java @@ -0,0 +1,79 @@ +/******************************************************************************* + * Copyright (c) 2016 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.suite; + +import static org.junit.Assert.assertFalse; + +import org.junit.Test; + +import tlc2.output.EC; +import tlc2.output.EC.ExitStatus; + +public class Test213 extends SuiteETestCase { + + public Test213() { + super(ExitStatus.ERROR_SPEC_PARSE); + } + + @Test + public void testSpec() { + assertFalse(recorder.recorded(EC.GENERAL)); + assertSubstring("Semantic errors:\n" + + "\n" + + "*** Errors: 5\n" + + "\n" + + "line 13, col 1 to line 13, col 52 of module test213\n" + + "\n" + + "Level error in instantiating module 'test213b':\n" + + "The level of the expression or operator substituted for 'C' \n" + + "must be at most 2.\n" + + "\n" + + "\n" + + "line 14, col 1 to line 14, col 52 of module test213\n" + + "\n" + + "Level error in instantiating module 'test213b':\n" + + "The level of the expression or operator substituted for 'D' \n" + + "must be at most 2.\n" + + "\n" + + "\n" + + "line 29, col 8 to line 29, col 15 of module test213\n" + + "\n" + + "Non-constant CASE for temporal goal.\n" + + "\n" + + "\n" + + "line 31, col 8 to line 31, col 15 of module test213\n" + + "\n" + + "Non-constant TAKE, WITNESS, or HAVE for temporal goal.\n" + + "\n" + + "\n" + + "line 33, col 8 to line 33, col 22 of module test213\n" + + "\n" + + "Non-constant TAKE, WITNESS, or HAVE for temporal goal.\n" + + "\n" + + "\n" + + ""); + } +} diff --git a/tlatools/test/tlc2/tool/suite/Test214.java b/tlatools/test/tlc2/tool/suite/Test214.java new file mode 100644 index 0000000000000000000000000000000000000000..d2cbd0887e04170b92772eea9a49f883fb17d65f --- /dev/null +++ b/tlatools/test/tlc2/tool/suite/Test214.java @@ -0,0 +1,55 @@ +/******************************************************************************* + * Copyright (c) 2016 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.suite; + +import static org.junit.Assert.assertFalse; + +import org.junit.Test; + +import tlc2.output.EC; +import tlc2.output.EC.ExitStatus; + +public class Test214 extends SuiteETestCase { + + public Test214() { + super(ExitStatus.ERROR_SPEC_PARSE); + } + + @Test + public void testSpec() { + assertFalse(recorder.recorded(EC.GENERAL)); + assertSubstring("Semantic errors:\n" + + "\n" + + "*** Errors: 1\n" + + "\n" + + "line 10, col 11 to line 10, col 14 of module test214\n" + + "\n" + + "The only expression allowed as a fact in a HIDE is \n" + + "the name of a theorem, assumption, or step.\n" + + "\n" + + "\n"); + } +} diff --git a/tlatools/test/tlc2/tool/suite/Test215.java b/tlatools/test/tlc2/tool/suite/Test215.java new file mode 100644 index 0000000000000000000000000000000000000000..0b1d74e75cd73c5caec98c4cd791867275912ded --- /dev/null +++ b/tlatools/test/tlc2/tool/suite/Test215.java @@ -0,0 +1,89 @@ +/******************************************************************************* + * Copyright (c) 2016 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.suite; + +import static org.junit.Assert.assertFalse; + +import org.junit.Test; + +import tlc2.output.EC; +import tlc2.output.EC.ExitStatus; + +public class Test215 extends SuiteETestCase { + + public Test215() { + super(ExitStatus.ERROR_SPEC_PARSE); + } + + @Test + public void testSpec() { + assertFalse(recorder.recorded(EC.GENERAL)); + assertSubstring("Semantic errors:\n" + + "\n" + + "*** Errors: 8\n" + + "\n" + + "line 8, col 6 to line 8, col 14 of module test215\n" + + "\n" + + "Action used where only temporal formula or state predicate allowed.\n" + + "\n" + + "\n" + + "line 9, col 6 to line 9, col 14 of module test215\n" + + "\n" + + "Action used where only temporal formula or state predicate allowed.\n" + + "\n" + + "\n" + + "line 12, col 6 to line 12, col 16 of module test215\n" + + "\n" + + "Action used where only temporal formula or state predicate allowed.\n" + + "\n" + + "\n" + + "line 13, col 6 to line 13, col 16 of module test215\n" + + "\n" + + "Action used where only temporal formula or state predicate allowed.\n" + + "\n" + + "\n" + + "line 16, col 6 to line 16, col 11 of module test215\n" + + "\n" + + "<> followed by action not of form <<A>>_v.\n" + + "\n" + + "\n" + + "line 20, col 8 to line 20, col 13 of module test215\n" + + "\n" + + "[] followed by action not of form [A]_v.\n" + + "\n" + + "\n" + + "line 23, col 25 to line 23, col 26 of module test215\n" + + "\n" + + "Action-level bound of quantified temporal formula.\n" + + "\n" + + "\n" + + "line 26, col 26 to line 26, col 27 of module test215\n" + + "\n" + + "Action-level bound of quantified temporal formula.\n" + + "\n" + + "\n"); + } +} diff --git a/tlatools/test/tlc2/tool/suite/Test216.java b/tlatools/test/tlc2/tool/suite/Test216.java new file mode 100644 index 0000000000000000000000000000000000000000..ace958c23af804ff79b3a63b90963494b2f9d6c7 --- /dev/null +++ b/tlatools/test/tlc2/tool/suite/Test216.java @@ -0,0 +1,45 @@ +/******************************************************************************* + * Copyright (c) 2016 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.suite; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.output.EC; + +public class Test216 extends SuiteETestCase { + @Test + public void testSpec() { + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "28", "7", "0")); + assertTrue(recorder.recordedWithStringValues(EC.TLC_INIT_GENERATED2, "21", "s", "7")); + assertFalse(recorder.recorded(EC.GENERAL)); + + assertUncovered("line 12, col 22 to line 12, col 32 of module test216: 0\n"); + } +} diff --git a/tlatools/test/tlc2/tool/suite/Test217.java b/tlatools/test/tlc2/tool/suite/Test217.java new file mode 100644 index 0000000000000000000000000000000000000000..4575c00f5737ab1d314bd5cee3a747204115b445 --- /dev/null +++ b/tlatools/test/tlc2/tool/suite/Test217.java @@ -0,0 +1,61 @@ +/******************************************************************************* + * Copyright (c) 2016 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.suite; + +import static org.junit.Assert.assertFalse; + +import org.junit.Test; + +import tlc2.output.EC; +import tlc2.output.EC.ExitStatus; + +public class Test217 extends SuiteETestCase { + + public Test217() { + super(ExitStatus.ERROR_SPEC_PARSE); + } + + @Test + public void testSpec() { + assertFalse(recorder.recorded(EC.GENERAL)); + assertSubstring("Semantic errors:\n" + + "\n" + + "*** Errors: 2\n" + + "\n" + + "line 12, col 11 to line 12, col 19 of module test217\n" + + "\n" + + "Level error in applying operator I!Foo:\n" + + "The level of argument 1 exceeds the maximum level allowed by the operator.\n" + + "\n" + + "\n" + + "line 13, col 9 to line 13, col 19 of module test217\n" + + "\n" + + "Level error in applying operator I!Foo:\n" + + "The level of argument 1 exceeds the maximum level allowed by the operator.\n" + + "\n" + + "\n"); + } +} diff --git a/tlatools/test/tlc2/tool/suite/Test219.java b/tlatools/test/tlc2/tool/suite/Test219.java new file mode 100644 index 0000000000000000000000000000000000000000..dcf9f9bdb36edf7f20f85a3d0a296b34711715c0 --- /dev/null +++ b/tlatools/test/tlc2/tool/suite/Test219.java @@ -0,0 +1,32 @@ +/******************************************************************************* + * Copyright (c) 2016 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.suite; + +public class Test219 extends SuiteTestCase { + public Test219() { + super("0", "0", "0", "0"); + } +} diff --git a/tlatools/test/tlc2/tool/suite/Test22.java b/tlatools/test/tlc2/tool/suite/Test22.java new file mode 100644 index 0000000000000000000000000000000000000000..9eb0e371b062c5e066af726c9e1db9ec77beb71b --- /dev/null +++ b/tlatools/test/tlc2/tool/suite/Test22.java @@ -0,0 +1,32 @@ +/******************************************************************************* + * Copyright (c) 2016 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.suite; + +public class Test22 extends SuiteTestCase { + public Test22() { + super("1344", "64", "0", "64"); + } +} diff --git a/tlatools/test/tlc2/tool/suite/Test220.java b/tlatools/test/tlc2/tool/suite/Test220.java new file mode 100644 index 0000000000000000000000000000000000000000..259446f119cc80aed98fb104d02536a7d435b97f --- /dev/null +++ b/tlatools/test/tlc2/tool/suite/Test220.java @@ -0,0 +1,51 @@ +/******************************************************************************* + * Copyright (c) 2020 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + ******************************************************************************/ +package tlc2.tool.suite; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.output.EC; + +public class Test220 extends SuiteETestCase { + + /* (non-Javadoc) + * @see tlc2.tool.liveness.ModelCheckerTestCase#checkDeadLock() + */ + protected boolean checkDeadLock() { + // Disable command-line parameter "-deadlock" to let configuration file take control. + return true; + } + + @Test + public void testSpec() { + assertFalse(recorder.recorded(EC.TLC_DEADLOCK_REACHED)); + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "2", "2", "0")); + assertFalse(recorder.recorded(EC.GENERAL)); + + assertZeroUncovered(); + } +} diff --git a/tlatools/test/tlc2/tool/suite/Test23.java b/tlatools/test/tlc2/tool/suite/Test23.java new file mode 100644 index 0000000000000000000000000000000000000000..a97ec032c739baa3ee5d4ddb4813ec7e1dbcdd40 --- /dev/null +++ b/tlatools/test/tlc2/tool/suite/Test23.java @@ -0,0 +1,30 @@ +/******************************************************************************* + * Copyright (c) 2016 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.suite; + +public class Test23 extends SuiteTestCase { + +} diff --git a/tlatools/test/tlc2/tool/suite/Test24.java b/tlatools/test/tlc2/tool/suite/Test24.java new file mode 100644 index 0000000000000000000000000000000000000000..ead4a5db9668a8f5c2e13c1e92555a38bc7a6d07 --- /dev/null +++ b/tlatools/test/tlc2/tool/suite/Test24.java @@ -0,0 +1,32 @@ +/******************************************************************************* + * Copyright (c) 2016 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.suite; + +public class Test24 extends SuiteTestCase { + public Test24() { + super("21", "2", "0", "1"); + } +} diff --git a/tlatools/test/tlc2/tool/suite/Test25.java b/tlatools/test/tlc2/tool/suite/Test25.java new file mode 100644 index 0000000000000000000000000000000000000000..c5b290ea24df3afda2a54d6219c9548091c0328b --- /dev/null +++ b/tlatools/test/tlc2/tool/suite/Test25.java @@ -0,0 +1,32 @@ +/******************************************************************************* + * Copyright (c) 2016 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.suite; + +public class Test25 extends SuiteTestCase { + public Test25() { + super("3", "1", "0", "1"); + } +} diff --git a/tlatools/test/tlc2/tool/suite/Test26.java b/tlatools/test/tlc2/tool/suite/Test26.java new file mode 100644 index 0000000000000000000000000000000000000000..fe30483472ca481f4cdffc09aa07ee39fbc61252 --- /dev/null +++ b/tlatools/test/tlc2/tool/suite/Test26.java @@ -0,0 +1,30 @@ +/******************************************************************************* + * Copyright (c) 2016 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.suite; + +public class Test26 extends SuiteTestCase { + +} diff --git a/tlatools/test/tlc2/tool/suite/Test27.java b/tlatools/test/tlc2/tool/suite/Test27.java new file mode 100644 index 0000000000000000000000000000000000000000..8353f98f97a4c88921ed0a759d451b16c639b932 --- /dev/null +++ b/tlatools/test/tlc2/tool/suite/Test27.java @@ -0,0 +1,32 @@ +/******************************************************************************* + * Copyright (c) 2016 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.suite; + +public class Test27 extends SuiteTestCase { + public Test27() { + super("1109824", "63504", "0", "16"); + } +} diff --git a/tlatools/test/tlc2/tool/suite/Test28.java b/tlatools/test/tlc2/tool/suite/Test28.java new file mode 100644 index 0000000000000000000000000000000000000000..05e3c4b2c6a3d37ce6124e9ad7e8329d0ac64352 --- /dev/null +++ b/tlatools/test/tlc2/tool/suite/Test28.java @@ -0,0 +1,30 @@ +/******************************************************************************* + * Copyright (c) 2016 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.suite; + +public class Test28 extends SuiteTestCase { + +} diff --git a/tlatools/test/tlc2/tool/suite/Test29.java b/tlatools/test/tlc2/tool/suite/Test29.java new file mode 100644 index 0000000000000000000000000000000000000000..2c6153c36aaf091acc7f4d9930ee0c5a11552d90 --- /dev/null +++ b/tlatools/test/tlc2/tool/suite/Test29.java @@ -0,0 +1,30 @@ +/******************************************************************************* + * Copyright (c) 2016 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.suite; + +public class Test29 extends SuiteTestCase { + +} diff --git a/tlatools/test/tlc2/tool/suite/Test3.java b/tlatools/test/tlc2/tool/suite/Test3.java new file mode 100644 index 0000000000000000000000000000000000000000..65b5dd3a8bb1ffeb04df73b333e7b23f15998cf5 --- /dev/null +++ b/tlatools/test/tlc2/tool/suite/Test3.java @@ -0,0 +1,30 @@ +/******************************************************************************* + * Copyright (c) 2016 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.suite; + +public class Test3 extends SuiteTestCase { + +} diff --git a/tlatools/test/tlc2/tool/suite/Test30.java b/tlatools/test/tlc2/tool/suite/Test30.java new file mode 100644 index 0000000000000000000000000000000000000000..1ebdf08a3be06b86b06a8761f88ec7cfc7fc48da --- /dev/null +++ b/tlatools/test/tlc2/tool/suite/Test30.java @@ -0,0 +1,30 @@ +/******************************************************************************* + * Copyright (c) 2016 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.suite; + +public class Test30 extends SuiteTestCase { + +} diff --git a/tlatools/test/tlc2/tool/suite/Test31.java b/tlatools/test/tlc2/tool/suite/Test31.java new file mode 100644 index 0000000000000000000000000000000000000000..1650624c2f2ca17c393d3539ef0104eeaea8a66d --- /dev/null +++ b/tlatools/test/tlc2/tool/suite/Test31.java @@ -0,0 +1,32 @@ +/******************************************************************************* + * Copyright (c) 2016 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.suite; + +public class Test31 extends SuiteTestCase { + public Test31() { + super("13", "3", "0", "1"); + } +} diff --git a/tlatools/test/tlc2/tool/suite/Test32.java b/tlatools/test/tlc2/tool/suite/Test32.java new file mode 100644 index 0000000000000000000000000000000000000000..94decb30f94b876e76acdbd948b3919973ab001d --- /dev/null +++ b/tlatools/test/tlc2/tool/suite/Test32.java @@ -0,0 +1,34 @@ +/******************************************************************************* + * Copyright (c) 2016 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.suite; + +public class Test32 extends SuiteTestCase { + public Test32() { + super("3", "1", "0", "1", "line 23, col 17 to line 23, col 21 of module test32: 0\n" + + "line 23, col 26 to line 23, col 29 of module test32: 0\n" + + "line 27, col 23 to line 27, col 29 of module test32: 0\n"); + } +} \ No newline at end of file diff --git a/tlatools/test/tlc2/tool/suite/Test33.java b/tlatools/test/tlc2/tool/suite/Test33.java new file mode 100644 index 0000000000000000000000000000000000000000..126489f802d569f8c02e4ddc348b115502f90d42 --- /dev/null +++ b/tlatools/test/tlc2/tool/suite/Test33.java @@ -0,0 +1,32 @@ +/******************************************************************************* + * Copyright (c) 2016 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.suite; + +public class Test33 extends SuiteTestCase { + public Test33() { + super("235298", "117649", "0", "117649"); + } +} \ No newline at end of file diff --git a/tlatools/test/tlc2/tool/suite/Test34.java b/tlatools/test/tlc2/tool/suite/Test34.java new file mode 100644 index 0000000000000000000000000000000000000000..32d0702fc5ab908890a3fe614128f8fc197f5403 --- /dev/null +++ b/tlatools/test/tlc2/tool/suite/Test34.java @@ -0,0 +1,30 @@ +/******************************************************************************* + * Copyright (c) 2016 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.suite; + +public class Test34 extends SuiteTestCase { + +} diff --git a/tlatools/test/tlc2/tool/suite/Test35.java b/tlatools/test/tlc2/tool/suite/Test35.java new file mode 100644 index 0000000000000000000000000000000000000000..bf9ef33f767fcbbb45d88bd0a5e9736dea6d8459 --- /dev/null +++ b/tlatools/test/tlc2/tool/suite/Test35.java @@ -0,0 +1,30 @@ +/******************************************************************************* + * Copyright (c) 2016 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.suite; + +public class Test35 extends SuiteTestCase { + +} diff --git a/tlatools/test/tlc2/tool/suite/Test36.java b/tlatools/test/tlc2/tool/suite/Test36.java new file mode 100644 index 0000000000000000000000000000000000000000..ce6762f6ca2d313aee3b940c5d518c28f3cc9e7a --- /dev/null +++ b/tlatools/test/tlc2/tool/suite/Test36.java @@ -0,0 +1,30 @@ +/******************************************************************************* + * Copyright (c) 2016 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.suite; + +public class Test36 extends SuiteTestCase { + +} diff --git a/tlatools/test/tlc2/tool/suite/Test37.java b/tlatools/test/tlc2/tool/suite/Test37.java new file mode 100644 index 0000000000000000000000000000000000000000..d94e6bdbc7c304a66fe2d66f09aad0f4a6907d38 --- /dev/null +++ b/tlatools/test/tlc2/tool/suite/Test37.java @@ -0,0 +1,30 @@ +/******************************************************************************* + * Copyright (c) 2016 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.suite; + +public class Test37 extends SuiteTestCase { + +} diff --git a/tlatools/test/tlc2/tool/suite/Test38.java b/tlatools/test/tlc2/tool/suite/Test38.java new file mode 100644 index 0000000000000000000000000000000000000000..63f083299e2e890675f9bf9c688fc6eec403ea3f --- /dev/null +++ b/tlatools/test/tlc2/tool/suite/Test38.java @@ -0,0 +1,30 @@ +/******************************************************************************* + * Copyright (c) 2016 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.suite; + +public class Test38 extends SuiteTestCase { + +} diff --git a/tlatools/test/tlc2/tool/suite/Test39.java b/tlatools/test/tlc2/tool/suite/Test39.java new file mode 100644 index 0000000000000000000000000000000000000000..f71ea0b574966446180bd32fb0c5e983b1e4747e --- /dev/null +++ b/tlatools/test/tlc2/tool/suite/Test39.java @@ -0,0 +1,32 @@ +/******************************************************************************* + * Copyright (c) 2016 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.suite; + +public class Test39 extends SuiteTestCase { + public Test39() { + super("6", "3", "0", "1"); + } +} \ No newline at end of file diff --git a/tlatools/test/tlc2/tool/suite/Test4.java b/tlatools/test/tlc2/tool/suite/Test4.java new file mode 100644 index 0000000000000000000000000000000000000000..2e42e72500bc020c470e158db88a4af2597b0768 --- /dev/null +++ b/tlatools/test/tlc2/tool/suite/Test4.java @@ -0,0 +1,32 @@ +/******************************************************************************* + * Copyright (c) 2016 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.suite; + +public class Test4 extends SuiteTestCase { + public Test4() { + super("11", "1", "0", "1"); + } +} diff --git a/tlatools/test/tlc2/tool/suite/Test40.java b/tlatools/test/tlc2/tool/suite/Test40.java new file mode 100644 index 0000000000000000000000000000000000000000..6ba4a84e94d9e78e7180f438fb105065d145d4ce --- /dev/null +++ b/tlatools/test/tlc2/tool/suite/Test40.java @@ -0,0 +1,30 @@ +/******************************************************************************* + * Copyright (c) 2016 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.suite; + +public class Test40 extends SuiteTestCase { + +} diff --git a/tlatools/test/tlc2/tool/suite/Test41.java b/tlatools/test/tlc2/tool/suite/Test41.java new file mode 100644 index 0000000000000000000000000000000000000000..059d9a1ad4870891ea82b7599d1a1a75f826ad51 --- /dev/null +++ b/tlatools/test/tlc2/tool/suite/Test41.java @@ -0,0 +1,30 @@ +/******************************************************************************* + * Copyright (c) 2016 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.suite; + +public class Test41 extends SuiteTestCase { + +} diff --git a/tlatools/test/tlc2/tool/suite/Test42.java b/tlatools/test/tlc2/tool/suite/Test42.java new file mode 100644 index 0000000000000000000000000000000000000000..a3b848de071e62f972365037331a47fbdbfbb2ae --- /dev/null +++ b/tlatools/test/tlc2/tool/suite/Test42.java @@ -0,0 +1,30 @@ +/******************************************************************************* + * Copyright (c) 2016 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.suite; + +public class Test42 extends SuiteTestCase { + +} diff --git a/tlatools/test/tlc2/tool/suite/Test43.java b/tlatools/test/tlc2/tool/suite/Test43.java new file mode 100644 index 0000000000000000000000000000000000000000..78103b9eae1c42b65118bfda40b8d6219f8c05b0 --- /dev/null +++ b/tlatools/test/tlc2/tool/suite/Test43.java @@ -0,0 +1,30 @@ +/******************************************************************************* + * Copyright (c) 2016 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.suite; + +public class Test43 extends SuiteTestCase { + +} diff --git a/tlatools/test/tlc2/tool/suite/Test44.java b/tlatools/test/tlc2/tool/suite/Test44.java new file mode 100644 index 0000000000000000000000000000000000000000..1fa8a4476348b56088e5e96bfa1c5c076fd5d727 --- /dev/null +++ b/tlatools/test/tlc2/tool/suite/Test44.java @@ -0,0 +1,30 @@ +/******************************************************************************* + * Copyright (c) 2016 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.suite; + +public class Test44 extends SuiteTestCase { + +} diff --git a/tlatools/test/tlc2/tool/suite/Test45.java b/tlatools/test/tlc2/tool/suite/Test45.java new file mode 100644 index 0000000000000000000000000000000000000000..2ba45bfc4b44cf749ecc8fe2ccbb48db189af0c9 --- /dev/null +++ b/tlatools/test/tlc2/tool/suite/Test45.java @@ -0,0 +1,30 @@ +/******************************************************************************* + * Copyright (c) 2016 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.suite; + +public class Test45 extends SuiteTestCase { + +} diff --git a/tlatools/test/tlc2/tool/suite/Test46.java b/tlatools/test/tlc2/tool/suite/Test46.java new file mode 100644 index 0000000000000000000000000000000000000000..a4c33c30e781103ba111330e3af6d09ebef302ab --- /dev/null +++ b/tlatools/test/tlc2/tool/suite/Test46.java @@ -0,0 +1,32 @@ +/******************************************************************************* + * Copyright (c) 2016 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.suite; + +public class Test46 extends SuiteTestCase { + public Test46() { + super("121", "6", "0", "1"); + } +} \ No newline at end of file diff --git a/tlatools/test/tlc2/tool/suite/Test47.java b/tlatools/test/tlc2/tool/suite/Test47.java new file mode 100644 index 0000000000000000000000000000000000000000..ded35b6adfb01db8ca83e56ae0359dfc6cc1a664 --- /dev/null +++ b/tlatools/test/tlc2/tool/suite/Test47.java @@ -0,0 +1,32 @@ +/******************************************************************************* + * Copyright (c) 2016 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.suite; + +public class Test47 extends SuiteTestCase { + public Test47() { + super("4", "3", "0", "1"); + } +} \ No newline at end of file diff --git a/tlatools/test/tlc2/tool/suite/Test48.java b/tlatools/test/tlc2/tool/suite/Test48.java new file mode 100644 index 0000000000000000000000000000000000000000..8537c7f72de825c891a6cc22eb6cf34329bba845 --- /dev/null +++ b/tlatools/test/tlc2/tool/suite/Test48.java @@ -0,0 +1,32 @@ +/******************************************************************************* + * Copyright (c) 2016 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.suite; + +public class Test48 extends SuiteTestCase { + public Test48() { + super("64", "1", "0", "1"); + } +} \ No newline at end of file diff --git a/tlatools/test/tlc2/tool/suite/Test49.java b/tlatools/test/tlc2/tool/suite/Test49.java new file mode 100644 index 0000000000000000000000000000000000000000..3f2d0fca27ef12db288308738d44b5a9eb5ebd5f --- /dev/null +++ b/tlatools/test/tlc2/tool/suite/Test49.java @@ -0,0 +1,32 @@ +/******************************************************************************* + * Copyright (c) 2016 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.suite; + +public class Test49 extends SuiteTestCase { + public Test49() { + super("10", "1", "0", "1"); + } +} \ No newline at end of file diff --git a/tlatools/test/tlc2/tool/suite/Test5.java b/tlatools/test/tlc2/tool/suite/Test5.java new file mode 100644 index 0000000000000000000000000000000000000000..ff3c020840a57abf8220df632eadd9c28d4ddf84 --- /dev/null +++ b/tlatools/test/tlc2/tool/suite/Test5.java @@ -0,0 +1,29 @@ +/******************************************************************************* + * Copyright (c) 2016 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.suite; + +public class Test5 extends SuiteTestCase { +} diff --git a/tlatools/test/tlc2/tool/suite/Test50.java b/tlatools/test/tlc2/tool/suite/Test50.java new file mode 100644 index 0000000000000000000000000000000000000000..3a33c567067e582fbfb7fde89ce8e90d42b1e895 --- /dev/null +++ b/tlatools/test/tlc2/tool/suite/Test50.java @@ -0,0 +1,30 @@ +/******************************************************************************* + * Copyright (c) 2016 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.suite; + +public class Test50 extends SuiteTestCase { + +} diff --git a/tlatools/test/tlc2/tool/suite/Test51.java b/tlatools/test/tlc2/tool/suite/Test51.java new file mode 100644 index 0000000000000000000000000000000000000000..ed9a0f0bb629140e1e1d720fc1e2ac0592fbf1a7 --- /dev/null +++ b/tlatools/test/tlc2/tool/suite/Test51.java @@ -0,0 +1,32 @@ +/******************************************************************************* + * Copyright (c) 2016 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.suite; + +public class Test51 extends SuiteTestCase { + public Test51() { + super("0", "0", "0", "0"); + } +} \ No newline at end of file diff --git a/tlatools/test/tlc2/tool/suite/Test52.java b/tlatools/test/tlc2/tool/suite/Test52.java new file mode 100644 index 0000000000000000000000000000000000000000..3f3d52317b4cfbd06a7cdaec4c0662de88b7b9cd --- /dev/null +++ b/tlatools/test/tlc2/tool/suite/Test52.java @@ -0,0 +1,32 @@ +/******************************************************************************* + * Copyright (c) 2016 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.suite; + +public class Test52 extends SuiteTestCase { + public Test52() { + super("3", "2", "0", "1"); + } +} diff --git a/tlatools/test/tlc2/tool/suite/Test53.java b/tlatools/test/tlc2/tool/suite/Test53.java new file mode 100644 index 0000000000000000000000000000000000000000..c9314bd6c320bca235ab77210730700644b69a84 --- /dev/null +++ b/tlatools/test/tlc2/tool/suite/Test53.java @@ -0,0 +1,32 @@ +/******************************************************************************* + * Copyright (c) 2016 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.suite; + +public class Test53 extends SuiteTestCase { + public Test53() { + super("10", "1", "0", "1"); + } +} \ No newline at end of file diff --git a/tlatools/test/tlc2/tool/suite/Test54.java b/tlatools/test/tlc2/tool/suite/Test54.java new file mode 100644 index 0000000000000000000000000000000000000000..36600ea77819d0b8497af0f00691690813a5b447 --- /dev/null +++ b/tlatools/test/tlc2/tool/suite/Test54.java @@ -0,0 +1,32 @@ +/******************************************************************************* + * Copyright (c) 2016 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.suite; + +public class Test54 extends SuiteTestCase { + public Test54() { + super("10", "1", "0", "1"); + } +} diff --git a/tlatools/test/tlc2/tool/suite/Test55.java b/tlatools/test/tlc2/tool/suite/Test55.java new file mode 100644 index 0000000000000000000000000000000000000000..a034f870693a844f213aab9320422b5528a01748 --- /dev/null +++ b/tlatools/test/tlc2/tool/suite/Test55.java @@ -0,0 +1,32 @@ +/******************************************************************************* + * Copyright (c) 2016 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.suite; + +public class Test55 extends SuiteTestCase { + public Test55() { + super("3", "2", "0", "1"); + } +} diff --git a/tlatools/test/tlc2/tool/suite/Test56.java b/tlatools/test/tlc2/tool/suite/Test56.java new file mode 100644 index 0000000000000000000000000000000000000000..1fdc56d9bc371f866c04516961ce7f62a78f5717 --- /dev/null +++ b/tlatools/test/tlc2/tool/suite/Test56.java @@ -0,0 +1,32 @@ +/******************************************************************************* + * Copyright (c) 2016 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.suite; + +public class Test56 extends SuiteTestCase { + public Test56() { + super("9", "6", "0", "3"); + } +} diff --git a/tlatools/test/tlc2/tool/suite/Test57.java b/tlatools/test/tlc2/tool/suite/Test57.java new file mode 100644 index 0000000000000000000000000000000000000000..7ee10dec3881f10844dbac36e338ab103d4ba3d8 --- /dev/null +++ b/tlatools/test/tlc2/tool/suite/Test57.java @@ -0,0 +1,54 @@ +/******************************************************************************* + * Copyright (c) 2015 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ + +package tlc2.tool.suite; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.output.EC; +import tlc2.output.EC.ExitStatus; + +public class Test57 extends SuiteTestCase { + + public Test57() { + // Can pass any value to super because testSpec is overriden. + super("-1", "-1", "-1", "-1"); + setExitStatus(ExitStatus.VIOLATION_SAFETY); + } + + @Test + public void testSpec() { + // ModelChecker has finished and generated the expected amount of states + assertTrue(recorder.recorded(EC.TLC_MODE_MC)); + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertFalse(recorder.recorded(EC.GENERAL)); + assertTrue(recorder.recorded(EC.TLC_COMPUTING_INIT)); + assertTrue(recorder.recordedWithStringValues(EC.TLC_INVARIANT_VIOLATED_INITIAL, "Invariant1", "x = 1\n")); + } +} diff --git a/tlatools/test/tlc2/tool/suite/Test58.java b/tlatools/test/tlc2/tool/suite/Test58.java new file mode 100644 index 0000000000000000000000000000000000000000..8d2bba3c4d3a35b05fdae4002d51b841b2e4d714 --- /dev/null +++ b/tlatools/test/tlc2/tool/suite/Test58.java @@ -0,0 +1,32 @@ +/******************************************************************************* + * Copyright (c) 2016 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.suite; + +public class Test58 extends SuiteTestCase { + public Test58() { + super("0", "0", "0", "0"); + } +} diff --git a/tlatools/test/tlc2/tool/suite/Test59.java b/tlatools/test/tlc2/tool/suite/Test59.java new file mode 100644 index 0000000000000000000000000000000000000000..0b2f1172a3edb6f73b73d19c90475a902d1fb80b --- /dev/null +++ b/tlatools/test/tlc2/tool/suite/Test59.java @@ -0,0 +1,32 @@ +/******************************************************************************* + * Copyright (c) 2016 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.suite; + +public class Test59 extends SuiteTestCase { + public Test59() { + super("6", "5", "0", "1"); + } +} diff --git a/tlatools/test/tlc2/tool/suite/Test6.java b/tlatools/test/tlc2/tool/suite/Test6.java new file mode 100644 index 0000000000000000000000000000000000000000..6877ab55644e57e8283b2cc1cfbf83ad13bbebd9 --- /dev/null +++ b/tlatools/test/tlc2/tool/suite/Test6.java @@ -0,0 +1,29 @@ +/******************************************************************************* + * Copyright (c) 2016 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.suite; + +public class Test6 extends SuiteTestCase { +} diff --git a/tlatools/test/tlc2/tool/suite/Test60.java b/tlatools/test/tlc2/tool/suite/Test60.java new file mode 100644 index 0000000000000000000000000000000000000000..fc639b61e0202bdd2145898cb2a43f04b606e762 --- /dev/null +++ b/tlatools/test/tlc2/tool/suite/Test60.java @@ -0,0 +1,32 @@ +/******************************************************************************* + * Copyright (c) 2016 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.suite; + +public class Test60 extends SuiteTestCase { + public Test60() { + super("0", "0", "0", "0"); + } +} diff --git a/tlatools/test/tlc2/tool/suite/Test61.java b/tlatools/test/tlc2/tool/suite/Test61.java new file mode 100644 index 0000000000000000000000000000000000000000..a951f4cd770b9b64bc32495d6045e47fb2b0907b --- /dev/null +++ b/tlatools/test/tlc2/tool/suite/Test61.java @@ -0,0 +1,33 @@ +/******************************************************************************* + * Copyright (c) 2016 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.suite; + +import org.junit.Ignore; + +@Ignore("known bug, it Handles JSpec but not ISpec") +public class Test61 extends SuiteTestCase { + +} diff --git a/tlatools/test/tlc2/tool/suite/Test62.java b/tlatools/test/tlc2/tool/suite/Test62.java new file mode 100644 index 0000000000000000000000000000000000000000..e057a74094cb68038107361d6d4c3d50276cc1ce --- /dev/null +++ b/tlatools/test/tlc2/tool/suite/Test62.java @@ -0,0 +1,32 @@ +/******************************************************************************* + * Copyright (c) 2016 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.suite; + +public class Test62 extends SuiteTestCase { + public Test62() { + super("0", "0", "0", "0"); + } +} diff --git a/tlatools/test/tlc2/tool/suite/Test63.java b/tlatools/test/tlc2/tool/suite/Test63.java new file mode 100644 index 0000000000000000000000000000000000000000..37a9b0701095bf07c6bcdf9ed4529850772324ba --- /dev/null +++ b/tlatools/test/tlc2/tool/suite/Test63.java @@ -0,0 +1,32 @@ +/******************************************************************************* + * Copyright (c) 2016 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.suite; + +public class Test63 extends SuiteTestCase { + public Test63() { + super("696", "216", "0", "72"); + } +} diff --git a/tlatools/test/tlc2/tool/suite/Test63a.java b/tlatools/test/tlc2/tool/suite/Test63a.java new file mode 100644 index 0000000000000000000000000000000000000000..5f02e3908ca68f7db16cfcd3a87511a68d816bfd --- /dev/null +++ b/tlatools/test/tlc2/tool/suite/Test63a.java @@ -0,0 +1,32 @@ +/******************************************************************************* + * Copyright (c) 2016 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.suite; + +public class Test63a extends SuiteTestCase { + public Test63a() { + super("24", "12", "0", "12"); + } +} diff --git a/tlatools/test/tlc2/tool/suite/Test64.java b/tlatools/test/tlc2/tool/suite/Test64.java new file mode 100644 index 0000000000000000000000000000000000000000..b5dbc4b3623a6c9a7f4291246f02ee747ef8065f --- /dev/null +++ b/tlatools/test/tlc2/tool/suite/Test64.java @@ -0,0 +1,32 @@ +/******************************************************************************* + * Copyright (c) 2016 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.suite; + +public class Test64 extends SuiteTestCase { + public Test64() { + super("3", "2", "0", "1"); + } +} diff --git a/tlatools/test/tlc2/tool/suite/Test64a.java b/tlatools/test/tlc2/tool/suite/Test64a.java new file mode 100644 index 0000000000000000000000000000000000000000..c86986761311805f3c5ce96d9831fef11e0f1760 --- /dev/null +++ b/tlatools/test/tlc2/tool/suite/Test64a.java @@ -0,0 +1,32 @@ +/******************************************************************************* + * Copyright (c) 2016 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.suite; + +public class Test64a extends SuiteTestCase { + public Test64a() { + super("3", "2", "0", "1"); + } +} diff --git a/tlatools/test/tlc2/tool/suite/Test65.java b/tlatools/test/tlc2/tool/suite/Test65.java new file mode 100644 index 0000000000000000000000000000000000000000..daa3b3364288c8fccfffd07b1724d9f9aac00111 --- /dev/null +++ b/tlatools/test/tlc2/tool/suite/Test65.java @@ -0,0 +1,32 @@ +/******************************************************************************* + * Copyright (c) 2016 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.suite; + +public class Test65 extends SuiteTestCase { + public Test65() { + super("0", "0", "0", "0"); + } +} diff --git a/tlatools/test/tlc2/tool/suite/Test65a.java b/tlatools/test/tlc2/tool/suite/Test65a.java new file mode 100644 index 0000000000000000000000000000000000000000..65aa645d8193d31bcd4f3077502e35608ee420ea --- /dev/null +++ b/tlatools/test/tlc2/tool/suite/Test65a.java @@ -0,0 +1,32 @@ +/******************************************************************************* + * Copyright (c) 2016 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.suite; + +public class Test65a extends SuiteTestCase { + public Test65a() { + super("0", "0", "0", "0"); + } +} diff --git a/tlatools/test/tlc2/tool/suite/Test7.java b/tlatools/test/tlc2/tool/suite/Test7.java new file mode 100644 index 0000000000000000000000000000000000000000..054ba5fb9e9baaa4d44345108ad4d7aa0e1a2bfd --- /dev/null +++ b/tlatools/test/tlc2/tool/suite/Test7.java @@ -0,0 +1,29 @@ +/******************************************************************************* + * Copyright (c) 2016 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.suite; + +public class Test7 extends SuiteTestCase { +} diff --git a/tlatools/test/tlc2/tool/suite/Test8.java b/tlatools/test/tlc2/tool/suite/Test8.java new file mode 100644 index 0000000000000000000000000000000000000000..f134ac13a0730df24a2bd4c069b93eac92dc49d7 --- /dev/null +++ b/tlatools/test/tlc2/tool/suite/Test8.java @@ -0,0 +1,29 @@ +/******************************************************************************* + * Copyright (c) 2016 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.suite; + +public class Test8 extends SuiteTestCase { +} diff --git a/tlatools/test/tlc2/tool/suite/Test9.java b/tlatools/test/tlc2/tool/suite/Test9.java new file mode 100644 index 0000000000000000000000000000000000000000..320f7f76442674748f0e30d584e9c7d22dfbc505 --- /dev/null +++ b/tlatools/test/tlc2/tool/suite/Test9.java @@ -0,0 +1,29 @@ +/******************************************************************************* + * Copyright (c) 2016 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.suite; + +public class Test9 extends SuiteTestCase { +} diff --git a/tlatools/test/tlc2/tool/suite/Test99.java b/tlatools/test/tlc2/tool/suite/Test99.java new file mode 100644 index 0000000000000000000000000000000000000000..9c4a29f2a4ab10e808cbde2d6c017c2eb9a4d9e3 --- /dev/null +++ b/tlatools/test/tlc2/tool/suite/Test99.java @@ -0,0 +1,30 @@ +/******************************************************************************* + * Copyright (c) 2016 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.suite; + +public class Test99 extends SuiteTestCase { + +} diff --git a/tlatools/test/tlc2/tool/suite/Test999.java b/tlatools/test/tlc2/tool/suite/Test999.java new file mode 100644 index 0000000000000000000000000000000000000000..aa282a7f681e137404bce82ce6fafdf4e650aa88 --- /dev/null +++ b/tlatools/test/tlc2/tool/suite/Test999.java @@ -0,0 +1,30 @@ +/******************************************************************************* + * Copyright (c) 2016 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.suite; + +public class Test999 extends SuiteTestCase { + +} diff --git a/tlatools/test/tlc2/tool/suite/TestInvalidInvariant.java b/tlatools/test/tlc2/tool/suite/TestInvalidInvariant.java new file mode 100644 index 0000000000000000000000000000000000000000..e0941db03c9974c4aa9b0029da99c161821877bc --- /dev/null +++ b/tlatools/test/tlc2/tool/suite/TestInvalidInvariant.java @@ -0,0 +1,52 @@ +/******************************************************************************* + * Copyright (c) 2017 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.tool.suite; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.output.EC; +import tlc2.output.EC.ExitStatus; +import tlc2.tool.liveness.ModelCheckerTestCase; + +public class TestInvalidInvariant extends ModelCheckerTestCase { + + public TestInvalidInvariant() { + super("testinvalidinvariant", ExitStatus.FAILURE_SAFETY_EVAL); + } + + @Test + public void testSpec() { + assertTrue(recorder.recorded(EC.TLC_FINISHED)); + assertTrue(recorder.recordedWithStringValue(EC.TLC_INVARIANT_VIOLATED_LEVEL, "Invariant")); + assertFalse(recorder.recorded(EC.GENERAL)); + // See LevelNode.java line 590ff and tlc2.tool.Spec.processConfigInvariants(). + assertFalse(recorder.recordedWithSubStringValue(EC.GENERAL, + "Note that a bug can cause TLC to incorrectly report this error.")); + } +} diff --git a/tlatools/test/tlc2/util/BitVectorTest.java b/tlatools/test/tlc2/util/BitVectorTest.java new file mode 100644 index 0000000000000000000000000000000000000000..f996d8ba3e485689bde1e0cf194be63c2f0828e9 --- /dev/null +++ b/tlatools/test/tlc2/util/BitVectorTest.java @@ -0,0 +1,67 @@ +/******************************************************************************* + * Copyright (c) 2016 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ + +package tlc2.util; + +import static org.junit.Assert.assertEquals; + +import org.junit.Test; + +public class BitVectorTest { + + /** + * Test method for {@link tlc2.util.BitVector#toString()}. + */ + @Test + public void testToString() { + final BitVector bitVector = new BitVector(8); + bitVector.set(0); + bitVector.set(1); + // unset + bitVector.set(3); + bitVector.set(4); + // unset + // unset + bitVector.set(7); + + assertEquals("[10011011]", bitVector.toString()); + } + + @Test + public void testToStringRange() { + final BitVector bitVector = new BitVector(8); + bitVector.set(0); + bitVector.set(1); + // unset + bitVector.set(3); + bitVector.set(4); + // unset + // unset + bitVector.set(7); + + assertEquals("[001]", bitVector.toString(4, 3)); + } +} diff --git a/tlatools/test/tlc2/util/BufferedRandomAccessFileTest.java b/tlatools/test/tlc2/util/BufferedRandomAccessFileTest.java new file mode 100644 index 0000000000000000000000000000000000000000..9221e6d54626cd6bf385a38f09fcb172d1792858 --- /dev/null +++ b/tlatools/test/tlc2/util/BufferedRandomAccessFileTest.java @@ -0,0 +1,136 @@ +/******************************************************************************* + * Copyright (c) 2016 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.util; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + +import java.io.File; +import java.io.IOException; + +import org.junit.Test; + +public class BufferedRandomAccessFileTest { + + @Test + public void testWrite() throws IOException { + final File tmpFile = File.createTempFile("BufferedRandomAccessFileTest_testWrite", ".bin"); + tmpFile.deleteOnExit(); + final java.io.RandomAccessFile raf = new BufferedRandomAccessFile(tmpFile, "rw"); + for (long i = 0L; i < BufferedRandomAccessFile.BuffSz / 8; i++) { + raf.writeLong(i); + } + raf.close(); + } + + @Test + public void testWriteSeek() throws IOException { + final File tmpFile = File.createTempFile("BufferedRandomAccessFileTest_testWriteSeek", ".bin"); + tmpFile.deleteOnExit(); + final java.io.RandomAccessFile raf = new BufferedRandomAccessFile(tmpFile, "rw"); + + raf.setLength(BufferedRandomAccessFile.BuffSz + 1L); + raf.seek(1); + + for (long i = 0L; i < BufferedRandomAccessFile.BuffSz / 8; i++) { + raf.writeLong(i); + } + raf.close(); + } + + @Test + public void testWriteSeekNoLength() throws IOException { + final File tmpFile = File.createTempFile("BufferedRandomAccessFileTest_testWriteSeekNoLength", ".bin"); + tmpFile.deleteOnExit(); + final java.io.RandomAccessFile raf = new BufferedRandomAccessFile(tmpFile, "rw"); + + // Do not set length, expect meaningful exception + raf.seek(1); + try { + for (long i = 0L; i < BufferedRandomAccessFile.BuffSz / 8; i++) { + raf.writeLong(i); + } + } catch (IOException expected) { + return; + } catch (Exception e) { + fail(e.getMessage()); + } finally { + raf.close(); + } + } + + @Test + public void testRead() throws IOException { + final File tmpFile = File.createTempFile("BufferedRandomAccessFileTest_testRead", ".bin"); + tmpFile.deleteOnExit(); + final java.io.RandomAccessFile raf = new BufferedRandomAccessFile(tmpFile, "rw"); + for (long i = 0L; i < BufferedRandomAccessFile.BuffSz / 8; i++) { + raf.writeLong(i); + } + + raf.seek(0); + for (long i = 0l; i < BufferedRandomAccessFile.BuffSz / 8; i++) { + assertEquals(i, raf.readLong()); + } + raf.close(); + } + + @Test + public void testReadSeek() throws IOException { + final File tmpFile = File.createTempFile("BufferedRandomAccessFileTest_testReadSeek", ".bin"); + tmpFile.deleteOnExit(); + final java.io.RandomAccessFile raf = new BufferedRandomAccessFile(tmpFile, "rw"); + + raf.setLength(BufferedRandomAccessFile.BuffSz + 1L); + raf.seek(1); + + for (int i = 0; i < BufferedRandomAccessFile.BuffSz / 8; i++) { + assertEquals(0L, raf.readLong()); + } + raf.close(); + } + + @Test + public void testReadSeekNoLength() throws IOException { + final File tmpFile = File.createTempFile("BufferedRandomAccessFileTest_testReadSeekNoLength", ".bin"); + tmpFile.deleteOnExit(); + final java.io.RandomAccessFile raf = new BufferedRandomAccessFile(tmpFile, "rw"); + + // Do not set length, expect meaningful exception + raf.seek(1); + try { + for (int i = 0; i < BufferedRandomAccessFile.BuffSz / 8; i++) { + raf.readLong(); + } + } catch (IOException expected) { + return; + } catch (Exception e) { + fail(e.getMessage()); + } finally { + raf.close(); + } + } +} diff --git a/tlatools/test/tlc2/util/ByteUtilsTest.java b/tlatools/test/tlc2/util/ByteUtilsTest.java index c5509c98de5e0cf3dad6f0e415cdd831ede0acf4..f84ecdf5d6c6f630874daf8b5e22ff2c85ff7f9c 100644 --- a/tlatools/test/tlc2/util/ByteUtilsTest.java +++ b/tlatools/test/tlc2/util/ByteUtilsTest.java @@ -8,7 +8,9 @@ import java.io.IOException; import java.math.BigInteger; import java.util.Random; -import junit.framework.TestCase; +import org.junit.Before; +import org.junit.Test; + import util.ToolIO; /** @@ -16,7 +18,7 @@ import util.ToolIO; * @author Simon Zambrovski * @version $Id$ */ -public class ByteUtilsTest extends TestCase +public class ByteUtilsTest { public static final int ARRAYSIZE = 10000; public static final int BITS = 1000; @@ -32,9 +34,9 @@ public class ByteUtilsTest extends TestCase long t1; long t2; - protected void setUp() throws Exception + @Before + public void setUp() throws Exception { - super.setUp(); Arr = new BigInteger[ARRAYSIZE]; Arr2 = new BigInteger[ARRAYSIZE]; Arr3 = new BigInteger[ARRAYSIZE]; @@ -54,7 +56,8 @@ public class ByteUtilsTest extends TestCase } - public void test1() + @Test + public void test1() { t1 = System.currentTimeMillis(); mainTestinttoByte(); @@ -62,7 +65,8 @@ public class ByteUtilsTest extends TestCase ToolIO.out.println("Testing IntToByteArray took " + (t2 - t1) + "ms"); } - public void test2() throws FileNotFoundException, IOException + @Test + public void test2() throws FileNotFoundException, IOException { t1 = System.currentTimeMillis(); mainTestWriteIntReadInt(); @@ -70,7 +74,8 @@ public class ByteUtilsTest extends TestCase ToolIO.out.println("Testing WriteInt, ReadInt took " + (t2 - t1) + "ms"); } - public void test3() + @Test + public void test3() { t1 = System.currentTimeMillis(); mainTestlongtoByte(); @@ -78,7 +83,8 @@ public class ByteUtilsTest extends TestCase ToolIO.out.println("Testing longToByteArray took " + (t2 - t1) + "ms"); } - public void test4() throws FileNotFoundException, IOException + @Test + public void test4() throws FileNotFoundException, IOException { t1 = System.currentTimeMillis(); mainTestWriteLongReadLong(); @@ -86,7 +92,8 @@ public class ByteUtilsTest extends TestCase ToolIO.out.println("Testing WriteLong, ReadLong took " + (t2 - t1) + "ms"); } - public void test5() throws FileNotFoundException, IOException + @Test + public void test5() throws FileNotFoundException, IOException { t1 = System.currentTimeMillis(); mainTestWriteReadSizeByteArray(); @@ -94,7 +101,8 @@ public class ByteUtilsTest extends TestCase ToolIO.out.println("Testing Write, Read took " + (t2 - t1) + "ms"); } - public void test6() throws FileNotFoundException, IOException + @Test + public void test6() throws FileNotFoundException, IOException { t1 = System.currentTimeMillis(); mainTestAppend(); diff --git a/tlatools/test/tlc2/util/CombinatoricsTest.java b/tlatools/test/tlc2/util/CombinatoricsTest.java new file mode 100644 index 0000000000000000000000000000000000000000..6696b19fd6d7e1e4a3b8e9970d1f76b70cf5b175 --- /dev/null +++ b/tlatools/test/tlc2/util/CombinatoricsTest.java @@ -0,0 +1,119 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.util; + +import static org.junit.Assert.assertEquals; + +import java.math.BigInteger; + +import org.junit.Test; + +public class CombinatoricsTest { + @Test + public void testChoose() { + for (int n = 0; n < Combinatorics.MAXCHOOSENUM; n++) { + for (int k = 0; k < Combinatorics.MAXCHOOSENUM; k++) { + long choose = Combinatorics.choose(n, k); + long binomial = Combinatorics.binomial(n, k); + assertEquals(String.format("n=%s, k=%s", n, k), choose, binomial); + } + } + } + @Test + public void testChooseBigChoose() { + for (int n = 0; n < Combinatorics.MAXCHOOSENUM + 1; n++) { + for (int k = 0; k < Combinatorics.MAXCHOOSENUM + 1; k++) { + long choose = Combinatorics.choose(n, k); + long bigChoose = Combinatorics.bigChoose(n, k).longValueExact(); + assertEquals(String.format("n=%s, k=%s", n, k), choose, bigChoose); + } + } + } + @Test + public void testSlowChooseBigChoose() { + for (int n = Combinatorics.MAXCHOOSENUM + 1; n < Combinatorics.MAXCHOOSENUM << 2; n++) { + for (int k = Combinatorics.MAXCHOOSENUM + 1; k < Combinatorics.MAXCHOOSENUM << 2; k++) { + BigInteger slowBigChoose = Combinatorics.slowBigChoose(n, k); + BigInteger bigChoose = Combinatorics.bigChoose(n, k); + assertEquals(String.format("n=%s, k=%s", n, k), slowBigChoose, bigChoose); + } + } + } + @Test + public void testBigChoose50c1() { + final BigInteger bigChoose = Combinatorics.bigChoose(50, 1); + assertEquals(6, bigChoose.bitLength()); + assertEquals(50L, bigChoose.longValueExact()); + } + @Test + public void testBigChoose50c10() { + final BigInteger bigChoose = Combinatorics.bigChoose(50, 10); + assertEquals(34, bigChoose.bitLength()); + assertEquals(10272278170L, bigChoose.longValueExact()); + } + @Test + public void testBigChoose50c20() { + final BigInteger bigChoose = Combinatorics.bigChoose(50, 20); + assertEquals(46, bigChoose.bitLength()); + assertEquals(47129212243960L, bigChoose.longValueExact()); + } + @Test + public void testBigChoose50c30() { + final BigInteger bigChoose = Combinatorics.bigChoose(50, 30); + assertEquals(46, bigChoose.bitLength()); + assertEquals(47129212243960L, bigChoose.longValueExact()); + } + @Test + public void testBigChoose400c1() { + final BigInteger bigChoose = Combinatorics.bigChoose(400, 1); + assertEquals(9, bigChoose.bitLength()); + assertEquals(400L, bigChoose.longValueExact()); + } + @Test + public void testBigChoose400c50() { + final BigInteger bigChoose = Combinatorics.bigChoose(400, 50); + assertEquals(214, bigChoose.bitLength()); + assertEquals( + "17035900270730601418919867558071677342938596450600561760371485120", + bigChoose.toString()); + } + @Test + public void testBigChoose400c100() { + final BigInteger bigChoose = Combinatorics.bigChoose(400, 100); + assertEquals(321, bigChoose.bitLength()); + assertEquals( + "2241854791554337561923210387201698554845411177476295990399942258896013007429693894018935107174320", + bigChoose.toString()); + } + @Test + public void testBigChoose400c200() { + final BigInteger bigChoose = Combinatorics.bigChoose(400, 200); + assertEquals(396, bigChoose.bitLength()); + assertEquals( + "102952500135414432972975880320401986757210925381077648234849059575923332372651958598336595518976492951564048597506774120", + bigChoose.toString()); + } +} diff --git a/tlatools/test/tlc2/util/ContextTest.java b/tlatools/test/tlc2/util/ContextTest.java index f916ed2f7313f25b3dd1b7c32d7598618ab1e28e..c85b502f6af20f15b1f8c87de7ed1245524edd32 100644 --- a/tlatools/test/tlc2/util/ContextTest.java +++ b/tlatools/test/tlc2/util/ContextTest.java @@ -26,8 +26,10 @@ package tlc2.util; -import junit.framework.TestCase; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import org.junit.Test; import org.w3c.dom.Document; import org.w3c.dom.Element; @@ -39,33 +41,37 @@ import tla2sany.semantic.SymbolNode; import tla2sany.xml.SymbolContext; import util.UniqueString; -public class ContextTest extends TestCase { +public class ContextTest { + @Test public void testLookupEmpty() { assertNull(Context.Empty.lookup(new DummySymbolNode())); assertNull(Context.Empty.lookup(new DummySymbolNode(), true)); assertNull(Context.Empty.lookup(new DummySymbolNode(), false)); - assertNull(Context.Empty.lookup(null)); + assertNull(Context.Empty.lookup((SymbolNode)null)); assertNull(Context.Empty.lookup(null, true)); assertNull(Context.Empty.lookup(null, false)); } + @Test public void testLookupBranch() { // BranchCtx -> Empty final Context ctx = Context.branch(Context.Empty); assertNull(ctx.lookup(new DummySymbolNode())); assertNull(ctx.lookup(new DummySymbolNode(), true)); assertNull(ctx.lookup(new DummySymbolNode(), false)); - assertNull(ctx.lookup(null)); + assertNull(ctx.lookup((SymbolNode)null)); assertNull(ctx.lookup(null, false)); assertNull(ctx.lookup(null, true)); } + @Test public void testLookupSymbolNodeNull() { final Context ctx = Context.branch(Context.Empty); - assertNull(ctx.lookup(null)); + assertNull(ctx.lookup((SymbolNode)null)); } + @Test public void testLookup() { final DummySymbolNode name = new DummySymbolNode("ctx1"); final Object value = "value1"; @@ -79,6 +85,7 @@ public class ContextTest extends TestCase { assertEquals(value, ctx3.lookup(name)); } + @Test public void testLookupCutOffFalse() { final DummySymbolNode name = new DummySymbolNode("ctx1"); final Object value = "value1"; @@ -93,6 +100,7 @@ public class ContextTest extends TestCase { } // Cutoff causes lookup to stop at branching context + @Test public void testLookupCutOffTrue() { final DummySymbolNode name = new DummySymbolNode("ctx1"); final Object value = "value1"; @@ -106,6 +114,7 @@ public class ContextTest extends TestCase { assertNull(ctx3.lookup(name, true)); } + @Test public void testLookupWithAtBranching() { final DummySymbolNode name = new DummySymbolNode("ctx1"); final Object value = "value1"; @@ -117,6 +126,7 @@ public class ContextTest extends TestCase { assertEquals(value, branch.lookup(name)); } + @Test public void testLookupWithCutOffFalseAtBranching() { final DummySymbolNode name = new DummySymbolNode("ctx1"); final Object value = "value1"; @@ -128,6 +138,7 @@ public class ContextTest extends TestCase { assertEquals(value, branch.lookup(name, false)); } + @Test public void testLookupWithCutOffTrueAtBranching() { final DummySymbolNode name = new DummySymbolNode("ctx1"); final Object value = "value1"; @@ -142,6 +153,7 @@ public class ContextTest extends TestCase { /** * Test method for {@link tlc2.util.Context#lookup(tla2sany.semantic.SymbolNode)}. */ + @Test public void testLookupSymbolNode() { final DummySymbolNode name = new DummySymbolNode(); final Object value = new Object(); diff --git a/tlatools/test/tlc2/util/FP64Test.java b/tlatools/test/tlc2/util/FP64Test.java new file mode 100644 index 0000000000000000000000000000000000000000..cc31a31523abb5836f4e8aa7fd9b72c34d2b279c --- /dev/null +++ b/tlatools/test/tlc2/util/FP64Test.java @@ -0,0 +1,51 @@ +/******************************************************************************* + * Copyright (c) 2019 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.util; + +import static org.junit.Assert.assertEquals; + +import java.util.Random; + +import org.junit.Before; +import org.junit.Test; + +public class FP64Test { + + @Before + public void setup() { + FP64.Init(); + } + + @Test + public void testExtendLongInt() { + final Random random = new Random(); + for (int i = 0; i < 1000; i++) { + final long fp = random.nextLong(); + final int x = random.nextInt(); + assertEquals(FP64.Extend(fp, x), FP64.ExtendLoop(fp, x)); + } + } +} diff --git a/tlatools/test/tlc2/util/GrowingLongVecTest.java b/tlatools/test/tlc2/util/GrowingLongVecTest.java index 5d9d914ce22f531904c0189fa2da87fa8683702e..fb2f913401a430915efb09365e584e1907333939 100644 --- a/tlatools/test/tlc2/util/GrowingLongVecTest.java +++ b/tlatools/test/tlc2/util/GrowingLongVecTest.java @@ -26,6 +26,10 @@ package tlc2.util; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + +import org.junit.Test; public class GrowingLongVecTest extends LongVecTest { @@ -34,6 +38,7 @@ public class GrowingLongVecTest extends LongVecTest { return new LongVec(0); } + @Test public void testGrowAndShrink() { // Zero capacity, LongVec has to grow final LongVec vec = new LongVec(0); diff --git a/tlatools/test/tlc2/util/LongVecTest.java b/tlatools/test/tlc2/util/LongVecTest.java index 2af02a8d0fb928fec0b8a2bf690527680bad503a..4b6903cf213e6b750a546a9a779c13ebf3209321 100644 --- a/tlatools/test/tlc2/util/LongVecTest.java +++ b/tlatools/test/tlc2/util/LongVecTest.java @@ -26,15 +26,19 @@ package tlc2.util; -import junit.framework.TestCase; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; -public class LongVecTest extends TestCase { +import org.junit.Test; + +public class LongVecTest { protected LongVec getLongVec() { // default capacity return new LongVec(); } + @Test public void testReadBeyondCapacity() { final LongVec vec = getLongVec(); try { @@ -45,6 +49,7 @@ public class LongVecTest extends TestCase { fail("Read beyond capacity"); } + @Test public void testAddAndReadBeyondCapacity() { final LongVec vec = getLongVec(); vec.addElement(1L); @@ -57,6 +62,7 @@ public class LongVecTest extends TestCase { fail("Read beyond capacity"); } + @Test public void testRemoveBeyondCapacity() { final LongVec vec = new LongVec(10); for (int i = -1; i <= 10; i++) { @@ -69,6 +75,7 @@ public class LongVecTest extends TestCase { } } + @Test public void testAddRemoveBeyondCapacity() { final LongVec vec = new LongVec(10); vec.addElement(1L); @@ -81,6 +88,7 @@ public class LongVecTest extends TestCase { fail("Read beyond capacity"); } + @Test public void testRemoveAndGet() { final LongVec vec = getLongVec(); vec.addElement(1L); @@ -103,6 +111,7 @@ public class LongVecTest extends TestCase { fail("A new elements magically appeared in LongVec"); } + @Test public void testRemoveWrongOrder() { final LongVec vec = getLongVec(); vec.addElement(1L); @@ -120,6 +129,7 @@ public class LongVecTest extends TestCase { fail("Removed non-existing element"); } + @Test public void testGetNegative() { final LongVec vec = getLongVec(); try { @@ -130,6 +140,7 @@ public class LongVecTest extends TestCase { fail("Read negative"); } + @Test public void testRemoveNegative() { final LongVec vec = getLongVec(); try { diff --git a/tlatools/test/tlc2/util/MemIntQueueTest.java b/tlatools/test/tlc2/util/MemIntQueueTest.java index cd645d2d93db3911471f102793666b9ce25f182f..e32fd2ff9b43d4ada16a0c134f1b2ed45c37c25d 100644 --- a/tlatools/test/tlc2/util/MemIntQueueTest.java +++ b/tlatools/test/tlc2/util/MemIntQueueTest.java @@ -26,12 +26,16 @@ package tlc2.util; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + import java.util.NoSuchElementException; -import junit.framework.TestCase; +import org.junit.Test; -public class MemIntQueueTest extends TestCase { +public class MemIntQueueTest { + @Test public void testDequeuePastLastElement() { final MemIntQueue queue = new MemIntQueue("irrelevant", "irrelevant"); queue.enqueueInt(1); @@ -47,6 +51,7 @@ public class MemIntQueueTest extends TestCase { // Add 0 three times and make sure it's only returned this many times. 0 // is MemIntQueue's internal default for an empty slot. + @Test public void testEnqueueZeros() { final MemIntQueue queue = new MemIntQueue("irrelevant", "irrelevant"); queue.enqueueInt(0); @@ -65,6 +70,7 @@ public class MemIntQueueTest extends TestCase { fail("Returned element where there should be none."); } + @Test public void testEnqueueLong() { final MemIntQueue queue = new MemIntQueue("irrelevant", "irrelevant"); queue.enqueueLong(0); @@ -80,6 +86,7 @@ public class MemIntQueueTest extends TestCase { fail("Returned element where there should be none."); } + @Test public void testEnqueueDequeueLong() { final MemIntQueue queue = new MemIntQueue("irrelevant", "irrelevant"); queue.enqueueLong(0); @@ -93,4 +100,36 @@ public class MemIntQueueTest extends TestCase { } fail("Returned element where there should be none."); } + + @Test + public void testGrow() { + final MemIntQueue queue = new MemIntQueue("irrelevant", "irrelevant", 4); + queue.enqueueInt(0); + queue.enqueueInt(1); + queue.enqueueInt(2); + queue.enqueueInt(3); + assertEquals(4, queue.size()); + + queue.dequeueInt(); + queue.dequeueInt(); + queue.dequeueInt(); + queue.dequeueInt(); + assertEquals(0, queue.size()); + + queue.enqueueInt(4); + queue.enqueueInt(5); + queue.enqueueInt(6); + queue.enqueueInt(7); + assertEquals(4, queue.size()); + + queue.dequeueInt(); + + // This should make the queue grow internally. + queue.enqueueInt(8); + queue.enqueueInt(9); + for(int i = 5; i < 10; i++) { + assertEquals(i, queue.dequeueInt()); + } + assertEquals(0, queue.size()); + } } diff --git a/tlatools/test/tlc2/util/MemIntStackTest.java b/tlatools/test/tlc2/util/MemIntStackTest.java new file mode 100644 index 0000000000000000000000000000000000000000..90ed6d70d64512ce1806f93347a137916d403c0e --- /dev/null +++ b/tlatools/test/tlc2/util/MemIntStackTest.java @@ -0,0 +1,49 @@ +/******************************************************************************* + * Copyright (c) 2015 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ + +package tlc2.util; + +import static org.junit.Assert.assertEquals; + +import org.junit.Test; + +public class MemIntStackTest { + + @Test + public void testPeak() { + final MemIntStack memIntStack = new MemIntStack("", ""); + + memIntStack.pushLong(4711L); + memIntStack.pushLong(2323L); + memIntStack.pushInt(1); + memIntStack.pushLong(77L); + + assertEquals(4711L, memIntStack.peakLong(0)); + assertEquals(2323L, memIntStack.peakLong(2)); + assertEquals(1, memIntStack.peakInt(4)); + assertEquals(77L, memIntStack.peakLong(5)); + } +} diff --git a/tlatools/test/tlc2/util/SynchronousDiskIntStackTest.java b/tlatools/test/tlc2/util/SynchronousDiskIntStackTest.java new file mode 100644 index 0000000000000000000000000000000000000000..1c52a98a268e0d8c7e1acc7754560eaa498e8e52 --- /dev/null +++ b/tlatools/test/tlc2/util/SynchronousDiskIntStackTest.java @@ -0,0 +1,79 @@ +/******************************************************************************* + * Copyright (c) 2015 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ + +package tlc2.util; + +import static org.junit.Assert.assertEquals; + +import java.io.File; + +import org.junit.Test; + +public class SynchronousDiskIntStackTest { + + @Test + public void testPushIntNoWrite() { + final String diskdir = System.getProperty("java.io.tmpdir") + File.separator + "SynchronousDiskIntStackTest_" + + System.currentTimeMillis(); + + final int size = 8; + + final IntStack diskIntStack = new SynchronousDiskIntStack(diskdir, "SynchronousDiskIntStackTest", size); + + // Fill stack + for (int i = 0; i < size; i++) { + diskIntStack.pushInt(i); + } + + // Check all elements still on stack + for (int i = size - 1; i >= 0; i--) { + assertEquals(i, diskIntStack.popInt()); + } + } + + @Test + public void testPushIntWrite() { + final String diskdir = System.getProperty("java.io.tmpdir") + File.separator + "SynchronousDiskIntStackTest_" + + System.currentTimeMillis(); + new File(diskdir).mkdirs(); + + final int size = 8; + + final IntStack diskIntStack = new SynchronousDiskIntStack(diskdir, "SynchronousDiskIntStackTest", size); + + // Fill stack trice + for (int i = 0; i < (size * 3); i++) { + diskIntStack.pushInt(i); + } + + // Check all elements still on stack + for (int i = (3*size) - 1; i >= 0; i--) { + assertEquals(i, diskIntStack.popInt()); + } + + assertEquals(0, diskIntStack.size()); + } +} diff --git a/tlatools/test/tlc2/util/statistics/BucketStatisticsTest.java b/tlatools/test/tlc2/util/statistics/BucketStatisticsTest.java index 390bc25ad3d2b207c89eb5022561ff2720635fc0..8c52812dfa3403bf7cbc19f1b52177b9f6e40630 100644 --- a/tlatools/test/tlc2/util/statistics/BucketStatisticsTest.java +++ b/tlatools/test/tlc2/util/statistics/BucketStatisticsTest.java @@ -18,7 +18,7 @@ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINbucketStatistic IN THE SOFTWARE. * * Contributors: * Markus Alexander Kuppe - initial API and implementation @@ -26,163 +26,194 @@ package tlc2.util.statistics; -import tlc2.util.statistics.BucketStatistics; -import junit.framework.TestCase; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; -public class BucketStatisticsTest extends TestCase { - +import java.lang.reflect.InvocationTargetException; +import java.util.Arrays; +import java.util.Collection; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +@RunWith(Parameterized.class) +public class BucketStatisticsTest { + + @SuppressWarnings("rawtypes") + @Parameterized.Parameters + public static Collection<Class> bucketStatistics() { + return Arrays.asList( + new Class[] { ConcurrentBucketStatistics.class, BucketStatistics.class }); + } + + private final IBucketStatistics bucketStatistic; + + @SuppressWarnings({ "rawtypes", "unchecked" }) + public BucketStatisticsTest(Class bucketStatistic) throws InstantiationException, IllegalAccessException, + IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException { + this.bucketStatistic = (IBucketStatistics) bucketStatistic.getConstructor(String.class) + .newInstance("BucketStatisticsTest"); + } + + @Test public void testInvalidArgument() { try { - final IBucketStatistics gs = new BucketStatistics(); - gs.addSample(-1); + bucketStatistic.addSample(-1); } catch (IllegalArgumentException e) { return; } fail(); } - + + @Test public void testMean() { - final IBucketStatistics gs = new BucketStatistics(); - assertEquals(-1.0d, gs.getMean()); - - gs.addSample(0); - assertEquals(0.0d, gs.getMean()); - - gs.addSample(1); - gs.addSample(2); - assertEquals(1.0d, gs.getMean()); - - gs.addSample(2); - gs.addSample(2); - assertEquals(1.4d, gs.getMean()); + assertTrue(Double.compare(-1.0d, bucketStatistic.getMean()) == 0); + + bucketStatistic.addSample(0); + assertTrue(Double.compare(0.0d, bucketStatistic.getMean()) == 0); + + bucketStatistic.addSample(1); + bucketStatistic.addSample(2); + assertTrue(Double.compare(1.0d, bucketStatistic.getMean()) == 0); + + bucketStatistic.addSample(2); + bucketStatistic.addSample(2); + assertTrue(Double.compare(1.4d, bucketStatistic.getMean()) == 0); } + @Test public void testMedian() { - final IBucketStatistics gs = new BucketStatistics(); - assertEquals(-1, gs.getMedian()); - - gs.addSample(0); - assertEquals(0, gs.getMedian()); - - gs.addSample(1); - gs.addSample(2); - assertEquals(1, gs.getMedian()); - - gs.addSample(2); - gs.addSample(2); - assertEquals(2, gs.getMedian()); + assertEquals(-1, bucketStatistic.getMedian()); + + bucketStatistic.addSample(0); + assertEquals(0, bucketStatistic.getMedian()); + + bucketStatistic.addSample(1); + bucketStatistic.addSample(2); + assertEquals(1, bucketStatistic.getMedian()); + + bucketStatistic.addSample(2); + bucketStatistic.addSample(2); + assertEquals(2, bucketStatistic.getMedian()); } + @Test public void testMin() { - final IBucketStatistics gs = new BucketStatistics(); - assertEquals(-1, gs.getMin()); - - gs.addSample(0); - gs.addSample(0); - gs.addSample(0); - gs.addSample(1); - gs.addSample(1); - gs.addSample(2); - gs.addSample(2); - gs.addSample(2); - assertEquals(0, gs.getMin()); + assertEquals(-1, bucketStatistic.getMin()); + + bucketStatistic.addSample(0); + bucketStatistic.addSample(0); + bucketStatistic.addSample(0); + bucketStatistic.addSample(1); + bucketStatistic.addSample(1); + bucketStatistic.addSample(2); + bucketStatistic.addSample(2); + bucketStatistic.addSample(2); + assertEquals(0, bucketStatistic.getMin()); } - + + @Test + public void testMin2() { + assertEquals(-1, bucketStatistic.getMin()); + + bucketStatistic.addSample(1); + bucketStatistic.addSample(1); + bucketStatistic.addSample(2); + bucketStatistic.addSample(2); + bucketStatistic.addSample(2); + assertEquals(1, bucketStatistic.getMin()); + } + + @Test public void testMax() { - final IBucketStatistics gs = new BucketStatistics(); - assertEquals(-1, gs.getMax()); - - gs.addSample(0); - gs.addSample(0); - gs.addSample(0); - gs.addSample(1); - gs.addSample(1); - gs.addSample(2); - gs.addSample(2); - gs.addSample(2); - gs.addSample(2); - gs.addSample(3); - assertEquals(3, gs.getMax()); + assertEquals(-1, bucketStatistic.getMax()); + + bucketStatistic.addSample(0); + bucketStatistic.addSample(0); + bucketStatistic.addSample(0); + bucketStatistic.addSample(1); + bucketStatistic.addSample(1); + bucketStatistic.addSample(2); + bucketStatistic.addSample(2); + bucketStatistic.addSample(2); + bucketStatistic.addSample(2); + bucketStatistic.addSample(3); + assertEquals(3, bucketStatistic.getMax()); } + @Test public void testStandardDeviation() { - final IBucketStatistics gs = new BucketStatistics(); - assertEquals(-1.0, gs.getStdDev()); - - gs.addSample(0); - gs.addSample(0); - gs.addSample(0); - gs.addSample(1); - gs.addSample(1); - gs.addSample(2); - gs.addSample(2); - gs.addSample(2); - gs.addSample(2); - gs.addSample(3); - assertEquals(1.005d, (Math.round(gs.getStdDev() * 10000d) / 10000d)); + assertEquals(-1.0, bucketStatistic.getStdDev(), 0); + + bucketStatistic.addSample(0); + bucketStatistic.addSample(0); + bucketStatistic.addSample(0); + bucketStatistic.addSample(1); + bucketStatistic.addSample(1); + bucketStatistic.addSample(2); + bucketStatistic.addSample(2); + bucketStatistic.addSample(2); + bucketStatistic.addSample(2); + bucketStatistic.addSample(3); + assertTrue(Double.compare(1.005d, (Math.round(bucketStatistic.getStdDev() * 10000d) / 10000d)) == 0); } + @Test public void testGetPercentile() { - final IBucketStatistics gs = new BucketStatistics(); - assertEquals(-1.0, gs.getPercentile(1)); - + assertEquals(-1.0, bucketStatistic.getPercentile(1), 0); + try { - gs.addSample(1); // <- first element - gs.getPercentile(1.1); - gs.getPercentile(-0.1); + bucketStatistic.addSample(1); // <- first element + bucketStatistic.getPercentile(1.1); + bucketStatistic.getPercentile(-0.1); } catch (Exception e) { fail(e.getMessage()); } - - gs.addSample(1); - gs.addSample(1); - gs.addSample(2); // <- 0.5 percentile - gs.addSample(2); - gs.addSample(2); - gs.addSample(3); - assertEquals(2.0d, gs.getPercentile(0.5d)); - assertEquals(2.0d, gs.getPercentile(0.75d)); - assertEquals(3.0d, gs.getPercentile(0.999d)); + + bucketStatistic.addSample(1); + bucketStatistic.addSample(1); + bucketStatistic.addSample(2); // <- 0.5 percentile + bucketStatistic.addSample(2); + bucketStatistic.addSample(2); + bucketStatistic.addSample(3); + assertTrue(Double.compare(2.0d, bucketStatistic.getPercentile(0.5d)) == 0); + assertTrue(Double.compare(2.0d, bucketStatistic.getPercentile(0.5d)) == 0); + assertTrue(Double.compare(2.0d, bucketStatistic.getPercentile(0.75d)) == 0); + assertTrue(Double.compare(3.0d, bucketStatistic.getPercentile(0.999d)) == 0); } + // NaN test + @Test public void testGetPercentileNaN() { try { - final IBucketStatistics gs = new BucketStatistics(); - gs.getPercentile(Double.NaN); + bucketStatistic.getPercentile(Double.NaN); } catch (IllegalArgumentException e) { return; } fail("Parameter not a number"); } - + + @Test public void testToString() { try { - //just invoke to check for exceptions - final IBucketStatistics gs = new BucketStatistics(); - gs.toString(); - - gs.addSample(0); - gs.addSample(0); - gs.addSample(0); - gs.addSample(1); - gs.addSample(1); - gs.addSample(2); - gs.addSample(2); - gs.addSample(2); - gs.addSample(2); - gs.addSample(3); - gs.toString(); + // just invoke to check for exceptions + bucketStatistic.toString(); + + bucketStatistic.addSample(0); + bucketStatistic.addSample(0); + bucketStatistic.addSample(0); + bucketStatistic.addSample(1); + bucketStatistic.addSample(1); + bucketStatistic.addSample(2); + bucketStatistic.addSample(2); + bucketStatistic.addSample(2); + bucketStatistic.addSample(2); + bucketStatistic.addSample(3); + bucketStatistic.toString(); } catch (Exception e) { fail(e.getMessage()); } } - - public void testMaximum() { - final IBucketStatistics gs = new BucketStatistics("test title", 8); - gs.addSample(16); - gs.addSample(16); - gs.addSample(16); - assertEquals(8, gs.getMedian()); - assertEquals(3, gs.getObservations()); - } } \ No newline at end of file diff --git a/tlatools/test/tlc2/util/statistics/FixedSizedBucketStatisticsTest.java b/tlatools/test/tlc2/util/statistics/FixedSizedBucketStatisticsTest.java new file mode 100644 index 0000000000000000000000000000000000000000..377150c6cb0200b152b83bc4ef581a53c0e2732c --- /dev/null +++ b/tlatools/test/tlc2/util/statistics/FixedSizedBucketStatisticsTest.java @@ -0,0 +1,131 @@ +/******************************************************************************* + * Copyright (c) 2015 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINbucketStatistic IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ + +package tlc2.util.statistics; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + +import java.lang.reflect.InvocationTargetException; +import java.util.Arrays; +import java.util.Collection; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +@RunWith(Parameterized.class) +public class FixedSizedBucketStatisticsTest { + + @SuppressWarnings("rawtypes") + @Parameterized.Parameters + public static Collection<Class> bucketStatistics() { + return Arrays.asList( + new Class[] { FixedSizedConcurrentBucketStatistics.class, FixedSizedBucketStatistics.class }); + } + + private final IBucketStatistics bucketStatistic; + + @SuppressWarnings({ "rawtypes", "unchecked" }) + public FixedSizedBucketStatisticsTest(Class bucketStatistic) throws InstantiationException, IllegalAccessException, + IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException { + this.bucketStatistic = (IBucketStatistics) bucketStatistic.getConstructor(String.class, int.class) + .newInstance("FixedSizedBucketStatisticsTest", 8); + } + + @Test + public void testMin() { + assertEquals(-1, bucketStatistic.getMin()); + + bucketStatistic.addSample(0); + bucketStatistic.addSample(1); + bucketStatistic.addSample(1); + bucketStatistic.addSample(2); + bucketStatistic.addSample(2); + bucketStatistic.addSample(2); + assertEquals(0, bucketStatistic.getMin()); + } + + @Test + public void testMin2() { + assertEquals(-1, bucketStatistic.getMin()); + + bucketStatistic.addSample(1); + bucketStatistic.addSample(1); + bucketStatistic.addSample(2); + bucketStatistic.addSample(2); + bucketStatistic.addSample(2); + assertEquals(1, bucketStatistic.getMin()); + } + + @Test + public void testMax() { + assertEquals(-1, bucketStatistic.getMax()); + + bucketStatistic.addSample(0); + bucketStatistic.addSample(0); + bucketStatistic.addSample(0); + bucketStatistic.addSample(1); + bucketStatistic.addSample(1); + bucketStatistic.addSample(2); + bucketStatistic.addSample(2); + bucketStatistic.addSample(2); + bucketStatistic.addSample(2); + bucketStatistic.addSample(3); + assertEquals(3, bucketStatistic.getMax()); + } + + @Test + public void testInvalidArgument() { + try { + bucketStatistic.addSample(-1); + } catch (IllegalArgumentException e) { + return; + } + fail(); + } + + // NaN test + @Test + public void testGetPercentileNaN() { + try { + bucketStatistic.getPercentile(Double.NaN); + } catch (IllegalArgumentException e) { + return; + } + fail("Parameter not a number"); + } + + @Test + public void testMaximum() { + bucketStatistic.addSample(16); + bucketStatistic.addSample(16); + bucketStatistic.addSample(16); + assertEquals(7, bucketStatistic.getMax()); + assertEquals(7, bucketStatistic.getMedian()); + assertEquals(3, bucketStatistic.getObservations()); + } +} \ No newline at end of file diff --git a/tlatools/test/tlc2/value/ValueInputOutputStreamTest.java b/tlatools/test/tlc2/value/ValueInputOutputStreamTest.java new file mode 100644 index 0000000000000000000000000000000000000000..e35e6d2126505e0a7bcf90507371ec991f287545 --- /dev/null +++ b/tlatools/test/tlc2/value/ValueInputOutputStreamTest.java @@ -0,0 +1,118 @@ +/******************************************************************************* + * Copyright (c) 2017 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.value; + +import static org.junit.Assert.assertEquals; + +import java.io.File; +import java.io.IOException; + +import org.junit.Test; + +import tlc2.TLCGlobals; + +public class ValueInputOutputStreamTest { + + @Test + public void testWriteShort() throws IOException { + final File tempFile = File.createTempFile("ValueOutputStreamTest-testWriteShort", ".vos"); + tempFile.deleteOnExit(); + + final ValueOutputStream out = new ValueOutputStream(tempFile); + out.writeShort(Short.MAX_VALUE); + out.writeShort(Short.MIN_VALUE); + out.writeShort((short) 0); + out.close(); + + final ValueInputStream in = new ValueInputStream(tempFile); + assertEquals(Short.MAX_VALUE, in.readShort()); + assertEquals(Short.MIN_VALUE, in.readShort()); + assertEquals((short) 0, in.readShort()); + in.close(); + } + + @Test + public void testWriteInt() throws IOException { + final File tempFile = File.createTempFile("ValueOutputStreamTest-testWriteInt", ".vos"); + tempFile.deleteOnExit(); + + final ValueOutputStream out = new ValueOutputStream(tempFile); + out.writeInt(Integer.MAX_VALUE); + out.writeInt(Integer.MIN_VALUE); + out.writeInt(0); + out.close(); + + final ValueInputStream in = new ValueInputStream(tempFile); + assertEquals(Integer.MAX_VALUE, in.readInt()); + assertEquals(Integer.MIN_VALUE, in.readInt()); + assertEquals(0, in.readInt()); + in.close(); + } + + @Test + public void testWriteShortNat() throws IOException { + final File tempFile = File.createTempFile("ValueOutputStreamTest-testWriteShortNat", ".vos"); + tempFile.deleteOnExit(); + + TLCGlobals.useGZIP = true; + final ValueOutputStream out = new ValueOutputStream(tempFile); + out.writeShortNat(Short.MAX_VALUE); + out.writeShortNat((short) 0); + out.close(); + + // 20 byte header + // 2 byte Short.MAX_VALUE + // 1 byte 0 + assertEquals(20 + 2 + 1, tempFile.length()); + + final ValueInputStream in = new ValueInputStream(tempFile); + assertEquals(Short.MAX_VALUE, in.readShortNat()); + assertEquals(0, in.readShortNat()); + in.close(); + } + + @Test + public void testWriteNat() throws IOException { + final File tempFile = File.createTempFile("ValueOutputStreamTest-testWriteNat", ".vos"); + tempFile.deleteOnExit(); + + TLCGlobals.useGZIP = true; + final ValueOutputStream out = new ValueOutputStream(tempFile); + out.writeNat(Integer.MAX_VALUE); + out.writeNat(0); + out.close(); + + // 20 byte header + // 4 byte Integer.MAX_VALUE + // 2 byte 0 + assertEquals(20 + 4 + 2, tempFile.length()); + + final ValueInputStream in = new ValueInputStream(tempFile); + assertEquals(Integer.MAX_VALUE, in.readNat()); + assertEquals(0, in.readNat()); + in.close(); + } +} diff --git a/tlatools/test/tlc2/value/impl/EnumerableValueTest.java b/tlatools/test/tlc2/value/impl/EnumerableValueTest.java new file mode 100644 index 0000000000000000000000000000000000000000..38584cb63f9743eed6aea9c95cd2c57fbcd40934 --- /dev/null +++ b/tlatools/test/tlc2/value/impl/EnumerableValueTest.java @@ -0,0 +1,151 @@ +/******************************************************************************* + * Copyright (c) 20178 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + * Ian Morris Nieves - added support for fingerprint stack trace + ******************************************************************************/ +package tlc2.value.impl; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import java.util.HashSet; +import java.util.Set; + +import org.junit.Test; + +import tlc2.value.IValue; +import tlc2.value.RandomEnumerableValues; +import tlc2.value.impl.EnumerableValue.SubsetEnumerator; + +public class EnumerableValueTest { + + @Test + public void test() { + RandomEnumerableValues.setSeed(15041980); + + final Set<Integer> indices = new HashSet<>(); + // For the first n \in Nat+ up to 10657 show that: + for (int n = 1; n < 10657; n++) { + final SubsetEnumerator enumerator = (SubsetEnumerator) new DummyValue(n).elements(n); + + while (enumerator.hasNext()) { + final int index = enumerator.nextIndex(); + assertTrue("Index %s out of bounds.", 0 <= index && index < n); + indices.add(index); + } + assertEquals("Missing indices.", n, indices.size()); + indices.clear(); + } + } + + @SuppressWarnings("serial") + class DummyValue extends EnumerableValue { + + private final int size; + + public DummyValue(int size) { + this.size = size; + } + + @Override + public int size() { + return size; + } + + @Override + public ValueEnumeration elements(final int k) { + return new EnumerableValue.SubsetEnumerator(k) { + @Override + public Value nextElement() { + return null; + } + }; + } + + @Override + public ValueEnumeration elements() { + return null; + } + + @Override + public byte getKind() { + return 0; + } + + @Override + public int compareTo(Object val) { + return 0; + } + + @Override + public boolean member(Value elem) { + return false; + } + + @Override + public Value takeExcept(ValueExcept ex) { + return null; + } + + @Override + public Value takeExcept(ValueExcept[] exs) { + return null; + } + + @Override + public boolean isNormalized() { + return false; + } + + @Override + public Value normalize() { + return this; + } + + @Override + public boolean isFinite() { + return false; + } + + @Override + public boolean isDefined() { + return false; + } + + @Override + public IValue deepCopy() { + return null; + } + + @Override + public boolean assignable(Value val) { + return false; + } + + @Override + public StringBuffer toString(StringBuffer sb, int offset, boolean swallow) { + return toString(sb, offset, swallow); + } + } +} diff --git a/tlatools/test/tlc2/value/impl/InitializeValueTest.java b/tlatools/test/tlc2/value/impl/InitializeValueTest.java new file mode 100644 index 0000000000000000000000000000000000000000..0628e479e70a1bb83039d5ff820dacd8dada1a0f --- /dev/null +++ b/tlatools/test/tlc2/value/impl/InitializeValueTest.java @@ -0,0 +1,283 @@ +/******************************************************************************* + * Copyright (c) 2019 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.value.impl; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.BeforeClass; +import org.junit.Test; + +import tlc2.util.FP64; +import util.InternTable; +import util.UniqueString; + +public class InitializeValueTest { + + @BeforeClass + public static void setup() { + FP64.Init(); + } + + /** + * Test method for {@link tlc2.value.impl.UnionValue#deepNormalize()}. + */ + @Test + public void union() { + final ValueVec vec = new ValueVec(); + vec.addElement(new SetEnumValue(new IntValue[] {IntValue.gen(42)}, false)); + vec.addElement(new SetEnumValue(new IntValue[] {IntValue.gen(23)}, false)); + vec.addElement(new SetEnumValue(new IntValue[] {IntValue.gen(4711)}, false)); + vec.addElement(new SetEnumValue(new IntValue[] {IntValue.gen(1)}, false)); + + final UnionValue uv = new UnionValue(new SetEnumValue(vec, false)); + assertFalse(uv.isNormalized()); + assertFalse(uv.set.isNormalized()); + + uv.initialize(); + + assertTrue(uv.set.isNormalized()); + assertTrue(uv.isNormalized()); + } + + /** + * Test method for {@link tlc2.value.impl.SetCapValue#deepNormalize()}. + */ + @Test + public void setcap() { + final ValueVec vec = new ValueVec(); + vec.addElement(IntValue.gen(42)); + vec.addElement(IntValue.gen(23)); + vec.addElement(IntValue.gen(4711)); + vec.addElement(IntValue.gen(1)); + + SetCapValue scv = new SetCapValue(new SetEnumValue(vec, false), new SetEnumValue(vec, false)); + assertFalse(scv.isNormalized()); + assertFalse(scv.set1.isNormalized()); + assertFalse(scv.set2.isNormalized()); + + scv.initialize(); + + assertTrue(scv.set1.isNormalized()); + assertTrue(scv.set2.isNormalized()); + assertTrue(scv.isNormalized()); + } + + @Test + public void setcup() { + final ValueVec vec = new ValueVec(); + vec.addElement(IntValue.gen(42)); + vec.addElement(IntValue.gen(23)); + vec.addElement(IntValue.gen(4711)); + vec.addElement(IntValue.gen(1)); + + SetCupValue scv = new SetCupValue(new SetEnumValue(vec, false), new SetEnumValue(vec, false)); + assertFalse(scv.isNormalized()); + assertFalse(scv.set1.isNormalized()); + assertFalse(scv.set2.isNormalized()); + + scv.initialize(); + + assertTrue(scv.set1.isNormalized()); + assertTrue(scv.set2.isNormalized()); + assertTrue(scv.isNormalized()); + } + + @Test + public void setdiff() { + final ValueVec vec = new ValueVec(); + vec.addElement(IntValue.gen(42)); + vec.addElement(IntValue.gen(23)); + vec.addElement(IntValue.gen(4711)); + vec.addElement(IntValue.gen(1)); + + SetDiffValue sdv = new SetDiffValue(new SetEnumValue(vec, false), new SetEnumValue(vec, false)); + assertFalse(sdv.isNormalized()); + assertFalse(sdv.set1.isNormalized()); + assertFalse(sdv.set2.isNormalized()); + + sdv.initialize(); + + assertTrue(sdv.isNormalized()); + assertTrue(sdv.set1.isNormalized()); + assertTrue(sdv.set2.isNormalized()); + } + + @Test + public void subset() { + final ValueVec vec = new ValueVec(); + vec.addElement(IntValue.gen(42)); + vec.addElement(IntValue.gen(23)); + vec.addElement(IntValue.gen(4711)); + vec.addElement(IntValue.gen(1)); + + SubsetValue sub = new SubsetValue(new SetEnumValue(vec, false)); + assertFalse(sub.isNormalized()); + assertFalse(sub.set.isNormalized()); + + sub.initialize(); + + assertTrue(sub.set.isNormalized()); + assertTrue(sub.isNormalized()); + } + + @Test + public void record() { + final InternTable internTable = new InternTable(2); + final UniqueString a = internTable.put("a"); + final UniqueString b = internTable.put("b"); + + final ValueVec vec = new ValueVec(); + vec.addElement(IntValue.gen(42)); + vec.addElement(IntValue.gen(23)); + vec.addElement(IntValue.gen(4711)); + vec.addElement(IntValue.gen(1)); + final Value aVal = new SetEnumValue(vec, false); + final Value bVal = new SetEnumValue(vec, false); + + final RecordValue rcdv = new RecordValue(new UniqueString[] {b, a}, new Value[] {bVal, aVal}, false); + + assertFalse(rcdv.isNormalized()); + for (Value v : rcdv.values) { + assertFalse(v.isNormalized()); + } + + rcdv.initialize(); + + for (Value v : rcdv.values) { + assertTrue(v.isNormalized()); + } + assertTrue(rcdv.isNormalized()); + } + + @Test + public void fcnrecord() { + final ValueVec vec = new ValueVec(); + vec.addElement(IntValue.gen(42)); + vec.addElement(IntValue.gen(23)); + vec.addElement(IntValue.gen(4711)); + vec.addElement(IntValue.gen(1)); + final Value aVal = new SetEnumValue(vec, false); + final Value bVal = new SetEnumValue(vec, false); + + final FcnRcdValue rcdv = new FcnRcdValue(new Value[] { new StringValue("B"), new StringValue("A") }, + new Value[] { bVal, aVal }, false); + + assertFalse(rcdv.isNormalized()); + for (Value v : rcdv.values) { + assertFalse(v.isNormalized()); + } + + rcdv.initialize(); + + for (Value v : rcdv.values) { + assertTrue(v.isNormalized()); + } + assertTrue(rcdv.isNormalized()); + } + + @Test + public void tuple() { + final ValueVec vec = new ValueVec(); + vec.addElement(IntValue.gen(42)); + vec.addElement(IntValue.gen(23)); + vec.addElement(IntValue.gen(4711)); + vec.addElement(IntValue.gen(1)); + final Value aVal = new SetEnumValue(vec, false); + + final TupleValue tuple = new TupleValue(aVal); + +// for (Value v : tuple.elems) { +// assertFalse(v.isNormalized()); +// } +// assertFalse(tuple.isNormalized()); + + tuple.initialize(); + + assertTrue(tuple.isNormalized()); + for (Value v : tuple.elems) { + assertTrue(v.isNormalized()); + } + } + + @Test + public void setOfTuple() { + final IntervalValue intVal = new IntervalValue(1, 2); + final SetOfTuplesValue inner = new SetOfTuplesValue(intVal, intVal); + final SetOfTuplesValue tuples = new SetOfTuplesValue(inner, inner); + +// for (Value v : tuples.sets) { +// assertFalse(v.isNormalized()); +// } +// assertFalse(tuples.isNormalized()); + + tuples.initialize(); + + assertTrue(tuples.isNormalized()); + for (Value v : tuples.sets) { + assertTrue(v.isNormalized()); + } + } + + @Test + public void setOfRcds() { + + final Value[] values = new Value[3]; + values[0] = new SetEnumValue(getValue(7, "a"), true); + values[1] = new IntervalValue(1, 2); + values[2] = new IntervalValue(1, 4); + + final SetOfRcdsValue setOfRcrds= new SetOfRcdsValue(getNames(3), values, true); + +// for (Value v : setOfRcrds.values) { +// assertFalse(v.isNormalized()); +// } +// assertFalse(setOfRcrds.isNormalized()); + + setOfRcrds.initialize(); + + assertTrue(setOfRcrds.isNormalized()); + for (Value v : setOfRcrds.values) { + assertTrue(v.isNormalized()); + } + } + + private static final Value[] getValue(final int n, String str) { + final Value[] values = new Value[n]; + for (int i = 0; i < n; i++) { + values[i] = new StringValue(str + i); + } + return values; + } + + private static final UniqueString[] getNames(final int n) { + final UniqueString[] names = new UniqueString[n]; + for (int i = 0; i < names.length; i++) { + names[i] = UniqueString.uniqueStringOf("N" + i); + } + return names; + } +} diff --git a/tlatools/test/tlc2/value/impl/IntervalValueTest.java b/tlatools/test/tlc2/value/impl/IntervalValueTest.java new file mode 100644 index 0000000000000000000000000000000000000000..759f3220b2a361bdc69f239a594c6690712496fc --- /dev/null +++ b/tlatools/test/tlc2/value/impl/IntervalValueTest.java @@ -0,0 +1,114 @@ +package tlc2.value.impl; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import org.junit.Test; + +import util.Assert.TLCRuntimeException; + +public class IntervalValueTest { + + @Test + public void testElementAt() { + final IntervalValue iv = new IntervalValue(3, 11); + for (int i = 0; i < iv.size(); i++) { + assertEquals(IntValue.gen(i + 3), iv.elementAt(i)); + } + } + + @Test + public void testElementAtOutOfBoundsNegative() { + final IntervalValue iv = new IntervalValue(3, 11); + try { + iv.elementAt(-1); + } catch (TLCRuntimeException e) { + return; + } + fail(); + } + + @Test + public void testElementAtOutOfBoundsSize() { + final IntervalValue iv = new IntervalValue(3, 11); + try { + iv.elementAt(iv.size()); + } catch (TLCRuntimeException e) { + return; + } + fail(); + } + + @Test + public void sizeOverflow() { + assertEquals(Integer.MAX_VALUE, new IntervalValue(1, Integer.MAX_VALUE).size()); + assertEquals(Integer.MAX_VALUE, new IntervalValue(Integer.MIN_VALUE, -2).size()); + + try { + assertEquals(0, new IntervalValue(-989_822_976, 1_157_660_672).size()); + } catch (TLCRuntimeException e) { + assertTrue(e.getMessage().contains("Size of interval value exceeds the maximum representable size (32bits)")); + return; + } + fail(); + } + + @Test + public void compareToOverflow1() { + final IntervalValue iv = new IntervalValue(Integer.MAX_VALUE - 1, Integer.MAX_VALUE); + assertEquals(2, iv.size()); + + final IntervalValue iv2 = new IntervalValue(Integer.MIN_VALUE + 1, Integer.MIN_VALUE + 2); + assertEquals(2, iv2.size()); + + assertEquals(1, iv.compareTo(iv2)); + } + + @Test + public void testCompareExtremeIntervals() { + final IntervalValue x = new IntervalValue(Integer.MIN_VALUE, -2); + final IntervalValue y = new IntervalValue(1, Integer.MAX_VALUE); + assertEquals(x.size(), y.size()); + assertTrue(x.compareTo(y) < 0); + assertTrue(y.compareTo(x) > 0); + } + + @Test + public void testEmptyIntervalEquality() { + IntervalValue i1 = new IntervalValue(10, 1); + IntervalValue i2 = new IntervalValue(20, 15); + assertEquals(i1, i2); + } + + @Test + public void testCompareEmptyIntervals() { + int cmp = new IntervalValue(10, 1).compareTo(new IntervalValue(20, 15)); + assertEquals(0, cmp); + } + + @Test + public void testSizeOfMaximumRepresentableInterval() { + final IntervalValue iv = new IntervalValue(Integer.MIN_VALUE, Integer.MAX_VALUE); + try { + iv.size(); + } catch (TLCRuntimeException e) { + assertTrue(e.getMessage().contains("Size of interval value exceeds the maximum representable size (32bits)")); + return; + } + fail(); + } + + @Test + public void testExtremeIntervalSize() { + final IntervalValue iv1 = new IntervalValue(Integer.MAX_VALUE, Integer.MAX_VALUE); + assertEquals(1, iv1.size()); + final IntervalValue iv2 = new IntervalValue(Integer.MIN_VALUE, Integer.MIN_VALUE); + assertEquals(1, iv2.size()); + final IntervalValue iv3 = new IntervalValue(Integer.MAX_VALUE-10, Integer.MAX_VALUE); + assertEquals(11, iv3.size()); + final IntervalValue iv4 = new IntervalValue(Integer.MIN_VALUE, Integer.MIN_VALUE+10); + assertEquals(11, iv4.size()); + } + +} diff --git a/tlatools/test/tlc2/value/impl/RecordValueTest.java b/tlatools/test/tlc2/value/impl/RecordValueTest.java new file mode 100644 index 0000000000000000000000000000000000000000..694acb43ad73a7b3bdfd3af480d11c1de4cced96 --- /dev/null +++ b/tlatools/test/tlc2/value/impl/RecordValueTest.java @@ -0,0 +1,81 @@ +/******************************************************************************* + * Copyright (c) 2015 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ + +package tlc2.value.impl; + +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import util.InternTable; +import util.UniqueString; + +public class RecordValueTest { + + /** + * Test method for {@link tlc2.value.impl.RecordValue#deepCopy()}. + * + * This test reveals a bug in the implementation of RecordValue#deepCopy() + * when the original instance from which the deep copy has been created is + * normalized afterwards and the normalization incorrectly ripples through + * to the copy. + */ + @Test + public void testDeepCopy() { + final InternTable internTable = new InternTable(2); + final UniqueString a = internTable.put("a"); + final UniqueString b = internTable.put("b"); + + final Value aVal = new StringValue("aVal"); + final Value bVal = new StringValue("bVal"); + + // Create the source to create a deep copy of + final RecordValue orig = new RecordValue(new UniqueString[] {b, a}, new Value[] {bVal, aVal}, false); + + // Verify the mappings in RecordValue are correct + assertTrue(orig.names[0].equals(b)); + assertTrue(orig.names[1].equals(a)); + assertTrue(orig.values[0].equals(bVal)); + assertTrue(orig.values[1].equals(aVal)); + + // Make a deep copy of te origina record value + final RecordValue deepCopy = (RecordValue) orig.deepCopy(); + + // Normalize the original record value and check its mappings have been + // re-organized + orig.deepNormalize(); + assertTrue(orig.names[0].equals(a)); + assertTrue(orig.names[1].equals(b)); + assertTrue(orig.values[0].equals(aVal)); + assertTrue(orig.values[1].equals(bVal)); + + // Check that the mappings in the deep copy didn't change. + assertTrue(deepCopy.names[0].equals(b)); + assertTrue(deepCopy.names[1].equals(a)); + assertTrue(deepCopy.values[0].equals(bVal)); + assertTrue(deepCopy.values[1].equals(aVal)); + } +} diff --git a/tlatools/test/tlc2/value/impl/SetOfFcnsValueTest.java b/tlatools/test/tlc2/value/impl/SetOfFcnsValueTest.java new file mode 100644 index 0000000000000000000000000000000000000000..98d73b1e61fc4ca8675f3a59ce73044da28e3b2a --- /dev/null +++ b/tlatools/test/tlc2/value/impl/SetOfFcnsValueTest.java @@ -0,0 +1,392 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.value.impl; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.function.Consumer; +import java.util.function.IntConsumer; +import java.util.stream.IntStream; + +import org.junit.Test; + +import tlc2.util.FP64; +import tlc2.value.impl.SetOfFcnsValue.SubsetEnumerator; +import util.Assert.TLCRuntimeException; + +public class SetOfFcnsValueTest { + + private static final Value[] getValue(String... strs) { + final Value[] values = new Value[strs.length]; + for (int i = 0; i < strs.length; i++) { + values[i] = new StringValue(strs[i]); + } + return values; + } + + @Test + public void testRangeSubsetValue() { + final Value[] values = new Value[] { ModelValue.make("m1"), ModelValue.make("m2"), ModelValue.make("m3") }; + final SetOfFcnsValue setOfFcnsValue = new SetOfFcnsValue(new SetEnumValue(values, true), + new SubsetValue(new SetEnumValue( + new Value[] { new StringValue("a"), new StringValue("b"), new StringValue("c") }, true))); + + assertEquals(512, setOfFcnsValue.size()); + + final SetOfFcnsValue.SubsetEnumerator enumerator = (SubsetEnumerator) setOfFcnsValue.elements(512); + final SetEnumValue emptyset = new SetEnumValue(); + int i = 0; + assertEquals(new FcnRcdValue(values, new Value[] { emptyset, emptyset, emptyset }, true), + enumerator.elementAt(i++)); + assertEquals( + new FcnRcdValue(values, + new Value[] { emptyset, emptyset, + new SetEnumValue(new Value[] { new StringValue("a") }, true) }, + true), + enumerator.elementAt(i++)); + assertEquals( + new FcnRcdValue(values, + new Value[] { emptyset, emptyset, + new SetEnumValue(new Value[] { new StringValue("b") }, true) }, + true), + enumerator.elementAt(i++)); + assertEquals(new FcnRcdValue(values, + new Value[] { emptyset, emptyset, + new SetEnumValue(new Value[] { new StringValue("c") }, true) }, + true), enumerator.elementAt(i++)); + assertEquals(new FcnRcdValue(values, + new Value[] { emptyset, emptyset, + new SetEnumValue(new Value[] { new StringValue("a"), new StringValue("b") }, true) }, + true), enumerator.elementAt(i++)); + + // Last element + final SetEnumValue setEnumValue = new SetEnumValue( + new Value[] { new StringValue("a"), new StringValue("b"), new StringValue("c") }, true); + assertEquals(new FcnRcdValue(values, new Value[] { setEnumValue, setEnumValue, setEnumValue }, true), + enumerator.elementAt(511)); + } + + @Test + public void testDomainEmpty() { + final SetEnumValue domain = new SetEnumValue(); + final SetOfFcnsValue setOfFcnsValue = new SetOfFcnsValue(domain, + new SetEnumValue(getValue("a", "b", "c"), true)); + + // Non-subset behavior is our prototype. + assertEquals(1, setOfFcnsValue.size()); + final ValueEnumeration elements = setOfFcnsValue.elements(); + assertEquals(new FcnRcdValue(new Value[0], new Value[0], true), elements.nextElement()); + assertNull(elements.nextElement()); + + // Subset behaves similar. + final Enumerable subset = setOfFcnsValue.getRandomSubset(5); + final ValueEnumeration subsetElements = subset.elements(); + assertEquals(1, subset.size()); + assertEquals(new FcnRcdValue(new Value[0], new Value[0], true), subsetElements.nextElement()); + assertNull(subsetElements.nextElement()); + } + + @Test + public void testRangeEmpty() { + final IntervalValue domain = new IntervalValue(1, 2); + final SetOfFcnsValue setOfFcnsValue = new SetOfFcnsValue(domain, new SetEnumValue(new ValueVec(), true)); + + // Non-subset behavior is our prototype. + assertEquals(0, setOfFcnsValue.size()); + assertNull(setOfFcnsValue.elements().nextElement()); + + // Subset behaves similar. + final Enumerable subset = setOfFcnsValue.getRandomSubset(5); + assertEquals(0, subset.size()); + assertNull(subset.elements().nextElement()); + assertEquals(new SetEnumValue(), subset); + } + + @Test + public void testDomainAndRangeEmpty() { + final SetEnumValue domain = new SetEnumValue(); + final SetOfFcnsValue setOfFcnsValue = new SetOfFcnsValue(domain, new SetEnumValue()); + + // Non-subset behavior is our prototype. + assertEquals(1, setOfFcnsValue.size()); + final ValueEnumeration elements = setOfFcnsValue.elements(); + assertEquals(new FcnRcdValue(new Value[0], new Value[0], true), elements.nextElement()); + assertNull(elements.nextElement()); + + // Subset behaves similar. + final Enumerable subset = setOfFcnsValue.getRandomSubset(5); + final ValueEnumeration subsetElements = subset.elements(); + assertEquals(1, subset.size()); + assertEquals(new FcnRcdValue(new Value[0], new Value[0], true), subsetElements.nextElement()); + assertNull(subsetElements.nextElement()); + } + + @Test + public void testRandomSubsetAndValueEnumerator() { + final Value[] domain = new Value[] { ModelValue.make("m1"), ModelValue.make("m2"), ModelValue.make("m3") }; + final SetOfFcnsValue setOfFcnsValue = new SetOfFcnsValue(new SetEnumValue(domain, true), + new SetEnumValue(getValue("a", "b", "c"), true)); + + assertEquals(27, setOfFcnsValue.size()); + + FP64.Init(); + final Set<FcnRcdValue> enumeratorValues = new HashSet<>(27); + + final SetOfFcnsValue.SubsetEnumerator enumerator = (SubsetEnumerator) setOfFcnsValue.elements(27); + for (int i = 0; i < setOfFcnsValue.size(); i++) { + FcnRcdValue rcd = (FcnRcdValue) enumerator.elementAt(i); + assertEquals(3, rcd.domain.length); + assertEquals(3, rcd.values.length); + enumeratorValues.add(rcd); + } + + final Enumerable randomSubset = setOfFcnsValue.getRandomSubset(27); + final Set<FcnRcdValue> randomsubsetValues = new HashSet<>(27); + + final ValueEnumeration enumerator2 = randomSubset.elements(); + FcnRcdValue rcd; + while ((rcd = (FcnRcdValue) enumerator2.nextElement()) != null) { + assertEquals(3, rcd.domain.length); + assertEquals(3, rcd.values.length); + randomsubsetValues.add(rcd); + // Check element is in the original SetOfFcnsValue. + assertTrue(setOfFcnsValue.member(rcd)); + } + + assertEquals(enumeratorValues.size(), randomsubsetValues.size()); + assertEquals(enumeratorValues, randomsubsetValues); + } + + @Test + public void testDomainModelValue() { + final Value[] domain = new Value[] { ModelValue.make("m1"), ModelValue.make("m2"), ModelValue.make("m3") }; + final SetOfFcnsValue setOfFcnsValue = new SetOfFcnsValue(new SetEnumValue(domain, true), + new SetEnumValue(getValue("a", "b", "c"), true)); + + assertEquals(27, setOfFcnsValue.size()); + + FP64.Init(); + final Set<FcnRcdValue> enumeratorValues = new HashSet<>(27); + + final SetOfFcnsValue.SubsetEnumerator enumerator = (SubsetEnumerator) setOfFcnsValue.elements(27); + for (int i = 0; i < setOfFcnsValue.size(); i++) { + FcnRcdValue rcd = (FcnRcdValue) enumerator.elementAt(i); + assertEquals(3, rcd.domain.length); + assertEquals(3, rcd.values.length); + enumeratorValues.add(rcd); + // Check element is in the original SetOfFcnsValue. + assertTrue(setOfFcnsValue.member(rcd)); + } + assertEquals(27, enumeratorValues.size()); + + int i = 0; + assertEquals(new FcnRcdValue(domain, getValue("a", "a", "a"), true), enumerator.elementAt(i++)); + assertEquals(new FcnRcdValue(domain, getValue("a", "a", "b"), true), enumerator.elementAt(i++)); + assertEquals(new FcnRcdValue(domain, getValue("a", "a", "c"), true), enumerator.elementAt(i++)); + assertEquals(new FcnRcdValue(domain, getValue("a", "b", "a"), true), enumerator.elementAt(i++)); + assertEquals(new FcnRcdValue(domain, getValue("a", "b", "b"), true), enumerator.elementAt(i++)); + assertEquals(new FcnRcdValue(domain, getValue("a", "b", "c"), true), enumerator.elementAt(i++)); + assertEquals(new FcnRcdValue(domain, getValue("a", "c", "a"), true), enumerator.elementAt(i++)); + assertEquals(new FcnRcdValue(domain, getValue("a", "c", "b"), true), enumerator.elementAt(i++)); + assertEquals(new FcnRcdValue(domain, getValue("a", "c", "c"), true), enumerator.elementAt(i++)); + assertEquals(new FcnRcdValue(domain, getValue("b", "a", "a"), true), enumerator.elementAt(i++)); + assertEquals(new FcnRcdValue(domain, getValue("b", "a", "b"), true), enumerator.elementAt(i++)); + assertEquals(new FcnRcdValue(domain, getValue("b", "a", "c"), true), enumerator.elementAt(i++)); + assertEquals(new FcnRcdValue(domain, getValue("b", "b", "a"), true), enumerator.elementAt(i++)); + assertEquals(new FcnRcdValue(domain, getValue("b", "b", "b"), true), enumerator.elementAt(i++)); + assertEquals(new FcnRcdValue(domain, getValue("b", "b", "c"), true), enumerator.elementAt(i++)); + assertEquals(new FcnRcdValue(domain, getValue("b", "c", "a"), true), enumerator.elementAt(i++)); + assertEquals(new FcnRcdValue(domain, getValue("b", "c", "b"), true), enumerator.elementAt(i++)); + assertEquals(new FcnRcdValue(domain, getValue("b", "c", "c"), true), enumerator.elementAt(i++)); + assertEquals(new FcnRcdValue(domain, getValue("c", "a", "a"), true), enumerator.elementAt(i++)); + assertEquals(new FcnRcdValue(domain, getValue("c", "a", "b"), true), enumerator.elementAt(i++)); + assertEquals(new FcnRcdValue(domain, getValue("c", "a", "c"), true), enumerator.elementAt(i++)); + assertEquals(new FcnRcdValue(domain, getValue("c", "b", "a"), true), enumerator.elementAt(i++)); + assertEquals(new FcnRcdValue(domain, getValue("c", "b", "b"), true), enumerator.elementAt(i++)); + assertEquals(new FcnRcdValue(domain, getValue("c", "b", "c"), true), enumerator.elementAt(i++)); + assertEquals(new FcnRcdValue(domain, getValue("c", "c", "a"), true), enumerator.elementAt(i++)); + assertEquals(new FcnRcdValue(domain, getValue("c", "c", "b"), true), enumerator.elementAt(i++)); + assertEquals(new FcnRcdValue(domain, getValue("c", "c", "c"), true), enumerator.elementAt(i++)); + } + + @Test + public void testDomainIntervalRangeSetEnumValueSize9() { + final IntervalValue domain = new IntervalValue(1, 2); + final SetOfFcnsValue setOfFcnsValue = new SetOfFcnsValue(domain, + new SetEnumValue(getValue("a", "b", "c"), true)); + + assertEquals(9, setOfFcnsValue.size()); + + final SetOfFcnsValue.SubsetEnumerator enumerator = (SubsetEnumerator) setOfFcnsValue.elements(9); + for (int i = 0; i < setOfFcnsValue.size(); i++) { + FcnRcdValue rcd = (FcnRcdValue) enumerator.elementAt(i); + assertEquals(2, rcd.domain.length); + assertEquals(2, rcd.values.length); + // Check element is in the original SetOfFcnsValue. + assertTrue(setOfFcnsValue.member(rcd)); + } + + int i = 0; + assertEquals(new FcnRcdValue(domain, getValue("a", "a")), enumerator.elementAt(i++)); + assertEquals(new FcnRcdValue(domain, getValue("a", "b")), enumerator.elementAt(i++)); + assertEquals(new FcnRcdValue(domain, getValue("a", "c")), enumerator.elementAt(i++)); + assertEquals(new FcnRcdValue(domain, getValue("b", "a")), enumerator.elementAt(i++)); + assertEquals(new FcnRcdValue(domain, getValue("b", "b")), enumerator.elementAt(i++)); + assertEquals(new FcnRcdValue(domain, getValue("b", "c")), enumerator.elementAt(i++)); + assertEquals(new FcnRcdValue(domain, getValue("c", "a")), enumerator.elementAt(i++)); + assertEquals(new FcnRcdValue(domain, getValue("c", "b")), enumerator.elementAt(i++)); + assertEquals(new FcnRcdValue(domain, getValue("c", "c")), enumerator.elementAt(i++)); + } + + @Test + public void testDomainIntervalRangeSetEnumValueSize27() { + final IntervalValue domain = new IntervalValue(1, 3); + final SetOfFcnsValue setOfFcnsValue = new SetOfFcnsValue(domain, + new SetEnumValue(getValue("a", "b", "c"), true)); + + assertEquals(27, setOfFcnsValue.size()); + + final SetOfFcnsValue.SubsetEnumerator enumerator = (SubsetEnumerator) setOfFcnsValue.elements(27); + for (int i = 0; i < setOfFcnsValue.size(); i++) { + FcnRcdValue rcd = (FcnRcdValue) enumerator.elementAt(i); + assertEquals(3, rcd.domain.length); + assertEquals(3, rcd.values.length); + // Check element is in the original SetOfFcnsValue. + assertTrue(setOfFcnsValue.member(rcd)); + } + + int i = 0; + assertEquals(new FcnRcdValue(domain, getValue("a", "a", "a")), enumerator.elementAt(i++)); + assertEquals(new FcnRcdValue(domain, getValue("a", "a", "b")), enumerator.elementAt(i++)); + assertEquals(new FcnRcdValue(domain, getValue("a", "a", "c")), enumerator.elementAt(i++)); + assertEquals(new FcnRcdValue(domain, getValue("a", "b", "a")), enumerator.elementAt(i++)); + assertEquals(new FcnRcdValue(domain, getValue("a", "b", "b")), enumerator.elementAt(i++)); + assertEquals(new FcnRcdValue(domain, getValue("a", "b", "c")), enumerator.elementAt(i++)); + assertEquals(new FcnRcdValue(domain, getValue("a", "c", "a")), enumerator.elementAt(i++)); + assertEquals(new FcnRcdValue(domain, getValue("a", "c", "b")), enumerator.elementAt(i++)); + assertEquals(new FcnRcdValue(domain, getValue("a", "c", "c")), enumerator.elementAt(i++)); + assertEquals(new FcnRcdValue(domain, getValue("b", "a", "a")), enumerator.elementAt(i++)); + assertEquals(new FcnRcdValue(domain, getValue("b", "a", "b")), enumerator.elementAt(i++)); + assertEquals(new FcnRcdValue(domain, getValue("b", "a", "c")), enumerator.elementAt(i++)); + assertEquals(new FcnRcdValue(domain, getValue("b", "b", "a")), enumerator.elementAt(i++)); + assertEquals(new FcnRcdValue(domain, getValue("b", "b", "b")), enumerator.elementAt(i++)); + assertEquals(new FcnRcdValue(domain, getValue("b", "b", "c")), enumerator.elementAt(i++)); + assertEquals(new FcnRcdValue(domain, getValue("b", "c", "a")), enumerator.elementAt(i++)); + assertEquals(new FcnRcdValue(domain, getValue("b", "c", "b")), enumerator.elementAt(i++)); + assertEquals(new FcnRcdValue(domain, getValue("b", "c", "c")), enumerator.elementAt(i++)); + assertEquals(new FcnRcdValue(domain, getValue("c", "a", "a")), enumerator.elementAt(i++)); + assertEquals(new FcnRcdValue(domain, getValue("c", "a", "b")), enumerator.elementAt(i++)); + assertEquals(new FcnRcdValue(domain, getValue("c", "a", "c")), enumerator.elementAt(i++)); + assertEquals(new FcnRcdValue(domain, getValue("c", "b", "a")), enumerator.elementAt(i++)); + assertEquals(new FcnRcdValue(domain, getValue("c", "b", "b")), enumerator.elementAt(i++)); + assertEquals(new FcnRcdValue(domain, getValue("c", "b", "c")), enumerator.elementAt(i++)); + assertEquals(new FcnRcdValue(domain, getValue("c", "c", "a")), enumerator.elementAt(i++)); + assertEquals(new FcnRcdValue(domain, getValue("c", "c", "b")), enumerator.elementAt(i++)); + assertEquals(new FcnRcdValue(domain, getValue("c", "c", "c")), enumerator.elementAt(i++)); + } + + @Test + public void testDomainIntervalRangeSetEnumValueSize256() { + final IntervalValue domain = new IntervalValue(1, 4); + final SetOfFcnsValue setOfFcnsValue = new SetOfFcnsValue(domain, + new SetEnumValue(getValue("a", "b", "c", "d"), true)); + + assertEquals(256, setOfFcnsValue.size()); + + final SetOfFcnsValue.SubsetEnumerator enumerator = (SubsetEnumerator) setOfFcnsValue.elements(256); + for (int i = 0; i < setOfFcnsValue.size(); i++) { + FcnRcdValue rcd = (FcnRcdValue) enumerator.elementAt(i); + assertEquals(4, rcd.domain.length); + assertEquals(4, rcd.values.length); + // Check element is in the original SetOfFcnsValue. + assertTrue(setOfFcnsValue.member(rcd)); + } + + int i = 0; + assertEquals(new FcnRcdValue(domain, getValue("a", "a", "a", "a")), enumerator.elementAt(i++)); + assertEquals(new FcnRcdValue(domain, getValue("a", "a", "a", "b")), enumerator.elementAt(i++)); + assertEquals(new FcnRcdValue(domain, getValue("a", "a", "a", "c")), enumerator.elementAt(i++)); + assertEquals(new FcnRcdValue(domain, getValue("a", "a", "a", "d")), enumerator.elementAt(i++)); + // ... (to lazy to type them out) + assertEquals(new FcnRcdValue(domain, getValue("d", "d", "d", "b")), enumerator.elementAt(253)); + assertEquals(new FcnRcdValue(domain, getValue("d", "d", "d", "c")), enumerator.elementAt(254)); + assertEquals(new FcnRcdValue(domain, getValue("d", "d", "d", "d")), enumerator.elementAt(255)); + } + + @Test + public void testRandomSubsetFromReallyLarge() { + final List<SetOfFcnsValue> l = new ArrayList<>(); + + l.add(new SetOfFcnsValue(new IntervalValue(1, 11), + new SetEnumValue(getValue("a", "b", "c", "d", "e", "f", "g", "h", "i", "j"), true))); + l.add(new SetOfFcnsValue(new IntervalValue(1, 44), new IntervalValue(1, 20))); + l.add(new SetOfFcnsValue(new IntervalValue(1, 121), new IntervalValue(1, 19))); + l.add(new SetOfFcnsValue(new IntervalValue(1, 321), new IntervalValue(1, 29))); + + l.forEach(new Consumer<SetOfFcnsValue>() { + @Override + public void accept(final SetOfFcnsValue sofv) { + try { + sofv.size(); + } catch (TLCRuntimeException tre) { + // OK, set is huge for size to reject it. Next get a tiny subset of it. + + IntStream.of(0, 1, 2, 799, 1024, 8932, 16933/*, 109031*/).forEach(new IntConsumer() { // 109031 causes the test to take a little long to be included in the overall test suite. + @Override + public void accept(int kOutOfN) { + final Enumerable randomSubset = sofv.getRandomSubset(kOutOfN); + + // Check expected amount of elements. + assertEquals(kOutOfN, randomSubset.size()); + + // Check for no duplicates. + FP64.Init(); + final Set<FcnRcdValue> set = new HashSet<>(kOutOfN); + + final ValueEnumeration elements = randomSubset.elements(); + FcnRcdValue rcd; + while ((rcd = (FcnRcdValue) elements.nextElement()) != null) { + // Check element is in the original SetOfFcnsValue. + assertTrue(sofv.member(rcd)); + set.add(rcd); + } + assertEquals(kOutOfN, set.size()); + } + }); + return; + } + fail(); + } + }); + } +} diff --git a/tlatools/test/tlc2/value/impl/SetOfRcrdValueTest.java b/tlatools/test/tlc2/value/impl/SetOfRcrdValueTest.java new file mode 100644 index 0000000000000000000000000000000000000000..8b1200dfbd06d41b7ac0a93cc44f534e21e9c39e --- /dev/null +++ b/tlatools/test/tlc2/value/impl/SetOfRcrdValueTest.java @@ -0,0 +1,196 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.value.impl; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import java.util.HashSet; +import java.util.Set; +import java.util.stream.IntStream; + +import org.junit.BeforeClass; +import org.junit.Test; + +import tlc2.util.FP64; +import tlc2.value.impl.SetOfRcdsValue.SubsetEnumerator; +import util.UniqueString; + +public class SetOfRcrdValueTest { + + private static final int a = 97; + + private static final Value[] getValue(final int n, String str) { + final Value[] values = new Value[n]; + for (int i = 0; i < n; i++) { + values[i] = new StringValue(str + i); + } + return values; + } + + private static final Value[] getValue(int n, String... strs) { + final Value[] values = new Value[strs.length]; + for (int i = 0; i < values.length; i++) { + values[i] = new SetEnumValue(getValue(n, strs[i]), false); + } + return values; + } + + private static final Value[] getValue(int n, UniqueString[] names) { + // a,b,c,d,e,... + return getValue(n, IntStream.range(a, a + names.length).mapToObj(ascii -> Character.toString((char) ascii)) + .toArray(String[]::new)); + } + + private static final UniqueString[] getNames(final int n) { + final UniqueString[] names = new UniqueString[n]; + for (int i = 0; i < names.length; i++) { + names[i] = UniqueString.uniqueStringOf("N" + i); + } + return names; + } + + @BeforeClass + public static void setup() { + FP64.Init(); + } + + @Test + public void testSimple() { + final UniqueString[] names = getNames(3); + + final Value[] values = new Value[3]; + values[0] = new SetEnumValue(getValue(7, "a"), true); + values[1] = new IntervalValue(1, 2); + values[2] = new IntervalValue(1, 4); + + final SetOfRcdsValue setOfRcrdValue = new SetOfRcdsValue(names, values, true); + + checkElements(names, values, setOfRcrdValue, (SubsetEnumerator) setOfRcrdValue.elements(setOfRcrdValue.size())); + } + + private static void checkElements(final UniqueString[] names, final Value[] values, final SetOfRcdsValue set, + final SubsetEnumerator rcds) { + for (int i = 0; i < set.size(); i++) { + // Check names are stable. + final RecordValue rcd = rcds.elementAt(i); + assertArrayEquals(names, rcd.names); + // Check values are from correct range. + final Value[] rcdValues = rcd.values; + for (int j = 0; j < rcdValues.length; j++) { + assertTrue(values[j].member(rcdValues[j])); + } + } + } + + @Test + public void testRangeSubsetValue() { + final UniqueString[] names = getNames(4); + final SetOfRcdsValue setOfRcrdValue = new SetOfRcdsValue(names, getValue(2, names), true); + + final Set<Value> actual = new HashSet<>(setOfRcrdValue.elements(setOfRcrdValue.size()).all()); + assertEquals(setOfRcrdValue.size(), actual.size()); + + final Set<Value> expected = new HashSet<>(setOfRcrdValue.elements().all()); + assertEquals(expected, actual); + } + + @Test + public void testRandomSubset() { + final UniqueString[] names = getNames(4); + final SetOfRcdsValue setOfRcrdValue = new SetOfRcdsValue(names, getValue(3, names), true); + + final int size = 27; + final Enumerable randomSubset = setOfRcrdValue.getRandomSubset(size); + final Set<RecordValue> randomsubsetValues = new HashSet<>(size); + + final ValueEnumeration enumerator = randomSubset.elements(); + RecordValue rcd; + while ((rcd = (RecordValue) enumerator.nextElement()) != null) { + assertEquals(names.length, rcd.names.length); + assertEquals(names.length, rcd.values.length); + randomsubsetValues.add(rcd); + // Check element is in the original SetOfFcnsValue. + assertTrue(setOfRcrdValue.member(rcd)); + } + + assertEquals(size, randomsubsetValues.size()); + } + + @Test + public void testRandomSubsetVaryingParameters() { + for (int n = 1; n < 7; n++) { + final UniqueString[] names = getNames(n); + for (int m = 1; m < 5; m++) { + final SetOfRcdsValue setOfRcrdValue = new SetOfRcdsValue(names, getValue(m, names), true); + final Set<RecordValue> randomsubsetValues = new HashSet<>(); + for (int kOutOfN = 0; kOutOfN < setOfRcrdValue.size(); kOutOfN++) { + final Enumerable randomSubset = setOfRcrdValue.getRandomSubset(kOutOfN); + + final ValueEnumeration enumerator = randomSubset.elements(); + RecordValue rcd; + while ((rcd = (RecordValue) enumerator.nextElement()) != null) { + assertEquals(names.length, rcd.names.length); + assertEquals(names.length, rcd.values.length); + randomsubsetValues.add(rcd); + // Check element is in the original SetOfFcnsValue. + assertTrue(setOfRcrdValue.member(rcd)); + } + + assertEquals(kOutOfN, randomsubsetValues.size()); + randomsubsetValues.clear(); + } + } + } + } + + @Test + public void testRandomSubsetAstronomically() { + // RandomSubset(10000, [a1 : {k : k \in 1..50}, a2 : {k : k \in 1..50}, a3 : {k : k \in 1..50}, + // a4 : {k : k \in 1..50}, a5 : {k : k \in 1..50}, a6 : {k : k \in 1..50}, + // a7 : {k : k \in 1..50}, a8 : {k : k \in 1..50}, a9 : {k : k \in 1..50}, + // a10 : {k : k \in 1..50}]) + final UniqueString[] names = getNames(10); + final SetOfRcdsValue setOfRcrdValue = new SetOfRcdsValue(names, getValue(50, names), true); + + final int k = 10000; + final Enumerable randomSubset = setOfRcrdValue.getRandomSubset(k); + final Set<RecordValue> randomsubsetValues = new HashSet<>(k); + + final ValueEnumeration enumerator = randomSubset.elements(); + RecordValue rcd; + while ((rcd = (RecordValue) enumerator.nextElement()) != null) { + assertEquals(names.length, rcd.names.length); + assertEquals(names.length, rcd.values.length); + randomsubsetValues.add(rcd); + // Check element is in the original SetOfFcnsValue. + assertTrue(setOfRcrdValue.member(rcd)); + } + + assertEquals(k, randomsubsetValues.size()); + } +} diff --git a/tlatools/test/tlc2/value/impl/SetOfTuplesValueTest.java b/tlatools/test/tlc2/value/impl/SetOfTuplesValueTest.java new file mode 100644 index 0000000000000000000000000000000000000000..ab56fd81fc6b92bc80db91da0d344944af77c970 --- /dev/null +++ b/tlatools/test/tlc2/value/impl/SetOfTuplesValueTest.java @@ -0,0 +1,50 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.value.impl; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import tlc2.TLCGlobals; +import tlc2.value.impl.IntervalValue; +import tlc2.value.impl.SetOfTuplesValue; + +public class SetOfTuplesValueTest { + + @Test + public void testToStringLazy() { + // Force toString representation to be lazy. + TLCGlobals.enumBound = 1; + + final IntervalValue intVal = new IntervalValue(1, 2); + final SetOfTuplesValue inner = new SetOfTuplesValue(intVal, intVal); + final SetOfTuplesValue outter = new SetOfTuplesValue(inner, inner); + assertTrue(outter.toString().contains("\\X")); + assertEquals("((1..2 \\X 1..2) \\X (1..2 \\X 1..2))", outter.toString()); + } +} diff --git a/tlatools/test/tlc2/value/impl/SubsetEnumeratorTest.java b/tlatools/test/tlc2/value/impl/SubsetEnumeratorTest.java new file mode 100644 index 0000000000000000000000000000000000000000..187ef6256c4d6bdfb26a6b508b0106ad1a45f2b2 --- /dev/null +++ b/tlatools/test/tlc2/value/impl/SubsetEnumeratorTest.java @@ -0,0 +1,172 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + * Ian Morris Nieves - added support for fingerprint stack trace + ******************************************************************************/ +package tlc2.value.impl; + +import static org.junit.Assert.assertEquals; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.function.DoubleConsumer; +import java.util.function.IntConsumer; +import java.util.stream.DoubleStream; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; + +import tlc2.util.FP64; + +@RunWith(Parameterized.class) +public class SubsetEnumeratorTest { + + private static final Value[] getValue(String... strs) { + final List<Value> values = new ArrayList<>(strs.length); + for (int i = 0; i < strs.length; i++) { + values.add(new StringValue(strs[i])); + } + return values.toArray(new Value[values.size()]); + } + + @Parameters + public static List<Enumerable> getEnumerable() { + final List<Enumerable> params = new ArrayList<Enumerable>(); + + // IntervalValue + params.add(new IntervalValue(1, 10)); + + // SetEnumValue + final ValueVec vec = new ValueVec(); + final String input = "ABCDEFGHIJ"; + input.chars().forEach(new IntConsumer() { + @Override + public void accept(final int value) { + vec.addElement(ModelValue.make(String.valueOf(value))); + } + }); + params.add(new SetEnumValue(vec, false)); + + // SetOfTuplesValue + params.add(new SetOfTuplesValue(new IntervalValue(1, 5), new IntervalValue(1, 5))); + params.add(new SetOfTuplesValue(new SetEnumValue(), new SetEnumValue())); // empty + + // UnionValue + params.add(new UnionValue( + new SetEnumValue(new Value[] { new IntervalValue(1, 5), new IntervalValue(5, 11) }, true))); + params.add(new UnionValue(new SetEnumValue())); // empty + + // SetOfFcnsValue + params.add(new SetOfFcnsValue(new IntervalValue(2, 5), + new SetEnumValue(new Value[] { new StringValue("a"), new StringValue("b"), new StringValue("c") }, true))); + params.add(new SetOfFcnsValue(new IntervalValue(3, 5), new SetEnumValue())); // empty range + + // SetOfFcnsValue with SubsetValue as range. + params.add(new SetOfFcnsValue( + new SetEnumValue(new Value[] { ModelValue.make("m1"), ModelValue.make("m2"), ModelValue.make("m3") }, + true), + new SubsetValue(new SetEnumValue( + new Value[] { new StringValue("a"), new StringValue("b"), new StringValue("c") }, true)))); + + // SetOfFcnsValue + final SetEnumValue domain = new SetEnumValue(getValue("A1", "A2", "A3"), true); + final SetEnumValue range = new SetEnumValue(getValue("v1", "v2", "v3"), true); + params.add(new SetOfFcnsValue(domain, range)); + + // SubsetValue + params.add(new SubsetValue(new SetEnumValue( + new Value[] { new StringValue("a"), new StringValue("b"), new StringValue("c") }, true))); + params.add(new SubsetValue(new SetEnumValue())); // empty + + // Adding values to Set<Value> requires fingerprinting. + FP64.Init(); + + return params; + } + + private final Enumerable enumerable; + + public SubsetEnumeratorTest(final Enumerable enumerable) { + this.enumerable = enumerable; + } + + @Test + public void testElementsInt() { + // for various fractions... + DoubleStream.of(0, .1, .2, .3, .4, .55, .625, .775, .8, .9, 1).forEach(new DoubleConsumer() { + @Override + public void accept(double fraction) { + final int k = (int) Math.ceil(enumerable.size() * fraction); + final List<Value> values = enumerable.elements(k).all(); + + // Expected size. + Assert.assertEquals(String.format("Failed for fraction: %s", fraction), k, values.size()); + + // Unique values. + Assert.assertEquals(String.format("Failed for fraction: %s", fraction), values.size(), + new HashSet<Value>(values).size()); + + // Each value is actually a member of enumerable. + for (Value v : values) { + Assert.assertTrue(String.format("Failed for fraction: %s", fraction), enumerable.member(v)); + } + } + }); + } + + @Test + public void testGetRandomSubset() { + DoubleStream.of(0, .1, .2, .3, .4, .55, .625, .775, .8, .9, 1).forEach(new DoubleConsumer() { + @Override + public void accept(final double fraction) { + final int k = (int) Math.ceil(enumerable.size() * fraction); + + final Enumerable enumValue = enumerable.getRandomSubset(k); + + // Expected size. + assertEquals(String.format("Failed for fraction: %s", fraction), k, enumValue.size()); + + final Set<Value> values = new HashSet<>(enumValue.size()); + + // Each value is actually a member of enumerable. + ValueEnumeration elements = enumValue.elements(); + Value v = null; + while ((v = elements.nextElement()) != null) { + Assert.assertTrue(String.format("Failed for fraction: %s", fraction), enumerable.member(v)); + values.add(v); + } + + // Unique values. + Assert.assertEquals(String.format("Failed for fraction: %s", fraction), enumValue.size(), + new HashSet<Value>(values).size()); + + } + }); + } +} diff --git a/tlatools/test/tlc2/value/impl/SubsetValueTest.java b/tlatools/test/tlc2/value/impl/SubsetValueTest.java new file mode 100644 index 0000000000000000000000000000000000000000..6270858465f43a5dd32ee7ac2d4f1c5ff672e0ed --- /dev/null +++ b/tlatools/test/tlc2/value/impl/SubsetValueTest.java @@ -0,0 +1,552 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package tlc2.value.impl; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.TreeSet; + +import org.junit.BeforeClass; +import org.junit.Test; + +import tlc2.util.FP64; +import tlc2.value.RandomEnumerableValues; +import tlc2.value.impl.SubsetValue.CoinTossingSubsetEnumerator; +import tlc2.value.impl.SubsetValue.KElementEnumerator; +import tlc2.value.impl.SubsetValue.SubsetEnumerator; +import tlc2.value.impl.SubsetValue.Unrank; +import util.Assert; + +public class SubsetValueTest { + + private static final Value[] getValue(String... strs) { + final List<Value> values = new ArrayList<>(strs.length); + for (int i = 0; i < strs.length; i++) { + values.add(new StringValue(strs[i])); + } + Collections.shuffle(values); + return values.toArray(new Value[values.size()]); + } + + @BeforeClass + public static void setup() { + // Make test repeatable by setting random seed always to same value. + RandomEnumerableValues.setSeed(15041980L); + // Needed to insert elements into java.util.Set (because of hashcode) later to + // detect duplicates. + FP64.Init(); + } + + private void doTest(final int expectedSize, final EnumerableValue innerSet) { + doTest(expectedSize, innerSet, expectedSize); + } + + private void doTest(final int expectedSize, final EnumerableValue innerSet, + int expectedElements) { + final SubsetValue subsetValue = new SubsetValue(innerSet); + assertEquals(expectedSize, subsetValue.size()); + + final Set<Value> s = new TreeSet<>(new Comparator<Value>() { + @Override + public int compare(Value o1, Value o2) { + // o1.normalize(); + // ((SetEnumValue) o1).elems.sort(true); + // + // o2.normalize(); + // ((SetEnumValue) o2).elems.sort(true); + + return o1.compareTo(o2); + } + }); + + final ValueEnumeration elements = subsetValue.elements(expectedElements); + assertTrue(elements instanceof SubsetEnumerator); + + SetEnumValue next = null; + while ((next = (SetEnumValue) elements.nextElement()) != null) { + final int size = next.elems.size(); + assertTrue(0 <= size && size <= innerSet.size()); + s.add(next); + } + assertEquals(expectedElements, s.size()); + } + + @Test + public void testRandomSubsetE7F1() { + final SetEnumValue innerSet = new SetEnumValue(getValue("a", "b", "c", "d", "e", "f", "g"), true); + doTest(1 << innerSet.size(), innerSet); + } + + @Test + public void testRandomSubsetE7F05() { + final SetEnumValue innerSet = new SetEnumValue(getValue("a", "b", "c", "d", "e", "f", "g"), true); + doTest(1 << innerSet.size(), innerSet, 64); + } + + @Test + public void testRandomSubsetE6F1() { + final SetEnumValue innerSet = new SetEnumValue(getValue("a", "b", "c", "d", "e", "f"), true); + doTest(1 << innerSet.size(), innerSet); + } + + @Test + public void testRandomSubsetE5F01() { + final SetEnumValue innerSet = new SetEnumValue(getValue("a", "b", "c", "d", "e"), true); + doTest(1 << innerSet.size(), innerSet, 4); + } + + @Test + public void testRandomSubsetE5F025() { + final SetEnumValue innerSet = new SetEnumValue(getValue("a", "b", "c", "d", "e"), true); + doTest(1 << innerSet.size(), innerSet, 8); + } + + @Test + public void testRandomSubsetE5F05() { + final SetEnumValue innerSet = new SetEnumValue(getValue("a", "b", "c", "d", "e"), true); + doTest(1 << innerSet.size(), innerSet, 16); + } + + @Test + public void testRandomSubsetE5F075() { + final SetEnumValue innerSet = new SetEnumValue(getValue("a", "b", "c", "d", "e"), true); + doTest(1 << innerSet.size(), innerSet, 24); + } + + @Test + public void testRandomSubsetE5F1() { + final SetEnumValue innerSet = new SetEnumValue(getValue("a", "b", "c", "d", "e"), true); + doTest(1 << innerSet.size(), innerSet); + } + + @Test + public void testRandomSubsetE32F1ENeg6() { + final IntervalValue innerSet = new IntervalValue(1, 32); + final SubsetValue subsetValue = new SubsetValue(innerSet); + + ValueEnumeration elements = subsetValue.elements(2342); + assertTrue(elements instanceof CoinTossingSubsetEnumerator); + + final Set<Value> s = new HashSet<>(); + SetEnumValue next = null; + while ((next = (SetEnumValue) elements.nextElement()) != null) { + final int size = next.elems.size(); + assertTrue(0 <= size && size <= innerSet.size()); + s.add(next); + } + + CoinTossingSubsetEnumerator tossingEnumerator = (CoinTossingSubsetEnumerator) elements; + assertTrue(tossingEnumerator.getNumOfPicks() - 100 <= s.size() && s.size() <= tossingEnumerator.getNumOfPicks()); + } + + @Test + public void testRandomSubsetE17F1ENeg3() { + final IntervalValue innerSet = new IntervalValue(1, 17); + final SubsetValue subsetValue = new SubsetValue(innerSet); + + final ValueEnumeration elements = subsetValue.elements(4223); + assertTrue(elements instanceof SubsetEnumerator); + + final Set<Value> s = new HashSet<>(); + SetEnumValue next = null; + while ((next = (SetEnumValue) elements.nextElement()) != null) { + final int size = next.elems.size(); + assertTrue(0 <= size && size <= innerSet.size()); + s.add(next); + } + + final SubsetEnumerator enumerator = (SubsetEnumerator) elements; + assertEquals(enumerator.k, s.size()); + } + + @Test + public void testRandomSubsetSubset16() { + final SetEnumValue innerSet = new SetEnumValue(getValue("a", "b"), true); + final SubsetValue innerSubset = new SubsetValue(innerSet); + final SubsetValue subsetValue = new SubsetValue(innerSubset); + + final int expectedSize = 1 << innerSubset.size(); + assertEquals(expectedSize, subsetValue.size()); + + // No duplicates + final Set<Value> s = new HashSet<>(expectedSize); + final ValueEnumeration elements = subsetValue.elements(expectedSize); + Value next = null; + while ((next = elements.nextElement()) != null) { + s.add(next); + } + assertEquals(expectedSize, s.size()); + } + + @Test + public void testRandomSubsetSubset256() { + final SetEnumValue innerSet = new SetEnumValue(getValue("a", "b", "c"), true); + final SubsetValue innerSubset = new SubsetValue(innerSet); + final SubsetValue subsetValue = new SubsetValue(innerSubset); + + final int expectedSize = 1 << innerSubset.size(); + assertEquals(expectedSize, subsetValue.size()); + + // No duplicates + final Set<Value> s = new HashSet<>(expectedSize); + final ValueEnumeration elements = subsetValue.elements(expectedSize); + Value next = null; + while ((next = elements.nextElement()) != null) { + s.add(next); + } + assertEquals(expectedSize, s.size()); + } + + @Test + public void testRandomSubsetSubset65536() { + final SetEnumValue innerSet = new SetEnumValue(getValue("a", "b", "c", "d"), true); + final SubsetValue innerSubset = new SubsetValue(innerSet); + final SubsetValue subsetValue = new SubsetValue(innerSubset); + + final int expectedSize = 1 << innerSubset.size(); + assertEquals(expectedSize, subsetValue.size()); + + // No duplicates + final Set<Value> s = new HashSet<>(expectedSize); + final ValueEnumeration elements = subsetValue.elements(expectedSize); + Value next = null; + while ((next = elements.nextElement()) != null) { + s.add(next); + } + assertEquals(expectedSize, s.size()); + } + + @Test + public void testRandomSubsetSubsetNoOverflow() { + final SetEnumValue innerSet = new SetEnumValue(getValue("a", "b", "c", "d", "e"), true); + final SubsetValue innerSubset = new SubsetValue(innerSet); + final SubsetValue subsetValue = new SubsetValue(innerSubset); + + try { + subsetValue.size(); + } catch (Assert.TLCRuntimeException e) { + final Set<Value> s = new HashSet<>(); + + final ValueEnumeration elements = subsetValue.elements(2148); + assertTrue(elements instanceof CoinTossingSubsetEnumerator); + Value next = null; + while ((next = elements.nextElement()) != null) { + s.add(next); + } + // No duplicates + assertEquals(2148, s.size()); + } + } + + @Test + public void testKSubsetEnumerator() { + final SetEnumValue innerSet = new SetEnumValue(getValue("a", "b", "c", "d"), true); + final SubsetValue subset = new SubsetValue(innerSet); + + assertEquals(1, subset.numberOfKElements(0)); + assertEquals(4, subset.numberOfKElements(1)); + assertEquals(6, subset.numberOfKElements(2)); + assertEquals(4, subset.numberOfKElements(3)); + assertEquals(1, subset.numberOfKElements(4)); + + ValueEnumeration enumerator = subset.kElements(0); + assertEquals(new SetEnumValue(), enumerator.nextElement()); + assertNull(enumerator.nextElement()); + + // Need to sort KElementEnumerator to be able to predict the order in which + // elements get returned. + enumerator = ((KElementEnumerator) subset.kElements(1)).sort(); + assertEquals(new SetEnumValue(getValue("a"), false), enumerator.nextElement()); + assertEquals(new SetEnumValue(getValue("b"), false), enumerator.nextElement()); + assertEquals(new SetEnumValue(getValue("c"), false), enumerator.nextElement()); + assertEquals(new SetEnumValue(getValue("d"), false), enumerator.nextElement()); + assertNull(enumerator.nextElement()); + + enumerator = ((KElementEnumerator) subset.kElements(2)).sort(); + assertEquals(new SetEnumValue(getValue("a", "b"), false), enumerator.nextElement()); + assertEquals(new SetEnumValue(getValue("a", "c"), false), enumerator.nextElement()); + assertEquals(new SetEnumValue(getValue("b", "c"), false), enumerator.nextElement()); + assertEquals(new SetEnumValue(getValue("a", "d"), false), enumerator.nextElement()); + assertEquals(new SetEnumValue(getValue("b", "d"), false), enumerator.nextElement()); + assertEquals(new SetEnumValue(getValue("c", "d"), false), enumerator.nextElement()); + assertNull(enumerator.nextElement()); + + enumerator = ((KElementEnumerator) subset.kElements(3)).sort(); + assertEquals(new SetEnumValue(getValue("a", "b", "c"), false), enumerator.nextElement()); + assertEquals(new SetEnumValue(getValue("a", "b", "d"), false), enumerator.nextElement()); + assertEquals(new SetEnumValue(getValue("a", "c", "d"), false), enumerator.nextElement()); + assertEquals(new SetEnumValue(getValue("b", "c", "d"), false), enumerator.nextElement()); + assertNull(enumerator.nextElement()); + + enumerator = ((KElementEnumerator) subset.kElements(4)).sort(); + assertEquals(new SetEnumValue(getValue("a", "b", "c", "d"), false), enumerator.nextElement()); + assertNull(enumerator.nextElement()); + } + + @Test + public void testKSubsetEnumeratorNegative() { + final SetEnumValue innerSet = new SetEnumValue(getValue("a", "b", "c", "d"), true); + final SubsetValue subset = new SubsetValue(innerSet); + try { + subset.kElements(-1); + } catch (IllegalArgumentException e) { + return; + } + fail(); + } + + @Test + public void testKSubsetEnumeratorGTCapacity() { + final SetEnumValue innerSet = new SetEnumValue(getValue("a", "b", "c", "d"), true); + final SubsetValue subset = new SubsetValue(innerSet); + try { + subset.kElements(innerSet.size() + 1); + } catch (IllegalArgumentException e) { + return; + } + fail(); + } + + @Test + public void testNumKSubset() { + final SetEnumValue innerSet = new SetEnumValue(getValue("a", "b", "c", "d", "e"), true); + final SubsetValue subset = new SubsetValue(innerSet); + + assertEquals(1, subset.numberOfKElements(0)); + assertEquals(5, subset.numberOfKElements(1)); + assertEquals(10, subset.numberOfKElements(2)); + assertEquals(10, subset.numberOfKElements(3)); + assertEquals(5, subset.numberOfKElements(4)); + assertEquals(1, subset.numberOfKElements(5)); + } + + @Test + public void testNumKSubset2() { + final SetEnumValue innerSet = new SetEnumValue(getValue("a", "b", "c", "d", "e", "f", "g", "h"), true); + final SubsetValue subset = new SubsetValue(innerSet); + + int sum = 0; + for (int i = 0; i <= innerSet.size(); i++) { + sum += subset.numberOfKElements(i); + } + assertEquals(1 << innerSet.size(), sum); + } + + @Test + public void testNumKSubsetNeg() { + final SetEnumValue innerSet = new SetEnumValue(getValue("a", "b", "c", "d", "e"), true); + final SubsetValue subset = new SubsetValue(innerSet); + + try { + subset.numberOfKElements(-1); + } catch (IllegalArgumentException e) { + return; + } + fail(); + } + + @Test + public void testNumKSubsetKGTN() { + final SetEnumValue innerSet = new SetEnumValue(getValue("a", "b", "c", "d", "e"), true); + final SubsetValue subset = new SubsetValue(innerSet); + + try { + subset.numberOfKElements(innerSet.size() + 1); + } catch (IllegalArgumentException e) { + return; + } + fail(); + } + + @Test + public void testNumKSubsetUpTo62() { + for (int i = 1; i < 62; i++) { + final SubsetValue subset = new SubsetValue(new IntervalValue(1, i)); + long sum = 0L; + for (int j = 0; j <= i; j++) { + sum += subset.numberOfKElements(j); + } + assertEquals(1L << i, sum); + } + } + + @Test + public void testNumKSubsetPreventsOverflow() { + final IntervalValue innerSet = new IntervalValue(1, 63); + final SubsetValue subset = new SubsetValue(innerSet); + for (int i = 0; i <= innerSet.size(); i++) { + try { + subset.numberOfKElements(i); + } catch (IllegalArgumentException e) { + continue; + } + fail(); + } + } + + @Test + public void testUnrankKSubsets() { + final SetEnumValue innerSet = new SetEnumValue(getValue("a", "b", "c", "d", "e"), true); + final SubsetValue subset = new SubsetValue(innerSet); + + final int sizeS = innerSet.size(); + for (int k = 0; k < sizeS; k++) { + final Unrank unranker = subset.getUnrank(k); + // for each k-subset... + final long numElementsInKSubset = subset.numberOfKElements(k); + final Set<Value> kSubset = new HashSet<>(); + for (int i = 0; i < numElementsInKSubset; i++) { + final Value kElementAt = unranker.subsetAt(i); + // check k-Subset is indeed k-Subset + assertEquals(k, kElementAt.size()); + kSubset.add(kElementAt); + } + // check for no duplicates. + assertEquals(numElementsInKSubset, kSubset.size()); + } + } + + @Test + public void testUnrank16viaRank() { + final IntervalValue innerSet = new IntervalValue(1, 16); + final SubsetValue subset = new SubsetValue(innerSet); + + int size = innerSet.size(); + + final long sizeS = 1L << size; // 2^innerSet.size() + final Set<Value> unranked = new HashSet<>((int)sizeS); + + for (int k = 0; k <= size; k++) { + final Unrank unranker = subset.getUnrank(k); + // for each k-subset... + final long numElementsInKSubset = subset.numberOfKElements(k); + for (int i = 0; i < numElementsInKSubset; i++) { + final Value kElementAt = unranker.subsetAt(i); + assertEquals(k, kElementAt.size()); + unranked.add(kElementAt); + } + } + assertEquals(sizeS, unranked.size()); + } + + @Test + public void testRandomSetOfSubsets() { + final IntervalValue innerSet = new IntervalValue(1, 22); + final SubsetValue subset = new SubsetValue(innerSet); + + final int maxLength = 10; + final int k = 23131; + final SetEnumValue setOfSubsets = (SetEnumValue) subset.getRandomSetOfSubsets(k, maxLength); + assertEquals(k, setOfSubsets.size()); + + final ValueEnumeration elements = setOfSubsets.elements(); + Value val = null; + while ((val = elements.nextElement()) != null) { + assertTrue(val.size() <= maxLength); + } + setOfSubsets.normalize(); + assertEquals(k, setOfSubsets.size()); + } + + @Test + public void testRandomSetOfSubsets300() { + final IntervalValue innerSet = new IntervalValue(1, 300); + final SubsetValue subset = new SubsetValue(innerSet); + + final int maxLength = 9; + final int k = 23071; + final SetEnumValue setOfSubsets = (SetEnumValue) subset.getRandomSetOfSubsets(k, maxLength); + assertEquals(k, setOfSubsets.size()); + + final ValueEnumeration elements = setOfSubsets.elements(); + Value val = null; + while ((val = elements.nextElement()) != null) { + assertTrue(val.size() <= maxLength); + } + setOfSubsets.normalize(); + assertEquals(k, setOfSubsets.size()); + } + + @Test + public void testRandomSetOfSubsets400() { + final IntervalValue innerSet = new IntervalValue(1, 400); + final SubsetValue subset = new SubsetValue(innerSet); + + final int maxLength = 9; + final int k = 23077; + final SetEnumValue setOfSubsets = (SetEnumValue) subset.getRandomSetOfSubsets(k, maxLength); + assertEquals(k, setOfSubsets.size()); + + final ValueEnumeration elements = setOfSubsets.elements(); + Value val = null; + while ((val = elements.nextElement()) != null) { + assertTrue(val.size() <= maxLength); + } + setOfSubsets.normalize(); + assertEquals(k, setOfSubsets.size()); + } + + @Test + public void testSubsetNeedsNormalization() { + final IntervalValue inner = new IntervalValue(1, 5); + final SubsetValue subset = new SubsetValue(inner); + + final ValueVec vec = new ValueVec(subset.size()); + for (int i = 0; i <= inner.size(); i++) { + List<Value> kElements = subset.kElements(i).all(); + kElements.forEach(e -> vec.addElement(e)); + } + final Value unnormalized = new SetEnumValue(vec, false); + + final Value normalized = subset.toSetEnum().normalize(); + + assertEquals(normalized, unnormalized); + } + + @Test + public void testSubsetNeedsNormalization2() { + final IntervalValue inner = new IntervalValue(1, 6); + final SubsetValue subset = new SubsetValue(inner); + + final ValueVec vec = new ValueVec(subset.size()); + final ValueEnumeration bElements = subset.elementsNormalized(); + bElements.forEach(e -> vec.addElement(e)); + final Value unnormalized = new SetEnumValue(vec, true); + + final Value normalized = subset.toSetEnum().normalize(); + + assertEquals(normalized, unnormalized); + } +} diff --git a/tlatools/test/util/ExecutionStatisticsCollectorTest.java b/tlatools/test/util/ExecutionStatisticsCollectorTest.java new file mode 100644 index 0000000000000000000000000000000000000000..2c991bb67183820c6266be2b1605bd0287f57b13 --- /dev/null +++ b/tlatools/test/util/ExecutionStatisticsCollectorTest.java @@ -0,0 +1,171 @@ +/******************************************************************************* + * Copyright (c) 2019 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package util; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; + +import org.junit.Test; + +public class ExecutionStatisticsCollectorTest { + + // Opt-out + + @Test + public void testOptOutNoFile() throws IOException { + final String tmpdir = System.getProperty("java.io.tmpdir"); + final File tempFile = new File(tmpdir + File.separator + "esc" + System.currentTimeMillis() + ".txt"); + tempFile.deleteOnExit(); + + final ExecutionStatisticsCollector esc = new ExecutionStatisticsCollector(true, tempFile.getAbsolutePath()); + + final String identifierA = esc.getIdentifier(); + assertNotNull(identifierA); + assertNotNull(esc.getIdentifier()); + assertEquals(identifierA, esc.getIdentifier()); + } + + @Test + public void testOptOutUnreadableFile() throws IOException { + File tempFile = File.createTempFile("esc", "txt"); + tempFile.setReadable(false); + tempFile.deleteOnExit(); + + final ExecutionStatisticsCollector esc = new ExecutionStatisticsCollector(true, tempFile.getAbsolutePath()); + + assertNull(esc.getIdentifier()); + } + + @Test + public void testOptOutEmptyFile() throws IOException { + File tempFile = File.createTempFile("esc", "txt"); + tempFile.deleteOnExit(); + + final ExecutionStatisticsCollector esc = new ExecutionStatisticsCollector(true, tempFile.getAbsolutePath()); + + assertNull(esc.getIdentifier()); + } + + @Test + public void testOptOutNoESCFile() throws IOException { + File tempFile = File.createTempFile("esc", "txt"); + Files.write(tempFile.toPath(), (" " + ExecutionStatisticsCollector.Selection.NO_ESC.toString() + " ").getBytes()); + tempFile.deleteOnExit(); + + final ExecutionStatisticsCollector esc = new ExecutionStatisticsCollector(true, tempFile.getAbsolutePath()); + + assertNull(esc.getIdentifier()); + } + + @Test + public void testOptOutRandomIdFile() throws IOException { + File tempFile = File.createTempFile("esc", "txt"); + Files.write(tempFile.toPath(), (" " + ExecutionStatisticsCollector.Selection.RANDOM_IDENTIFIER.toString() + " ").getBytes()); + tempFile.deleteOnExit(); + + final ExecutionStatisticsCollector esc = new ExecutionStatisticsCollector(true, tempFile.getAbsolutePath()); + + final String identifierA = esc.getIdentifier(); + assertNotNull(identifierA); + final String identifierB = esc.getIdentifier(); + assertNotNull(identifierB); + assertNotEquals(identifierA, identifierB); + } + + @Test + public void testOptOutUserDefinedIdFile() throws IOException { + File tempFile = File.createTempFile("esc", "txt"); + Files.write(tempFile.toPath(), " 123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ ".getBytes()); + tempFile.deleteOnExit(); + + final ExecutionStatisticsCollector esc = new ExecutionStatisticsCollector(true, tempFile.getAbsolutePath()); + + assertEquals("123456789ABCDEFGHIJKLMNOPQRSTUVW", esc.getIdentifier()); + } + + // Opt-In + + @Test + public void testNoFile() { + assertNull(new ExecutionStatisticsCollector("/path/does/not/exist").getIdentifier()); + } + + @Test + public void testUnreadableFile() throws IOException { + File tempFile = File.createTempFile("esc", "txt"); + tempFile.setReadable(false); + tempFile.deleteOnExit(); + + assertNull(new ExecutionStatisticsCollector(tempFile.getAbsolutePath()).getIdentifier()); + } + + @Test + public void testEmptyFile() throws IOException { + File tempFile = File.createTempFile("esc", "txt"); + tempFile.deleteOnExit(); + + assertNull(new ExecutionStatisticsCollector(tempFile.getAbsolutePath()).getIdentifier()); + } + + @Test + public void testNoESCFile() throws IOException { + File tempFile = File.createTempFile("esc", "txt"); + Files.write(tempFile.toPath(), (" " + ExecutionStatisticsCollector.Selection.NO_ESC.toString() + " ").getBytes()); + tempFile.deleteOnExit(); + + assertNull(new ExecutionStatisticsCollector(tempFile.getAbsolutePath()).getIdentifier()); + } + + @Test + public void testRandomIdFile() throws IOException { + File tempFile = File.createTempFile("esc", "txt"); + Files.write(tempFile.toPath(), (" " + ExecutionStatisticsCollector.Selection.RANDOM_IDENTIFIER.toString() + " ").getBytes()); + tempFile.deleteOnExit(); + + ExecutionStatisticsCollector esc = new ExecutionStatisticsCollector(tempFile.getAbsolutePath()); + + final String identifierA = esc.getIdentifier(); + assertNotNull(identifierA); + final String identifierB = esc.getIdentifier(); + assertNotNull(identifierB); + assertNotEquals(identifierA, identifierB); + } + + @Test + public void testUserDefinedIdFile() throws IOException { + File tempFile = File.createTempFile("esc", "txt"); + Files.write(tempFile.toPath(), " 123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ ".getBytes()); + tempFile.deleteOnExit(); + + assertEquals("123456789ABCDEFGHIJKLMNOPQRSTUVW", new ExecutionStatisticsCollector(tempFile.getAbsolutePath()).getIdentifier()); + } +} diff --git a/tlatools/test/util/IsolatedTestCaseRunner.java b/tlatools/test/util/IsolatedTestCaseRunner.java new file mode 100644 index 0000000000000000000000000000000000000000..05c44d4a1e4e85006c5a00146da232f12157fefe --- /dev/null +++ b/tlatools/test/util/IsolatedTestCaseRunner.java @@ -0,0 +1,107 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package util; + +import java.lang.reflect.InvocationTargetException; +import java.net.URLClassLoader; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import org.junit.runner.Description; +import org.junit.runner.Runner; +import org.junit.runner.notification.RunNotifier; +import org.junit.runners.JUnit4; +import org.junit.runners.model.InitializationError; + +public class IsolatedTestCaseRunner extends Runner { + + private final JUnit4 delegate; + + public IsolatedTestCaseRunner(final Class<?> testFileClass) + throws InitializationError, ClassNotFoundException, InstantiationException, IllegalAccessException, + IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException { + + // Since IsolatedTestCaseRunner runs several isolated tests in a single VM, it + // is good practice to clean resources before each new test. + System.gc(); + + ClassLoader classLoader = IsolatedTestCaseRunner.class.getClassLoader(); + if (classLoader instanceof URLClassLoader) { + // When run within e.g. Eclipse, the classloader might not be of instance + // URLClassLoader. In this case, just use the provided class loader which won't + // isolate tests. A set of tests can thus only be run in a single VM from ant + // or maven which pass a URLClassLoader. + classLoader = new IsolatedTestCaseClassLoader((URLClassLoader) classLoader); + } + delegate = new JUnit4(classLoader.loadClass(testFileClass.getName())); + } + + @Override + public Description getDescription() { + return delegate.getDescription(); + } + + @Override + public void run(RunNotifier notifier) { + delegate.run(notifier); + } + + private class IsolatedTestCaseClassLoader extends URLClassLoader { + + private final Map<String, Class<?>> cache = new HashMap<>(); + private final Set<String> packages = new HashSet<>(); + + public IsolatedTestCaseClassLoader(URLClassLoader classLoader) { + super(classLoader.getURLs()); + + // All of TLC's java packages. + packages.add("tla2sany"); + packages.add("pcal"); + packages.add("util"); + packages.add("tla2tex"); + packages.add("tlc2"); + } + + @Override + public Class<?> loadClass(String name) throws ClassNotFoundException { + if (cache.containsKey(name)) { + // Cache to not load classes twice for a single test case which results in a + // LinkageError. + return cache.get(name); + } + for (final String pkg : packages) { + if (name.startsWith(pkg)) { + final Class<?> findClass = findClass(name); + cache.put(name, findClass); + return findClass; + } + } + return super.loadClass(name); + } + } +} diff --git a/tlatools/test/util/SimpleFilenameToStreamTest.java b/tlatools/test/util/SimpleFilenameToStreamTest.java index 3082129fc822977b8ea928694efa82b0785b3164..18322e3bfaec3c95c7695d3a874f6c185c516d83 100644 --- a/tlatools/test/util/SimpleFilenameToStreamTest.java +++ b/tlatools/test/util/SimpleFilenameToStreamTest.java @@ -1,14 +1,18 @@ package util; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + import java.io.File; -import junit.framework.TestCase; +import org.junit.Test; -public class SimpleFilenameToStreamTest extends TestCase { +public class SimpleFilenameToStreamTest { /** * Try to load a standard module */ + @Test public void testResolveStandardModule() { final SimpleFilenameToStream sfts = new SimpleFilenameToStream(); final File file = sfts.resolve("TLC.tla", true); @@ -16,4 +20,22 @@ public class SimpleFilenameToStreamTest extends TestCase { assertNotNull(file); assertTrue(file.getAbsolutePath() + " does not exist!", file.exists()); } + + /** + * Test whether the fix for #424 still works + */ + @Test + public void testWindowsTLAFileCreation() { + if (System.getProperty("os.name").toLowerCase().indexOf("win") > -1) { + final String driveLetter = "X:"; + final String parentDirectory = driveLetter + "\\Develop\\myspecs\\DecentSpec\\"; + final String child = parentDirectory + "Fromage.tla"; + final FilenameToStream.TLAFile file = new FilenameToStream.TLAFile(parentDirectory, child, null); + final int driveLetterCount = file.getAbsolutePath().split(driveLetter).length - 1; + + assertTrue("There should be 1 drive letter in the child's absolute path, but there are " + driveLetterCount, + (1 == driveLetterCount)); + } + } + } diff --git a/tlatools/test/util/StringHelperTest.java b/tlatools/test/util/StringHelperTest.java new file mode 100644 index 0000000000000000000000000000000000000000..f5dc5ff71dc2ab365d573fa674f78b30cb44c135 --- /dev/null +++ b/tlatools/test/util/StringHelperTest.java @@ -0,0 +1,205 @@ +/******************************************************************************* + * Copyright (c) 2015 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ + +package util; + +import static org.junit.Assert.assertArrayEquals; + +import junit.framework.TestCase; +import util.StringHelper; + +public class StringHelperTest extends TestCase { + + public void testGetWords() { + String[] words = StringHelper.getWords("Abc def ghi."); + assertArrayEquals(new String[]{"Abc", "def", "ghi."}, words); + + words = StringHelper.getWords("Abc def ghi. "); + assertArrayEquals(new String[]{"Abc", "def", "ghi."}, words); + } + public void testGetWordsLeadingSpace() { + String[] words = StringHelper.getWords(" Abc def ghi."); + assertArrayEquals(new String[]{"Abc", "def", "ghi."}, words); + } +//TODO What is the intended behavior here? +// public void testGetWordsEmpty() { +// String[] words = StringHelper.getWords(""); +// assertArrayEquals(new String[0], words); +// } + public void testGetWordsNull() { + try { + StringHelper.getWords(null); + } catch (NullPointerException e) { + return; + } + fail("null should not be accepted!"); + } + + public void testCopyString0() { + String copyString = StringHelper.copyString("abc", 0); + assertEquals("", copyString); + } + public void testCopyStringPos1() { + String copyString = StringHelper.copyString("abc", 1); + assertEquals("abc", copyString); + } + public void testCopyStringPos5() { + String copyString = StringHelper.copyString("abc", 5); + assertEquals("abcabcabcabcabc", copyString); + } + public void testCopyStringNeg1() { + String copyString = StringHelper.copyString("abc", -1); + assertEquals("", copyString); + } + public void testCopyStringNeg5() { + String copyString = StringHelper.copyString("abc", -5); + assertEquals("", copyString); + } +//TODO What is the intended behavior of passing a null string? +// public void testCopyStringNullPos() { +// try { +// StringHelper.copyString(null, 1); +// } catch (NullPointerException e) { +// return; +// } +// fail("null should not be accepted!"); +// } +//TODO What is the intended behavior of passing a null string? +// public void testCopyStringNullNeg() { +// try { +// StringHelper.copyString(null, -1); +// } catch (NullPointerException e) { +// return; +// } +// fail("null should not be accepted!"); +// } + + public void testOnlySpaces() { + assertTrue(StringHelper.onlySpaces(" ")); + assertTrue(StringHelper.onlySpaces(" ")); + assertFalse(StringHelper.onlySpaces(" a ")); + assertFalse(StringHelper.onlySpaces(" a")); + assertFalse(StringHelper.onlySpaces("a ")); + assertFalse(StringHelper.onlySpaces("a")); + } + public void testOnlySpacesNull() { + try { + StringHelper.onlySpaces(null); + } catch (NullPointerException e) { + return; + } + fail("null should not be accepted!"); + } +//TODO What is the intended behavior for the empty string? +// public void testOnlySpacesEmptyString() { +// assertFalse(StringHelper.onlySpaces("")); +// } + + public void testTrimFront() { + assertEquals("", StringHelper.trimFront(" ")); + assertEquals("a", StringHelper.trimFront(" a")); + assertEquals("a ", StringHelper.trimFront(" a ")); + assertEquals("aaa", StringHelper.trimFront(" aaa")); + assertEquals("aa", StringHelper.trimFront(" aa")); + assertEquals("a ", StringHelper.trimFront(" a ")); + assertEquals("aa a ", StringHelper.trimFront(" aa a ")); + } + public void testTrimFrontNull() { + try { + StringHelper.trimFront(null); + } catch (NullPointerException e) { + return; + } + fail("null should not be accepted!"); + } + public void testTrimFrontWhitespaces() { + assertEquals("Abc def ghi.", StringHelper.trimFront("Abc def ghi.")); + } + + public void testTrimEnd() { + assertEquals("", StringHelper.trimEnd(" ")); + assertEquals("a", StringHelper.trimEnd("a ")); + assertEquals(" a", StringHelper.trimEnd(" a ")); + assertEquals("aaa", StringHelper.trimEnd("aaa ")); + assertEquals("aa", StringHelper.trimEnd("aa ")); + assertEquals(" a", StringHelper.trimEnd(" a ")); + assertEquals(" a aa", StringHelper.trimEnd(" a aa ")); + } + public void testTrimEndNull() { + try { + StringHelper.trimEnd(null); + } catch (NullPointerException e) { + return; + } + fail("null should not be accepted!"); + } + public void testTrimEndWhitespaces() { + assertEquals("Abc def ghi.", StringHelper.trimEnd("Abc def ghi.")); + } + + public void testLeadingSpace() { + assertEquals(1, StringHelper.leadingSpaces(" ")); + assertEquals(2, StringHelper.leadingSpaces(" ")); + assertEquals(1, StringHelper.leadingSpaces(" a ")); + assertEquals(2, StringHelper.leadingSpaces(" a a ")); + assertEquals(0, StringHelper.leadingSpaces("a")); + } + public void testLeadingSpacesNull() { + try { + StringHelper.leadingSpaces(null); + } catch (NullPointerException e) { + return; + } + fail("null should not be accepted!"); + } + + public void testIsIdentifier() { + assertFalse(StringHelper.isIdentifier("123")); + assertFalse(StringHelper.isIdentifier(" 123")); + assertFalse(StringHelper.isIdentifier("123 ")); + assertFalse(StringHelper.isIdentifier(" 123 ")); + assertTrue(StringHelper.isIdentifier("1A1")); + assertTrue(StringHelper.isIdentifier("_1")); + assertTrue(StringHelper.isIdentifier("1_")); + assertTrue(StringHelper.isIdentifier("_")); + } + public void testIsIdentifierNull() { + try { + StringHelper.isIdentifier(null); + } catch (NullPointerException e) { + return; + } + fail("null should not be accepted!"); + } +//TODO What is the intended behavior for the empty string? +// public void testIsIdentifierMetaChars() { +// assertTrue(StringHelper.isIdentifier("\\")); +// } +//TODO What is the intended behavior for the empty string? +// public void testIsIdentifierEmptyString() { +// assertTrue(StringHelper.isIdentifier("")); +// } +} diff --git a/tlatools/test/util/TLCRuntimeTest.java b/tlatools/test/util/TLCRuntimeTest.java new file mode 100644 index 0000000000000000000000000000000000000000..4853b42a8d12cd5f6e6f65ccc78b7c32bd157167 --- /dev/null +++ b/tlatools/test/util/TLCRuntimeTest.java @@ -0,0 +1,41 @@ +/******************************************************************************* + * Copyright (c) 2019 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package util; + +import static org.junit.Assert.*; + +import org.junit.Test; + +public class TLCRuntimeTest { + + @Test + public void testIsThroughputOptimized() { + // customBuild.xml executes the tests with UseParallelGC set. This should thus + // pass. It will pass without UseParallelGC set on a pre Java 9 VM where + // parallel GC used to be the default. + assertTrue(TLCRuntime.getInstance().isThroughputOptimizedGC()); + } +} diff --git a/tlatools/test/util/TestPrintStream.java b/tlatools/test/util/TestPrintStream.java new file mode 100644 index 0000000000000000000000000000000000000000..ad12a39b826eba7f167eed3e091692ab88490f98 --- /dev/null +++ b/tlatools/test/util/TestPrintStream.java @@ -0,0 +1,79 @@ +/******************************************************************************* + * Copyright (c) 2017 Microsoft Research. All rights reserved. + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributors: + * Markus Alexander Kuppe - initial API and implementation + ******************************************************************************/ +package util; + +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import java.io.PipedOutputStream; +import java.io.PrintStream; +import java.util.ArrayList; +import java.util.List; + +public class TestPrintStream extends PrintStream { + + private final StringBuffer buf = new StringBuffer(); + private final List<String> strings = new ArrayList<String>(); + + public TestPrintStream() { + super(new PipedOutputStream()); + } + + /* (non-Javadoc) + * @see java.io.PrintStream#println(java.lang.String) + */ + public void println(String x) { + strings.add(x); + buf.append(x + "\n"); + System.out.println(x); + super.println(x); + } + + public void assertEmpty() { + assertTrue(this.strings.isEmpty()); + } + + public void assertContains(final String seq) { + assertTrue(buf.toString().contains(seq)); + } + + public void assertSubstring(String substring) { + for (String string : strings) { + if (string.contains(substring)) { + return; + } + } + fail("Substring not found"); + } + + public void assertNoSubstring(String substring) { + for (String string : strings) { + if (string.contains(substring)) { + fail("Substring not found"); + } + } + } +} \ No newline at end of file